@north-light/crouter 0.3.236 → 0.3.238

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 (177) hide show
  1. package/dist/api/client.d.ts +2 -1
  2. package/dist/api/client.js +3 -0
  3. package/dist/api/dto/broker-ops.d.ts +1 -1
  4. package/dist/api/dto/common.d.ts +2 -0
  5. package/dist/api/dto/lifecycle.d.ts +9 -2
  6. package/dist/api/dto/messages.d.ts +5 -0
  7. package/dist/api/dto/nodes.d.ts +10 -2
  8. package/dist/api/dto/worktree.d.ts +10 -0
  9. package/dist/api/routes.d.ts +1 -0
  10. package/dist/api/routes.js +1 -0
  11. package/dist/builtin-memory/00-runtime-base/00-authoring.md +1 -1
  12. package/dist/builtin-memory/internal/agent-shaping.md +1 -1
  13. package/dist/builtin-pi-packages/pi-crtr-extensions/README.md +1 -1
  14. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/sysprompt-window.ts +1 -1
  15. package/dist/clients/attach/render/tool-calls.js +18 -3
  16. package/dist/clients/attach/viewer.js +442 -442
  17. package/dist/clients/inbox/review/__tests__/editor-roundtrip.test.js +6 -0
  18. package/dist/clients/inbox/review/roundtrip.js +1 -1
  19. package/dist/commands/memory/lint.js +22 -13
  20. package/dist/commands/memory/shared.js +1 -1
  21. package/dist/commands/node/create.d.ts +1 -1
  22. package/dist/commands/node/create.js +1 -1
  23. package/dist/commands/node/lifecycle.js +1 -1
  24. package/dist/commands/node-worktree.js +39 -8
  25. package/dist/commands/revive.js +11 -2
  26. package/dist/commands/sys/context/admin/actions.d.ts +48 -0
  27. package/dist/commands/sys/context/admin/actions.js +246 -0
  28. package/dist/commands/sys/context/admin/axes-panel.d.ts +21 -0
  29. package/dist/commands/sys/context/admin/axes-panel.js +117 -0
  30. package/dist/commands/sys/context/admin/detail-panel.d.ts +53 -0
  31. package/dist/commands/sys/context/admin/detail-panel.js +178 -0
  32. package/dist/commands/sys/context/admin/docs-panel.d.ts +45 -0
  33. package/dist/commands/sys/context/admin/docs-panel.js +186 -0
  34. package/dist/commands/sys/context/admin/model.d.ts +75 -0
  35. package/dist/commands/sys/context/admin/model.js +209 -0
  36. package/dist/commands/sys/context/admin/shell.d.ts +82 -0
  37. package/dist/commands/sys/context/admin/shell.js +669 -0
  38. package/dist/commands/sys/context/admin.d.ts +1 -0
  39. package/dist/commands/sys/context/admin.js +124 -0
  40. package/dist/commands/sys/context/doc.d.ts +1 -0
  41. package/dist/commands/sys/context/doc.js +196 -0
  42. package/dist/commands/sys/context/prompt-review.d.ts +1 -0
  43. package/dist/commands/sys/{prompt-review.js → context/prompt-review.js} +10 -10
  44. package/dist/commands/sys/context/resolve.d.ts +55 -0
  45. package/dist/commands/sys/context/resolve.js +428 -0
  46. package/dist/commands/sys/context/sysprompt.d.ts +1 -0
  47. package/dist/commands/sys/{sysprompt.js → context/sysprompt.js} +11 -11
  48. package/dist/commands/sys/context.d.ts +2 -0
  49. package/dist/commands/sys/context.js +20 -0
  50. package/dist/commands/sys.js +3 -4
  51. package/dist/core/__tests__/child-death-wake.test.js +38 -22
  52. package/dist/core/__tests__/cron-self-cancel-delivery.test.js +72 -0
  53. package/dist/core/__tests__/daemon-boot.test.js +40 -28
  54. package/dist/core/__tests__/human-deliver.test.js +1 -1
  55. package/dist/core/__tests__/human-node-not-supervised.test.js +10 -15
  56. package/dist/core/__tests__/integration/revive.test.js +12 -12
  57. package/dist/core/__tests__/integration/spawn-root.test.js +6 -0
  58. package/dist/core/__tests__/integration/worktree-reap.test.js +32 -0
  59. package/dist/core/__tests__/lifecycle-terminal-reasons.test.d.ts +1 -0
  60. package/dist/core/__tests__/lifecycle-terminal-reasons.test.js +61 -0
  61. package/dist/core/__tests__/migration.test.js +38 -1
  62. package/dist/core/__tests__/push-final-guard.test.js +1 -1
  63. package/dist/core/__tests__/revive-capacity.test.d.ts +1 -0
  64. package/dist/core/__tests__/revive-capacity.test.js +127 -0
  65. package/dist/core/__tests__/revive-parked-fresh.test.js +7 -7
  66. package/dist/core/__tests__/seam/broker-cap-freeze.test.d.ts +1 -0
  67. package/dist/core/__tests__/seam/broker-cap-freeze.test.js +81 -0
  68. package/dist/core/__tests__/seam/dormancy-release.test.js +7 -7
  69. package/dist/core/__tests__/seam/yield-refresh-transaction.test.js +1 -1
  70. package/dist/core/canvas/browse/render.js +11 -3
  71. package/dist/core/canvas/canvas.d.ts +15 -3
  72. package/dist/core/canvas/canvas.js +52 -16
  73. package/dist/core/canvas/migrations.js +21 -0
  74. package/dist/core/canvas/nav-render.js +3 -1
  75. package/dist/core/canvas/remote-canvas-source.js +2 -0
  76. package/dist/core/canvas/render-source.d.ts +3 -1
  77. package/dist/core/canvas/render-source.js +7 -3
  78. package/dist/core/canvas/status-glyph.d.ts +10 -1
  79. package/dist/core/canvas/status-glyph.js +19 -3
  80. package/dist/core/canvas/types.d.ts +20 -1
  81. package/dist/core/feed/feed.js +1 -1
  82. package/dist/core/human/feedback-companion.js +5 -2
  83. package/dist/core/keybindings/catalog.d.ts +1 -1
  84. package/dist/core/keybindings/catalog.js +6 -2
  85. package/dist/core/memory/lint.d.ts +19 -0
  86. package/dist/core/memory/lint.js +97 -0
  87. package/dist/core/memory-resolver.d.ts +4 -0
  88. package/dist/core/memory-resolver.js +7 -7
  89. package/dist/core/preview-registry.js +4 -4
  90. package/dist/core/review/realize.js +4 -2
  91. package/dist/core/runtime/close.js +1 -1
  92. package/dist/core/runtime/fleet.d.ts +29 -1
  93. package/dist/core/runtime/fleet.js +23 -0
  94. package/dist/core/runtime/host.js +32 -11
  95. package/dist/core/runtime/launch-prompt.d.ts +1 -1
  96. package/dist/core/runtime/launch-prompt.js +2 -2
  97. package/dist/core/runtime/lifecycle.d.ts +4 -2
  98. package/dist/core/runtime/lifecycle.js +12 -1
  99. package/dist/core/runtime/memory.js +1 -1
  100. package/dist/core/runtime/reopen.js +10 -1
  101. package/dist/core/runtime/reset.js +1 -1
  102. package/dist/core/runtime/revive-all.d.ts +3 -0
  103. package/dist/core/runtime/revive-all.js +10 -4
  104. package/dist/core/runtime/revive.d.ts +12 -0
  105. package/dist/core/runtime/revive.js +62 -3
  106. package/dist/core/runtime/spawn.d.ts +4 -0
  107. package/dist/core/runtime/spawn.js +36 -7
  108. package/dist/core/runtime/tmux-bindings.js +4 -0
  109. package/dist/core/substrate/frontmatter-validation.js +1 -1
  110. package/dist/core/substrate/index.d.ts +6 -3
  111. package/dist/core/substrate/index.js +4 -3
  112. package/dist/core/substrate/on-read.d.ts +0 -8
  113. package/dist/core/substrate/on-read.js +45 -124
  114. package/dist/core/substrate/plan.d.ts +94 -0
  115. package/dist/core/substrate/plan.js +266 -0
  116. package/dist/core/substrate/render.d.ts +5 -6
  117. package/dist/core/substrate/render.js +76 -129
  118. package/dist/core/substrate/schema.d.ts +1 -6
  119. package/dist/core/substrate/schema.js +1 -15
  120. package/dist/core/substrate/session-cache.d.ts +2 -10
  121. package/dist/core/substrate/session-cache.js +9 -47
  122. package/dist/core/substrate/subject-fields.d.ts +1 -4
  123. package/dist/core/substrate/subject-fields.js +2 -5
  124. package/dist/core/substrate/surface-match.d.ts +37 -17
  125. package/dist/core/substrate/surface-match.js +44 -53
  126. package/dist/core/tui/page-host.d.ts +6 -0
  127. package/dist/core/tui/page-host.js +12 -3
  128. package/dist/core/worktree.d.ts +8 -0
  129. package/dist/core/worktree.js +65 -0
  130. package/dist/daemon/__tests__/reconciler-signature.test.js +3 -2
  131. package/dist/daemon/__tests__/thaw-order.test.d.ts +1 -0
  132. package/dist/daemon/__tests__/thaw-order.test.js +55 -0
  133. package/dist/daemon/api/__tests__/canvas-snapshot-fields.test.js +6 -0
  134. package/dist/daemon/api/__tests__/node-create-description.test.js +6 -0
  135. package/dist/daemon/api/__tests__/profile-launch-gates.test.js +1 -1
  136. package/dist/daemon/api/bridge.js +5 -1
  137. package/dist/daemon/api/handlers/attach.js +5 -1
  138. package/dist/daemon/api/handlers/bash-jobs.js +1 -1
  139. package/dist/daemon/api/handlers/messages.js +10 -2
  140. package/dist/daemon/api/handlers/nodes.js +8 -1
  141. package/dist/daemon/api/handlers/worktree.js +10 -1
  142. package/dist/daemon/api/map.js +7 -0
  143. package/dist/daemon/companion-retire.js +1 -1
  144. package/dist/daemon/cron/passes.d.ts +0 -5
  145. package/dist/daemon/cron/passes.js +0 -20
  146. package/dist/daemon/cron/sinks.js +1 -1
  147. package/dist/daemon/cron-run.d.ts +5 -6
  148. package/dist/daemon/cron-run.js +7 -16
  149. package/dist/daemon/crtrd.d.ts +0 -10
  150. package/dist/daemon/crtrd.js +23 -61
  151. package/dist/daemon/fleet.d.ts +10 -7
  152. package/dist/daemon/fleet.js +71 -12
  153. package/dist/daemon/messaging/node-message.js +3 -1
  154. package/dist/daemon/profile-delete.js +1 -1
  155. package/dist/daemon/reconcilers/broker-supervision.d.ts +26 -9
  156. package/dist/daemon/reconcilers/broker-supervision.js +39 -52
  157. package/dist/daemon/reconcilers/controller-death.d.ts +7 -0
  158. package/dist/daemon/reconcilers/controller-death.js +26 -0
  159. package/dist/daemon/reconcilers/cron-lane.d.ts +0 -3
  160. package/dist/daemon/reconcilers/cron-lane.js +0 -3
  161. package/dist/daemon/reconcilers/live-obligation.js +3 -1
  162. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +53 -0
  163. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +170 -0
  164. package/dist/daemon/reconcilers/node-lifecycle/respawn-policy.d.ts +5 -0
  165. package/dist/daemon/reconcilers/node-lifecycle/respawn-policy.js +16 -0
  166. package/dist/daemon/reconcilers/node-lifecycle/terminating.d.ts +4 -0
  167. package/dist/daemon/reconcilers/node-lifecycle/terminating.js +13 -0
  168. package/dist/daemon/reconcilers/node-lifecycle/tick.d.ts +26 -0
  169. package/dist/daemon/reconcilers/node-lifecycle/tick.js +140 -0
  170. package/package.json +1 -1
  171. package/runtime.lock.json +2 -2
  172. package/dist/commands/sys/prompt-review.d.ts +0 -1
  173. package/dist/commands/sys/sysprompt.d.ts +0 -1
  174. package/dist/core/__tests__/cron-broker-capacity.test.js +0 -119
  175. package/dist/daemon/reconcilers/dormant-inbox.d.ts +0 -18
  176. package/dist/daemon/reconcilers/dormant-inbox.js +0 -157
  177. /package/dist/core/__tests__/{cron-broker-capacity.test.d.ts → cron-self-cancel-delivery.test.d.ts} +0 -0
@@ -10,10 +10,10 @@
10
10
  // rung (preview → a `# read when:` routing line; name → a bare entry), a
11
11
  // directory that carries its own document rendering that entry on the
12
12
  // directory's own row; a doc with NO boot entry leaks only into its dir's
13
- // `[+N more]` count, never a name — EXCEPT a node-store doc with no boot entry:
14
- // renderKnowledgeForSubject floors it up to `name` so it still shows its bare
15
- // name rather than collapsing into a hidden count. A node-store doc with boot
16
- // entries remains absent when none of those entries participates.
13
+ // `[+N more]` count, never a name — EXCEPT a node-store doc with no boot entry,
14
+ // which the plan floors up to `name` so it still shows its bare name rather
15
+ // than collapsing into a hidden count. A node-store doc with boot entries
16
+ // remains absent when none of those entries participates.
17
17
  //
18
18
  // • the SYSTEM-PROMPT half — `<memory kind="preference">`
19
19
  // (renderPreferencesSection) — the always-present memory-usage guidance plus
@@ -31,13 +31,11 @@
31
31
  // context rank. It never rewrites the frozen system snapshot.
32
32
  //
33
33
  // The pipeline is explicitly TWO-STEP and must never run in the other order:
34
- // 1. SELECT WINNERS — MemoryDoc → parseSubstrateDoc → (null-filter
35
- // non-substrate) → per-doc boot rung (`bootRung`, the max `at` over
36
- // participating `on: boot` surfaces entries) → gatePasses → first-wins DEDUP by
37
- // name over the resolver's precedence order (nearest project > user >
38
- // builtin). Node-store docs follow the same gate rule, but the
39
- // knowledge-block render floors only a node-store doc with no boot entry to
40
- // `name` — the one rung-floor exemption, scoped to the node store only.
34
+ // 1. PLAN — planDelivery (plan.ts) computes every document's boot decision:
35
+ // rung, gates, profile cap, the node-store floor, and first-wins DEDUP by
36
+ // name over the corpus's precedence order (nearest project > user >
37
+ // builtin), per boot partition. This module only projects the plan's
38
+ // winners into its BootDoc render shape.
41
39
  // 2. RENDER IN DISPLAY ORDER — split winners into content vs. the rest;
42
40
  // order content general→specific (builtin, user, project outermost→
43
41
  // nearest, node store last) then by tree position then filename; place
@@ -63,82 +61,74 @@
63
61
  // directory's own line, with the directory's members nested beneath it.
64
62
  import { sep } from 'node:path';
65
63
  import { interpolateNodePaths } from '../canvas/paths.js';
66
- import { listAllMemoryDocs, loadMemoryTargetView, resolveMemoryDoc } from '../memory-resolver.js';
64
+ import { ambientMemoryTarget, listAllMemoryDocs, loadMemoryTargetView, resolveMemoryDoc, } from '../memory-resolver.js';
67
65
  import { buildMemoryTree } from '../memory/tree.js';
68
66
  import { subKindsAvailableTo } from '../config.js';
69
67
  import { projectScopeRoots } from '../scope.js';
70
- import { gatePasses } from './gate.js';
71
68
  // De-barreled to LEAF modules on purpose: importing from './index.js' would
72
69
  // pull subject.js (canvas-db) transitively, re-tainting every CLI consumer of
73
70
  // the pure render fns. The canvas-db wrappers (renderPreferencesSection /
74
71
  // renderKnowledgeBlock) live in render-node.js instead.
75
72
  import { documentExposedAtOrAbove, registerDocumentExposure, } from './injected-store.js';
