@rulvar/core 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +486 -531
  2. package/dist/index.js +305 -325
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
14
14
  //#region src/l0/errors.ts
15
15
  /**
16
16
  * Base class for all engine-raised errors. "Retryable" means the engine's
17
- * own retry machinery (RetryPolicy under the journal, docs/04) MAY retry;
17
+ * own retry machinery (RetryPolicy under the journal) MAY retry;
18
18
  * it never means a provider SDK autoretry, which is disabled.
19
19
  */
20
20
  var RulvarError = class extends Error {
@@ -79,8 +79,8 @@ var ScriptRejected = class extends RulvarError {
79
79
  };
80
80
  /**
81
81
  * Refusal to open a journal whose hashVersion falls outside the engine's
82
- * support window (docs/03, section "hashVersion"; producers ship in M2).
83
- * The registry code is 'journal_compat'; the docs/03 sub-codes live on
82
+ * support window (producers ship in M2).
83
+ * The registry code is 'journal_compat'; the sub-codes live on
84
84
  * `subCode` and in `data`.
85
85
  */
86
86
  var JournalCompatibilityError = class extends RulvarError {
@@ -118,8 +118,7 @@ var JournalCompatibilityError = class extends RulvarError {
118
118
  };
119
119
  /**
120
120
  * A resolution attempt against an already-closed suspension, rejected under
121
- * the first-closing-wins fold; appends no entry (docs/03, section
122
- * "Suspension and resolutions"; producers ship in M2).
121
+ * the first-closing-wins fold; appends no entry (producers ship in M2).
123
122
  */
124
123
  var InvalidResolutionError = class extends RulvarError {
125
124
  code = "invalid_resolution";
@@ -132,7 +131,7 @@ var InvalidResolutionError = class extends RulvarError {
132
131
  };
133
132
  /**
134
133
  * A breach of the total per-run append order: an unfenced concurrent writer
135
- * or a store violating contract A2 (docs/03, section "Storage SPI").
134
+ * or a store violating contract A2 (https://docs.rulvar.com/guide/stores).
136
135
  */
137
136
  var JournalOrderViolation = class extends RulvarError {
138
137
  code = "journal_order_violation";
@@ -143,7 +142,7 @@ var JournalOrderViolation = class extends RulvarError {
143
142
  });
144
143
  }
145
144
  };
146
- /** PlanRunner plan-invariant rejection (docs/07; producers ship in M7). */
145
+ /** PlanRunner plan-invariant rejection (producers ship in M7). */
147
146
  var PlanInvariantError = class extends RulvarError {
148
147
  code = "plan_invariant";
149
148
  constructor(message, opts) {
@@ -155,7 +154,7 @@ var PlanInvariantError = class extends RulvarError {
155
154
  };
156
155
  /**
157
156
  * Raised at resume when the refolded plan state disagrees with the
158
- * journaled planHash chain (docs/07; producers ship in M7).
157
+ * journaled planHash chain (producers ship in M7).
159
158
  */
160
159
  var ReplayPlanHashMismatch = class extends RulvarError {
161
160
  code = "replay_plan_hash_mismatch";
@@ -168,8 +167,7 @@ var ReplayPlanHashMismatch = class extends RulvarError {
168
167
  };
169
168
  /**
170
169
  * Invalid orchestrator cap and finalize-reserve configuration, thrown
171
- * before the first LLM call (docs/06, section "Three-layer budget", DEF-7;
172
- * producers ship in M6/M7).
170
+ * before the first LLM call (DEF-7; producers ship in M6/M7).
173
171
  */
174
172
  var OrchestratorCapConfigError = class extends RulvarError {
175
173
  code = "orchestrator_cap_config";
@@ -196,8 +194,7 @@ var JournalMissError = class extends RulvarError {
196
194
  /**
197
195
  * The run budget ceiling blocked further work. The budget guard denial is
198
196
  * a decision entry; ctx primitives throw this as AgentError kind 'budget';
199
- * the run reports outcome 'exhausted', overriding 'error' (docs/06, section
200
- * "Three-layer budget").
197
+ * the run reports outcome 'exhausted', overriding 'error'.
201
198
  */
202
199
  var BudgetExhaustedError = class extends RulvarError {
203
200
  code = "budget_exhausted";
@@ -210,13 +207,12 @@ var BudgetExhaustedError = class extends RulvarError {
210
207
  };
211
208
  /**
212
209
  * A structural admission rejection (maxDepth, maxChildrenPerNode,
213
- * maxTotalSpawns) from the AdmissionController (docs/07, section
214
- * "AdmissionController"; M6-T06). The rejection verdict is embedded in
210
+ * maxTotalSpawns) from the AdmissionController (M6-T06). The rejection verdict is embedded in
215
211
  * the carrying spawn-admission decision entry and replays identically;
216
212
  * the error surfaces the embedded AdmitRejectReason in `data` to the
217
213
  * caller (a typed tool error for orchestrators) and MUST NOT tear down
218
214
  * the run. Budget-code rejections throw BudgetExhaustedError instead,
219
- * keeping the docs/06 5.7 exhaustion semantics.
215
+ * keeping the budget exhaustion semantics (https://docs.rulvar.com/guide/budgets).
220
216
  */
221
217
  var AdmissionRejectedError = class extends RulvarError {
222
218
  code = "admission_rejected";
@@ -228,8 +224,8 @@ var AdmissionRejectedError = class extends RulvarError {
228
224
  }
229
225
  };
230
226
  /**
231
- * A WorkerSandboxRunner resource-limit breach (docs/06, section 8.2;
232
- * M6-T02): crossing timeoutMs or memoryMb terminates the worker and the
227
+ * A WorkerSandboxRunner resource-limit breach (M6-T02): crossing
228
+ * timeoutMs or memoryMb terminates the worker and the
233
229
  * run completes with outcome 'error' carrying this error's WireError
234
230
  * projection; `data` records { reason: 'timeout' | 'memory', limit }.
235
231
  * The class itself is never journaled as an entry of its own.
@@ -245,8 +241,7 @@ var SandboxError = class extends RulvarError {
245
241
  };
246
242
  /**
247
243
  * acquire() on a currently held lease. Retryable by contract: retry after
248
- * the lease ttl elapses or the holder releases (docs/03, section
249
- * "Storage SPI").
244
+ * the lease ttl elapses or the holder releases.
250
245
  */
251
246
  var LeaseHeldError = class extends RulvarError {
252
247
  code = "lease_held";
@@ -260,8 +255,7 @@ var LeaseHeldError = class extends RulvarError {
260
255
  /**
261
256
  * commit() on a ModelKnowledgeStore against a snapshot version that is
262
257
  * no longer current. Retryable by contract: re-read current(), rebase
263
- * the ops, commit again, mirroring the lease fencing discipline
264
- * (docs/05, section "Commit discipline").
258
+ * the ops, commit again, mirroring the lease fencing discipline.
265
259
  */
266
260
  var KnowledgeCasError = class extends RulvarError {
267
261
  code = "knowledge_cas";
@@ -274,8 +268,8 @@ var KnowledgeCasError = class extends RulvarError {
274
268
  };
275
269
  /**
276
270
  * Projects an AgentError to its WireError form: code 'agent', with kind,
277
- * retryAfterMs, and issues carried in data (docs/02, section "Error
278
- * taxonomy"). Issue paths are flattened to JSON-safe segments.
271
+ * retryAfterMs, and issues carried in data. Issue paths are flattened to
272
+ * JSON-safe segments.
279
273
  */
280
274
  function agentErrorToWire(error, message) {
281
275
  const data = { kind: error.kind };
@@ -319,8 +313,7 @@ function agentErrorFromWire(wire) {
319
313
  //#region src/l0/serialization.ts
320
314
  /**
321
315
  * The L0 serialization hook and the default secret-masking policy
322
- * (M8-T04; docs/03, section "Serialization hook"; docs/09, section
323
- * "Redaction and sensitive data"; OQ-20/OQ-22 interim rules executed).
316
+ * (M8-T04; OQ-20/OQ-22 interim rules executed).
324
317
  *
325
318
  * The hook is the single policy point between the engine and
326
319
  * persistence: redact/encrypt at the append and put boundaries,
@@ -336,13 +329,13 @@ function agentErrorFromWire(wire) {
336
329
  * emitted WorkflowEvent passes it (opt out per engine via
337
330
  * `redaction: { maskEvents: false }`); the OTel exporter applies it to
338
331
  * string attributes. It masks strings that look like credentials; it is
339
- * deliberately conservative (docs/09, section 8: the pattern set is
332
+ * deliberately conservative (the pattern set is
340
333
  * tuned on dogfood payloads, OQ-22 stays open for that).
341
334
  */
342
335
  /**
343
336
  * The kernel orders and matches on these BEFORE values are consulted;
344
337
  * a hook that rewrites them would corrupt replay silently, so drift is
345
- * a loud ConfigError at the boundary (docs/03, 12.8).
338
+ * a loud ConfigError at the boundary.
346
339
  */
347
340
  const PINNED_FIELDS = [
348
341
  "hashVersion",
@@ -543,8 +536,7 @@ function monotonicUlidFactory(options) {
543
536
  //#region src/l0/messages.ts
544
537
  /**
545
538
  * Returns a per-engine minter of CanonicalId values. Monotonic within the
546
- * factory instance; never a module-level singleton (docs/02, section
547
- * "Dependency rules": no module state).
539
+ * factory instance; never a module-level singleton (no module state).
548
540
  */
549
541
  function createCanonicalIdMinter(options) {
550
542
  return monotonicUlidFactory(options);
@@ -1442,8 +1434,7 @@ var Validator = class {
1442
1434
  /**
1443
1435
  * RFC 8785 (JSON Canonicalization Scheme) serializer.
1444
1436
  *
1445
- * Backs content-key derivation and schema hashing (docs/03, sections
1446
- * "Identity model" and "schemaHash and toolsetHash derivation"):
1437
+ * Backs content-key derivation and schema hashing:
1447
1438
  * lexicographically sorted object members (UTF-16 code units), minimal
1448
1439
  * escaping, no insignificant whitespace, ECMAScript number formatting.
1449
1440
  *
@@ -1488,8 +1479,8 @@ function jcsSerialize(value) {
1488
1479
  * SchemaSpec, Out<S> inference, JSON Schema projection, canonical schema
1489
1480
  * derivation, and the schemaHash/toolsetHash functions (M1-T03).
1490
1481
  *
1491
- * Owning specs: docs/08-tools-permissions-spec.md, section "SchemaSpec";
1492
- * docs/03-journal-spec.md, section "schemaHash and toolsetHash derivation".
1482
+ * Public contracts: https://docs.rulvar.com/guide/tools (SchemaSpec) and
1483
+ * https://docs.rulvar.com/guide/journal (hash derivation).
1493
1484
  */
1494
1485
  /**
1495
1486
  * Form-1 guard: the value implements the Standard Schema interface. Some
@@ -1504,8 +1495,7 @@ function isSchemaPairSpec(spec) {
1504
1495
  return typeof spec === "object" && spec !== null && !("~standard" in spec) && "jsonSchema" in spec && typeof spec.validate === "function";
1505
1496
  }
1506
1497
  /**
1507
- * Derives the JSON Schema of a SchemaSpec (docs/08, section "JSON Schema
1508
- * derivation and acceptance rules"). Form 1 projects via the
1498
+ * Derives the JSON Schema of a SchemaSpec. Form 1 projects via the
1509
1499
  * StandardJSONSchemaV1 input() converter, target draft 2020-12 with
1510
1500
  * draft-07 fallback; a library without the projection is a typed
1511
1501
  * ConfigError at definition time, never at first call. Transforming
@@ -1532,8 +1522,7 @@ function projectToJsonSchema(spec) {
1532
1522
  }
1533
1523
  /**
1534
1524
  * Annotation-only keywords stripped by canonicalization; `format` is
1535
- * retained because it is validation-relevant in the vendored validator
1536
- * (docs/03, section "schemaHash and toolsetHash derivation").
1525
+ * retained because it is validation-relevant in the vendored validator.
1537
1526
  */
1538
1527
  const ANNOTATION_KEYWORDS = /* @__PURE__ */ new Set([
1539
1528
  "title",
@@ -1577,8 +1566,7 @@ const SUBSCHEMA_ARRAY_KEYWORDS = /* @__PURE__ */ new Set([
1577
1566
  /**
1578
1567
  * Keywords that are pure reference infrastructure: dead weight once every
1579
1568
  * local $ref has been inlined, removed from the canonical form so that a
1580
- * $defs rename or an unused definition never shifts a content key
1581
- * (docs/03, section "schemaHash and toolsetHash derivation").
1569
+ * $defs rename or an unused definition never shifts a content key.
1582
1570
  */
1583
1571
  const REF_INFRASTRUCTURE_KEYWORDS = /* @__PURE__ */ new Set([
1584
1572
  "$defs",
@@ -1652,8 +1640,7 @@ function canonicalizeNode(node, root, refStack) {
1652
1640
  return out;
1653
1641
  }
1654
1642
  /**
1655
- * Canonical schema derivation (docs/03, section "schemaHash and
1656
- * toolsetHash derivation"): local fragment-only $ref inlined (recursion is
1643
+ * Canonical schema derivation: local fragment-only $ref inlined (recursion is
1657
1644
  * a ConfigError), remote and dynamic references forbidden, annotation
1658
1645
  * keywords stripped (format retained), reference infrastructure ($defs,
1659
1646
  * definitions, $anchor) removed once inlined. The result feeds JCS
@@ -1667,8 +1654,7 @@ function sha256Hex$3(text) {
1667
1654
  }
1668
1655
  /**
1669
1656
  * The schemaHash used when no structured-output schema is declared: the
1670
- * hash of the canonical `true` schema (docs/03, section "schemaHash and
1671
- * toolsetHash derivation").
1657
+ * hash of the canonical `true` schema.
1672
1658
  */
1673
1659
  const EMPTY_SCHEMA_HASH = sha256Hex$3("true");
1674
1660
  /** The toolsetHash of an empty toolset: the hash of the canonical empty contract array. */
@@ -1691,8 +1677,7 @@ function schemaHashOfSpec(spec) {
1691
1677
  * contract tuples (name, description, canonical parameters, version)
1692
1678
  * sorted by name. Tool description IS part of the contract; schema
1693
1679
  * annotations inside parameters are not. An absent version participates as
1694
- * absent (docs/03, section "schemaHash and toolsetHash derivation";
1695
- * docs/08, section "toolsetHash contract").
1680
+ * absent.
1696
1681
  */
1697
1682
  function toolsetHash(contracts) {
1698
1683
  return sha256Hex$3(jcsSerialize([...contracts].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0).map((contract) => {
@@ -1714,7 +1699,7 @@ function pointerToPath(instanceLocation) {
1714
1699
  });
1715
1700
  }
1716
1701
  /**
1717
- * Runtime validation per form (docs/08, section "Runtime validation"):
1702
+ * Runtime validation per form:
1718
1703
  * form 1 via the Standard Schema's own validate, form 2 via the pair's
1719
1704
  * type guard, form 3 via the vendored draft 2020-12 validator. The same
1720
1705
  * machinery backs the structured-output tiers of the Agent Runtime.
@@ -1767,8 +1752,7 @@ const CURRENT_HASH_VERSION = 2;
1767
1752
  /**
1768
1753
  * Round-1 normalization: hashVersion is taken from `hashVersion`, else
1769
1754
  * from the legacy `v` field, else 1. Stores are never rewritten;
1770
- * normalization happens at read (docs/03, section "The single versioning
1771
- * mechanism").
1755
+ * normalization happens at read.
1772
1756
  */
1773
1757
  function normalizeEntry(raw) {
1774
1758
  const record = raw;
@@ -1783,15 +1767,15 @@ function normalizeEntry(raw) {
1783
1767
  //#endregion
1784
1768
  //#region src/knowledge/decay.ts
1785
1769
  /**
1786
- * Grounding and decay (M11-T03; docs/05, section "Grounding and
1787
- * decay"). The decay owner: the asymmetric TTL table, the expiry
1770
+ * Grounding and decay (M11-T03). The decay owner: the asymmetric
1771
+ * TTL table, the expiry
1788
1772
  * filter the read path applies at every pin AND repin (M10-T03), the
1789
1773
  * re-measurement queue (a STATUS FILTER, not infrastructure), and the
1790
1774
  * archive-never-delete maintenance helpers (historical runs keep their
1791
1775
  * audit trail).
1792
1776
  */
1793
1777
  /**
1794
- * The asymmetric TTL table (docs/05, section "Grounding and decay"):
1778
+ * The asymmetric TTL table:
1795
1779
  * a false negative is costlier through lock-in, so weaknesses expire
1796
1780
  * sooner than strengths.
1797
1781
  */
@@ -1807,14 +1791,14 @@ const CLAIM_TTL_DAYS = {
1807
1791
  };
1808
1792
  /** Inbox proposals expire after 14 days (reserved for M12 phase 3). */
1809
1793
  const INBOX_PROPOSAL_TTL_DAYS = 14;
1810
- /** The docs/05 TTL applied to an observedAt ISO date. */
1794
+ /** The asymmetric TTL applied to an observedAt ISO date. */
1811
1795
  function claimExpiry(claimClass, polarity, observedAt) {
1812
1796
  const base = Date.parse(observedAt);
1813
1797
  if (Number.isNaN(base)) throw new ConfigError(`claimExpiry: observedAt is not a date: '${observedAt}'`);
1814
1798
  const days = CLAIM_TTL_DAYS[claimClass][polarity];
1815
1799
  return new Date(base + days * 864e5).toISOString();
1816
1800
  }
1817
- /** True when the claim steers nothing at `at` (docs/05, read-path filters). */
1801
+ /** True when the claim steers nothing at `at` (the read-path filter). */
1818
1802
  function claimExpired(claim, at) {
1819
1803
  const expiry = Date.parse(claim.expiresAt);
1820
1804
  const now = Date.parse(at);
@@ -1824,7 +1808,7 @@ function ttlState(claim, at) {
1824
1808
  return claimExpired(claim, at) ? "expired" : "holds";
1825
1809
  }
1826
1810
  /**
1827
- * The re-measurement queue (docs/05, section "Grounding and decay"):
1811
+ * The re-measurement queue:
1828
1812
  * expired eval-measured claims that are still ACTIVE. Just a status
1829
1813
  * filter: the next sweep re-measures these subjects; nothing archives
1830
1814
  * them (archiving would empty the queue and hide the decay).
@@ -1833,9 +1817,9 @@ function remeasureQueue(claims, at) {
1833
1817
  return claims.filter((claim) => claim.status === "active" && claim.class === "eval-measured" && claimExpired(claim, at));
1834
1818
  }
1835
1819
  /**
1836
- * Deprecation maintenance (docs/05: "deprecations, which archive
1837
- * claims, never delete them, so historical runs keep their audit
1838
- * trail"): archive ops for every non-terminal claim of the deprecated
1820
+ * Deprecation maintenance (deprecations archive claims, never delete
1821
+ * them, so historical runs keep their audit trail): archive ops for
1822
+ * every non-terminal claim of the deprecated
1839
1823
  * models. The caller commits them under its own gate-free archive ops.
1840
1824
  */
1841
1825
  function archiveDeprecatedModelOps(claims, deprecated) {
@@ -1849,8 +1833,7 @@ function archiveDeprecatedModelOps(claims, deprecated) {
1849
1833
  //#endregion
1850
1834
  //#region src/knowledge/claims.ts
1851
1835
  /**
1852
- * Claim validators, the editorial path (M10-T02; docs/05, sections
1853
- * "Data model", "The human gate", "Grounding and decay"). The types
1836
+ * Claim validators, the editorial path (M10-T02). The types
1854
1837
  * live with the SPI (l0/spi/knowledge.ts); this module owns the
1855
1838
  * RUNTIME enforcement that the types promise:
1856
1839
  *
@@ -1864,9 +1847,9 @@ function archiveDeprecatedModelOps(claims, deprecated) {
1864
1847
  * - statements stay bounded, evidence stays mandatory, TTL fields stay
1865
1848
  * coherent.
1866
1849
  */
1867
- /** docs/06, Appendix A: KB active-claims cap, default 8 per (model, taskClass). */
1850
+ /** Appendix A: KB active-claims cap, default 8 per (model, taskClass). */
1868
1851
  const KB_ACTIVE_CLAIMS_CAP = 8;
1869
- /** docs/05, section "Data model": statement <= 200 chars. */
1852
+ /** The committed data model bound: statement <= 200 chars. */
1870
1853
  const CLAIM_STATEMENT_MAX_CHARS = 200;
1871
1854
  const RULED_OUT_VOCABULARY = /* @__PURE__ */ new Set([
1872
1855
  "prompt",
@@ -1913,8 +1896,8 @@ function claimIssues(claim, path, options) {
1913
1896
  return issues;
1914
1897
  }
1915
1898
  /**
1916
- * The coherence square of the committer identity (docs/05, 5.4;
1917
- * M11-T01): an eval-committer-gated claim MUST be eval-measured,
1899
+ * The coherence square of the committer identity (M11-T01): an
1900
+ * eval-committer-gated claim MUST be eval-measured,
1918
1901
  * authored by the eval pipeline, and carry metrics; anything else is
1919
1902
  * an identity mismatch, schema-enforced.
1920
1903
  */
@@ -1941,7 +1924,7 @@ function claimOpIssues(op, index) {
1941
1924
  return issues;
1942
1925
  }
1943
1926
  /**
1944
- * The commit-time cap (docs/06, Appendix A): active claims per
1927
+ * The commit-time cap (Appendix A): active claims per
1945
1928
  * (model, taskClass) after the batch applies. Supersede chains keep
1946
1929
  * only the head active by construction (applyClaimOps flips the prior
1947
1930
  * to 'superseded'), so a supersede never grows the count.
@@ -1971,8 +1954,7 @@ function validateEditorialCommit(ops, claimsAfter, options) {
1971
1954
  //#endregion
1972
1955
  //#region src/knowledge/epoch.ts
1973
1956
  /**
1974
- * modelEpoch capture (M11-T04; docs/05, section "Grounding and
1975
- * decay"). An HONESTLY COARSE signal: the registry version, the
1957
+ * modelEpoch capture (M11-T04). An HONESTLY COARSE signal: the registry version, the
1976
1958
  * price-table version, and the caps hash catch overt model swaps and
1977
1959
  * deprecations; silent alias re-pointing is a documented uncaught case
1978
1960
  * absent probes (the canary fingerprint in @rulvar/evals compensates,
@@ -1999,8 +1981,7 @@ function modelEpochOf(inputs) {
1999
1981
  * FileModelKnowledgeStore (M10-T01): the default ModelKnowledgeStore, a
2000
1982
  * single JSON file in the project (`./rulvar.models.json`),
2001
1983
  * git-diffable, serverless, embeddable. The git review of that file IS
2002
- * the human gate's medium (docs/05, sections "Data model" and "Format
2003
- * decision rationale"); the store itself only enforces the mechanics:
1984
+ * the human gate's medium; the store itself only enforces the mechanics:
2004
1985
  * CAS by monotonic version (mirroring the lease fencing discipline),
2005
1986
  * append-only claim evolution (supersede and archive flip status, never
2006
1987
  * delete), and atomic replace on write.
@@ -2130,7 +2111,7 @@ var FileModelKnowledgeStore = class {
2130
2111
  *
2131
2112
  * Named strong default models live ONLY in the umbrella `rulvar`
2132
2113
  * package config, never here: the core ships the floor mechanism, the
2133
- * umbrella ships opinions (docs/04, section "Role quality floors").
2114
+ * umbrella ships opinions.
2134
2115
  */
2135
2116
  function violates(ref, constraint) {
2136
2117
  if (constraint === void 0) return;
@@ -2154,11 +2135,11 @@ function checkFloors(options) {
2154
2135
  }
2155
2136
  //#endregion
2156
2137
  //#region src/knowledge/card.ts
2157
- /** docs/06, Appendix A: the KB card render budget (characters). */
2138
+ /** The KB card render budget (characters). */
2158
2139
  const KB_CARD_RENDER_BUDGET_CHARS = 4096;
2159
2140
  /**
2160
2141
  * The ladders a run declares: every advertised profile whose model
2161
- * spec is a ladder (docs/04, section 12). The card is tier-relative to
2142
+ * spec is a ladder. The card is tier-relative to
2162
2143
  * exactly these.
2163
2144
  */
2164
2145
  function collectDeclaredLadders(profiles) {
@@ -2193,7 +2174,7 @@ function floored(model, taskClass, floors) {
2193
2174
  }
2194
2175
  }
2195
2176
  /**
2196
- * The admission filter (docs/05, 4.1): status active, unexpired at
2177
+ * The admission filter: status active, unexpired at
2197
2178
  * `now`, and the subject reachable through the run's declared ladders
2198
2179
  * after the role-floor filter.
2199
2180
  */
@@ -2211,8 +2192,7 @@ function tiersOf(claim, ladders) {
2211
2192
  return coordinates;
2212
2193
  }
2213
2194
  /**
2214
- * The verified-layer compiler (M11-T06; docs/05, sections "Read path"
2215
- * and "Composition with the model layer"): start-tier recommendations
2195
+ * The verified-layer compiler (M11-T06): start-tier recommendations
2216
2196
  * per (ladder, taskClass) compiled EXCLUSIVELY from eval-measured
2217
2197
  * claims. A strength on a rung below the default votes down (start
2218
2198
  * cheaper); a weakness on the default rung or below votes up. The net
@@ -2257,9 +2237,9 @@ function compileVerifiedLayer(claims, ladders) {
2257
2237
  return rows;
2258
2238
  }
2259
2239
  /**
2260
- * The deterministic card render (docs/05, 4.3). Pure: same filtered
2240
+ * The deterministic card render. Pure: same filtered
2261
2241
  * claims and ladders give byte-identical text. The render budget is
2262
- * docs/06 Appendix A (4096 chars); over it, the OLDEST-observed notes
2242
+ * 4096 chars; over it, the OLDEST-observed notes
2263
2243
  * withhold first behind an explicit marker.
2264
2244
  */
2265
2245
  function modelKnowledgeCard(claims, ladders, options) {
@@ -2271,6 +2251,30 @@ function modelKnowledgeCard(claims, ladders, options) {
2271
2251
  lines.push("Verified layer (start-tier recommendations, clamped one rung from the default):");
2272
2252
  for (const row of verified) lines.push(`- ladder '${row.ladder}', taskClass '${row.taskClass}': start tier ${String(row.recommendedTier)} (default ${String(row.defaultTier)}, eval evidence, ${String(row.votes)} claim${row.votes === 1 ? "" : "s"})`);
2273
2253
  }
2254
+ const profileLines = [];
2255
+ for (const [name, profile] of Object.entries(options?.profiles ?? {}).sort(([left], [right]) => left < right ? -1 : 1)) {
2256
+ const model = profile.model;
2257
+ if (typeof model !== "string") continue;
2258
+ const matched = claims.filter((claim) => claim.class === "eval-measured" && claim.subject.model === model && (profile.effort === void 0 || claim.subject.effort === profile.effort));
2259
+ if (matched.length === 0) continue;
2260
+ const byClass = /* @__PURE__ */ new Map();
2261
+ for (const claim of [...matched].sort((left, right) => left.id < right.id ? -1 : 1)) {
2262
+ const verdict = claim.polarity === "strength" ? "strong" : "weak";
2263
+ const prior = byClass.get(claim.taskClass);
2264
+ byClass.set(claim.taskClass, prior === "weak" ? "weak" : verdict);
2265
+ }
2266
+ const strong = [...byClass.entries()].filter(([, verdict]) => verdict === "strong").map(([taskClass]) => taskClass).sort();
2267
+ const weak = [...byClass.entries()].filter(([, verdict]) => verdict === "weak").map(([taskClass]) => taskClass).sort();
2268
+ const parts = [];
2269
+ if (strong.length > 0) parts.push(`strong ${strong.join(", ")}`);
2270
+ if (weak.length > 0) parts.push(`weak ${weak.join(", ")}`);
2271
+ profileLines.push(`- ${name}: ${parts.join("; ")}`);
2272
+ }
2273
+ if (profileLines.length > 0) {
2274
+ lines.push("Profile evidence (eval-measured, folded over each profile model):");
2275
+ lines.push(...profileLines);
2276
+ lines.push("Spawn guidance: prefer the cheapest profile marked strong for the task at hand; avoid profiles marked weak at it.");
2277
+ }
2274
2278
  const noteLines = claims.filter((claim) => claim.class === "human-editorial").sort((left, right) => left.observedAt === right.observedAt ? left.id < right.id ? -1 : 1 : left.observedAt < right.observedAt ? 1 : -1).map((claim) => {
2275
2279
  return `- [${tiersOf(claim, ladders).join(", ")}] ${claim.taskClass} ${claim.polarity} (confidence ${claim.confidence}, observed ${claim.observedAt}, expires ${claim.expiresAt}): ${claim.statement}`;
2276
2280
  });
@@ -2318,7 +2322,7 @@ function compilePermissionPreset(preset) {
2318
2322
  //#endregion
2319
2323
  //#region src/tools/shell-matcher.ts
2320
2324
  /**
2321
- * Lexes a command into segments per the docs/08 5.2 algorithm. Quotes
2325
+ * Lexes a command into segments per the matching algorithm above. Quotes
2322
2326
  * and escapes are honored; nothing is expanded; `$(`, backticks, `<(`,
2323
2327
  * `>(`, and `<<` (outside single quotes) poison their segment.
2324
2328
  */
@@ -2498,16 +2502,15 @@ function matchShellCommand(command, rules) {
2498
2502
  * execute body never invalidates a journal; changing semantics is
2499
2503
  * signaled by bumping version.
2500
2504
  *
2501
- * Owning spec: docs/08-tools-permissions-spec.md, sections "Tool
2502
- * definition and toolsetHash" and "SchemaSpec".
2505
+ * Public docs: https://docs.rulvar.com/guide/tools
2503
2506
  */
2504
- /** First-party provider tool-name constraint intersection (docs/08, section 1.1). */
2507
+ /** First-party provider tool-name constraint intersection. */
2505
2508
  const TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]{1,64}$/;
2506
2509
  /**
2507
2510
  * Defines a tool. Definition-time failures are typed ConfigErrors, never
2508
2511
  * first-call surprises: an illegal name, a Standard Schema without the
2509
2512
  * JSON Schema projection, a recursive local $ref, or a remote/dynamic
2510
- * reference all fail here (docs/08, sections 1.1 and 2.3).
2513
+ * reference all fail here.
2511
2514
  */
2512
2515
  function tool(init) {
2513
2516
  if (!TOOL_NAME_PATTERN.test(init.name)) throw new ConfigError(`tool name '${init.name}' must match ^[a-zA-Z0-9_-]{1,64}$ (docs/08, section "tool() definition and ToolDef")`);
@@ -2526,8 +2529,7 @@ function tool(init) {
2526
2529
  }
2527
2530
  /**
2528
2531
  * The identity projection: the contract tuple that enters toolsetHash.
2529
- * parameters is the canonicalized derived JSON Schema (docs/03, section
2530
- * "schemaHash and toolsetHash derivation").
2532
+ * parameters is the canonicalized derived JSON Schema.
2531
2533
  */
2532
2534
  function toolContract(def) {
2533
2535
  const parameters = canonicalizeSchema(projectToJsonSchema(def.parameters));
@@ -2549,8 +2551,7 @@ function toolContract(def) {
2549
2551
  * for the agent's lifetime; provider-side drift of a source's tools
2550
2552
  * changes the content key of NEW spawns only.
2551
2553
  *
2552
- * Owning spec: docs/08-tools-permissions-spec.md, sections "toolsetHash
2553
- * contract", "ToolSource seam", and "Filtering and prefixing".
2554
+ * Docs: https://docs.rulvar.com/guide/tools.
2554
2555
  */
2555
2556
  /** The empty toolset (no tools declared anywhere). */
2556
2557
  function emptyToolset() {
@@ -2565,8 +2566,8 @@ function isToolDef(spec) {
2565
2566
  }
2566
2567
  /**
2567
2568
  * Expands sources, validates every tool name and duplicate names across
2568
- * the whole toolset (ConfigError at spawn time; docs/08 sections 1.1 and
2569
- * 6.4), and computes the toolsetHash over contracts sorted by name.
2569
+ * the whole toolset (ConfigError at spawn time), and computes the
2570
+ * toolsetHash over contracts sorted by name.
2570
2571
  */
2571
2572
  async function resolveToolset(specs, session) {
2572
2573
  if (specs === void 0 || specs.length === 0) return emptyToolset();
@@ -2622,7 +2623,7 @@ function buildToolContext(seed) {
2622
2623
  * contract. Pinned SDK line: @modelcontextprotocol/sdk ^1.29 (the v2
2623
2624
  * migration is the explicit post-M3 task M5-T10; risk R1).
2624
2625
  *
2625
- * Owning spec: docs/08-tools-permissions-spec.md, section "MCP bus".
2626
+ * Docs: https://docs.rulvar.com/guide/mcp.
2626
2627
  */
2627
2628
  function validateConfig(cfg) {
2628
2629
  const forbid = (key) => {
@@ -2674,7 +2675,7 @@ function errorText(result) {
2674
2675
  * first tools() call; tools/list is fetched with cursor pagination until
2675
2676
  * exhaustion and cached per session; a listChanged notification
2676
2677
  * invalidates the cache, affecting subsequently spawned agents only (a
2677
- * spawn's toolset snapshot is immutable by construction; docs/08 6.3).
2678
+ * spawn's toolset snapshot is immutable by construction).
2678
2679
  */
2679
2680
  function mcp(cfg) {
2680
2681
  validateConfig(cfg);
@@ -2773,13 +2774,12 @@ function mcp(cfg) {
2773
2774
  * retain failed trees under the shared pin cap.
2774
2775
  *
2775
2776
  * The sandbox is a determinism and blast-radius boundary, NOT a security
2776
- * boundary (docs/08, sections 7.2 and 8; docs/01 NFR security posture).
2777
+ * boundary.
2777
2778
  *
2778
- * Owning spec: docs/08-tools-permissions-spec.md, section "Isolation and
2779
- * worktree lifecycle".
2779
+ * Full contract: https://docs.rulvar.com/guide/tools
2780
2780
  */
2781
2781
  const execFileAsync = promisify(execFile);
2782
- /** docs/06 Appendix A: the shared pin cap (park/unpark and retainWorktree). */
2782
+ /** Appendix A: the shared pin cap (park/unpark and retainWorktree). */
2783
2783
  const DEFAULT_MAX_PINNED_WORKTREES = 4;
2784
2784
  async function git(cwd, args) {
2785
2785
  const { stdout } = await execFileAsync("git", [
@@ -2791,7 +2791,7 @@ async function git(cwd, args) {
2791
2791
  }
2792
2792
  /**
2793
2793
  * The shipped git worktree lifecycle. A non-git host is a typed
2794
- * ConfigError at acquire (docs/08, section 8.3, rule 1).
2794
+ * ConfigError at acquire.
2795
2795
  */
2796
2796
  var GitWorktreeProvider = class {
2797
2797
  repoRoot;
@@ -2881,7 +2881,7 @@ var GitWorktreeProvider = class {
2881
2881
  * spawn kind and content-key derivation, sha256 over RFC 8785 JCS
2882
2882
  * canonical JSON. Frozen as part of the hashVersion 2 profile in M2.
2883
2883
  *
2884
- * Owning spec: docs/03-journal-spec.md, section "Identity model" (DEF-6
2884
+ * Identity model contract: https://docs.rulvar.com/guide/journal (DEF-6
2885
2885
  * framing). Excluded from every content key: cosmetics (label, phase),
2886
2886
  * handling policy (onError, retry, replay), policy fields
2887
2887
  * (memoizeOutcome), lineage blocks, and spanId.
@@ -2889,8 +2889,8 @@ var GitWorktreeProvider = class {
2889
2889
  /**
2890
2890
  * The identity projection of a CanonicalModelSpec. For the plain-model
2891
2891
  * kind the projection is `{ model, effort? }` WITHOUT the kind
2892
- * discriminant, exactly as fixed by the docs/03 section 1.5 worked
2893
- * example; `effort` is omitted when unresolved. The ladder embedding lands
2892
+ * discriminant, exactly as frozen by the hashVersion 2 profile;
2893
+ * `effort` is omitted when unresolved. The ladder embedding lands
2894
2894
  * with ladder execution (M7).
2895
2895
  */
2896
2896
  function modelSpecIdentity(spec) {
@@ -2924,7 +2924,7 @@ function identityJcs(input) {
2924
2924
  return jcsSerialize(projectIdentity(input));
2925
2925
  }
2926
2926
  /**
2927
- * key = sha256(JCS(IdentityInput)) (docs/03, section "Content key").
2927
+ * key = sha256(JCS(IdentityInput)).
2928
2928
  */
2929
2929
  function deriveContentKey(input) {
2930
2930
  return createHash("sha256").update(identityJcs(input), "utf8").digest("hex");
@@ -2936,7 +2936,7 @@ function deriveContentKey(input) {
2936
2936
  * of wall-clock (invariant I3: structure comes from call-and-return only).
2937
2937
  * The grammar is part of the hashVersion 2 profile.
2938
2938
  *
2939
- * Owning spec: docs/03-journal-spec.md, section "Scope-path grammar".
2939
+ * Full contract: https://docs.rulvar.com/guide/journal.
2940
2940
  *
2941
2941
  * Segment rules: a sequential body is ONE scope (sequential calls add no
2942
2942
  * segment; they are distinguished by key and ordinal only). ctx.phase is
@@ -3071,12 +3071,12 @@ var ParallelSiteCounter = class {
3071
3071
  * per-engine deriver registry, the support-window compatibility scan, and
3072
3072
  * the versioned KeyRing for matching. A profile is immutable after
3073
3073
  * release and versions the ENTIRE identity and replay pipeline as one
3074
- * unit (docs/03, section "hashVersion").
3074
+ * unit. Full contract: https://docs.rulvar.com/guide/journal-compatibility.
3075
3075
  */
3076
3076
  function sha256Hex$2(text) {
3077
3077
  return createHash("sha256").update(text, "utf8").digest("hex");
3078
3078
  }
3079
- /** The full v2 table; the three kernel amendments live in these rules (docs/03, section 6.3). */
3079
+ /** The full v2 table; the three kernel amendments live in these rules. */
3080
3080
  const V2_TABLE = {
3081
3081
  ok: "replay",
3082
3082
  escalated: "replay",
@@ -3111,7 +3111,7 @@ const deriverV2 = {
3111
3111
  budgetAccount: "root"
3112
3112
  }
3113
3113
  };
3114
- /** Kinds that did not exist in round 1: incomparable under v1 (docs/03, section 4.3). */
3114
+ /** Kinds that did not exist in round 1: incomparable under v1. */
3115
3115
  const V1_INEXPRESSIBLE_KINDS = /* @__PURE__ */ new Set([
3116
3116
  "decision",
3117
3117
  "plan.revision",
@@ -3159,8 +3159,8 @@ function isKeyDeriver(value) {
3159
3159
  }
3160
3160
  /**
3161
3161
  * Builds the per-engine deriver registry: the shipped v1/v2 profiles plus
3162
- * EngineOptions.extraDerivers, the ONLY window extender (docs/03, section
3163
- * 4.5). A malformed extra deriver is a ConfigError before any run effect.
3162
+ * EngineOptions.extraDerivers, the ONLY window extender. A malformed
3163
+ * extra deriver is a ConfigError before any run effect.
3164
3164
  */
3165
3165
  function buildDeriverRegistry(extraDerivers) {
3166
3166
  const registry = /* @__PURE__ */ new Map([[deriverV1.hashVersion, deriverV1], [deriverV2.hashVersion, deriverV2]]);
@@ -3173,7 +3173,7 @@ function buildDeriverRegistry(extraDerivers) {
3173
3173
  /**
3174
3174
  * The one compatibility scan: immediately after load, strictly BEFORE any
3175
3175
  * live call, any append, and any admission reserve; repeated at lease
3176
- * acquire in queue mode (docs/03, section 4.5). Side-effect free.
3176
+ * acquire in queue mode. Side-effect free.
3177
3177
  */
3178
3178
  function scanJournalCompatibility(runId, entries, registry) {
3179
3179
  const versions = [...registry.keys()];
@@ -3197,8 +3197,7 @@ function scanJournalCompatibility(runId, entries, registry) {
3197
3197
  }
3198
3198
  /**
3199
3199
  * KeyRing over the registry: the live call is projected DOWN into the
3200
- * profile of the stored entry; there is no upward canonization (docs/03,
3201
- * section 4.7).
3200
+ * profile of the stored entry; there is no upward canonization.
3202
3201
  */
3203
3202
  function registryKeyRing(registry) {
3204
3203
  return { keyFor(identity, hashVersion) {
@@ -3219,11 +3218,11 @@ function registryKeyRing(registry) {
3219
3218
  * addressable); step 2 applies the per-status table of the ENTRY'S OWN
3220
3219
  * hashVersion profile, carrying the three kernel amendments:
3221
3220
  * memoizeOutcome on task-class failures, abandon-derived skipped, and
3222
- * escalated-replays-as-ok (docs/03, section "Replay predicate (DEF-1)").
3221
+ * escalated-replays-as-ok (https://docs.rulvar.com/guide/journal).
3223
3222
  */
3224
3223
  /**
3225
3224
  * task-class: schema-mismatch, terminal, non-retryable tool. transport,
3226
- * rate-limit, and budget are never memoized (docs/03, section 6.4).
3225
+ * rate-limit, and budget are never memoized.
3227
3226
  */
3228
3227
  function classifyAgentError(e) {
3229
3228
  if (e.kind === "schema-mismatch" || e.kind === "terminal") return "task";
@@ -3232,7 +3231,7 @@ function classifyAgentError(e) {
3232
3231
  }
3233
3232
  /**
3234
3233
  * The child scope-prefix an abandon over `target` covers transitively.
3235
- * Agent spawns nest under agent:<seq> (docs/03, section 2.2); a child
3234
+ * Agent spawns nest under agent:<seq>; a child
3236
3235
  * workflow's subtree runs under the wf:<name>:<ordinal> scope recorded in
3237
3236
  * its dispatch payload (M6-T06). A child entry without the payload
3238
3237
  * (foreign journals) degrades to the agent:<seq> convention, which covers
@@ -3249,7 +3248,7 @@ function childCoveragePrefix(target) {
3249
3248
  * Builds the AbandonFold in ONE pass at load, in append order, pinned for
3250
3249
  * the entire resume (DEF-1 ordering rule 4). Coverage is the target seq
3251
3250
  * itself plus, transitively, every entry under the target's child
3252
- * scope-prefix (docs/03, sections 6.2 and 8.4). Repeated abandons over an
3251
+ * scope-prefix. Repeated abandons over an
3253
3252
  * already-covered target fold to noop.
3254
3253
  */
3255
3254
  function buildAbandonFold(entries) {
@@ -3335,8 +3334,8 @@ function dispositionHook(fold, registry, invalidated) {
3335
3334
  * Lineage: LogicalTaskId, approach signatures, and the counter folds
3336
3335
  * (M7-T02, DEF-3).
3337
3336
  *
3338
- * Owning spec: docs/03-journal-spec.md, section "Lineage (DEF-3)";
3339
- * docs/07-adaptive-orchestration-spec.md, section "Lineage (DEF-3)".
3337
+ * Public contract: https://docs.rulvar.com/guide/journal and
3338
+ * https://docs.rulvar.com/guide/adaptive-orchestration.
3340
3339
  *
3341
3340
  * The LTID answers "is this the same logical task across rebirths".
3342
3341
  * NodeId remains plan-node identity; the content key remains the identity
@@ -3349,9 +3348,9 @@ function dispositionHook(fold, registry, invalidated) {
3349
3348
  * in decision entries are READ on replay, never recomputed; a fold
3350
3349
  * recomputation over the same prefix serves only as an integrity assert.
3351
3350
  */
3352
- /** approachSig/approachSigCoarse derivation version (docs/03, 10.7). */
3351
+ /** approachSig/approachSigCoarse derivation version. */
3353
3352
  const LINEAGE_SIG_VERSION = 1;
3354
- /** Deterministic LTIDs canonized onto legacy journals (docs/03, 10.7). */
3353
+ /** Deterministic LTIDs canonized onto legacy journals. */
3355
3354
  const LEGACY_LTID_PREFIX = "legacy:";
3356
3355
  const DEFAULT_ESCALATION_LIMITS = {
3357
3356
  maxEscalationsPerLogicalTask: 2,
@@ -3377,7 +3376,7 @@ function sha256Hex$1(text) {
3377
3376
  return createHash("sha256").update(text, "utf8").digest("hex");
3378
3377
  }
3379
3378
  /**
3380
- * Approach-tag normalization (docs/03, 10.2): NFC, lowercase, runs of
3379
+ * Approach-tag normalization: NFC, lowercase, runs of
3381
3380
  * non-alphanumerics collapse into a hyphen, truncate to 32 characters; an
3382
3381
  * empty value canonicalizes to 'default'. Prompt prose never enters any
3383
3382
  * signature: rephrasings collide by construction, not by heuristic.
@@ -3386,7 +3385,7 @@ function normalizeApproachTag(raw) {
3386
3385
  const collapsed = (raw ?? "").normalize("NFC").toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 32);
3387
3386
  return collapsed === "" ? "default" : collapsed;
3388
3387
  }
3389
- /** The isolation string entering approachSigCoarse (docs/03, 10.3). */
3388
+ /** The isolation string entering approachSigCoarse. */
3390
3389
  function canonicalIsolationTag(spec) {
3391
3390
  if (spec === void 0) return "none";
3392
3391
  return typeof spec === "string" ? spec : spec.kind;
@@ -3394,7 +3393,7 @@ function canonicalIsolationTag(spec) {
3394
3393
  /**
3395
3394
  * approachSigCoarse = sha256(JCS({ sigVersion, agentType, toolsetHash,
3396
3395
  * schemaHash, isolation })). Feeds the stall detector and the oscillation
3397
- * guard, which keys ACROSS LTID boundaries (docs/07, 3.8).
3396
+ * guard, which keys ACROSS LTID boundaries.
3398
3397
  */
3399
3398
  function approachSigCoarse(inputs) {
3400
3399
  return sha256Hex$1(jcsSerialize({
@@ -3417,7 +3416,7 @@ function approachSigOf(coarse, tag) {
3417
3416
  * The deterministic signature inputs assigned to legacy spawns (journals
3418
3417
  * written before lineage existed) and to attempts whose producers did not
3419
3418
  * record signature inputs: stable constants, never wall-clock, so replay
3420
- * canonizes identically on every engine (docs/03, 10.7).
3419
+ * canonizes identically on every engine.
3421
3420
  */
3422
3421
  const LEGACY_SIGNATURE_INPUTS = {
3423
3422
  agentType: "legacy",
@@ -3441,7 +3440,7 @@ function classifyAttemptOutcome(terminal) {
3441
3440
  default: return "task-error";
3442
3441
  }
3443
3442
  }
3444
- /** Outcome classes that lengthen the stall streak (docs/03, 10.4). */
3443
+ /** Outcome classes that lengthen the stall streak. */
3445
3444
  const STALLING_OUTCOMES = /* @__PURE__ */ new Set([
3446
3445
  "task-error",
3447
3446
  "no-progress",
@@ -3459,7 +3458,7 @@ function asRecord$2(value) {
3459
3458
  * Reads the computed SpawnLineage block of a decision payload, tolerating
3460
3459
  * pre-DEF-3 producers (M6 journals): a verdict lineage block without
3461
3460
  * signatures canonizes onto the deterministic legacy signature constants,
3462
- * so folds over old journals stay byte-stable (docs/03, 10.7).
3461
+ * so folds over old journals stay byte-stable.
3463
3462
  */
3464
3463
  function readSpawnLineage(decision) {
3465
3464
  if (decision === void 0) return;
@@ -3484,8 +3483,7 @@ function readSpawnLineage(decision) {
3484
3483
  * The incremental lineage fold: attempts, escalation debits, stall
3485
3484
  * streaks, single-live-attempt, and legacy canonization, computed from
3486
3485
  * journal entries only. `absorb` is idempotent by seq cursor; every read
3487
- * accepts an optional `uptoSeq` pin so renders stay snapshot-stable
3488
- * (docs/03, 10.4; docs/07, 8.3).
3486
+ * accepts an optional `uptoSeq` pin so renders stay snapshot-stable.
3489
3487
  */
3490
3488
  var LineageIndex = class {
3491
3489
  attemptsByLtid = /* @__PURE__ */ new Map();
@@ -3671,7 +3669,7 @@ var LineageIndex = class {
3671
3669
  * attempt whose bound key matches (an at-least-once redispatch of the
3672
3670
  * same slot after cancelled/error/limit); else a legacy attempt is
3673
3671
  * canonized with the deterministic 'legacy:' + contentHash LTID
3674
- * (docs/03, 10.7: random ULIDs on replay are forbidden).
3672
+ * (random ULIDs on replay are forbidden).
3675
3673
  */
3676
3674
  bindRoot(slotScope, entry) {
3677
3675
  const queue = this.queueByScope.get(slotScope) ?? [];
@@ -3721,13 +3719,13 @@ var LineageIndex = class {
3721
3719
  * True while the LTID has an unsettled attempt (admitted, dispatched, or
3722
3720
  * redispatched without a terminal), including admits whose decision
3723
3721
  * entries have not landed yet. Backs the single-live-attempt invariant:
3724
- * a competing admit gets `lineage_busy` (docs/03, 10.5).
3722
+ * a competing admit gets `lineage_busy`.
3725
3723
  */
3726
3724
  hasLiveAttempt(logicalTaskId) {
3727
3725
  if ((this.pendingAdmits.get(logicalTaskId) ?? 0) > 0) return true;
3728
3726
  return (this.attemptsByLtid.get(logicalTaskId) ?? []).some((attempt) => attempt.outcome === void 0);
3729
3727
  }
3730
- /** The stall streak per docs/03, 10.4 (pinnable to a snapshot seq). */
3728
+ /** The stall streak (pinnable to a snapshot seq). */
3731
3729
  stallStreak(logicalTaskId, uptoSeq = Number.POSITIVE_INFINITY) {
3732
3730
  let streak = 0;
3733
3731
  for (const attempt of this.attemptsOf(logicalTaskId, uptoSeq)) {
@@ -3741,7 +3739,7 @@ var LineageIndex = class {
3741
3739
  }
3742
3740
  return streak;
3743
3741
  }
3744
- /** The pinned LineageStats render (docs/03, 10.3). */
3742
+ /** The pinned LineageStats render. */
3745
3743
  statsOf(logicalTaskId, uptoSeq = Number.POSITIVE_INFINITY) {
3746
3744
  const attempts = this.attemptsOf(logicalTaskId, uptoSeq);
3747
3745
  const groups = /* @__PURE__ */ new Map();
@@ -3781,8 +3779,8 @@ var LineageIndex = class {
3781
3779
  /**
3782
3780
  * TerminationAccount and the termination lemma (M7-T03, DEF-2).
3783
3781
  *
3784
- * Owning spec: docs/07-adaptive-orchestration-spec.md, section 11;
3785
- * docs/06-execution-spec.md, Appendix A defaults; XF-07/XF-09 cap fields.
3782
+ * Public contract: https://docs.rulvar.com/guide/budgets;
3783
+ * committed defaults declared below; XF-07/XF-09 cap fields.
3786
3784
  *
3787
3785
  * One construction is committed: a single per-run account with an
3788
3786
  * exclusively DEBIT-ONLY API and a limits vector frozen at start in the
@@ -3808,7 +3806,7 @@ function sha256Hex(text) {
3808
3806
  /**
3809
3807
  * Reads the declared ladder length of one agent profile. Ladders are
3810
3808
  * declared through the profile's ModelSpec (`model: { ladder }`, or the
3811
- * loop-role routing entry; docs/04, section 12). The reader is defensive
3809
+ * loop-role routing entry). The reader is defensive
3812
3810
  * so the snapshot is total over every registry shape (an undeclared
3813
3811
  * ladder has length 1: the single implicit rung).
3814
3812
  */
@@ -3829,7 +3827,7 @@ function kMaxOf(profiles) {
3829
3827
  /**
3830
3828
  * The deterministic profile-registry snapshot hash frozen inside
3831
3829
  * termination.init: profile names mapped to their declared ladder
3832
- * lengths, canonical JSON, sha256 (docs/07, 11.6).
3830
+ * lengths, canonical JSON, sha256.
3833
3831
  */
3834
3832
  function profileRegistrySnapshotHash(profiles) {
3835
3833
  const projection = {};
@@ -3871,11 +3869,11 @@ function validateTerminationLimits(raw) {
3871
3869
  function lineageWeightOf(limits) {
3872
3870
  return limits.maxEscalationsPerLogicalTask + limits.kMax;
3873
3871
  }
3874
- /** Phi0 = V0 + C * S0, finite and fixed in termination.init (docs/07, 11.4). */
3872
+ /** Phi0 = V0 + C * S0, finite and fixed in termination.init. */
3875
3873
  function phiInitialOf(limits) {
3876
3874
  return limits.maxRevisionsPerRun + lineageWeightOf(limits) * limits.maxTotalSpawns;
3877
3875
  }
3878
- /** Builds the termination.init value payload (docs/07, 11.6). */
3876
+ /** Builds the termination.init value payload. */
3879
3877
  function buildTerminationInitValue(limits, registrySnapshotHash) {
3880
3878
  return {
3881
3879
  limits,
@@ -3891,7 +3889,7 @@ function readTerminationInit(entry) {
3891
3889
  return value;
3892
3890
  }
3893
3891
  /**
3894
- * Config-drift detection at resume (docs/07, 11.2): the journaled vector
3892
+ * Config-drift detection at resume: the journaled vector
3895
3893
  * always wins; every differing field is reported for the
3896
3894
  * `termination:config-drift` event. Dynamic budget top-up via restart is
3897
3895
  * excluded by construction.
@@ -3909,9 +3907,9 @@ function terminationConfigDrift(frozen, live) {
3909
3907
  return drift;
3910
3908
  }
3911
3909
  /**
3912
- * The single per-run TerminationAccount (docs/07, 11.5): debit ONLY. No
3910
+ * The single per-run TerminationAccount: debit ONLY. No
3913
3911
  * credit operation exists by construction; reclaim never replenishes
3914
- * anything (DEF-5 interaction, docs/07 7.3). Live: the engine debits the
3912
+ * anything (DEF-5 interaction). Live: the engine debits the
3915
3913
  * in-memory account, writes the carrying entry with the balance-after,
3916
3914
  * then applies effects. Resume state is rebuilt by TerminationFold from
3917
3915
  * the journal, never from live config.
@@ -3950,7 +3948,7 @@ var TerminationAccount = class {
3950
3948
  phi: this.phi()
3951
3949
  };
3952
3950
  }
3953
- /** Phi = V + C * S + sum over live lineages (E + R) (docs/07, 11.4). */
3951
+ /** Phi = V + C * S + sum over live lineages (E + R). */
3954
3952
  phi() {
3955
3953
  let phi = this.revisionUnits + lineageWeightOf(this.limits) * this.spawnUnits;
3956
3954
  for (const state of this.lineages.values()) phi += state.escalationUnitsRemaining + state.rungsRemaining;
@@ -3968,7 +3966,7 @@ var TerminationAccount = class {
3968
3966
  return this.revisionUnits;
3969
3967
  }
3970
3968
  /**
3971
- * The spawn-admission debit (docs/07, 11.3b): minus one spawnUnit for
3969
+ * The spawn-admission debit: minus one spawnUnit for
3972
3970
  * an admitted spawn of ANY origin; a NEW lineage receives E0 escalation
3973
3971
  * units and (K_l - 1) rung transitions in the same atomic step, so the
3974
3972
  * lemma's per-spawn decrease is C - (E0 + K_l - 1) = kMax - K_l + 1,
@@ -3996,7 +3994,7 @@ var TerminationAccount = class {
3996
3994
  };
3997
3995
  }
3998
3996
  /**
3999
- * The plan_revise debit (docs/07, 11.3a and 11.7): minus one
3997
+ * The plan_revise debit: minus one
4000
3998
  * revisionUnit on EVERY journaled plan.revision, regardless of the op
4001
3999
  * count, guard verdicts, or the auto-rebase outcome; conflict spam is
4002
4000
  * never a free retry.
@@ -4013,7 +4011,7 @@ var TerminationAccount = class {
4013
4011
  };
4014
4012
  }
4015
4013
  /**
4016
- * The escalation debit (docs/07, 11.3d): minus one escalationUnit of
4014
+ * The escalation debit: minus one escalationUnit of
4017
4015
  * the affected lineage, including EACH lineage of a class-level
4018
4016
  * decision and timeout defaultDecisions. Conditioned on the
4019
4017
  * countsAgainstLimit flag embedded in the decision entry by the caller.
@@ -4031,7 +4029,7 @@ var TerminationAccount = class {
4031
4029
  };
4032
4030
  }
4033
4031
  /**
4034
- * The ladder-raise debit (docs/07, 11.3c): minus one rung of the
4032
+ * The ladder-raise debit: minus one rung of the
4035
4033
  * lineage; rungIndex is strictly monotone, there are no demotions and
4036
4034
  * no runtime startTier promotion in v1.
4037
4035
  */
@@ -4050,7 +4048,7 @@ var TerminationAccount = class {
4050
4048
  };
4051
4049
  }
4052
4050
  /**
4053
- * The docs/07 11.5 debit surface: attempts the named resource and, on
4051
+ * The unified debit surface: attempts the named resource and, on
4054
4052
  * underflow, writes `termination.denied` strictly BEFORE resolving with
4055
4053
  * the typed failure (the caller surfaces the error only after this
4056
4054
  * settles). Requires a deniedWriter; pure-fold contexts use the
@@ -4136,7 +4134,7 @@ var TerminationAccount = class {
4136
4134
  return lineage;
4137
4135
  }
4138
4136
  };
4139
- /** The typed error code surfaced after a denied debit (docs/07, 11.3). */
4137
+ /** The typed error code surfaced after a denied debit. */
4140
4138
  function exhaustionCodeOf(resource) {
4141
4139
  switch (resource) {
4142
4140
  case "revisionUnits": return "revision_budget_exhausted";
@@ -4150,7 +4148,7 @@ function asRecord$1(value) {
4150
4148
  return typeof value === "object" && value !== null ? value : void 0;
4151
4149
  }
4152
4150
  /**
4153
- * The replay fold (docs/07, 11.6): rebuilds the account from
4151
+ * The replay fold: rebuilds the account from
4154
4152
  * termination.init and the debiting decision entries, asserting every
4155
4153
  * embedded balance-after against the recomputation. A divergence raises
4156
4154
  * the typed journal-integrity error at exactly the diverging entry;
@@ -4283,7 +4281,8 @@ function applySpawnDebit(account, entry, admission, assertBalance) {
4283
4281
  * Reuse-by-reference: SpawnKey dedup, donor rules, node.link, and the
4284
4282
  * abandoned-spend ledger (M7-T07, DEF-5).
4285
4283
  *
4286
- * Owning spec: docs/03-journal-spec.md, section 9; docs/07, section 7.3.
4284
+ * Full contract: https://docs.rulvar.com/guide/journal and
4285
+ * https://docs.rulvar.com/guide/adaptive-orchestration.
4287
4286
  * Oscillation (cancel followed by a byte-identical re-add) no longer
4288
4287
  * means full repayment: completed work under an abandoned scope comes
4289
4288
  * back by reference, partially completed work grafts through a
@@ -4299,7 +4298,7 @@ function applySpawnDebit(account, entry, admission, assertBalance) {
4299
4298
  */
4300
4299
  const DEFAULT_MAX_OSCILLATIONS_PER_KEY = 2;
4301
4300
  /**
4302
- * node.link identity (docs/03, 9.5): sha256 of {kind, spawnKey,
4301
+ * node.link identity: sha256 of {kind, spawnKey,
4303
4302
  * donorScope, targetNodeId}; targetNodeId is deterministic on replay
4304
4303
  * because NodeIds are assigned inside plan.revision.
4305
4304
  */
@@ -4433,7 +4432,7 @@ var DedupIndex = class DedupIndex {
4433
4432
  allDonorsOf(spawnKey) {
4434
4433
  return this.donors.get(spawnKey) ?? [];
4435
4434
  }
4436
- /** Link count per key: the oscillation counter (docs/03, 9.7). */
4435
+ /** Link count per key: the oscillation counter. */
4437
4436
  oscillationCountOf(spawnKey) {
4438
4437
  return this.links.get(spawnKey) ?? 0;
4439
4438
  }
@@ -4446,7 +4445,7 @@ var DedupIndex = class DedupIndex {
4446
4445
  };
4447
4446
  }
4448
4447
  };
4449
- /** A plan-node scope (docs/03, 2.1): its entries belong to one node. */
4448
+ /** A plan-node scope: its entries belong to one node. */
4450
4449
  function isPlanNodeScope(scope) {
4451
4450
  return /(^|\/)plan\/[0-9A-Z]{26}$/.test(scope);
4452
4451
  }
@@ -4457,8 +4456,8 @@ function readIsolation(entry) {
4457
4456
  return "none";
4458
4457
  }
4459
4458
  /**
4460
- * The four-outcome verdict evaluation on a SpawnKey match (docs/03,
4461
- * 9.4), computed once live at the fold head and embedded into the
4459
+ * The four-outcome verdict evaluation on a SpawnKey match, computed
4460
+ * once live at the fold head and embedded into the
4462
4461
  * deciding entry; replay never re-evaluates.
4463
4462
  */
4464
4463
  function evaluateReuse(index, spawnKey, config) {
@@ -4587,7 +4586,7 @@ function decodeCheckpoint(blob) {
4587
4586
  * The journal append JSON-serializability check (M1-T04): every journaled
4588
4587
  * value MUST be JSON-serializable; a violation raises a typed
4589
4588
  * NonSerializableValueError at the calling site without journaling
4590
- * anything (docs/03, section "Serialization requirements").
4589
+ * anything.
4591
4590
  */
4592
4591
  function check(value, path) {
4593
4592
  if (value === null) return;
@@ -4644,7 +4643,7 @@ const KNOWN_KINDS = /* @__PURE__ */ new Set([
4644
4643
  "termination.init",
4645
4644
  "termination.denied"
4646
4645
  ]);
4647
- /** Legal stored statuses per kind (docs/03, section 5.3). */
4646
+ /** Legal stored statuses per kind. */
4648
4647
  const LEGAL_STATUSES = {
4649
4648
  agent: [
4650
4649
  "running",
@@ -4740,8 +4739,7 @@ function validateEntryShape(entry) {
4740
4739
  * resolution and abandon are appends of new entries plus a pure
4741
4740
  * deterministic fold; JournalStore stays exactly five methods.
4742
4741
  *
4743
- * Owning spec: docs/03-journal-spec.md, sections "Suspension and
4744
- * resolutions (DEF-4)" and "Abandon, derived skipped" (9.1).
4742
+ * Full contract: https://docs.rulvar.com/guide/durability
4745
4743
  */
4746
4744
  /**
4747
4745
  * The first-closing-wins fold over a loaded journal: one pass by seq,
@@ -4751,7 +4749,7 @@ function validateEntryShape(entry) {
4751
4749
  * schema-invalid offline resolution classifies invalid and does NOT close
4752
4750
  * the target. Abandon coverage is the target seq plus the transitive
4753
4751
  * child scope-prefix; the AbandonFold consumed by the replay predicate is
4754
- * a projection of THIS fold (docs/03, section 6.2: not a separate pass).
4752
+ * a projection of THIS fold (not a separate pass).
4755
4753
  */
4756
4754
  var ResolutionFold = class {
4757
4755
  targets = /* @__PURE__ */ new Map();
@@ -4891,8 +4889,8 @@ var ResolutionFold = class {
4891
4889
  }
4892
4890
  };
4893
4891
  /**
4894
- * Per-run, per-target FIFO serializer of resolution/abandon attempts
4895
- * (docs/03, section 8.5): classification against the in-memory fold ->
4892
+ * Per-run, per-target FIFO serializer of resolution/abandon attempts:
4893
+ * classification against the in-memory fold ->
4896
4894
  * durable append -> settle exactly once; losing attempts are ALSO
4897
4895
  * appended and become journaled noops by fold classification. Winner
4898
4896
  * effects run strictly after the critical section (the caller's job).
@@ -4985,7 +4983,7 @@ var ResolutionArbiter = class {
4985
4983
  };
4986
4984
  //#endregion
4987
4985
  //#region src/journal/matching.ts
4988
- /** Kinds excluded from forward-matching cursors (docs/03, section 8.2). */
4986
+ /** Kinds excluded from forward-matching cursors. */
4989
4987
  const REF_ENTRY_KINDS = /* @__PURE__ */ new Set(["resolution", "abandon"]);
4990
4988
  function currentOnlyKeyRing() {
4991
4989
  return { keyFor(identity, hashVersion) {
@@ -5009,7 +5007,7 @@ var JournalMatcher = class {
5009
5007
  keyRing;
5010
5008
  disposition;
5011
5009
  aliasDisposition;
5012
- /** Scope-prefix aliases (DEF-5, docs/03 9.5): donor prefix -> target prefix. */
5010
+ /** Scope-prefix aliases (DEF-5): donor prefix -> target prefix. */
5013
5011
  aliases = [];
5014
5012
  keyCache = /* @__PURE__ */ new Map();
5015
5013
  hitsInternal = 0;
@@ -5040,8 +5038,8 @@ var JournalMatcher = class {
5040
5038
  this.disposition = disposition;
5041
5039
  }
5042
5040
  /**
5043
- * The disposition applied to alias-sourced candidates (DEF-5, docs/03
5044
- * 9.5): the skipped overlay from abandon is bypassed ONLY through the
5041
+ * The disposition applied to alias-sourced candidates (DEF-5): the
5042
+ * skipped overlay from abandon is bypassed ONLY through the
5045
5043
  * alias, so entries regain their pre-abandon terminal status for
5046
5044
  * matching in the NEW scope; the standalone old scope stays skipped.
5047
5045
  */
@@ -5097,7 +5095,7 @@ var JournalMatcher = class {
5097
5095
  * Forward-matches one live call. A miss does not advance any cursor and
5098
5096
  * does not extinguish future hits: the scan always starts at the scope
5099
5097
  * head and skips consumed operations, so insertion stability holds by
5100
- * construction (docs/03, section 7.1).
5098
+ * construction.
5101
5099
  */
5102
5100
  match(scope, identity, mode) {
5103
5101
  if (mode === "never") {
@@ -5208,19 +5206,17 @@ var JournalMatcher = class {
5208
5206
  * The journal kernel write path (M1-T04): two-phase entries, ordinal
5209
5207
  * assignment, the per-run serialized append queue with the JSON
5210
5208
  * serializability check, and the budget-ledger fold. Scoped
5211
- * forward-matching and the replay predicate land with resume in M2
5212
- * (docs/03, sections "Scoped forward-matching" and "Replay predicate");
5209
+ * forward-matching and the replay predicate land with resume in M2;
5213
5210
  * in M1 every lookup is live.
5214
5211
  *
5215
- * Owning spec: docs/03-journal-spec.md, sections "JournalEntry form",
5216
- * "Two-phase entries, dispatch, and the budget ledger"; component sketch
5217
- * in docs/02-architecture.md, section "Journal Kernel".
5212
+ * Full contract: https://docs.rulvar.com/guide/journal; architecture
5213
+ * overview: https://docs.rulvar.com/guide/architecture.
5218
5214
  */
5219
- /** docs/06 Appendix A: large-value soft warn threshold (committed for M2). */
5215
+ /** Large-value soft warn threshold (committed for M2). */
5220
5216
  const LARGE_VALUE_WARN_BYTES = 262144;
5221
5217
  /**
5222
5218
  * Per-run journal kernel front end. Everything is per instance: no module
5223
- * state anywhere (docs/02, section "Dependency rules").
5219
+ * state anywhere.
5224
5220
  */
5225
5221
  var Replayer = class {
5226
5222
  runId;
@@ -5266,8 +5262,8 @@ var Replayer = class {
5266
5262
  }
5267
5263
  }
5268
5264
  /**
5269
- * Forward-matches one live call against the prior journal (docs/03,
5270
- * section 7). Fresh runs always miss; the M2-T06 predicate is injected
5265
+ * Forward-matches one live call against the prior journal. Fresh
5266
+ * runs always miss; the M2-T06 predicate is injected
5271
5267
  * through setDisposition once folds are built.
5272
5268
  */
5273
5269
  match(scope, identity, mode) {
@@ -5283,7 +5279,7 @@ var Replayer = class {
5283
5279
  this.matcher.setDisposition(disposition);
5284
5280
  }
5285
5281
  /**
5286
- * The disposition for alias-sourced candidates (DEF-5, docs/03 9.5):
5282
+ * The disposition for alias-sourced candidates (DEF-5):
5287
5283
  * bypasses the abandon overlay so donor entries regain their
5288
5284
  * pre-abandon terminal status when matched through the alias.
5289
5285
  */
@@ -5291,7 +5287,7 @@ var Replayer = class {
5291
5287
  this.matcher.setAliasDisposition(disposition);
5292
5288
  }
5293
5289
  /**
5294
- * Registers a node.link scope-prefix rewrite (DEF-5, docs/03 9.5):
5290
+ * Registers a node.link scope-prefix rewrite (DEF-5):
5295
5291
  * donorPrefix forward-matches into targetPrefix at every nested level.
5296
5292
  * Idempotent; the alias map is rebuilt by fold on resume.
5297
5293
  */
@@ -5299,9 +5295,9 @@ var Replayer = class {
5299
5295
  this.matcher.registerAlias(donorPrefix, targetPrefix);
5300
5296
  }
5301
5297
  /**
5302
- * invalidate/retry (docs/03, section 6.5): explicit unpinning of a
5298
+ * invalidate/retry: explicit unpinning of a
5303
5299
  * memoized failure; the invalidated entry reruns on this resume. The
5304
- * safety boundary is an open question (docs/14).
5300
+ * safety boundary is an open question.
5305
5301
  */
5306
5302
  invalidate(seq) {
5307
5303
  this.invalidated.add(seq);
@@ -5348,8 +5344,8 @@ var Replayer = class {
5348
5344
  });
5349
5345
  }
5350
5346
  /**
5351
- * Submits a resolution attempt through the per-target FIFO arbiter
5352
- * (docs/03, section 8.7). Losing attempts are journaled noops.
5347
+ * Submits a resolution attempt through the per-target FIFO arbiter.
5348
+ * Losing attempts are journaled noops.
5353
5349
  */
5354
5350
  resolveSuspended(target, attempt) {
5355
5351
  const targetEntry = this.entries.find((entry) => entry.seq === target);
@@ -5361,12 +5357,12 @@ var Replayer = class {
5361
5357
  if (targetEntry === void 0) throw new ConfigError(`abandonBranch: seq ${attempt.target} does not exist`);
5362
5358
  return this.arbiter.submitAbandon(targetEntry.scope, targetEntry.spanId, attempt);
5363
5359
  }
5364
- /** Pure fold view, snapshot-pinned (docs/03, section 8.7). */
5360
+ /** Pure fold view, snapshot-pinned. */
5365
5361
  suspensionState(target) {
5366
5362
  return this.foldInternal.suspensionState(target);
5367
5363
  }
5368
5364
  /**
5369
- * Value size policy (docs/03, section "Normative payload schemas"):
5365
+ * Value size policy:
5370
5366
  * there is NO automatic offload in v1; oversized values warn and
5371
5367
  * proceed. Large artifacts belong in TranscriptStore by reference.
5372
5368
  */
@@ -5393,7 +5389,7 @@ var Replayer = class {
5393
5389
  * Two-phase dispatch: the running entry (kinds agent, step, child).
5394
5390
  * `value` is legal on child dispatches only: the child payload
5395
5391
  * `{ workflow, childScope }` lets the abandon fold compute the child's
5396
- * transitive scope coverage (docs/03, section 8.4; M6-T06). Values
5392
+ * transitive scope coverage (M6-T06). Values
5397
5393
  * never enter identity.
5398
5394
  */
5399
5395
  appendRunning(input) {
@@ -5457,8 +5453,7 @@ var Replayer = class {
5457
5453
  });
5458
5454
  }
5459
5455
  /**
5460
- * The budget ledger fold (docs/03, section "Budget ledger fold on
5461
- * resume"): usage sums over terminal entries exactly once; agentsSpawned
5456
+ * The budget ledger fold: usage sums over terminal entries exactly once; agentsSpawned
5462
5457
  * counts agent dispatches.
5463
5458
  */
5464
5459
  ledger() {
@@ -5545,7 +5540,7 @@ var Replayer = class {
5545
5540
  * resolveExternal validates against the pinned schema BEFORE append on
5546
5541
  * the live path and settles the waiting promise in place without replay.
5547
5542
  *
5548
- * Owning specs: docs/06, section 2.7; docs/03, section 8.
5543
+ * Full contract: https://docs.rulvar.com/guide/durability
5549
5544
  */
5550
5545
  /**
5551
5546
  * Normalizes a resolution value into an ApprovalDecision. Anything that
@@ -5562,7 +5557,7 @@ function toApprovalDecision(value) {
5562
5557
  * Per-run registry of open external suspensions plus the run's activity
5563
5558
  * counter: when every in-flight branch is blocked on suspensions
5564
5559
  * (activity zero, waiters open), the run quiesces into outcome
5565
- * 'suspended' (docs/06, section 2.7).
5560
+ * 'suspended'.
5566
5561
  */
5567
5562
  var ExternalRegistry = class ExternalRegistry {
5568
5563
  replayer;
@@ -5677,7 +5672,7 @@ var ExternalRegistry = class ExternalRegistry {
5677
5672
  });
5678
5673
  }
5679
5674
  /**
5680
- * Tool-approval suspension (M3-T03; docs/08, section 3.6): journals (or
5675
+ * Tool-approval suspension (M3-T03): journals (or
5681
5676
  * re-matches) the suspended approval entry keyed by (toolName, input)
5682
5677
  * in the agent's child scope and parks until a resolution closes it.
5683
5678
  * The ask verdict is journaled together with the turn checkpoint; on
@@ -5735,7 +5730,7 @@ var ExternalRegistry = class ExternalRegistry {
5735
5730
  });
5736
5731
  }
5737
5732
  /**
5738
- * Flavor B escalation suspension (M3-T07; docs/07, section 6.2): the
5733
+ * Flavor B escalation suspension (M3-T07): the
5739
5734
  * escalate tool suspends the agent on the SAME machinery as approvals
5740
5735
  * (kind 'approval', toolName 'escalate') with a journaled deadlineAt so
5741
5736
  * deadlines survive resume; the resolution VALUE is the raw
@@ -5812,7 +5807,7 @@ var ExternalRegistry = class ExternalRegistry {
5812
5807
  /**
5813
5808
  * RunHandle.resolveExternal: the live path validates BEFORE append and
5814
5809
  * throws InvalidResolutionError without journaling; a winning attempt
5815
- * settles the waiting promise in place (docs/03, section 8.7).
5810
+ * settles the waiting promise in place.
5816
5811
  */
5817
5812
  async resolveExternal(key, value) {
5818
5813
  const waiter = [...this.waiters.values()].find((candidate) => candidate.key === key);
@@ -5917,7 +5912,7 @@ var InMemoryTranscriptStore = class {
5917
5912
  * beside the journal and are replaced atomically, so listRuns never
5918
5913
  * parses payloads.
5919
5914
  *
5920
- * Contract (docs/03, section "Storage SPI", DEF-4 tightening):
5915
+ * Contract (DEF-4 tightening):
5921
5916
  * - A1 atomicity: a torn trailing line (crash mid-append) is never
5922
5917
  * visible in load; it is dropped and overwritten by the next append.
5923
5918
  * - A2 total per-run order: load returns append order, stable across
@@ -5928,7 +5923,7 @@ var InMemoryTranscriptStore = class {
5928
5923
  *
5929
5924
  * Leasing is NOT implemented here: LeasableStore ships with
5930
5925
  * @rulvar/store-sqlite (M5); JsonlFileStore is single-writer by
5931
- * convention (docs/03, section "Shipped stores").
5926
+ * convention.
5932
5927
  */
5933
5928
  const JOURNAL_SUFFIX = ".jsonl";
5934
5929
  const META_SUFFIX = ".meta.json";
@@ -6012,7 +6007,7 @@ const TRANSCRIPT_SUFFIX = ".bin";
6012
6007
  /**
6013
6008
  * File-backed TranscriptStore (M6-T02): blobs (transcripts, checkpoints,
6014
6009
  * persisted CompiledWorkflow sources) as one file per ref under `dir`,
6015
- * so compiled runs resume across processes (docs/06, 10.2). Refs follow
6010
+ * so compiled runs resume across processes. Refs follow
6016
6011
  * the `<runId>/<name>` convention; each path segment is checked
6017
6012
  * filesystem-safe and nested segments become directories.
6018
6013
  */
@@ -6073,7 +6068,7 @@ var FileTranscriptStore = class {
6073
6068
  //#endregion
6074
6069
  //#region src/engine/cost-report.ts
6075
6070
  /**
6076
- * CostReport builders (M5-T03; docs/09, section "CostReport"). Two
6071
+ * CostReport builders (M5-T03). Two
6077
6072
  * sources, one shape:
6078
6073
  *
6079
6074
  * - `buildCostReport` folds the LIVE per-run attribution buckets (ctx
@@ -6087,8 +6082,7 @@ var FileTranscriptStore = class {
6087
6082
  * facts that entries do not carry, so those buckets are empty here;
6088
6083
  * byRole and the orchestrator block complete in M7 (DEF-7).
6089
6084
  *
6090
- * Unpriced models surface in `unpriced`, never as a silent zero
6091
- * (docs/04, section "Pricing").
6085
+ * Unpriced models surface in `unpriced`, never as a silent zero.
6092
6086
  */
6093
6087
  const ROLES = [
6094
6088
  "orchestrate",
@@ -6172,8 +6166,8 @@ function costReportFromJournal(entries, priceUsd) {
6172
6166
  //#endregion
6173
6167
  //#region src/engine/run-profiles.ts
6174
6168
  /**
6175
- * The shipped presets (docs/06, section 11: fast / standard / deep /
6176
- * ultra "and similar"). Data only; a review-time assertion checks the
6169
+ * The shipped presets (fast / standard / deep / ultra "and similar").
6170
+ * Data only; a review-time assertion checks the
6177
6171
  * engine has zero behavioral branches keyed on these names.
6178
6172
  */
6179
6173
  const RUN_PROFILES = {
@@ -6240,7 +6234,7 @@ const TIER_ORDER = {
6240
6234
  /**
6241
6235
  * Strict-schema compatibility as both first-class providers define it:
6242
6236
  * every object node declares `additionalProperties: false` and lists every
6243
- * property in `required` (docs/04, section 5.2). Boolean schemas and
6237
+ * property in `required`. Boolean schemas and
6244
6238
  * non-object shapes are trivially compatible.
6245
6239
  */
6246
6240
  function isStrictCompatibleSchema(schema) {
@@ -6280,10 +6274,10 @@ function isStrictCompatibleSchema(schema) {
6280
6274
  return true;
6281
6275
  }
6282
6276
  /**
6283
- * Tier selection (docs/04, section 8.4): the model's declared ceiling
6277
+ * Tier selection: the model's declared ceiling
6284
6278
  * bounds the tier; the native tier additionally requires a
6285
- * strict-compatible canonical schema (docs/04, section 5.2: relying on
6286
- * silent server-side fallback is forbidden), degrading to forced-tool.
6279
+ * strict-compatible canonical schema (relying on silent server-side
6280
+ * fallback is forbidden), degrading to forced-tool.
6287
6281
  * Prefill is not a tier.
6288
6282
  */
6289
6283
  function selectStructuredOutputTier(caps, canonicalSchema) {
@@ -6301,10 +6295,10 @@ function tierWithinCaps(tier, caps) {
6301
6295
  * Scheduler and concurrency (M1-T08): the per-run semaphore with a FIFO
6302
6296
  * queue (default 12 concurrent model calls). The engine lifetime spawn cap
6303
6297
  * is enforced by the budget layer at admission; parallel/pipeline
6304
- * composition semantics live with ctx (docs/06, section "Scheduler").
6298
+ * composition semantics live with ctx.
6305
6299
  * Per-provider concurrency keys land with M4.
6306
6300
  */
6307
- /** FIFO semaphore; default per-run width is 12 (docs/06, Appendix A). */
6301
+ /** FIFO semaphore; default per-run width is 12. */
6308
6302
  const DEFAULT_PER_RUN_CONCURRENCY = 12;
6309
6303
  var Semaphore = class {
6310
6304
  limit;
@@ -6350,7 +6344,7 @@ var Semaphore = class {
6350
6344
  //#region src/model/concurrency.ts
6351
6345
  /**
6352
6346
  * Per-provider concurrency keys (M4-T07): a keyed limiter beside the
6353
- * router, ENGINE-scoped (docs/06, section 4: keys constrain calls
6347
+ * router, ENGINE-scoped (keys constrain calls
6354
6348
  * across a single engine per adapter). The Appendix A default is
6355
6349
  * unlimited: an embeddable library must not surprise-throttle hosts, so
6356
6350
  * the per-run semaphore stays the only default bound and provider 429s
@@ -6359,7 +6353,7 @@ var Semaphore = class {
6359
6353
  *
6360
6354
  * There is deliberately NO distributed cross-process limiter: two
6361
6355
  * processes sharing one API key coordinate nothing here (a
6362
- * process-global limiter is an open question, docs/14).
6356
+ * process-global limiter is an open question).
6363
6357
  */
6364
6358
  var KeyedLimiter = class {
6365
6359
  semaphores = /* @__PURE__ */ new Map();
@@ -6382,7 +6376,7 @@ var KeyedLimiter = class {
6382
6376
  };
6383
6377
  //#endregion
6384
6378
  //#region src/model/failover.ts
6385
- /** Normalizes the author-facing ModelChoice.fallbacks list (docs/04, 8.1). */
6379
+ /** Normalizes the author-facing ModelChoice.fallbacks list. */
6386
6380
  function normalizeFallbacks(refs) {
6387
6381
  return (refs ?? []).map((model) => ({ model }));
6388
6382
  }
@@ -6407,8 +6401,8 @@ function nextFailover(targets, trigger, from) {
6407
6401
  }
6408
6402
  }
6409
6403
  /**
6410
- * Classifies a terminal agent outcome for the degenerate fallback
6411
- * (docs/04, 11.3 as amended): schema-mismatch errors are
6404
+ * Classifies a terminal agent outcome for the degenerate fallback:
6405
+ * schema-mismatch errors are
6412
6406
  * 'schema-exhausted'; any other error is 'error'; limit terminals (the
6413
6407
  * no-progress abort included) are 'limit'; cancelled, escalated, and
6414
6408
  * skipped never trigger.
@@ -6428,11 +6422,11 @@ function resolvePricing(ref, table, capsPricing) {
6428
6422
  return table?.models[ref] ?? capsPricing;
6429
6423
  }
6430
6424
  /**
6431
- * Dollars from normalized usage against one pricing row (docs/04,
6432
- * section 1.6: the adapter normalized the usage; inputTokens is the
6425
+ * Dollars from normalized usage against one pricing row (the adapter
6426
+ * normalized the usage; inputTokens is the
6433
6427
  * full prompt). Cache writes price at the 5m premium rate; the 1h rate
6434
6428
  * applies where a provider distinguishes it in usage, which the
6435
- * canonical Usage does not yet carry (docs/04, section 10).
6429
+ * canonical Usage does not yet carry.
6436
6430
  */
6437
6431
  function priceUsdOf(pricing, usage) {
6438
6432
  return usage.inputTokens / 1e6 * pricing.inputUsdPerMTok + usage.outputTokens / 1e6 * pricing.outputUsdPerMTok + usage.cacheReadTokens / 1e6 * (pricing.cacheReadUsdPerMTok ?? 0) + usage.cacheWriteTokens / 1e6 * (pricing.cacheWriteUsdPerMTok ?? 0);
@@ -6494,7 +6488,7 @@ function projectHistory(messages, targetProvider) {
6494
6488
  }
6495
6489
  /**
6496
6490
  * Lifts the adapter-shipped retention payload of one finished turn into
6497
- * provider-raw parts (docs/04, section 2.3 retention transport). Reads
6491
+ * provider-raw parts (the retention transport). Reads
6498
6492
  * providerMetadata[<adapter id>].retainedParts and tags each block with
6499
6493
  * the adapter's provider family. Returns [] when the adapter shipped
6500
6494
  * nothing.
@@ -6532,9 +6526,8 @@ const DEFAULT_RETRY_POLICY = {
6532
6526
  /**
6533
6527
  * Classifies a WireError for the retry engine. Task-class failures are
6534
6528
  * never retryable by construction: adapters mark them retryable: false
6535
- * and this returns undefined. The kind travels in WireError.data.kind
6536
- * (docs/04, section 4.9); anything retryable without a specific kind is
6537
- * transport.
6529
+ * and this returns undefined. The kind travels in WireError.data.kind;
6530
+ * anything retryable without a specific kind is transport.
6538
6531
  */
6539
6532
  function retryClassOf(error) {
6540
6533
  if (!error.retryable) return;
@@ -6575,7 +6568,7 @@ function canRideLoopTurn(tier, toolsAvailable) {
6575
6568
  * to a different model OR the loop model's caps cannot serve the required
6576
6569
  * tier OR finalize is routed, in which case the schema never rides a loop
6577
6570
  * or synthesis turn). Otherwise the schema rides the last loop turn with
6578
- * no extra call (docs/04, sections 8.3 and 8.4 as amended in M4-T01).
6571
+ * no extra call (as amended in M4-T01).
6579
6572
  */
6580
6573
  function needsSeparateExtract(input) {
6581
6574
  if (!input.schemaSet) return false;
@@ -6586,7 +6579,7 @@ function needsSeparateExtract(input) {
6586
6579
  * map. This is the finalize TRIGGER: firing is decided by the presence of
6587
6580
  * a routing entry at any layer; the model it fires ON still resolves
6588
6581
  * through the full chain (a higher layer's all-roles `model` may override
6589
- * the routed choice per docs/04, section 8.2).
6582
+ * the routed choice).
6590
6583
  */
6591
6584
  function roleConfiguredInRouting(role, layers) {
6592
6585
  return layers.some((layer) => layer?.routing?.[role] !== void 0);
@@ -6594,8 +6587,8 @@ function roleConfiguredInRouting(role, layers) {
6594
6587
  /**
6595
6588
  * The finalize firing rule: only if configured in routing, and only after
6596
6589
  * tools stop, which presupposes a non-empty toolset. A no-tools agent's
6597
- * single loop turn is already its synthesis (docs/04, section 8.4 as
6598
- * amended in M4-T01). The caller additionally gates on the loop having
6590
+ * single loop turn is already its synthesis (as amended in M4-T01). The
6591
+ * caller additionally gates on the loop having
6599
6592
  * ended without an abort: a limit/error/cancelled/escalated loop never
6600
6593
  * reaches synthesis.
6601
6594
  */
@@ -6604,7 +6597,7 @@ function finalizeFires(options) {
6604
6597
  }
6605
6598
  /**
6606
6599
  * The summarize trigger: the compaction threshold on the context window
6607
- * (docs/06, Appendix A: default 0.8). Pure predicate; the compaction
6600
+ * (default 0.8). Pure predicate; the compaction
6608
6601
  * pipeline that acts on it is M4-T03.
6609
6602
  */
6610
6603
  function atCompactionThreshold(usedTokens, contextWindow, threshold) {
@@ -6613,12 +6606,12 @@ function atCompactionThreshold(usedTokens, contextWindow, threshold) {
6613
6606
  }
6614
6607
  //#endregion
6615
6608
  //#region src/runtime/compaction.ts
6616
- /** Appendix A: compaction threshold default, 0.8 of contextWindow. */
6609
+ /** Compaction threshold default, 0.8 of contextWindow. */
6617
6610
  const DEFAULT_COMPACTION_THRESHOLD = .8;
6618
6611
  /** Deterministic marker opening every compaction summary message. */
6619
6612
  const COMPACTION_SUMMARY_PREFIX = "Summary of the conversation so far:";
6620
6613
  /**
6621
- * The threshold check (docs/06, M4-T03 committed semantics): the context
6614
+ * The threshold check (M4-T03 committed semantics): the context
6622
6615
  * estimate is the last loop turn's inputTokens + outputTokens; the Usage
6623
6616
  * invariant makes inputTokens the full prompt, and the turn's output
6624
6617
  * joins the next prompt.
@@ -6664,14 +6657,11 @@ function compactMessages(messages, summaryText) {
6664
6657
  * parsing, the per-invocation resolution chain, canonicalization into
6665
6658
  * CanonicalModelSpec, and caps scrubbing with visible scrub notes.
6666
6659
  *
6667
- * Owning spec: docs/04-model-layer-spec.md, sections "Router and
6668
- * resolution chain", "Canonical effort", and "Caps scrubbing and
6669
- * structured-output tier selection".
6660
+ * Public contract: https://docs.rulvar.com/guide/model-routing.
6670
6661
  */
6671
6662
  /**
6672
6663
  * Per-engine adapter registry: strictly per engine, no global mutable
6673
- * registry exists. A duplicate adapterId is a typed ConfigError
6674
- * (docs/04, section "Registry and ModelRef").
6664
+ * registry exists. A duplicate adapterId is a typed ConfigError.
6675
6665
  */
6676
6666
  function buildAdapterRegistry(adapters) {
6677
6667
  const registry = /* @__PURE__ */ new Map();
@@ -6695,12 +6685,10 @@ function parseModelRef(ref) {
6695
6685
  };
6696
6686
  }
6697
6687
  /**
6698
- * Role effort defaults (docs/04, section "Invocation roles and firing
6699
- * protocol"): orchestrate and plan default to high; summarize and extract
6688
+ * Role effort defaults: orchestrate and plan default to high; summarize and extract
6700
6689
  * default to low. loop and finalize have NO role default: when the chain
6701
6690
  * resolves nothing, the wire omits effort and identity records the spec
6702
- * with the effort member absent (docs/04, section "Router and resolution
6703
- * chain", as amended).
6691
+ * with the effort member absent.
6704
6692
  */
6705
6693
  const ROLE_EFFORT_DEFAULTS = {
6706
6694
  orchestrate: "high",
@@ -6755,7 +6743,7 @@ const SAMPLING_KEYS = [
6755
6743
  * Resolution runs on every model invocation, not once per agent: a layered
6756
6744
  * merge of { model, effort, providerOptions, fallbacks } in the order call
6757
6745
  * override > agent profile > workflow defaults > engine defaults, with the
6758
- * invocation role attached as a tag (docs/04, section "Resolution chain").
6746
+ * invocation role attached as a tag.
6759
6747
  * After resolution the router reads ModelCaps and scrubs illegal
6760
6748
  * parameters visibly: unsupported effort is removed from the wire but
6761
6749
  * kept in identity; sampling params rejected by the model are removed
@@ -6839,7 +6827,7 @@ function resolveModelInvocation(options) {
6839
6827
  if (merged.fallbacks !== void 0) resolved.fallbacks = merged.fallbacks;
6840
6828
  return resolved;
6841
6829
  }
6842
- /** The closed trigger vocabulary guard (docs/04, section 12). */
6830
+ /** The closed trigger vocabulary guard. */
6843
6831
  const TRIGGER_CLASSES = [
6844
6832
  "error",
6845
6833
  "limit",
@@ -6863,7 +6851,7 @@ function validateGate(gate, rungCount, index) {
6863
6851
  if (!(gate.fraction > 0 && gate.fraction <= 1)) throw new ConfigError(`ladder acceptance gate ${String(index)}: a spot-check fraction lies in (0, 1], got ${String(gate.fraction)}`);
6864
6852
  }
6865
6853
  /**
6866
- * Canonicalizes a declared LadderSpec (docs/04, section 12): validates the
6854
+ * Canonicalizes a declared LadderSpec: validates the
6867
6855
  * shape once (FR-119 judge declaration included) and resolves every rung's
6868
6856
  * effort to an explicit value. `chainEffort` is the effort the resolution
6869
6857
  * chain would contribute at the declaring layer; a rung that resolves no
@@ -6901,7 +6889,7 @@ function canonicalizeLadder(spec, options) {
6901
6889
  /**
6902
6890
  * The concrete ModelChoice of one rung attempt: each attempt is an
6903
6891
  * ordinary agent scope whose CanonicalModelSpec is that rung's
6904
- * `{ kind: 'model' }` form (docs/04, section 8.2).
6892
+ * `{ kind: 'model' }` form.
6905
6893
  */
6906
6894
  function ladderRungChoice(ladder, index) {
6907
6895
  const rung = ladder.rungs[index];
@@ -6917,7 +6905,7 @@ const DEFAULT_MAX_TURNS = 32;
6917
6905
  const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 12e4;
6918
6906
  /**
6919
6907
  * Limits merge per spawn: AgentOpts.limits over profile limits over engine
6920
- * defaults.limits (docs/06, section "UsageLimits").
6908
+ * defaults.limits.
6921
6909
  */
6922
6910
  function mergeUsageLimits(call, profile, engine) {
6923
6911
  const pick = (key) => call?.[key] ?? profile?.[key] ?? engine?.[key];
@@ -6945,13 +6933,13 @@ var ModelRetry = class extends Error {
6945
6933
  if (opts?.data !== void 0) this.data = opts.data;
6946
6934
  }
6947
6935
  };
6948
- /** Bounded semantic retries per tool call chain (docs/06, Appendix A). */
6936
+ /** Bounded semantic retries per tool call chain. */
6949
6937
  const DEFAULT_MODEL_RETRY_ATTEMPTS = 2;
6950
6938
  //#endregion
6951
6939
  //#region src/runtime/escalation.ts
6952
6940
  const ESCALATE_TOOL_NAME = "escalate";
6953
6941
  /**
6954
- * The exact tool schema of docs/07, section 4.9. costToDate and salvage
6942
+ * The escalate tool's exact request schema. costToDate and salvage
6955
6943
  * MUST NOT appear here: additionalProperties false rejects model-authored
6956
6944
  * values for them at argument validation.
6957
6945
  */
@@ -6995,7 +6983,7 @@ const ESCALATION_REQUEST_SCHEMA = {
6995
6983
  }
6996
6984
  }
6997
6985
  };
6998
- /** The full-report schema applied BEFORE append (docs/03, section 5.4). */
6986
+ /** The full-report schema applied BEFORE append. */
6999
6987
  const ESCALATION_REPORT_SCHEMA = {
7000
6988
  type: "object",
7001
6989
  additionalProperties: false,
@@ -7066,7 +7054,7 @@ const ESCALATION_REPORT_SCHEMA = {
7066
7054
  }
7067
7055
  };
7068
7056
  /**
7069
- * The engine opt-in tool (docs/08, section 6.6): registered through the
7057
+ * The engine opt-in tool: registered through the
7070
7058
  * same path as any tool under escalation opt-in of EITHER flavor (the
7071
7059
  * worker's only authoring channel for a report), never available without
7072
7060
  * opt-in, and dispatched through the same permission chain. The loop
@@ -7088,7 +7076,7 @@ async function validateEscalationReport(report) {
7088
7076
  return validation.valid ? [] : validation.issues;
7089
7077
  }
7090
7078
  /**
7091
- * countsAgainstLimit derivation (docs/07, section 6.3, XF-06): true iff
7079
+ * countsAgainstLimit derivation (XF-06): true iff
7092
7080
  * scope_bigger; scope_different and blocked_with_evidence are exempt and
7093
7081
  * never debit the escalation counter.
7094
7082
  */
@@ -7102,11 +7090,11 @@ function countsAgainstLimit(kind) {
7102
7090
  * journaled as a first-class terminal abort distinct from user
7103
7091
  * cancellation (a cancelled entry always reruns; a no-progress abort
7104
7092
  * must replay, or every resume would re-pay the stuck turns). The
7105
- * interim heuristic is committed in docs/06 Appendix A: N consecutive
7093
+ * interim heuristic is committed: N consecutive
7106
7094
  * turns without tool calls or artifact deltas, N = 3; the broader
7107
- * heuristic stays OQ-15 (docs/14), revisited on dogfood traces.
7095
+ * heuristic stays OQ-15, revisited on dogfood traces.
7108
7096
  *
7109
- * Encoding (docs/03, sections 6.3 and 6.6): the abort is the agent's
7097
+ * Encoding: the abort is the agent's
7110
7098
  * terminal entry with status 'limit', an error payload carrying
7111
7099
  * abortClass 'no-progress', and memoizeOutcome stamped by the ENGINE on
7112
7100
  * the terminal entry, so the frozen memoize-limit rule replays it on
@@ -7114,7 +7102,7 @@ function countsAgainstLimit(kind) {
7114
7102
  * per-turn artifact channel, so the tool-call test subsumes artifact
7115
7103
  * deltas; per-turn artifact producers arrive with M4 compaction.
7116
7104
  */
7117
- /** docs/06 Appendix A: the committed no-progress detector N. */
7105
+ /** The committed no-progress detector N. */
7118
7106
  const DEFAULT_NO_PROGRESS_TURNS = 3;
7119
7107
  /**
7120
7108
  * Counts consecutive progress-free turns. A turn with at least one tool
@@ -7154,14 +7142,14 @@ var NoProgressDetector = class {
7154
7142
  * allow: allow is only ever falling through to canUseTool or the
7155
7143
  * terminal default.
7156
7144
  *
7157
- * Owning spec: docs/08-tools-permissions-spec.md, section "Permission
7158
- * chain". Risk presets, the argv shell matcher, domain rules, and the
7145
+ * Full contract: https://docs.rulvar.com/guide/tools.
7146
+ * Risk presets, the argv shell matcher, domain rules, and the
7159
7147
  * audit/dry-run surface land in M5.
7160
7148
  */
7161
7149
  /**
7162
7150
  * Merges the engine-wide config and the profile config into one chain.
7163
7151
  * Layers concatenate engine-first; since rules only deny or ask, ordering
7164
- * within a layer cannot change the verdict (docs/08, section 4.2). The
7152
+ * within a layer cannot change the verdict. The
7165
7153
  * profile's canUseTool wins over the engine's (a single slot by
7166
7154
  * construction). A declared preset compiles INTO the same layers, after
7167
7155
  * the host-authored rules, never as a fifth layer (M5-T05).
@@ -7189,7 +7177,7 @@ function compilePermissionChain(engine, profile) {
7189
7177
  ...canUseTool === void 0 ? {} : { canUseTool }
7190
7178
  };
7191
7179
  }
7192
- /** The command text an argv rule matches against (docs/08, section 5). */
7180
+ /** The command text an argv rule matches against. */
7193
7181
  function commandOf(input) {
7194
7182
  if (typeof input === "string") return input;
7195
7183
  if (typeof input === "object" && input !== null) {
@@ -7214,7 +7202,7 @@ function ruleMatches(rule, toolName, risk, input) {
7214
7202
  return true;
7215
7203
  }
7216
7204
  /**
7217
- * Advisory domain-rule matches for the audit payload (docs/08, 4.4):
7205
+ * Advisory domain-rule matches for the audit payload:
7218
7206
  * reported, never enforced outside first-party fetch.
7219
7207
  */
7220
7208
  function advisoryMatches(chain, toolName) {
@@ -7222,7 +7210,7 @@ function advisoryMatches(chain, toolName) {
7222
7210
  }
7223
7211
  /**
7224
7212
  * Unmatchable segments (command/process substitution, here-docs) yield
7225
- * ask, ALWAYS, for any tool that has argv rules (docs/08, 5.2 step 3).
7213
+ * ask, ALWAYS, for any tool that has argv rules.
7226
7214
  */
7227
7215
  function argvUnmatchableAsk(chain, toolName, input) {
7228
7216
  if (![...chain.deny, ...chain.ask].some((rule) => "argv" in rule && (Array.isArray(rule.tool) ? rule.tool : [rule.tool]).includes(toolName))) return false;
@@ -7230,7 +7218,7 @@ function argvUnmatchableAsk(chain, toolName, input) {
7230
7218
  if (command === void 0) return true;
7231
7219
  return lexShellCommand(command).some((segment) => segment.unmatchable);
7232
7220
  }
7233
- /** A stub ToolContext for offline (dry-run) evaluations (docs/08, 4.5). */
7221
+ /** A stub ToolContext for offline (dry-run) evaluations. */
7234
7222
  function offlineContext(toolName) {
7235
7223
  return {
7236
7224
  runId: "dry-run",
@@ -7244,14 +7232,14 @@ function offlineContext(toolName) {
7244
7232
  }
7245
7233
  /**
7246
7234
  * Evaluates the chain for one dispatch, or OFFLINE against a
7247
- * hypothetical call by tool name (the dry-run API of docs/08, section
7248
- * 4.5: nothing executes; shells and tests read the verdict, the
7235
+ * hypothetical call by tool name (the dry-run API: nothing executes;
7236
+ * shells and tests read the verdict, the
7249
7237
  * deciding layer, and the matched rule). Hooks run in deterministic
7250
7238
  * registration order; { modifiedInput } substitutes the input and
7251
7239
  * continues; the first decisive verdict wins. The returned input is what
7252
- * execute receives and what the approval identity hashes (docs/03,
7253
- * section 1.2: post hook modification). Advisory domain-rule matches
7254
- * ride every verdict for the audit payload (docs/08, 4.4).
7240
+ * execute receives and what the approval identity hashes (post hook
7241
+ * modification). Advisory domain-rule matches
7242
+ * ride every verdict for the audit payload.
7255
7243
  */
7256
7244
  async function evaluatePermission(chain, tool, input, ctx) {
7257
7245
  const def = typeof tool === "string" ? {
@@ -7439,8 +7427,8 @@ function formatRePrompt(issues, attempt, maxAttempts) {
7439
7427
  * with M3/M4; the escalated status arrives in M3 as the flagged breaking
7440
7428
  * change.
7441
7429
  *
7442
- * Owning specs: docs/06-execution-spec.md, section "Agent runtime
7443
- * binding"; docs/04-model-layer-spec.md (roles, tiers, refusal).
7430
+ * Docs: https://docs.rulvar.com/guide/agents (agent runtime binding);
7431
+ * https://docs.rulvar.com/guide/model-routing (roles, tiers, refusal).
7444
7432
  */
7445
7433
  function isEscalated(r) {
7446
7434
  return r.status === "escalated";
@@ -7466,8 +7454,7 @@ function addUsage(total, turn) {
7466
7454
  }
7467
7455
  /**
7468
7456
  * The Usage invariant is verified at the adapter boundary: inputTokens is
7469
- * the FULL prompt including cache reads and writes (docs/04, section
7470
- * "Usage invariant").
7457
+ * the FULL prompt including cache reads and writes.
7471
7458
  */
7472
7459
  function assertUsageInvariant(usage, adapterId) {
7473
7460
  if (usage.inputTokens < usage.cacheReadTokens + usage.cacheWriteTokens) throw new Error(`adapter '${adapterId}' violated the Usage invariant: inputTokens (${usage.inputTokens}) < cacheReadTokens + cacheWriteTokens (${usage.cacheReadTokens} + ${usage.cacheWriteTokens})`);
@@ -7612,7 +7599,7 @@ function buildRequest(resolved, messages, limits, tools) {
7612
7599
  * parts go at the HEAD: on both first-class providers the retained
7613
7600
  * blocks (thinking blocks, reasoning items) precede the turn's text and
7614
7601
  * tool calls, and head placement reproduces that order on re-projection
7615
- * (docs/04, section 2.3, M4-T02).
7602
+ * (M4-T02).
7616
7603
  */
7617
7604
  function assistantMsg(turn, retained = []) {
7618
7605
  const parts = [...retained];
@@ -7636,8 +7623,7 @@ function assistantMsg(turn, retained = []) {
7636
7623
  * surfaced to the model as error tool results and never thrown past
7637
7624
  * policy: unknown names, argument-validation issues, ModelRetry (bounded
7638
7625
  * per tool call chain), NonSerializableValueError, and arbitrary execute
7639
- * throws all land as { isError: true } results (docs/08, sections 1.1 and
7640
- * 2.4; docs/06, section "ModelRetry").
7626
+ * throws all land as { isError: true } results.
7641
7627
  */
7642
7628
  async function executeToolCall(options) {
7643
7629
  const { call, runtime } = options;
@@ -8581,7 +8567,7 @@ async function runAgent(options) {
8581
8567
  * ceiling severing live streams, with partial usage written usageApprox.
8582
8568
  * B0 is immutable after start: no API tops it up.
8583
8569
  *
8584
- * The account tree (docs/06, section 5.4): the run root plus one
8570
+ * The account tree: the run root plus one
8585
8571
  * sub-account per admitted child workflow (and, from M7, the orchestrator
8586
8572
  * account and plan/NodeId accounts). A child's spend propagates to ALL
8587
8573
  * ancestors up to the run root; the root ceiling remains the true
@@ -8590,11 +8576,11 @@ async function runAgent(options) {
8590
8576
  * reserves are recovered from spawn-admission decision entries); the
8591
8577
  * per-account historical fold completes with DEF-7 in M7.
8592
8578
  *
8593
- * Owning spec: docs/06-execution-spec.md, section "Three-layer budget".
8579
+ * Full contract: https://docs.rulvar.com/guide/budgets
8594
8580
  */
8595
- /** Last resort of the admission reserve formula (docs/06, Appendix A). */
8581
+ /** Last resort of the admission reserve formula. */
8596
8582
  const DEFAULT_FLAT_RESERVE_USD = .5;
8597
- /** The run-root account scope (docs/06, section 5.4 scope vocabulary). */
8583
+ /** The run-root account scope. */
8598
8584
  const ROOT_ACCOUNT = "run";
8599
8585
  const ZERO_USAGE = {
8600
8586
  inputTokens: 0,
@@ -8603,8 +8589,7 @@ const ZERO_USAGE = {
8603
8589
  cacheWriteTokens: 0
8604
8590
  };
8605
8591
  /**
8606
- * The admission reserve for a spawn (docs/06, section "Layer 1: admission
8607
- * before spawn"): opts.estCost, else profile.estCost, else
8592
+ * The admission reserve for a spawn: opts.estCost, else profile.estCost, else
8608
8593
  * price(countTokens(input) + caps.maxOutputTokens), else the engine flat
8609
8594
  * default.
8610
8595
  */
@@ -8670,7 +8655,7 @@ var RunBudget = class {
8670
8655
  return chain;
8671
8656
  }
8672
8657
  /**
8673
- * Opens a child sub-account under `parentScope` (docs/06, section 5.4).
8658
+ * Opens a child sub-account under `parentScope`.
8674
8659
  * Re-opening an existing scope is the resume roll-forward path: the
8675
8660
  * recorded ceiling wins once and the accumulated state is kept.
8676
8661
  */
@@ -8727,7 +8712,7 @@ var RunBudget = class {
8727
8712
  /**
8728
8713
  * Marks the run exhausted without a ceiling event: the orchestrator
8729
8714
  * finalize fallback maps to outcome 'exhausted' with the synthesized
8730
- * partial value (DEF-7, docs/07 12.4; exhaustion is never null).
8715
+ * partial value (DEF-7; exhaustion is never null).
8731
8716
  */
8732
8717
  markExhausted() {
8733
8718
  this.exhaustedInternal = true;
@@ -8744,7 +8729,7 @@ var RunBudget = class {
8744
8729
  * Layer 1: admission before spawn. Blocks when spent + committedReserve
8745
8730
  * has reached the ceiling on ANY account in the ancestor chain of
8746
8731
  * `accountScope`, otherwise commits the reserve along the whole chain.
8747
- * Also enforces the engine lifetime spawn cap (docs/06, "Scheduler").
8732
+ * Also enforces the engine lifetime spawn cap.
8748
8733
  */
8749
8734
  admitSpawn(reserveUsd, accountScope = "run") {
8750
8735
  if (this.agentsSpawnedInternal >= this.lifetimeSpawnCap) {
@@ -8768,7 +8753,7 @@ var RunBudget = class {
8768
8753
  /**
8769
8754
  * Resume roll-forward: commits a reserve recovered from a journaled
8770
8755
  * spawn-admission decision entry without re-evaluating admission
8771
- * (docs/06, 5.1: reserves are recovered, never re-estimated).
8756
+ * (reserves are recovered, never re-estimated).
8772
8757
  */
8773
8758
  admitRecovered(reserveUsd, accountScope = "run") {
8774
8759
  this.agentsSpawnedInternal += 1;
@@ -8776,7 +8761,7 @@ var RunBudget = class {
8776
8761
  this.emitUpdate();
8777
8762
  }
8778
8763
  /**
8779
- * Registers the orchestrator finalize reserve (DEF-7, docs/07 12.2):
8764
+ * Registers the orchestrator finalize reserve (DEF-7):
8780
8765
  * absolute dollars set on the named account AND the run root, so
8781
8766
  * admission never lets any spawn eat the finalization money even
8782
8767
  * against whole-run exhaustion. Kept SEPARATE from committedReserveUsd
@@ -8852,7 +8837,7 @@ var RunBudget = class {
8852
8837
  agentsSpawned: this.agentsSpawnedInternal
8853
8838
  };
8854
8839
  }
8855
- /** Null when the run has no USD ceiling (docs/06, section "Canonical Ctx interface"). */
8840
+ /** Null when the run has no USD ceiling. */
8856
8841
  remaining() {
8857
8842
  const root = this.root;
8858
8843
  if (root.ceilingUsd === void 0) return null;
@@ -8877,8 +8862,8 @@ var RunBudget = class {
8877
8862
  /**
8878
8863
  * AdmissionController v1 (M6-T06; DEF-2, DEF-3, DEF-5 substrate).
8879
8864
  *
8880
- * Owning spec: docs/07-adaptive-orchestration-spec.md, section
8881
- * "AdmissionController". The single admission point for ALL spawns of any
8865
+ * Public contract: https://docs.rulvar.com/guide/adaptive-orchestration.
8866
+ * The single admission point for ALL spawns of any
8882
8867
  * origin: ctx.workflow, the orchestrator spawn tools (M6-T07), escalation
8883
8868
  * decomposition and rung respawns (M7). `admit(spec)` is called BEFORE
8884
8869
  * the carrying spawn-admission decision entry is journaled; the verdict
@@ -8941,8 +8926,8 @@ var AdmissionController = class {
8941
8926
  return this.lineageLimits;
8942
8927
  }
8943
8928
  /**
8944
- * Binds the run's TerminationAccount (DEF-2; PlanRunner runs only,
8945
- * docs/07 section 1): from bind time on, every admitted spawn of any
8929
+ * Binds the run's TerminationAccount (DEF-2; PlanRunner runs only):
8930
+ * from bind time on, every admitted spawn of any
8946
8931
  * origin debits one spawnUnit atomically with its decision entry, and
8947
8932
  * a declared ladder longer than the frozen kMax rejects with
8948
8933
  * ladder_exceeds_frozen. Non-PlanRunner runs never bind an account and
@@ -8957,7 +8942,7 @@ var AdmissionController = class {
8957
8942
  return this.terminationAccount;
8958
8943
  }
8959
8944
  /**
8960
- * The lineage half of admission (DEF-3, docs/03 section 10.5): folds are
8945
+ * The lineage half of admission (DEF-3): folds are
8961
8946
  * computed live STRICTLY BEFORE the carrying decision entry is appended;
8962
8947
  * the caller embeds the returned block in the entry and replay reads it
8963
8948
  * back byte-exact. Enforces the single-live-attempt invariant
@@ -9170,7 +9155,7 @@ var AdmissionController = class {
9170
9155
  * Resume roll-forward for a child that already SETTLED before the
9171
9156
  * resume: re-registers the counters (maxChildrenPerNode, the lifetime
9172
9157
  * cap, statsBefore fidelity) without committing any reserve; the spend
9173
- * itself sits in the root ledger seed (docs/03, 13.3).
9158
+ * itself sits in the root ledger seed.
9174
9159
  */
9175
9160
  recoverSettled(parentAccountScope) {
9176
9161
  this.budget.admitRecovered(0, parentAccountScope);
@@ -9180,8 +9165,8 @@ var AdmissionController = class {
9180
9165
  /**
9181
9166
  * Resume roll-forward for an admission whose decision entry exists but
9182
9167
  * whose child has NOT settled: re-applies the recorded reserve and
9183
- * counters without re-evaluating any limit (docs/07, 7.1: replay never
9184
- * re-evaluates admission; docs/06, 5.1: reserves are recovered, never
9168
+ * counters without re-evaluating any limit (replay never
9169
+ * re-evaluates admission; reserves are recovered, never
9185
9170
  * re-estimated).
9186
9171
  */
9187
9172
  recoverInFlight(parentAccountScope, verdict) {
@@ -9195,7 +9180,7 @@ var AdmissionController = class {
9195
9180
  //#endregion
9196
9181
  //#region src/orchestrator/handles.ts
9197
9182
  /**
9198
- * The committed WakeDigest render budget (docs/06, Appendix A: 400
9183
+ * The committed WakeDigest render budget (Appendix A: 400
9199
9184
  * chars per outputSummary row, the character measure; committed at M10
9200
9185
  * entry by adopting the implemented distillation cap unchanged, the
9201
9186
  * value frozen into every cassette since M6). One value serves both
@@ -9205,8 +9190,8 @@ var AdmissionController = class {
9205
9190
  const WAKE_SUMMARY_RENDER_BUDGET_CHARS = 400;
9206
9191
  /**
9207
9192
  * The M6 outputSummary: a deterministic truncation of the child's
9208
- * output (or error message), identical live and on replay (docs/07
9209
- * section 2, clause 3: distillation lives with the child, ordered by
9193
+ * output (or error message), identical live and on replay (distillation
9194
+ * lives with the child, ordered by
9210
9195
  * spawn ordinal; the LLM distillation upgrade is M7 territory).
9211
9196
  */
9212
9197
  function summarizeOutput(result) {
@@ -9226,7 +9211,7 @@ function digestOf(record, result) {
9226
9211
  }
9227
9212
  //#endregion
9228
9213
  //#region src/orchestrator/wake.ts
9229
- /** docs/07 4.8: the wait_for_events parameter schema (normative). */
9214
+ /** The wait_for_events parameter schema (normative). */
9230
9215
  const WAIT_FOR_EVENTS_SCHEMA = {
9231
9216
  type: "object",
9232
9217
  additionalProperties: false,
@@ -9303,7 +9288,7 @@ function emptyDigestBlocks() {
9303
9288
  }
9304
9289
  //#endregion
9305
9290
  //#region src/orchestrator/spawn-tools.ts
9306
- /** docs/07 4.2: the spawn_agent parameter schema (normative). */
9291
+ /** The spawn_agent parameter schema (normative). */
9307
9292
  const SPAWN_AGENT_SCHEMA = {
9308
9293
  type: "object",
9309
9294
  additionalProperties: false,
@@ -9354,7 +9339,7 @@ const SPAWN_AGENT_SCHEMA = {
9354
9339
  taskClass: { type: "string" }
9355
9340
  }
9356
9341
  };
9357
- /** docs/07 4.3: parallel_agents wraps the spawn_agent params. */
9342
+ /** parallel_agents wraps the spawn_agent params. */
9358
9343
  const PARALLEL_AGENTS_SCHEMA = {
9359
9344
  type: "object",
9360
9345
  additionalProperties: false,
@@ -9366,7 +9351,7 @@ const PARALLEL_AGENTS_SCHEMA = {
9366
9351
  } },
9367
9352
  $defs: { spawnAgentParams: SPAWN_AGENT_SCHEMA }
9368
9353
  };
9369
- /** docs/07 4.4: await_any and await_all share one parameter shape. */
9354
+ /** await_any and await_all share one parameter shape. */
9370
9355
  const AWAIT_SCHEMA = {
9371
9356
  type: "object",
9372
9357
  additionalProperties: false,
@@ -9380,7 +9365,7 @@ const AWAIT_SCHEMA = {
9380
9365
  }
9381
9366
  } }
9382
9367
  };
9383
- /** docs/07 4.5: cancel_agent. */
9368
+ /** The cancel_agent parameter schema. */
9384
9369
  const CANCEL_AGENT_SCHEMA = {
9385
9370
  type: "object",
9386
9371
  additionalProperties: false,
@@ -9393,7 +9378,7 @@ const CANCEL_AGENT_SCHEMA = {
9393
9378
  reason: { type: "string" }
9394
9379
  }
9395
9380
  };
9396
- /** docs/07 4.11: finish; result validates against the declared output schema. */
9381
+ /** finish; result validates against the declared output schema. */
9397
9382
  const FINISH_SCHEMA = {
9398
9383
  type: "object",
9399
9384
  additionalProperties: false,
@@ -9407,7 +9392,7 @@ const FINISH_TOOL_NAME = "finish";
9407
9392
  /**
9408
9393
  * Builds the mode (c) toolset over the per-call runtime. profileCardText
9409
9394
  * rides the spawn tools' descriptions so both modes speak one agent
9410
- * vocabulary (docs/06 9.3; M6-T04).
9395
+ * vocabulary (M6-T04).
9411
9396
  */
9412
9397
  function buildOrchestratorTools(runtime, profileCardText) {
9413
9398
  return [
@@ -9508,15 +9493,13 @@ function runtimeOf(ctx) {
9508
9493
  * log, budget, and the deterministic shims; workflow/orchestrate/
9509
9494
  * awaitExternal/brief land with their milestones (M2/M6).
9510
9495
  *
9511
- * Owning spec: docs/06-execution-spec.md, sections "Canonical Ctx
9512
- * interface", "Error policy and dropped results", "Scheduler".
9496
+ * Public contract: https://docs.rulvar.com/guide/workflows.
9513
9497
  */
9514
9498
  /**
9515
9499
  * The rejection carrier of ctx.agent value-form calls: a real Error that
9516
- * structurally satisfies the typed AgentError (docs/06, section "ctx.agent
9517
- * and AgentOpts") and carries the full AgentResult for Settled mapping.
9518
- * Deliberately not a RulvarError: AgentError is not in the closed code
9519
- * registry (docs/02, section "Error taxonomy").
9500
+ * structurally satisfies the typed AgentError and carries the full
9501
+ * AgentResult for Settled mapping. Deliberately not a RulvarError:
9502
+ * AgentError is not in the closed code registry.
9520
9503
  */
9521
9504
  var AgentCallError = class extends Error {
9522
9505
  kind;
@@ -9560,9 +9543,9 @@ function bump(map, key, usd) {
9560
9543
  }
9561
9544
  /**
9562
9545
  * Completes a model-authored escalation request into the full report:
9563
- * costToDate and salvage are runtime-filled, never model-filled (docs/07,
9564
- * section 6.3). The worktree patch ref lands after collect(); the
9565
- * pre-dispose preview for flavor B decision-makers omits it.
9546
+ * costToDate and salvage are runtime-filled, never model-filled. The
9547
+ * worktree patch ref lands after collect(); the pre-dispose preview for
9548
+ * flavor B decision-makers omits it.
9566
9549
  */
9567
9550
  function buildEscalationReport(request, result, worktreePatchRef) {
9568
9551
  return {
@@ -10528,7 +10511,7 @@ function createCtx(internals) {
10528
10511
  }
10529
10512
  /**
10530
10513
  * Per-(scope, name) invocation ordinals of ctx.workflow, in execution
10531
- * order (docs/03, section 2.2: nested workflow scopes).
10514
+ * order (nested workflow scopes).
10532
10515
  */
10533
10516
  const workflowOrdinals = /* @__PURE__ */ new Map();
10534
10517
  const nextWorkflowOrdinal = (scope, name) => {
@@ -10546,7 +10529,7 @@ function createCtx(internals) {
10546
10529
  rebuilt.name = wire.code;
10547
10530
  return rebuilt;
10548
10531
  };
10549
- /** Maps an embedded admission rejection onto its typed error (docs/07, 7.3). */
10532
+ /** Maps an embedded admission rejection onto its typed error. */
10550
10533
  const rejectionError = (reason, name) => {
10551
10534
  if (reason.code === "budget" || reason.code === "lifetime") return new BudgetExhaustedError(`admission rejected child workflow '${name}' (${reason.code})`, { data: { reason } });
10552
10535
  return new AdmissionRejectedError(`admission rejected child workflow '${name}' (${reason.code}; maxDepth/maxChildrenPerNode are set via createEngine budgetDefaults)`, { data: { reason } });
@@ -10850,8 +10833,7 @@ async function executeWorkflow(internals, wf, args) {
10850
10833
  /**
10851
10834
  * The mode (c) dynamic orchestrator (M6-T07/T08).
10852
10835
  *
10853
- * Owning spec: docs/06-execution-spec.md section 9.3 and
10854
- * docs/07-adaptive-orchestration-spec.md sections 1 and 4. An ordinary
10836
+ * Full contract: https://docs.rulvar.com/guide/adaptive-orchestration. An ordinary
10855
10837
  * workflow whose agent (role 'orchestrate') holds the typed spawn tools;
10856
10838
  * both surfaces (top-level orchestrate() and ctx.orchestrate) share this
10857
10839
  * one implementation, the nested surface riding ctx.workflow so the
@@ -10862,7 +10844,7 @@ async function executeWorkflow(internals, wf, args) {
10862
10844
  * ordinary kind 'agent' entry; a crashed orchestrate() restores its
10863
10845
  * history from the checkpoint and finds child results by content keys,
10864
10846
  * WITHOUT regenerating spawn decisions and without re-paying children.
10865
- * Non-PlanRunner applicability (docs/07 section 1): only the lifetime
10847
+ * Non-PlanRunner applicability: only the lifetime
10866
10848
  * cap, maxDepth, and the budget layers apply; no termination.init is
10867
10849
  * written; escalated children simply settle into their digests.
10868
10850
  */
@@ -10881,7 +10863,7 @@ function orchestratorPrompt(goal, maxSpawns, extensionLines) {
10881
10863
  }
10882
10864
  /**
10883
10865
  * Resolves per-spawn dispatch options against the engine registries
10884
- * (docs/08: registered SchemaSpec and tool profile names; M7-T05). An
10866
+ * (registered SchemaSpec and tool profile names; M7-T05). An
10885
10867
  * unknown ref is a typed ConfigError, surfaced as a tool error to the
10886
10868
  * orchestrator and never a run failure.
10887
10869
  */
@@ -11164,7 +11146,7 @@ function makeOrchestratorWorkflow(goal, opts) {
11164
11146
  const forcedFinishController = new AbortController();
11165
11147
  let capInFlight = false;
11166
11148
  /**
11167
- * The at-cap freeze (docs/07, 12.4): EXACTLY one decision entry
11149
+ * The at-cap freeze: EXACTLY one decision entry
11168
11150
  * strictly before any effects; then the plan freezes for adaptation,
11169
11151
  * wake triggers except quiescence disarm, and the orchestrator is
11170
11152
  * driven to the reserved final wake. Crash between the entry and the
@@ -11211,7 +11193,7 @@ function makeOrchestratorWorkflow(goal, opts) {
11211
11193
  }, callingState.spanId);
11212
11194
  forcedFinishController.abort("rulvar:forced-finish");
11213
11195
  };
11214
- /** Layer-1 soft boundary before delivering each wake (docs/07, 12.3). */
11196
+ /** Layer-1 soft boundary before delivering each wake. */
11215
11197
  const overSoftBoundary = () => {
11216
11198
  if (capState === void 0 || orchestratorAccount === void 0 || extension === void 0) return false;
11217
11199
  return (internals.budget.accountView(orchestratorAccount)?.spentUsd ?? 0) + capState.turnEstimateUsd > capState.effectiveCapUsd - capState.finalizeReserveUsd;
@@ -11463,7 +11445,7 @@ function makeOrchestratorWorkflow(goal, opts) {
11463
11445
  ladders,
11464
11446
  ...internals.floors === void 0 ? {} : { floors: internals.floors },
11465
11447
  now: new Date(internals.now()).toISOString()
11466
- }), ladders);
11448
+ }), ladders, { profiles: advertisedProfiles });
11467
11449
  await internals.replayer.appendSinglePhase({
11468
11450
  scope: callingState.scope,
11469
11451
  key,
@@ -11520,6 +11502,7 @@ function makeOrchestratorWorkflow(goal, opts) {
11520
11502
  role: "orchestrate",
11521
11503
  result: "full",
11522
11504
  tools: [...buildOrchestratorTools(orchestratorRuntime, fullCardText), ...extension?.tools(io) ?? []],
11505
+ ...capState === void 0 ? {} : { estCost: capState.effectiveCapUsd },
11523
11506
  ...opts?.model === void 0 ? {} : { model: opts.model },
11524
11507
  ...opts?.limits === void 0 ? {} : { limits: opts.limits },
11525
11508
  [kOnRunning]: (seq) => {
@@ -11533,7 +11516,7 @@ function makeOrchestratorWorkflow(goal, opts) {
11533
11516
  if (orchestratorAccount !== void 0) orchestratorState.budgetScope = orchestratorAccount;
11534
11517
  orchestratorState.signal = callingState.signal === void 0 ? forcedFinishController.signal : AbortSignal.any([callingState.signal, forcedFinishController.signal]);
11535
11518
  /**
11536
- * The reserved final wake (docs/07, 12.4 d): a FRESH agent entry on
11519
+ * The reserved final wake: a FRESH agent entry on
11537
11520
  * the restricted single-tool toolset (a different toolsetHash), a
11538
11521
  * prompt deterministically derived from the journaled cap decision
11539
11522
  * and the pinned digest, and a finalizeTurns limit, paid from the
@@ -11551,6 +11534,7 @@ function makeOrchestratorWorkflow(goal, opts) {
11551
11534
  result: "full",
11552
11535
  tools: finishOnly,
11553
11536
  limits: { maxTurns: capState?.finalizeTurns ?? 2 },
11537
+ ...capState === void 0 ? {} : { estCost: capState.finalizeReserveUsd },
11554
11538
  ...opts?.model === void 0 ? {} : { model: opts.model },
11555
11539
  [kTerminalTool]: { name: FINISH_TOOL_NAME }
11556
11540
  };
@@ -11603,7 +11587,7 @@ function makeOrchestratorWorkflow(goal, opts) {
11603
11587
  return result.output;
11604
11588
  });
11605
11589
  }
11606
- /** Top-level surface: creates a run (docs/06 9.3). */
11590
+ /** Top-level surface: creates a run. */
11607
11591
  function orchestrate(engine, goal, opts) {
11608
11592
  return engine.run(makeOrchestratorWorkflow(goal, opts), void 0);
11609
11593
  }
@@ -11613,16 +11597,13 @@ function orchestrate(engine, goal, opts) {
11613
11597
  * Per-run event machinery (M1-T10): the span registry (run > phase >
11614
11598
  * agent > tool > child hierarchy) and the event bus that stamps the
11615
11599
  * WorkflowEvent envelope, feeds RunHandle.events / on(), and fans out to
11616
- * subscribers. EventSink is deliberately not an SPI (docs/02, section
11617
- * "SPI seams and the 1.0 freeze").
11600
+ * subscribers. EventSink is deliberately not an SPI.
11618
11601
  *
11619
- * Owning spec: docs/09-observability-testing-spec.md, section "Event
11620
- * stream".
11602
+ * Full contract: https://docs.rulvar.com/guide/observability.
11621
11603
  */
11622
11604
  /**
11623
11605
  * Spans form a tree per run; spanId values are engine-minted opaque
11624
- * strings, unique per run, pure telemetry, never identity (docs/09,
11625
- * section "Span hierarchy").
11606
+ * strings, unique per run, pure telemetry, never identity.
11626
11607
  */
11627
11608
  var SpanRegistry = class {
11628
11609
  parents = /* @__PURE__ */ new Map();
@@ -11787,15 +11768,14 @@ var InProcessRunner = class {
11787
11768
  * Engine entry points (M1-T11): createEngine and engine.run over the
11788
11769
  * InProcessRunner. Every registry hangs off the engine instance; nothing
11789
11770
  * is module-global, so two engines in one process are fully isolated and
11790
- * ctx is created per run (docs/02, section "Engine anatomy"; docs/06,
11791
- * section "Engine and ops API"). engine.resume lands with the journal
11771
+ * ctx is created per run. engine.resume lands with the journal
11792
11772
  * kernel in M2.
11793
11773
  */
11794
- /** Content hash of an in-process workflow body (run-to-definition binding, docs/06 10.2). */
11774
+ /** Content hash of an in-process workflow body (run-to-definition binding). */
11795
11775
  function hashWorkflowBody(wf) {
11796
11776
  return createHash("sha256").update(wf.body.toString(), "utf8").digest("hex");
11797
11777
  }
11798
- /** Content hash of a compiled workflow source (run-to-definition binding, docs/06 10.2). */
11778
+ /** Content hash of a compiled workflow source (run-to-definition binding). */
11799
11779
  function hashWorkflowSource(source) {
11800
11780
  return createHash("sha256").update(source, "utf8").digest("hex");
11801
11781
  }
@@ -12212,11 +12192,11 @@ function createEngine(options) {
12212
12192
  /**
12213
12193
  * The host half of the worker sandbox contract (M6-T02).
12214
12194
  *
12215
- * Owning spec: docs/06-execution-spec.md, section 8.2. WorkerSandboxRunner
12195
+ * Full contract: https://docs.rulvar.com/guide/planner. WorkerSandboxRunner
12216
12196
  * (@rulvar/planner) owns the worker lifecycle and the MessagePort; this
12217
12197
  * core-owned bridge serves every sandbox primitive against the canonical
12218
- * ctx of the run, so the runner builds exclusively from the public API
12219
- * (docs/02, dependency rules). The boundary is journal-compatible JSON
12198
+ * ctx of the run, so the runner builds exclusively from the public API.
12199
+ * The boundary is journal-compatible JSON
12220
12200
  * validated on both sides; raw structured clone is NOT the contract.
12221
12201
  *
12222
12202
  * Responsibilities: