@north-light/crouter 0.3.235 → 0.3.237

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 (181) 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 +14 -5
  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 +399 -399
  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/__tests__/node-new.test.js +58 -0
  20. package/dist/commands/memory/lint.js +22 -13
  21. package/dist/commands/memory/shared.js +1 -1
  22. package/dist/commands/node/create.d.ts +1 -1
  23. package/dist/commands/node/create.js +4 -1
  24. package/dist/commands/node/lifecycle.js +2 -2
  25. package/dist/commands/node-worktree.js +39 -8
  26. package/dist/commands/revive.js +11 -2
  27. package/dist/commands/sys/context/admin/actions.d.ts +48 -0
  28. package/dist/commands/sys/context/admin/actions.js +246 -0
  29. package/dist/commands/sys/context/admin/axes-panel.d.ts +21 -0
  30. package/dist/commands/sys/context/admin/axes-panel.js +117 -0
  31. package/dist/commands/sys/context/admin/detail-panel.d.ts +53 -0
  32. package/dist/commands/sys/context/admin/detail-panel.js +178 -0
  33. package/dist/commands/sys/context/admin/docs-panel.d.ts +45 -0
  34. package/dist/commands/sys/context/admin/docs-panel.js +186 -0
  35. package/dist/commands/sys/context/admin/model.d.ts +75 -0
  36. package/dist/commands/sys/context/admin/model.js +209 -0
  37. package/dist/commands/sys/context/admin/shell.d.ts +82 -0
  38. package/dist/commands/sys/context/admin/shell.js +669 -0
  39. package/dist/commands/sys/context/admin.d.ts +1 -0
  40. package/dist/commands/sys/context/admin.js +124 -0
  41. package/dist/commands/sys/context/doc.d.ts +1 -0
  42. package/dist/commands/sys/context/doc.js +196 -0
  43. package/dist/commands/sys/context/prompt-review.d.ts +1 -0
  44. package/dist/commands/sys/{prompt-review.js → context/prompt-review.js} +10 -10
  45. package/dist/commands/sys/context/resolve.d.ts +55 -0
  46. package/dist/commands/sys/context/resolve.js +428 -0
  47. package/dist/commands/sys/context/sysprompt.d.ts +1 -0
  48. package/dist/commands/sys/{sysprompt.js → context/sysprompt.js} +11 -11
  49. package/dist/commands/sys/context.d.ts +2 -0
  50. package/dist/commands/sys/context.js +20 -0
  51. package/dist/commands/sys.js +3 -4
  52. package/dist/core/__tests__/canvas-inbox-watcher-naming.test.d.ts +1 -0
  53. package/dist/core/__tests__/canvas-inbox-watcher-naming.test.js +258 -0
  54. package/dist/core/__tests__/child-death-wake.test.js +38 -22
  55. package/dist/core/__tests__/cron-self-cancel-delivery.test.d.ts +1 -0
  56. package/dist/core/__tests__/cron-self-cancel-delivery.test.js +72 -0
  57. package/dist/core/__tests__/daemon-boot.test.js +40 -28
  58. package/dist/core/__tests__/helpers/harness.js +1 -0
  59. package/dist/core/__tests__/human-deliver.test.js +1 -1
  60. package/dist/core/__tests__/human-node-not-supervised.test.js +10 -15
  61. package/dist/core/__tests__/integration/revive.test.js +12 -12
  62. package/dist/core/__tests__/integration/spawn-root.test.js +6 -0
  63. package/dist/core/__tests__/integration/worktree-reap.test.js +32 -0
  64. package/dist/core/__tests__/lifecycle-terminal-reasons.test.d.ts +1 -0
  65. package/dist/core/__tests__/lifecycle-terminal-reasons.test.js +61 -0
  66. package/dist/core/__tests__/migration.test.js +38 -1
  67. package/dist/core/__tests__/push-final-guard.test.js +1 -1
  68. package/dist/core/__tests__/revive-capacity.test.d.ts +1 -0
  69. package/dist/core/__tests__/revive-capacity.test.js +127 -0
  70. package/dist/core/__tests__/revive-parked-fresh.test.js +7 -7
  71. package/dist/core/__tests__/seam/broker-cap-freeze.test.d.ts +1 -0
  72. package/dist/core/__tests__/seam/broker-cap-freeze.test.js +81 -0
  73. package/dist/core/__tests__/seam/dormancy-release.test.js +76 -7
  74. package/dist/core/__tests__/seam/yield-refresh-transaction.test.js +1 -1
  75. package/dist/core/canvas/browse/render.js +11 -3
  76. package/dist/core/canvas/canvas.d.ts +15 -3
  77. package/dist/core/canvas/canvas.js +52 -16
  78. package/dist/core/canvas/migrations.js +21 -0
  79. package/dist/core/canvas/nav-render.js +3 -1
  80. package/dist/core/canvas/remote-canvas-source.js +2 -0
  81. package/dist/core/canvas/render-source.d.ts +3 -1
  82. package/dist/core/canvas/render-source.js +7 -3
  83. package/dist/core/canvas/status-glyph.d.ts +10 -1
  84. package/dist/core/canvas/status-glyph.js +19 -3
  85. package/dist/core/canvas/types.d.ts +22 -3
  86. package/dist/core/feed/feed.js +1 -1
  87. package/dist/core/human/feedback-companion.js +5 -2
  88. package/dist/core/memory/lint.d.ts +19 -0
  89. package/dist/core/memory/lint.js +97 -0
  90. package/dist/core/memory-resolver.d.ts +4 -0
  91. package/dist/core/memory-resolver.js +7 -7
  92. package/dist/core/preview-registry.js +4 -4
  93. package/dist/core/review/realize.js +4 -2
  94. package/dist/core/runtime/close.js +1 -1
  95. package/dist/core/runtime/fleet.d.ts +29 -1
  96. package/dist/core/runtime/fleet.js +23 -0
  97. package/dist/core/runtime/host.js +32 -11
  98. package/dist/core/runtime/launch-prompt.d.ts +1 -1
  99. package/dist/core/runtime/launch-prompt.js +2 -2
  100. package/dist/core/runtime/launch-target.js +1 -1
  101. package/dist/core/runtime/lifecycle.d.ts +4 -2
  102. package/dist/core/runtime/lifecycle.js +12 -1
  103. package/dist/core/runtime/memory.js +1 -1
  104. package/dist/core/runtime/reopen.js +10 -1
  105. package/dist/core/runtime/reset.js +1 -1
  106. package/dist/core/runtime/revive-all.d.ts +3 -0
  107. package/dist/core/runtime/revive-all.js +10 -4
  108. package/dist/core/runtime/revive.d.ts +12 -0
  109. package/dist/core/runtime/revive.js +62 -3
  110. package/dist/core/runtime/spawn.d.ts +9 -13
  111. package/dist/core/runtime/spawn.js +40 -17
  112. package/dist/core/substrate/frontmatter-validation.js +1 -1
  113. package/dist/core/substrate/index.d.ts +6 -3
  114. package/dist/core/substrate/index.js +4 -3
  115. package/dist/core/substrate/on-read.d.ts +0 -8
  116. package/dist/core/substrate/on-read.js +45 -124
  117. package/dist/core/substrate/plan.d.ts +94 -0
  118. package/dist/core/substrate/plan.js +266 -0
  119. package/dist/core/substrate/render.d.ts +5 -6
  120. package/dist/core/substrate/render.js +76 -129
  121. package/dist/core/substrate/schema.d.ts +1 -6
  122. package/dist/core/substrate/schema.js +1 -15
  123. package/dist/core/substrate/session-cache.d.ts +2 -10
  124. package/dist/core/substrate/session-cache.js +9 -47
  125. package/dist/core/substrate/subject-fields.d.ts +1 -4
  126. package/dist/core/substrate/subject-fields.js +2 -5
  127. package/dist/core/substrate/surface-match.d.ts +37 -17
  128. package/dist/core/substrate/surface-match.js +44 -53
  129. package/dist/core/tui/page-host.d.ts +6 -0
  130. package/dist/core/tui/page-host.js +12 -3
  131. package/dist/core/worktree.d.ts +8 -0
  132. package/dist/core/worktree.js +65 -0
  133. package/dist/daemon/__tests__/reconciler-signature.test.js +3 -2
  134. package/dist/daemon/__tests__/thaw-order.test.d.ts +1 -0
  135. package/dist/daemon/__tests__/thaw-order.test.js +55 -0
  136. package/dist/daemon/api/__tests__/canvas-snapshot-fields.test.js +6 -0
  137. package/dist/daemon/api/__tests__/node-create-description.test.js +6 -0
  138. package/dist/daemon/api/__tests__/profile-launch-gates.test.js +1 -1
  139. package/dist/daemon/api/bridge.js +5 -1
  140. package/dist/daemon/api/handlers/attach.js +5 -1
  141. package/dist/daemon/api/handlers/bash-jobs.js +1 -1
  142. package/dist/daemon/api/handlers/messages.js +10 -2
  143. package/dist/daemon/api/handlers/nodes.js +8 -4
  144. package/dist/daemon/api/handlers/worktree.js +10 -1
  145. package/dist/daemon/api/map.js +7 -0
  146. package/dist/daemon/companion-retire.js +1 -1
  147. package/dist/daemon/cron/passes.d.ts +0 -5
  148. package/dist/daemon/cron/passes.js +0 -20
  149. package/dist/daemon/cron/sinks.js +1 -1
  150. package/dist/daemon/cron-run.d.ts +5 -6
  151. package/dist/daemon/cron-run.js +7 -16
  152. package/dist/daemon/crtrd.d.ts +0 -10
  153. package/dist/daemon/crtrd.js +23 -61
  154. package/dist/daemon/fleet.d.ts +10 -7
  155. package/dist/daemon/fleet.js +71 -12
  156. package/dist/daemon/messaging/node-message.js +3 -1
  157. package/dist/daemon/profile-delete.js +1 -1
  158. package/dist/daemon/reconcilers/broker-supervision.d.ts +26 -9
  159. package/dist/daemon/reconcilers/broker-supervision.js +39 -52
  160. package/dist/daemon/reconcilers/controller-death.d.ts +7 -0
  161. package/dist/daemon/reconcilers/controller-death.js +26 -0
  162. package/dist/daemon/reconcilers/cron-lane.d.ts +0 -3
  163. package/dist/daemon/reconcilers/cron-lane.js +0 -3
  164. package/dist/daemon/reconcilers/live-obligation.js +3 -1
  165. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +53 -0
  166. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +170 -0
  167. package/dist/daemon/reconcilers/node-lifecycle/respawn-policy.d.ts +5 -0
  168. package/dist/daemon/reconcilers/node-lifecycle/respawn-policy.js +16 -0
  169. package/dist/daemon/reconcilers/node-lifecycle/terminating.d.ts +4 -0
  170. package/dist/daemon/reconcilers/node-lifecycle/terminating.js +13 -0
  171. package/dist/daemon/reconcilers/node-lifecycle/tick.d.ts +26 -0
  172. package/dist/daemon/reconcilers/node-lifecycle/tick.js +140 -0
  173. package/dist/pi-extensions/canvas-inbox-watcher.js +12 -0
  174. package/package.json +1 -1
  175. package/runtime.lock.json +2 -2
  176. package/dist/commands/sys/prompt-review.d.ts +0 -1
  177. package/dist/commands/sys/sysprompt.d.ts +0 -1
  178. package/dist/core/__tests__/cron-broker-capacity.test.js +0 -119
  179. package/dist/daemon/reconcilers/dormant-inbox.d.ts +0 -18
  180. package/dist/daemon/reconcilers/dormant-inbox.js +0 -157
  181. /package/dist/{core/__tests__/cron-broker-capacity.test.d.ts → commands/__tests__/node-new.test.d.ts} +0 -0
@@ -0,0 +1,94 @@
1
+ import { type MemoryDoc, type MemoryScope, type MemoryTarget } from '../memory-resolver.js';
2
+ import { type DocKind, type Rung, type SubstrateDoc, type SurfaceEntry, type SurfaceEvent } from './schema.js';
3
+ import type { EventCorpusDoc } from './session-cache.js';
4
+ import type { NodeConfigSubject } from './subject-fields.js';
5
+ /** What the event exposes to match against. Every field is optional: an event
6
+ * invoked without its payload matches nothing rather than throwing. */
7
+ export interface DeliveryPayload {
8
+ /** `read`: the file that was read. Realpathed by the planner. */
9
+ absoluteFile?: string;
10
+ /** `read`: the read file's own frontmatter. Parsed from the file when omitted. */
11
+ readFrontmatter?: Record<string, unknown>;
12
+ /** `memory-read`: the exact canonical name the read resolved to. */
13
+ resolvedCanonicalName?: string;
14
+ /** `memory-read`: realpath of the resolved document, which never delivers to
15
+ * itself. */
16
+ resolvedDocPath?: string;
17
+ /** `command` / `pre-command`: the command string. */
18
+ command?: string;
19
+ }
20
+ /** A gate verdict with the reason a failure carries. */
21
+ export type GateOutcome = {
22
+ pass: true;
23
+ } | {
24
+ pass: false;
25
+ reason: string;
26
+ };
27
+ /** One authored surface entry of this event and how it fared. `matched` folds
28
+ * the entry's constraints AND its own gate; `gate` isolates the gate so a
29
+ * reader can tell "did not fit" from "excluded by this node's config". */
30
+ export interface EntryDecision {
31
+ entry: SurfaceEntry;
32
+ matched: boolean;
33
+ gate: GateOutcome;
34
+ }
35
+ /** One document's delivery decision for one event. */
36
+ export interface DeliveryRecord {
37
+ /** Canonical name — the identity dedup resolves. */
38
+ name: string;
39
+ kind: DocKind;
40
+ scope: MemoryScope;
41
+ /** Absolute path to the document file. */
42
+ storePath: string;
43
+ /** From the node's own store, which is floored rather than capped at boot. */
44
+ nodeLocal: boolean;
45
+ /** Every entry the document authored for this event. */
46
+ authoredEntries: EntryDecision[];
47
+ /** The highest-rung entry that matched, or null when none did. */
48
+ matchedEntry: SurfaceEntry | null;
49
+ /** The matched entry's rung — what the author asked for, before the cap. */
50
+ authoredRung: Rung;
51
+ /** The profile relationship's cap on this document's store, or null when the
52
+ * event is uncapped or the document is node-local. */
53
+ projectMemoryCap: Rung | null;
54
+ /** `minRung(authoredRung, projectMemoryCap)`. */
55
+ cappedRung: Rung;
56
+ docGate: GateOutcome;
57
+ /** The matched entry's gate, or the verdict that excluded every authored
58
+ * entry; null when the document authored no entry for this event. */
59
+ entryGate: GateOutcome | null;
60
+ /** What this document delivers at after gates, cap, and the boot node-local
61
+ * floor. `none` = delivers nothing. */
62
+ finalRung: Rung;
63
+ /** This document is the one its canonical name resolves to for this event. */
64
+ winner: boolean;
65
+ /** The document that took this one's name, when it lost the dedup. */
66
+ shadowedBy: {
67
+ name: string;
68
+ scope: MemoryScope;
69
+ path: string;
70
+ } | null;
71
+ /** The parsed document — the renderers' input. */
72
+ doc: SubstrateDoc;
73
+ /** The resolver descriptor behind it: physical path, representation, and the
74
+ * routing anchor a `./` memory-read glob matches against. */
75
+ source: MemoryDoc;
76
+ }
77
+ /** Every document considered for one event, in corpus (precedence) order. */
78
+ export interface DeliveryPlan {
79
+ event: SurfaceEvent;
80
+ subject: NodeConfigSubject | null;
81
+ target: MemoryTarget;
82
+ docs: DeliveryRecord[];
83
+ }
84
+ /** Which boot block a record belongs to. The node's own store is the catch-all
85
+ * this-node store and rides into the knowledge block whatever its kind, so a
86
+ * record's kind alone does not answer this. */
87
+ export declare function bootPartitionOf(record: Pick<DeliveryRecord, 'kind' | 'nodeLocal'>): DocKind;
88
+ /** Plan one event's delivery.
89
+ *
90
+ * `corpus` is the already-loaded document set. The live renderers pass their
91
+ * session cache (the corpus is scanned and parsed once per session); an
92
+ * inspection caller omits it and the planner loads the event's corpus from
93
+ * `target`. Either way the planner writes nothing. */
94
+ export declare function planDelivery(subject: NodeConfigSubject | null, target: MemoryTarget, event: SurfaceEvent, payload?: DeliveryPayload, corpus?: readonly EventCorpusDoc[]): DeliveryPlan;
@@ -0,0 +1,266 @@
1
+ // plan.ts — the pure delivery planner: for one (subject, target, event,
2
+ // payload) tuple it computes the structured per-document delivery decision the
3
+ // renderers turn into text and the inspection commands print as a table. It
4
+ // renders nothing and touches no exposure state.
5
+ //
6
+ // The pipeline has two halves and this module is the UPSTREAM one:
7
+ //
8
+ // planDelivery (subject,event -> decisions) -> exposure ledger dedup ->
9
+ // render -> registerDocumentExposure
10
+ //
11
+ // The ledger (injected-store.ts) records what a live context has ALREADY been
12
+ // given; the planner computes what a state WOULD be given. Renderers own the
13
+ // downstream half, so a plan can be computed for a hypothetical subject without
14
+ // disturbing any node's real ledger.
15
+ //
16
+ // Two decisions are event-specific and deliberately so:
17
+ //
18
+ // • THE PROFILE CAP reaches boot and workspace-open only. Every other event
19
+ // is reached by an explicit act (a read, a command) and delivers at the
20
+ // authored rung.
21
+ // • DEDUP PARTICIPATION differs. At boot every ELIGIBLE document participates
22
+ // — including one with no boot entry, because the boot catalog counts it
23
+ // into its directory's `[+N more]` — and the dedup runs per boot partition,
24
+ // since the boot render emits an independent block per partition. At every
25
+ // other event only a document that actually DELIVERS participates, so a
26
+ // nearer document that does not match never suppresses a farther one that
27
+ // does.
28
+ import { homedir } from 'node:os';
29
+ import { dirname, parse, sep } from 'node:path';
30
+ import { parseFrontmatterGeneric } from '../frontmatter.js';
31
+ import { readText, realpathOrSelf } from '../fs-utils.js';
32
+ import { listProjectMemoryDocs, loadMemoryTargetView, loadStoreMemoryDocs, openProjectMemoryStore, } from '../memory-resolver.js';
33
+ import { userScopeRoot } from '../scope.js';
34
+ import { gatePasses, surfaceEntryGatePasses } from './gate.js';
35
+ import { minRung, parseSubstrateDoc, rungAtLeast, } from './schema.js';
36
+ import { matchingSurfaceEntry, surfaceEntryParticipates } from './surface-match.js';
37
+ /** Which boot block a record belongs to. The node's own store is the catch-all
38
+ * this-node store and rides into the knowledge block whatever its kind, so a
39
+ * record's kind alone does not answer this. */
40
+ export function bootPartitionOf(record) {
41
+ return record.nodeLocal ? 'knowledge' : record.kind;
42
+ }
43
+ /** Plan one event's delivery.
44
+ *
45
+ * `corpus` is the already-loaded document set. The live renderers pass their
46
+ * session cache (the corpus is scanned and parsed once per session); an
47
+ * inspection caller omits it and the planner loads the event's corpus from
48
+ * `target`. Either way the planner writes nothing. */
49
+ export function planDelivery(subject, target, event, payload = {}, corpus) {
50
+ const resolved = resolvePayload(event, payload);
51
+ const considered = considerFor(event, corpus ?? loadEventCorpus(target, event), resolved);
52
+ const docs = considered.map(({ doc, source }) => decide(doc, source, subject, event, resolved));
53
+ resolveWinners(event, docs);
54
+ return { event, subject, target, docs };
55
+ }
56
+ function resolvePayload(event, payload) {
57
+ if (event !== 'read' || payload.absoluteFile === undefined) {
58
+ return { ...payload, readFrontmatter: payload.readFrontmatter ?? {} };
59
+ }
60
+ const absoluteFile = realpathOrSelf(payload.absoluteFile);
61
+ return {
62
+ ...payload,
63
+ absoluteFile,
64
+ readFrontmatter: payload.readFrontmatter ?? readFileFrontmatter(absoluteFile),
65
+ };
66
+ }
67
+ function readFileFrontmatter(absReadFile) {
68
+ if (!/\.(md|mdx|markdown)$/i.test(absReadFile))
69
+ return {};
70
+ try {
71
+ return parseFrontmatterGeneric(readText(absReadFile)).data ?? {};
72
+ }
73
+ catch {
74
+ return {};
75
+ }
76
+ }
77
+ // The considered set — the corpus plus the event's own discovery and
78
+ // self-exclusion rules.
79
+ function considerFor(event, corpus, payload) {
80
+ if (event === 'read') {
81
+ const abs = payload.absoluteFile;
82
+ if (abs === undefined)
83
+ return [...corpus];
84
+ // Enclosing stores lead: a store discovered from the read file is the
85
+ // nearest source of an identity the mounted corpus may also carry.
86
+ return [...enclosingProjectDocs(abs), ...corpus].filter(({ doc }) => realpathOrSelf(doc.path) !== abs);
87
+ }
88
+ if (event === 'memory-read' && payload.resolvedDocPath !== undefined) {
89
+ const exclude = payload.resolvedDocPath;
90
+ return corpus.filter(({ doc }) => realpathOrSelf(doc.path) !== exclude);
91
+ }
92
+ return [...corpus];
93
+ }
94
+ const JUNK_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.next', '.cache', '.yalc']);
95
+ function isJunkAncestor(dir) {
96
+ return dir.split(sep).some((segment) => JUNK_DIRS.has(segment));
97
+ }
98
+ /** Project docs from every workspace store enclosing the read file, nearest
99
+ * first. This is a discovery walk only: each returned doc must still match an
100
+ * explicit trigger. A store that declares no namespace does not mount here
101
+ * either — an unmounted store has no canonical identities to deliver under.
102
+ * The stores carry the neutral ceiling: the read event is uncapped by design,
103
+ * reached only by an explicit file read. */
104
+ function enclosingProjectDocs(absReadFile) {
105
+ const out = [];
106
+ const seen = new Set();
107
+ const home = realpathOrSelf(homedir());
108
+ const userRoot = realpathOrSelf(userScopeRoot());
109
+ const fsRoot = parse(absReadFile).root;
110
+ let dir = dirname(absReadFile);
111
+ while (true) {
112
+ if (dir !== home && dir !== userRoot && !isJunkAncestor(dir)) {
113
+ const store = openProjectMemoryStore(dir);
114
+ if (store.mountStatus === 'ready' && !seen.has(store.storeRoot)) {
115
+ seen.add(store.storeRoot);
116
+ for (const source of loadStoreMemoryDocs(store, true)) {
117
+ const doc = parseSubstrateDoc(source);
118
+ if (doc !== null)
119
+ out.push({ doc, source });
120
+ }
121
+ }
122
+ }
123
+ if (dir === home || dir === fsRoot)
124
+ break;
125
+ const parent = dirname(dir);
126
+ if (parent === dir)
127
+ break;
128
+ dir = parent;
129
+ }
130
+ return out;
131
+ }
132
+ /** The event's corpus for a target, for callers that hold no session cache.
133
+ * Boot orders node-local docs LAST so a resolver document keeps its identity
134
+ * against a same-named node-local one; workspace-open sees project stores
135
+ * only; every other event sees the whole mounted corpus. */
136
+ function loadEventCorpus(target, event) {
137
+ const sources = event === 'workspace-open'
138
+ ? listProjectMemoryDocs(target.cwd, target.profileId)
139
+ : loadMemoryTargetView(target, { quiet: true }).docs;
140
+ const parsed = [];
141
+ for (const source of sources) {
142
+ const doc = parseSubstrateDoc(source);
143
+ if (doc !== null)
144
+ parsed.push({ doc, source });
145
+ }
146
+ if (event !== 'boot')
147
+ return parsed;
148
+ return [
149
+ ...parsed.filter((e) => e.doc.scope !== 'node'),
150
+ ...parsed.filter((e) => e.doc.scope === 'node'),
151
+ ];
152
+ }
153
+ // The per-document decision.
154
+ function decide(doc, source, subject, event, payload) {
155
+ const match = {
156
+ absoluteFile: payload.absoluteFile,
157
+ readFrontmatter: payload.readFrontmatter,
158
+ resolvedCanonicalName: payload.resolvedCanonicalName,
159
+ routingAnchor: source.routingAnchor,
160
+ command: payload.command,
161
+ };
162
+ const docGate = documentGateOutcome(doc, subject);
163
+ const authoredEntries = doc.surfaces
164
+ .filter((entry) => entry.on === event)
165
+ .map((entry) => ({
166
+ entry,
167
+ matched: surfaceEntryParticipates(entry, doc, subject, event, match),
168
+ gate: entryGateOutcome(entry, subject),
169
+ }));
170
+ const matchedEntry = matchingSurfaceEntry(doc, subject, event, match);
171
+ const authoredRung = matchedEntry?.at ?? 'none';
172
+ const nodeLocal = doc.scope === 'node';
173
+ const capped = event === 'boot' || event === 'workspace-open';
174
+ const projectMemoryCap = capped && !nodeLocal ? doc.projectMemory : null;
175
+ const cappedRung = projectMemoryCap === null ? authoredRung : minRung(authoredRung, projectMemoryCap);
176
+ let finalRung = docGate.pass ? cappedRung : 'none';
177
+ // The one rung FLOOR, scoped to the node store at boot: a node-local doc with
178
+ // no boot entry still shows its bare name rather than collapsing into its
179
+ // directory's hidden count. A node-local doc whose boot entries all fail
180
+ // stays absent.
181
+ if (event === 'boot' &&
182
+ nodeLocal &&
183
+ docGate.pass &&
184
+ !rungAtLeast(finalRung, 'name') &&
185
+ !doc.surfaces.some((entry) => entry.on === 'boot')) {
186
+ finalRung = 'name';
187
+ }
188
+ return {
189
+ name: doc.name,
190
+ kind: doc.kind,
191
+ scope: doc.scope,
192
+ storePath: doc.path,
193
+ nodeLocal,
194
+ authoredEntries,
195
+ matchedEntry,
196
+ authoredRung,
197
+ projectMemoryCap,
198
+ cappedRung,
199
+ docGate,
200
+ entryGate: recordEntryGate(authoredEntries, matchedEntry),
201
+ finalRung,
202
+ winner: false,
203
+ shadowedBy: null,
204
+ doc,
205
+ source,
206
+ };
207
+ }
208
+ /** A missing subject cannot satisfy a gate, so a gated document is skipped and
209
+ * an ungated one still delivers. A predicate that throws excludes only its own
210
+ * document — never the whole render. */
211
+ function documentGateOutcome(doc, subject) {
212
+ if (doc.gate === undefined)
213
+ return { pass: true };
214
+ if (subject === null)
215
+ return { pass: false, reason: 'gated document with no node config to match' };
216
+ try {
217
+ return gatePasses(doc, subject)
218
+ ? { pass: true }
219
+ : { pass: false, reason: 'document gate does not match this node config' };
220
+ }
221
+ catch (err) {
222
+ return { pass: false, reason: `document gate failed to evaluate: ${String(err)}` };
223
+ }
224
+ }
225
+ function entryGateOutcome(entry, subject) {
226
+ if (entry.gate === undefined)
227
+ return { pass: true };
228
+ return surfaceEntryGatePasses(entry, subject)
229
+ ? { pass: true }
230
+ : { pass: false, reason: 'entry gate does not match this node config' };
231
+ }
232
+ function recordEntryGate(entries, matched) {
233
+ if (entries.length === 0)
234
+ return null;
235
+ if (matched !== null)
236
+ return { pass: true };
237
+ const gatedOut = entries.find((e) => !e.gate.pass);
238
+ return gatedOut === undefined ? { pass: true } : gatedOut.gate;
239
+ }
240
+ // Winner resolution — first-win by canonical name over the corpus's precedence
241
+ // order, within the event's participating set.
242
+ function participates(record, event) {
243
+ if (!record.docGate.pass)
244
+ return false;
245
+ // Boot counts an eligible document even when it delivers nothing, because the
246
+ // catalog tree still counts it. A `none` cap removes the document from the
247
+ // corpus entirely, so it cannot shadow a wider-scope document either.
248
+ if (event === 'boot')
249
+ return record.projectMemoryCap !== 'none';
250
+ return rungAtLeast(record.finalRung, 'name');
251
+ }
252
+ function resolveWinners(event, docs) {
253
+ const winners = new Map();
254
+ for (const record of docs) {
255
+ if (!participates(record, event))
256
+ continue;
257
+ const key = event === 'boot' ? `${bootPartitionOf(record)}:${record.name}` : record.name;
258
+ const held = winners.get(key);
259
+ if (held === undefined) {
260
+ winners.set(key, record);
261
+ record.winner = true;
262
+ continue;
263
+ }
264
+ record.shadowedBy = { name: held.name, scope: held.scope, path: held.storePath };
265
+ }
266
+ }
@@ -4,7 +4,7 @@ import type { NodeConfigSubject } from './subject-fields.js';
4
4
  /** The computed "sub-personas you may spawn" menu for a subject's kind — the
5
5
  * live restoration of the old `personas/resolve.ts` static menu, now derived
6
6
  * at render time from the merged kind registry (`subKindsAvailableTo`) so it
7
- * can never drift from `sys prompt-review --list`'s `subPersonas` metadata.
7
+ * can never drift from `sys context prompt-review --list`'s `subPersonas` metadata.
8
8
  * Returns '' when the kind has zero available sub-kinds (no dangling empty
9
9
  * header). Emitted for BOTH base and orchestrator modes per the CTO's ruling —
10
10
  * mode is irrelevant to which sub-kinds are spawnable. */
@@ -20,11 +20,10 @@ export declare function renderPreferencesForSubject(subject: NodeConfigSubject |
20
20
  * doc PLUS the node-local memory docs (any kind) — resolver docs win
21
21
  * first-wins dedup over a same-named node-local doc. GROUPED by rung exactly
22
22
  * like the preference block: content prose first (general→specific,
23
- * node-local last), then the preview/name catalog tree. Node-local docs are
24
- * floored HERE — a bootless node-local doc's rung is bumped to `name` before
25
- * grouping, so it still surfaces as a bare-name tree entry rather than
26
- * collapsing into a `[+N more]` count (its body still never renders: floored
27
- * docs never reach the `content` rung). Procedural guidance and factual
23
+ * node-local last), then the preview/name catalog tree. A bootless node-local
24
+ * doc arrives floored to `name`, so it surfaces as a bare-name tree entry
25
+ * rather than collapsing into a `[+N more]` count (its body still never
26
+ * renders: floored docs never reach the `content` rung). Procedural guidance and factual
28
27
  * references both live here as `knowledge`. Returns '' when nothing is
29
28
  * eligible. */
30
29
  export declare function renderKnowledgeForSubject(subject: NodeConfigSubject, nodeId: string, target?: ExposureTarget): string;