76
- import { bootRung, minRung, parseSubstrateDoc, previewLine, rungRank, } from './schema.js';
77
- import { cachedAllMemoryDocs, cachedSubstrateDocs } from './session-cache.js';
78
- /** First-wins dedup by (already-normalized) name, preserving the input's
79
- * precedence order. The array's ORDER is the precedence signal — callers must
80
- * pass docs already ordered highest-precedence-first; this function never
81
- * reorders, only drops later duplicates. */
82
- function dedupeFirstWins(docs) {
83
- const seen = new Set();
84
- const out = [];
85
- for (const d of docs) {
86
- if (seen.has(d.name))
87
- continue;
88
- seen.add(d.name);
89
- out.push(d);
90
- }
91
- return out;
92
- }
93
- /** STEP 1 — the resolver-provided WINNER docs of one `kind`, eligible at boot
94
- * for `subject`: parsed (non-substrate docs null-filtered), gate-passed, then
95
- * first-wins DEDUPED by name over the resolver's precedence order (nearest
96
- * project > user > builtin) — each at its own boot rung, INCLUDING docs with
97
- * NO boot entry (the tree counts them into `[+N more]`). This is winner
98
- * selection ONLY; display ordering is a separate, later step (see
99
- * renderGrouped) so a builtin doc can never mask a project/user override by
100
- * virtue of sorting first. Uses the per-session cache so the full corpus is
101
- * scanned + parsed at most once per session across the boot-render calls.
73
+ import { bootPartitionOf, planDelivery } from './plan.js';
74
+ import { parseSubstrateDoc, previewLine, rungRank, } from './schema.js';
75
+ import { cachedEventCorpusInclusive } from './session-cache.js';
76
+ /** The node's own store (`nodes/<id>/context/memory/`), loaded through the
77
+ * resolver's descriptor loader like every other store. Returned across ALL
78
+ * kinds — the node store is the catch-all this-node store and rides into the
79
+ * knowledge block.
102
80
  *
103
- * The profile relationship's cap applies HERE, before dedup: a `none` project
104
- * leaves the corpus entirely (it contributes nothing, not even a `[+N more]`
105
- * count, and so cannot shadow a wider-scope automatic winner), and every
106
- * surviving doc's authored boot rung is clamped to its cap before dedup, the
107
- * session cache, and delivery seeding see it. */
108
- function selectWinners(subject, kind) {
109
- let corpus;
81
+ * The store is addressed by the EXPLICIT `nodeId` rather than the host
82
+ * process's, because one process renders for other nodes (crtrd claiming a
83
+ * warm spare, `sys context prompt-review` walking the canvas). */
84
+ function nodeStoreCorpus(nodeId) {
85
+ if (nodeId === null)
86
+ return [];
87
+ let sources;
110
88
  try {
111
- corpus = bootCorpus();
89
+ // Narrowed to the node scope, so cwd and profile select no store.
90
+ sources = loadMemoryTargetView({ cwd: process.cwd(), profileId: null, nodeId }, { scope: 'node', quiet: true }).docs;
112
91
  }
113
92
  catch {
114
93
  return [];
115
94
  }
116
- const eligible = [];
117
- for (const d of corpus.docs) {
118
- if (d.kind !== kind || d.projectMemory === 'none' || !gatePasses(d, subject))
119
- continue;
120
- const source = corpus.sources.get(d.path);
121
- if (source === undefined)
122
- continue;
123
- eligible.push({ ...d, source, bootRung: minRung(bootRung(d, subject), d.projectMemory) });
95
+ const out = [];
96
+ for (const source of sources) {
97
+ const doc = parseSubstrateDoc(source);
98
+ if (doc !== null)
99
+ out.push({ doc, source });
124
100
  }
125
- return dedupeFirstWins(eligible);
101
+ return out;
126
102
  }
127
- const bootSources = new WeakMap();
128
- /** The session's boot corpus, each parsed doc joined back to its descriptor.
129
- * `cachedSubstrateDocs` is a filtered map of `cachedAllMemoryDocs`, and an
130
- * absolute path names exactly one document in a corpus (store roots are
131
- * realpath-deduped), so the path is the join key. The join is memoized per
132
- * cached array because one session renders preferences, knowledge, and any
133
- * config-change delta off the same corpus. */
134
- function bootCorpus() {
135
- const docs = cachedSubstrateDocs(listAllMemoryDocs, parseSubstrateDoc);
136
- let sources = bootSources.get(docs);
137
- if (sources === undefined) {
138
- sources = new Map(cachedAllMemoryDocs(listAllMemoryDocs).map((d) => [d.path, d]));
139
- bootSources.set(docs, sources);
103
+ /** The session's boot corpus in PRECEDENCE order: the resolver's stores first
104
+ * (nearest project > user > builtin), the node's own store last so a resolver
105
+ * document keeps its identity against a same-named node-local one. The
106
+ * session cache already pairs each parsed doc with its descriptor, so the full
107
+ * corpus is scanned and parsed at most once per session across the boot
108
+ * renders. Node scope is filtered out of it because `nodeStoreCorpus` is the
109
+ * one path that carries node docs into a boot render — they must not
110
+ * double-surface. */
111
+ function bootCorpus(nodeId) {
112
+ let resolver;
113
+ try {
114
+ resolver = cachedEventCorpusInclusive(listAllMemoryDocs, parseSubstrateDoc).filter((e) => e.doc.scope !== 'node');
115
+ }
116
+ catch {
117
+ return nodeStoreCorpus(nodeId);
140
118
  }
141
- return { docs, sources };
119
+ return [...resolver, ...nodeStoreCorpus(nodeId)];
120
+ }
121
+ function bootPlan(subject, nodeId) {
122
+ return planDelivery(subject, { ...ambientMemoryTarget(), nodeId }, 'boot', {}, bootCorpus(nodeId));
123
+ }
124
+ /** STEP 1 — the plan's winners for one boot partition, in corpus order, each
125
+ * carrying the rung the plan settled on. Display ordering is a separate, later
126
+ * step (see renderGrouped) so a builtin doc can never mask a project/user
127
+ * override by virtue of sorting first. */
128
+ function bootWinners(plan, partition) {
129
+ return plan.docs
130
+ .filter((record) => record.winner && bootPartitionOf(record) === partition)
131
+ .map((record) => ({ ...record.doc, source: record.source, bootRung: record.finalRung }));
142
132
  }
143
133
  // Exposure registration
144
134
  function rendersAtBoot(doc) {
@@ -237,41 +227,6 @@ function renderGrouped(winners, rootLabel) {
237
227
  .join('\n\n');
238
228
  return { contentProse, tree: buildTree(rest, rootLabel), contentDocs: content };
239
229
  }
240
- /** The node's own store (`nodes/<id>/context/memory/`), loaded through the
241
- * resolver's descriptor loader like every other store, then gate-passed.
242
- * Returned across ALL kinds — the node store is the catch-all this-node store
243
- * and rides into the knowledge block.
244
- *
245
- * The store is addressed by the EXPLICIT `nodeId` rather than the host
246
- * process's, because one process renders for other nodes (crtrd claiming a
247
- * warm spare, `sys prompt-review` walking the canvas). The boot resolver
248
- * corpus deliberately excludes node scope (`cachedAllMemoryDocs`), so this is
249
- * the one path that carries node docs into a boot render and they cannot
250
- * double-surface.
251
- *
252
- * Node docs follow the same GATE rule as every other store. A doc with no boot
253
- * entry is retained here; renderKnowledgeForSubject floors it to `name` so it
254
- * shows as a bare name instead of collapsing into `[+N more]`. */
255
- function nodeStoreDocs(nodeId, subject) {
256
- let docs;
257
- try {
258
- // Narrowed to the node scope, so cwd and profile select no store.
259
- docs = loadMemoryTargetView({ cwd: process.cwd(), profileId: null, nodeId }, { scope: 'node', quiet: true }).docs;
260
- }
261
- catch {
262
- return [];
263
- }
264
- const out = [];
265
- for (const source of docs) {
266
- const parsed = parseSubstrateDoc(source);
267
- if (parsed === null || !gatePasses(parsed, subject))
268
- continue;
269
- const rung = bootRung(parsed, subject);
270
- const floored = isVisible(rung) || parsed.surfaces.some((e) => e.on === 'boot') ? rung : 'name';
271
- out.push({ ...parsed, source, bootRung: floored });
272
- }
273
- return out;
274
- }
275
230
  function leafSegment(name) {
276
231
  const i = name.lastIndexOf('/');
277
232
  return i === -1 ? name : name.slice(i + 1);
@@ -391,10 +346,10 @@ function renderChildren(dir, childPrefix, lines) {
391
346
  * headed by `rootLabel`. Returns '' when there are no docs at all (the
392
347
  * empty-tree contract).
393
348
  *
394
- * Callers (renderGrouped) pass docs already first-wins deduped by step 1
395
- * (selectWinners); merging them into the canonical tree keeps that first-wins
396
- * outcome structurally, because a node's `document` is the first candidate at
397
- * its identity in source order. */
349
+ * Callers (renderGrouped) pass docs already first-wins deduped by the plan;
350
+ * merging them into the canonical tree keeps that first-wins outcome
351
+ * structurally, because a node's `document` is the first candidate at its
352
+ * identity in source order. */
398
353
  function buildTree(docs, rootLabel) {
399
354
  if (docs.length === 0)
400
355
  return '';
@@ -463,7 +418,7 @@ function subKindDocCitation(subKind) {
463
418
  /** The computed "sub-personas you may spawn" menu for a subject's kind — the
464
419
  * live restoration of the old `personas/resolve.ts` static menu, now derived
465
420
  * at render time from the merged kind registry (`subKindsAvailableTo`) so it
466
- * can never drift from `sys prompt-review --list`'s `subPersonas` metadata.
421
+ * can never drift from `sys context prompt-review --list`'s `subPersonas` metadata.
467
422
  * Returns '' when the kind has zero available sub-kinds (no dangling empty
468
423
  * header). Emitted for BOTH base and orchestrator modes per the CTO's ruling —
469
424
  * mode is irrelevant to which sub-kinds are spawnable. */
@@ -507,7 +462,9 @@ function buildPreferencesBlock(subject, target) {
507
462
  let protocol = '';
508
463
  let memBody = MEMORY_USAGE_GUIDANCE;
509
464
  if (subject !== null) {
510
- const winners = selectWinners(subject, 'preference');
465
+ // No nodeId: a node-local doc rides the knowledge partition whatever its
466
+ // kind, so it never reaches the preference block.
467
+ const winners = bootWinners(bootPlan(subject, null), 'preference');
511
468
  recordRendered(target, winners);
512
469
  const grouped = renderGrouped(winners, 'preferences');
513
470
  contentDocs = grouped.contentDocs;
@@ -541,31 +498,20 @@ export function renderPreferencesForSubject(subject, target) {
541
498
  return buildPreferencesBlock(subject, target);
542
499
  }
543
500
  // 2. Knowledge — `<memory kind="knowledge">` (the first-message bearings).
544
- /** STEP 1 for the knowledge block, shared by the boot render and the
545
- * config-change delta: the resolver's knowledge winners ∪ the node's own store
546
- * (any kind), first-wins deduped resolver-first so a same-named node doc
547
- * loses. Node docs carry the one rung floor — a node doc with NO boot entry is
548
- * bumped to `name` so it surfaces as a bare name instead of collapsing into a
549
- * `[+N more]` count; a node doc whose boot entries all fail their gates stays
550
- * absent. */
551
- function knowledgeWinners(subject, nodeId) {
552
- return dedupeFirstWins([...selectWinners(subject, 'knowledge'), ...nodeStoreDocs(nodeId, subject)]);
553
- }
554
501
  /** The `<memory kind="knowledge">` block embedded in the session_start bearings
555
502
  * message (bearings.ts pushes it as a SIBLING of `<crtr-bearings>`, or drops it
556
503
  * when ''). The consultable catalog: every eligible `kind: knowledge` resolver
557
504
  * doc PLUS the node-local memory docs (any kind) — resolver docs win
558
505
  * first-wins dedup over a same-named node-local doc. GROUPED by rung exactly
559
506
  * like the preference block: content prose first (general→specific,
560
- * node-local last), then the preview/name catalog tree. Node-local docs are
561
- * floored HERE — a bootless node-local doc's rung is bumped to `name` before
562
- * grouping, so it still surfaces as a bare-name tree entry rather than
563
- * collapsing into a `[+N more]` count (its body still never renders: floored
564
- * docs never reach the `content` rung). Procedural guidance and factual
507
+ * node-local last), then the preview/name catalog tree. A bootless node-local
508
+ * doc arrives floored to `name`, so it surfaces as a bare-name tree entry
509
+ * rather than collapsing into a `[+N more]` count (its body still never
510
+ * renders: floored docs never reach the `content` rung). Procedural guidance and factual
565
511
  * references both live here as `knowledge`. Returns '' when nothing is
566
512
  * eligible. */
567
513
  export function renderKnowledgeForSubject(subject, nodeId, target) {
568
- const winners = knowledgeWinners(subject, nodeId);
514
+ const winners = bootWinners(bootPlan(subject, nodeId), 'knowledge');
569
515
  recordRendered(target, winners);
570
516
  const { contentProse, tree } = renderGrouped(winners, 'knowledge');
571
517
  if (contentProse === '' && tree === '')
@@ -601,8 +547,9 @@ function undelivered(target, docs) {
601
547
  * because content already loaded into this context cannot be withdrawn.
602
548
  * Rendered deltas register as transcript exposure through `target`. */
603
549
  export function renderConfigChangeDelta(subject, nodeId, target) {
604
- const prefs = undelivered(target, selectWinners(subject, 'preference'));
605
- const knowledgeDelta = undelivered(target, knowledgeWinners(subject, nodeId));
550
+ const plan = bootPlan(subject, nodeId);
551
+ const prefs = undelivered(target, bootWinners(plan, 'preference'));
552
+ const knowledgeDelta = undelivered(target, bootWinners(plan, 'knowledge'));
606
553
  recordRendered(target, [...prefs, ...knowledgeDelta]);
607
554
  const { contentDocs, tree } = renderGrouped(prefs, 'preferences');
608
555
  // Same split as the boot preference render: shipped bodies are protocol prose
@@ -1,6 +1,5 @@
1
1
  import { type ProfileProjectMemory } from '../../api/dto/profiles.js';
2
2
  import type { MemoryDoc, MemoryScope } from '../memory-resolver.js';
3
- import type { NodeConfigSubject } from './subject-fields.js';
4
3
  export declare const KINDS: readonly ["knowledge", "preference"];
5
4
  export type DocKind = (typeof KINDS)[number];
6
5
  /** Is `v` one of the two valid document kinds? */
@@ -19,7 +18,7 @@ export declare const SURFACE_EVENTS: readonly ["boot", "workspace-open", "read",
19
18
  export type SurfaceEvent = (typeof SURFACE_EVENTS)[number];
20
19
  /** The rungs an entry may deliver at. There is no `none` — silence is the
21
20
  * absence of an entry. (`Rung`'s `none` survives only as the internal
22
- * ranking floor, e.g. `bootRung` of a doc with no boot entry.) */
21
+ * ranking floor — the planner's verdict for a doc that delivers nothing.) */
23
22
  export declare const SURFACE_RUNGS: readonly ["name", "preview", "content"];
24
23
  export type SurfaceRung = (typeof SURFACE_RUNGS)[number];
25
24
  /** One routing entry: when event `on` exposes a subject that fits `match`
@@ -41,10 +40,6 @@ export interface SurfaceEntry {
41
40
  gate?: GatePredicate;
42
41
  at: SurfaceRung;
43
42
  }
44
- /** A doc's boot rung for `subject`: the highest `at` over participating
45
- * `boot` entries, `none` when it carries none. The boot render's per-doc
46
- * selection input. */
47
- export declare function bootRung(doc: Pick<SubstrateSchema, 'surfaces'>, subject: NodeConfigSubject): Rung;
48
43
  /** A gate predicate tree, evaluated by predicate.ts (`evalCondition`) against
49
44
  * the node-config subject. Typed loosely on purpose — the matcher engine owns
50
45
  * validation; structurally it is a field→matcher map with optional
@@ -8,7 +8,6 @@
8
8
  // never throw): invalid `surfaces` entries are dropped rather than crashing a
9
9
  // render, and `crtr memory lint` owns strict authoring-time enforcement.
10
10
  import { PROFILE_PROJECT_MEMORY_VALUES } from '../../api/dto/profiles.js';
11
- import { surfaceEntryGatePasses } from './gate.js';
12
11
  // Kinds — the two semantic kinds (design §3): `knowledge` (consult — procedural
13
12
  // playbooks + factual references merged) vs `preference` (behave — standing
14
13
  // directives). `kind` is a load-bearing binary, not a content taxonomy.
@@ -51,21 +50,8 @@ export { normalizeNameSegment, normalizeDocName, resolveLocalName as resolveDocN
51
50
  export const SURFACE_EVENTS = ['boot', 'workspace-open', 'read', 'memory-read', 'command', 'pre-command'];
52
51
  /** The rungs an entry may deliver at. There is no `none` — silence is the
53
52
  * absence of an entry. (`Rung`'s `none` survives only as the internal
54
- * ranking floor, e.g. `bootRung` of a doc with no boot entry.) */
53
+ * ranking floor — the planner's verdict for a doc that delivers nothing.) */
55
54
  export const SURFACE_RUNGS = ['name', 'preview', 'content'];
56
- /** A doc's boot rung for `subject`: the highest `at` over participating
57
- * `boot` entries, `none` when it carries none. The boot render's per-doc
58
- * selection input. */
59
- export function bootRung(doc, subject) {
60
- let r = 'none';
61
- for (const e of doc.surfaces) {
62
- if (e.on !== 'boot' || !surfaceEntryGatePasses(e, subject))
63
- continue;
64
- if (rungRank(e.at) > rungRank(r))
65
- r = e.at;
66
- }
67
- return r;
68
- }
69
55
  // Parse / validate.
70
56
  /** Parse a raw frontmatter record (from `parseFrontmatterGeneric`, via the
71
57
  * resolver) into a typed schema with defaults applied. Returns `null` when the
@@ -16,20 +16,12 @@ export declare function clearSessionCache(): void;
16
16
  /** The FULL corpus (listAllMemoryDocs()), INCLUDING node scope, scanned at
17
17
  * most once per session. `listFn` is injected by the caller to avoid
18
18
  * circular imports at module init. This is the single filesystem walk the
19
- * whole module performs — `cachedAllMemoryDocs` below derives its
20
- * node-excluded view from this result rather than re-scanning. Consumers
21
- * that need every referenceable doc (inline memory-reference inventory) call
22
- * this directly instead of `cachedAllMemoryDocs`. */
19
+ * whole module performs — every other entry derives from this result rather
20
+ * than re-scanning. */
23
21
  export declare function cachedAllMemoryDocsInclusive(listFn: ListAllMemoryDocsFn): MemoryDoc[];
24
22
  /** allMemoryDocsInclusive parsed and paired with its descriptors, INCLUDING
25
23
  * node scope — the event corpus, and the module's only parse pass. */
26
24
  export declare function cachedEventCorpusInclusive(listFn: ListAllMemoryDocsFn, parseFn: ParseSubstrateDocFn): EventCorpusDoc[];
27
25
  /** The parsed docs of the inclusive event corpus, without their descriptors. */
28
26
  export declare function cachedSubstrateDocsInclusive(listFn: ListAllMemoryDocsFn, parseFn: ParseSubstrateDocFn): SubstrateDoc[];
29
- /** All memory docs (listAllMemoryDocs()), scanned once per session.
30
- * `listFn` is injected by the caller to avoid circular imports at module init. */
31
- export declare function cachedAllMemoryDocs(listFn: ListAllMemoryDocsFn): MemoryDoc[];
32
- /** The node-excluded parsed view — render.ts's boot corpus. Projected out of
33
- * the inclusive parse rather than parsed again. */
34
- export declare function cachedSubstrateDocs(listFn: ListAllMemoryDocsFn, parseFn: ParseSubstrateDocFn): SubstrateDoc[];
35
27
  export {};
@@ -23,22 +23,17 @@
23
23
  // filtering/mapping, never by calling listFn() again.
24
24
  // • eventCorpusInclusive — allMemoryDocsInclusive parsed once, each surviving
25
25
  // SubstrateDoc paired with the resolver descriptor it
26
- // parsed from. The ONE parse pass; both substrate views
27
- // below project out of it. on-read.ts consumes the pairs
28
- // because `./` memory-read globs anchor on the
29
- // descriptor's canonical routing anchor.
26
+ // parsed from. The ONE parse pass every delivery plan
27
+ // reads from. The pairs matter: `./` memory-read globs
28
+ // anchor on the descriptor's canonical routing anchor,
29
+ // and the boot tree orders by its physical path. Boot
30
+ // filters node scope out of it and loads the node's own
31
+ // store separately (render.ts), so a node doc never
32
+ // double-surfaces there.
30
33
  // • substrateDocsInclusive — the parsed docs alone, still including node
31
34
  // scope. Consumers that need every referenceable doc
32
35
  // (e.g. the inline memory-reference inventory) use this
33
36
  // pair with allMemoryDocsInclusive.
34
- // • allMemoryDocs — allMemoryDocsInclusive with node-scope docs excluded
35
- // (boot-only view — see the comment on
36
- // cachedAllMemoryDocs). Consumed by render.ts.
37
- // • substrateDocs — the same node-excluded view, parsed. Used by
38
- // render.ts's resolver corpus. The event corpus
39
- // (on-read.ts) uses the INCLUSIVE pairs instead: node
40
- // docs' surfaces entries fire on read/memory-read/
41
- // command like any other store's.
42
37
  // Node subjects are deliberately NOT cached: lifecycle, mode, kind, and other
43
38
  // gate inputs can change while a resident broker/session remains live. Rendering
44
39
  // must always gate against the current persisted node metadata.
@@ -46,8 +41,6 @@ const _cache = {
46
41
  allMemoryDocsInclusive: null,
47
42
  eventCorpusInclusive: null,
48
43
  substrateDocsInclusive: null,
49
- allMemoryDocs: null,
50
- substrateDocs: null,
51
44
  };
52
45
  /** Called by canvas-doc-substrate.ts on every `session_start`.
53
46
  * Resets all cached values so the next access triggers a fresh scan. */
@@ -55,16 +48,12 @@ export function clearSessionCache() {
55
48
  _cache.allMemoryDocsInclusive = null;
56
49
  _cache.eventCorpusInclusive = null;
57
50
  _cache.substrateDocsInclusive = null;
58
- _cache.allMemoryDocs = null;
59
- _cache.substrateDocs = null;
60
51
  }
61
52
  /** The FULL corpus (listAllMemoryDocs()), INCLUDING node scope, scanned at
62
53
  * most once per session. `listFn` is injected by the caller to avoid
63
54
  * circular imports at module init. This is the single filesystem walk the
64
- * whole module performs — `cachedAllMemoryDocs` below derives its
65
- * node-excluded view from this result rather than re-scanning. Consumers
66
- * that need every referenceable doc (inline memory-reference inventory) call
67
- * this directly instead of `cachedAllMemoryDocs`. */
55
+ * whole module performs — every other entry derives from this result rather
56
+ * than re-scanning. */
68
57
  export function cachedAllMemoryDocsInclusive(listFn) {
69
58
  if (_cache.allMemoryDocsInclusive === null) {
70
59
  _cache.allMemoryDocsInclusive = listFn();
@@ -92,30 +81,3 @@ export function cachedSubstrateDocsInclusive(listFn, parseFn) {
92
81
  }
93
82
  return _cache.substrateDocsInclusive;
94
83
  }
95
- /** All memory docs (listAllMemoryDocs()), scanned once per session.
96
- * `listFn` is injected by the caller to avoid circular imports at module init. */
97
- export function cachedAllMemoryDocs(listFn) {
98
- if (_cache.allMemoryDocs === null) {
99
- // The BOOT pipeline (render.ts) EXCLUDES node-scope docs: node-local
100
- // memory surfaces at boot via render.ts's nodeLocalDocs (any kind, floored
101
- // into the knowledge block), NOT through the resolver corpus, so a node doc
102
- // must not also enter there (it would double-surface at boot). The event
103
- // corpus (on-read.ts) uses the inclusive view — node docs' surfaces
104
- // entries fire on read/memory-read/command. The `node` scope reaches CLI
105
- // read/list/find/delete through the uncached listAllMemoryDocs() path.
106
- // Derived from the inclusive cache by filtering, so listFn() itself still
107
- // runs at most once per session even though both views are consumed.
108
- _cache.allMemoryDocs = cachedAllMemoryDocsInclusive(listFn).filter((d) => d.scope !== 'node');
109
- }
110
- return _cache.allMemoryDocs;
111
- }
112
- /** The node-excluded parsed view — render.ts's boot corpus. Projected out of
113
- * the inclusive parse rather than parsed again. */
114
- export function cachedSubstrateDocs(listFn, parseFn) {
115
- if (_cache.substrateDocs === null) {
116
- _cache.substrateDocs = cachedEventCorpusInclusive(listFn, parseFn)
117
- .filter((e) => e.doc.scope !== 'node')
118
- .map((e) => e.doc);
119
- }
120
- return _cache.substrateDocs;
121
- }
@@ -31,10 +31,7 @@ export interface NodeConfigSubject {
31
31
  }
32
32
  /** The scope a cwd resolves into: `project` when scope.ts finds a
33
33
  * nearest-ancestor `.crouter/` at/above it, else `user`. Reuses the existing
34
- * resolver (`projectScopeRoot`). NOTE: `findProjectScopeRoot` is process-cached
35
- * on first call, so for a node assembling its OWN subject (cwd === process.cwd)
36
- * this is exact; resolving an arbitrary unrelated cwd in the same process would
37
- * return the cached root — fine for per-node subject assembly. */
34
+ * resolver (`projectScopeRoot`). */
38
35
  export declare function scopeForCwd(cwd: string): 'user' | 'project';
39
36
  /** Resolve `profile_id` to the manifest's mutable `name` for the gate subject.
40
37
  * NEVER throws: a missing/deleted profile resolves to `null` silently (the
@@ -1,7 +1,7 @@
1
1
  // subject-fields.ts — the PURE half of subject assembly: the NodeConfigSubject
2
2
  // shape plus the two derived-field resolvers (`scopeForCwd`, `profileNameFor`)
3
3
  // that read scope/profile config but NEVER the canvas-db. Split out of
4
- // subject.ts so CLI consumers (`sys prompt-review`) can build a subject from an
4
+ // subject.ts so CLI consumers (`sys context prompt-review`) can build a subject from an
5
5
  // API-fetched node without pulling subject.ts's canvas-db reach (getNode/getRow).
6
6
  // The canvas-db assembler (`assembleNodeSubject`, `spineDepth`) stays in
7
7
  // subject.ts for broker/daemon-side callers.
@@ -10,10 +10,7 @@ import { loadProfileManifest } from '../profiles/manifest.js';
10
10
  import { CrtrError } from '../errors.js';
11
11
  /** The scope a cwd resolves into: `project` when scope.ts finds a
12
12
  * nearest-ancestor `.crouter/` at/above it, else `user`. Reuses the existing
13
- * resolver (`projectScopeRoot`). NOTE: `findProjectScopeRoot` is process-cached
14
- * on first call, so for a node assembling its OWN subject (cwd === process.cwd)
15
- * this is exact; resolving an arbitrary unrelated cwd in the same process would
16
- * return the cached root — fine for per-node subject assembly. */
13
+ * resolver (`projectScopeRoot`). */
17
14
  export function scopeForCwd(cwd) {
18
15
  return projectScopeRoot(cwd) !== null ? 'project' : 'user';
19
16
  }
@@ -1,4 +1,4 @@
1
- import { type Rung, type SubstrateDoc, type SurfaceEntry } from './schema.js';
1
+ import { type Rung, type SubstrateDoc, type SurfaceEntry, type SurfaceEvent } from './schema.js';
2
2
  import type { NodeConfigSubject } from './subject-fields.js';
3
3
  /** The project directory owning a doc's store: the path segment stack above
4
4
  * the doc's `.crouter` dir. `null` when the doc's path carries no `.crouter`
@@ -14,12 +14,12 @@ export declare function underOwningRoot(doc: Pick<SubstrateDoc, 'path'>, absFile
14
14
  * realpathed. `readFrontmatter` is the read file's own parsed YAML (empty
15
15
  * for non-markdown files); an empty record never satisfies
16
16
  * `matchFrontmatter`. */
17
- export declare function matchesReadEntry(entry: SurfaceEntry, doc: Pick<SubstrateDoc, 'path' | 'scope'>, subject: NodeConfigSubject, absReadFile: string, readFrontmatter: Record<string, unknown>): boolean;
17
+ export declare function matchesReadEntry(entry: SurfaceEntry, doc: Pick<SubstrateDoc, 'path' | 'scope'>, subject: NodeConfigSubject | null, absReadFile: string, readFrontmatter: Record<string, unknown>): boolean;
18
18
  /** Does a `memory-read` entry on a carrier anchored at `routingAnchor` fit a
19
19
  * resolved read of the doc known by its exact canonical `name`? */
20
20
  export declare function matchesMemoryReadEntry(entry: SurfaceEntry, routingAnchor: string, subject: NodeConfigSubject | null, name: string): boolean;
21
21
  /** Does a `command` entry fit the executed command string? */
22
- export declare function matchesCommandEntry(entry: SurfaceEntry, subject: NodeConfigSubject, command: string): boolean;
22
+ export declare function matchesCommandEntry(entry: SurfaceEntry, subject: NodeConfigSubject | null, command: string): boolean;
23
23
  /** Remove heredoc BODIES (the stdin data between `<<DELIM` and its closing
24
24
  * `DELIM` line) from a command before it is matched. The opening line is kept
25
25
  * — `crtr push final <<'EOF'` is still a real `push final` invocation — but the
@@ -34,21 +34,41 @@ export declare function stripHeredocs(command: string): string;
34
34
  * command with its heredoc bodies stripped. Subshell parentheses, backticks,
35
35
  * and path-prefixed binaries stay unhandled: this is a guardrail against the
36
36
  * faithful-but-uninformed action, not an enforcement boundary. */
37
- export declare function matchesPreCommandEntry(entry: SurfaceEntry, subject: NodeConfigSubject, command: string): boolean;
37
+ export declare function matchesPreCommandEntry(entry: SurfaceEntry, subject: NodeConfigSubject | null, command: string): boolean;
38
+ /** What an event occurrence exposes for matching. A field the event does not
39
+ * use is ignored; a field the event needs and lacks matches nothing. */
40
+ export interface SurfaceMatchPayload {
41
+ /** `read`: the read file, already realpathed. */
42
+ absoluteFile?: string;
43
+ /** `read`: the read file's own parsed YAML (empty for a non-markdown file). */
44
+ readFrontmatter?: Record<string, unknown>;
45
+ /** `memory-read`: the exact canonical name the read resolved to. */
46
+ resolvedCanonicalName?: string;
47
+ /** `memory-read`: the CARRIER's routing anchor, from its resolver descriptor
48
+ * — not derivable from a canonical name, which cannot say whether the doc
49
+ * fronts its own directory. */
50
+ routingAnchor?: string;
51
+ /** `command` / `pre-command`: the command string. */
52
+ command?: string;
53
+ }
54
+ /** A doc as the matchers see it. `path`/`scope` are consulted only by `read`
55
+ * (a `./`-anchored glob resolves against the store's owning repo dir). */
56
+ type MatchDoc = Pick<SubstrateDoc, 'surfaces'> & Partial<Pick<SubstrateDoc, 'path' | 'scope'>>;
57
+ /** Does ONE entry of `event` fit this occurrence — constraints and the entry's
58
+ * own gate together? */
59
+ export declare function surfaceEntryParticipates(entry: SurfaceEntry, doc: MatchDoc, subject: NodeConfigSubject | null, event: SurfaceEvent, payload: SurfaceMatchPayload): boolean;
60
+ /** The entry that decides this doc's delivery for one event occurrence: the
61
+ * highest-rung participating entry, or null when none participates. */
62
+ export declare function matchingSurfaceEntry(doc: MatchDoc, subject: NodeConfigSubject | null, event: SurfaceEvent, payload: SurfaceMatchPayload): SurfaceEntry | null;
38
63
  /** The doc's delivery rung for a read of `absReadFile` (already realpathed). */
39
- export declare function readDeliveryRung(doc: Pick<SubstrateDoc, 'surfaces' | 'path' | 'scope'>, subject: NodeConfigSubject, absReadFile: string, readFrontmatter: Record<string, unknown>): Rung;
40
- /** The doc's workspace-open rung. Presence of an entry is the match; callers
41
- * scope the corpus to project stores mounted by the subject's cwd/profile. */
42
- export declare function workspaceOpenRung(doc: Pick<SubstrateDoc, 'surfaces'>, subject: NodeConfigSubject): Rung;
64
+ export declare function readDeliveryRung(doc: Pick<SubstrateDoc, 'surfaces' | 'path' | 'scope'>, subject: NodeConfigSubject | null, absReadFile: string, readFrontmatter: Record<string, unknown>): Rung;
65
+ /** The doc's workspace-open rung. */
66
+ export declare function workspaceOpenRung(doc: Pick<SubstrateDoc, 'surfaces'>, subject: NodeConfigSubject | null): Rung;
43
67
  /** The doc's delivery rung for a `crtr memory read` that resolved a doc known
44
- * by its exact canonical `name` — no mount-stripped or other alias.
45
- * `routingAnchor` comes from the doc's resolver descriptor — it is not
46
- * derivable from the canonical name, which cannot say whether the doc fronts
47
- * its own directory. */
68
+ * by its exact canonical `name` — no mount-stripped or other alias. */
48
69
  export declare function memoryReadDeliveryRung(doc: Pick<SubstrateDoc, 'surfaces'>, routingAnchor: string, subject: NodeConfigSubject | null, name: string): Rung;
49
70
  /** The doc's delivery rung for an executed command string. */
50
- export declare function commandDeliveryRung(doc: Pick<SubstrateDoc, 'surfaces'>, subject: NodeConfigSubject, command: string): Rung;
51
- /** The doc's delivery rung for a command about to run — the highest `at`
52
- * over the doc's matching `pre-command` entries, folded the same way as every
53
- * other event. */
54
- export declare function preCommandDeliveryRung(doc: Pick<SubstrateDoc, 'surfaces'>, subject: NodeConfigSubject, command: string): Rung;
71
+ export declare function commandDeliveryRung(doc: Pick<SubstrateDoc, 'surfaces'>, subject: NodeConfigSubject | null, command: string): Rung;
72
+ /** The doc's delivery rung for a command about to run. */
73
+ export declare function preCommandDeliveryRung(doc: Pick<SubstrateDoc, 'surfaces'>, subject: NodeConfigSubject | null, command: string): Rung;
74
+ export {};