@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,124 @@
1
+ // `crtr sys context admin` — the interactive surface over the context system:
2
+ // dial a configuration snapshot, watch every document's resolved rung across
3
+ // the six events, and edit the surfaces, gates, and caps that decide it.
4
+ // Mutations route through `memory edit` / `memory move` and the profile
5
+ // manifest, never through direct file writes.
6
+ import { resolve as resolvePath } from 'node:path';
7
+ import { defineLeaf } from '../../../core/command.js';
8
+ import { InputError } from '../../../core/io.js';
9
+ import { ApiError } from '../../../api/index.js';
10
+ import { cliClient } from '../../api-client.js';
11
+ import { SURFACE_EVENTS } from '../../../core/substrate/schema.js';
12
+ import { ambientMemoryTarget } from '../../../core/memory-resolver.js';
13
+ import { resolveProfileOperand } from '../../../core/profiles/manifest.js';
14
+ export const adminLeaf = defineLeaf({
15
+ name: 'admin',
16
+ description: 'browse and edit the context surface interactively',
17
+ whenToUse: 'you are shaping what agents load rather than answering one question about it — comparing snapshots, sweeping which documents fire where, and editing surfaces, gates, or caps in place',
18
+ help: {
19
+ name: 'sys context admin',
20
+ summary: 'interactive browser and editor for the memory context surface',
21
+ params: [
22
+ { kind: 'flag', name: 'node', type: 'string', required: false, constraint: 'Open with the snapshot derived from this live node instead of the defaults.' },
23
+ { kind: 'flag', name: 'kind', type: 'string', required: false, constraint: 'Opening node kind. Defaults to the --node value, else `general`.' },
24
+ { kind: 'flag', name: 'mode', type: 'enum', choices: ['base', 'orchestrator'], required: false, constraint: 'Opening node mode. Defaults to `base`.' },
25
+ { kind: 'flag', name: 'profile', type: 'string', required: false, constraint: 'Profile id whose memory store and project-memory caps apply. Defaults to the selected profile.' },
26
+ { kind: 'flag', name: 'cwd', type: 'path', required: false, constraint: 'Directory the project stores resolve from. Defaults to the current directory.' },
27
+ { kind: 'flag', name: 'event', type: 'enum', choices: [...SURFACE_EVENTS], required: false, constraint: 'Opening event for the detail panel. Defaults to `boot`.' },
28
+ ],
29
+ output: [
30
+ { name: 'edited', type: 'array', required: false, constraint: 'Canonical names of the documents changed during the session; absent when nothing was changed.' },
31
+ { name: 'cap_changes', type: 'array', required: false, constraint: 'Profile project-memory caps changed during the session, each as store path and new rung; absent when none were.' },
32
+ ],
33
+ outputKind: 'object',
34
+ effects: [
35
+ 'Interactive: requires a TTY.',
36
+ 'Edits memory documents through `memory edit` / `memory move` with rationale `editor` when you choose an edit action.',
37
+ 'Writes profile project-memory caps to the profile manifest when you change one.',
38
+ ],
39
+ },
40
+ run: async (input) => {
41
+ if (!process.stdout.isTTY) {
42
+ throw new InputError({
43
+ error: 'not_a_tty',
44
+ message: 'sys context admin is interactive and needs a TTY',
45
+ next: 'Run it in a terminal, or use `crtr sys context resolve` and `crtr sys context doc` for scriptable reads.',
46
+ });
47
+ }
48
+ const node = await fetchNodeOrNull(input['node']?.trim() ?? '');
49
+ const ambient = ambientMemoryTarget();
50
+ const profileId = input['profile']?.trim();
51
+ const cwd = input['cwd']?.trim();
52
+ const seed = {
53
+ kind: input['kind']?.trim() || node?.kind || 'general',
54
+ mode: input['mode'] ?? node?.mode ?? 'base',
55
+ lifecycle: node?.lifecycle ?? 'terminal',
56
+ hasManager: node?.parent != null,
57
+ profileId: profileId === undefined || profileId === ''
58
+ ? (node?.profile_id ?? ambient.profileId)
59
+ : resolveProfileOperand(profileId).profileId,
60
+ cwd: cwd === undefined || cwd === '' ? (node?.cwd ?? ambient.cwd) : resolvePath(cwd),
61
+ nodeId: node?.node_id ?? null,
62
+ depth: node === null ? 0 : await spineDepth(node),
63
+ event: (input['event'] ?? 'boot'),
64
+ file: '',
65
+ docName: '',
66
+ command: '',
67
+ };
68
+ // Deferred so the page host, its terminal, and the whole TUI dependency
69
+ // chain load only for the interactive run — every other `sys context` leaf
70
+ // stays a plain read.
71
+ const { runContextAdmin } = await import('./admin/shell.js');
72
+ const result = await runContextAdmin(seed);
73
+ if (result === undefined)
74
+ return {};
75
+ return {
76
+ ...(result.edited.length > 0 ? { edited: result.edited } : {}),
77
+ ...(result.capChanges.length > 0
78
+ ? { cap_changes: result.capChanges.map((change) => ({ path: change.path, memory: change.memory })) }
79
+ : {}),
80
+ };
81
+ },
82
+ render: (r) => {
83
+ const edited = r['edited'] ?? [];
84
+ const caps = r['cap_changes'] ?? [];
85
+ if (edited.length === 0 && caps.length === 0)
86
+ return 'Closed the context admin with nothing changed.';
87
+ const lines = [
88
+ ...edited.map((name) => `- edited ${name}`),
89
+ ...caps.map((cap) => `- ceiling ${cap.path} → ${cap.memory}`),
90
+ ];
91
+ return lines.join('\n');
92
+ },
93
+ });
94
+ /** A `--node` id that no longer resolves seeds nothing rather than failing: the
95
+ * page is useful without a live node, and the ids an agent has to hand are the
96
+ * ones most likely to have been pruned. */
97
+ async function fetchNodeOrNull(nodeId) {
98
+ if (nodeId === '')
99
+ return null;
100
+ try {
101
+ return await cliClient().getNode(nodeId);
102
+ }
103
+ catch (err) {
104
+ if (err instanceof ApiError && err.status === 404)
105
+ return null;
106
+ throw err;
107
+ }
108
+ }
109
+ /** Hops from a node up to its root orchestrator, walked through the API so this
110
+ * leaf never reaches the canvas db. A pruned ancestor ends the walk. */
111
+ async function spineDepth(node) {
112
+ const seen = new Set([node.node_id]);
113
+ let parent = node.parent;
114
+ let hops = 0;
115
+ while (parent != null && !seen.has(parent)) {
116
+ seen.add(parent);
117
+ hops += 1;
118
+ const next = await fetchNodeOrNull(parent);
119
+ if (next === null)
120
+ break;
121
+ parent = next.parent;
122
+ }
123
+ return hops;
124
+ }
@@ -0,0 +1 @@
1
+ export declare const docLeaf: import("../../../core/command.js").LeafDef;
@@ -0,0 +1,196 @@
1
+ // `crtr sys context doc` — the reverse question: for ONE document, everywhere
2
+ // it surfaces and why. Authored surface entries plus a computed row per event
3
+ // under a single configuration snapshot, from the same plan the live renderers
4
+ // deliver from (`planDelivery`, src/core/substrate/plan.ts).
5
+ import { defineLeaf } from '../../../core/command.js';
6
+ import { realpathOrSelf } from '../../../core/fs-utils.js';
7
+ import { resolveMemoryDocForTarget } from '../../../core/memory-resolver.js';
8
+ import { InputError } from '../../../core/io.js';
9
+ import { planDelivery } from '../../../core/substrate/plan.js';
10
+ import { parseSubstrateDoc, SURFACE_EVENTS } from '../../../core/substrate/schema.js';
11
+ import { buildSnapshot, cell, delivers, describeSubject, entryText, exclusionReason, gateText, optionalString, payloadFor, subjectSummary, } from './resolve.js';
12
+ export const docLeaf = defineLeaf({
13
+ name: 'doc',
14
+ description: 'show one document\u2019s delivery across every surface event',
15
+ whenToUse: 'a document is reaching a node it should not, or failing to reach one it should — this says which event delivers it, at what rung, and what excluded it everywhere else',
16
+ help: {
17
+ name: 'sys context doc',
18
+ summary: 'show where one document delivers, at what rung, and what excludes it elsewhere',
19
+ params: [
20
+ { kind: 'positional', name: 'name', required: true, constraint: 'Canonical document name, exactly as `crtr memory read` takes it.' },
21
+ { kind: 'flag', name: 'node', type: 'string', required: false, constraint: 'Evaluate under this live node\u2019s configuration — derives kind, mode, lifecycle, has-manager, profile, and cwd from it.' },
22
+ { kind: 'flag', name: 'kind', type: 'string', required: false, constraint: 'Node kind of the synthetic subject. Defaults to the --node value, else `general`.' },
23
+ { kind: 'flag', name: 'mode', type: 'enum', choices: ['base', 'orchestrator'], required: false, constraint: 'Node mode of the synthetic subject. Defaults to `base`.' },
24
+ { kind: 'flag', name: 'lifecycle', type: 'enum', choices: ['terminal', 'resident'], required: false, constraint: 'Node lifecycle of the synthetic subject. Defaults to `terminal` for base, `resident` for orchestrator.' },
25
+ { kind: 'flag', name: 'has-manager', type: 'enum', choices: ['true', 'false'], required: false, constraint: 'Treat the subject as having a manager (a subscriber above it). Defaults to the --node value, else true.' },
26
+ { kind: 'flag', name: 'profile', type: 'string', required: false, constraint: 'Profile id whose memory store and project-memory caps apply. Defaults to the selected profile.' },
27
+ { kind: 'flag', name: 'cwd', type: 'path', required: false, constraint: 'Directory the project stores resolve from. Defaults to the current directory.' },
28
+ { kind: 'flag', name: 'event', type: 'enum', choices: [...SURFACE_EVENTS], required: false, constraint: 'Report only this event instead of all six.' },
29
+ { kind: 'flag', name: 'file', type: 'path', required: false, constraint: 'Payload for the read event: the file being read. Without it, no read entry matches.' },
30
+ { kind: 'flag', name: 'read-name', type: 'string', required: false, constraint: 'Payload for the memory-read event: the exact canonical name that read resolved to.' },
31
+ { kind: 'flag', name: 'command', type: 'string', required: false, constraint: 'Payload for the command and pre-command events: the command string.' },
32
+ ],
33
+ output: [
34
+ { name: 'name', type: 'string', required: true, constraint: 'The document\u2019s canonical name.' },
35
+ { name: 'kind', type: 'string', required: true, constraint: 'knowledge or preference.' },
36
+ { name: 'scope', type: 'string', required: true, constraint: 'node, project, profile, user, or builtin.' },
37
+ { name: 'store_path', type: 'string', required: true, constraint: 'Absolute path to the document file.' },
38
+ { name: 'project_memory_cap', type: 'string', required: true, constraint: 'The profile relationship\u2019s cap on this document\u2019s store; it binds at boot and workspace-open only.' },
39
+ { name: 'subject', type: 'object', required: true, constraint: 'The configuration snapshot every row was computed under.' },
40
+ { name: 'surfaces', type: 'array', required: true, constraint: 'The document\u2019s authored surface entries: event, match globs, gate, rung.' },
41
+ { name: 'events', type: 'array', required: true, constraint: 'One row per event: event, delivers, authored_rung, capped_rung, final_rung, doc_gate, entry_gate, matched_entry, winner, shadowed_by, and — when the event\u2019s corpus does not carry this document at all — excluded saying so.' },
42
+ ],
43
+ outputKind: 'object',
44
+ effects: ['None. Read-only.'],
45
+ },
46
+ run: async (input) => {
47
+ const snapshot = await buildSnapshot(input);
48
+ const doc = resolveDoc(input['name'], snapshot.target);
49
+ const events = optionalString(input['event']) === null
50
+ ? [...SURFACE_EVENTS]
51
+ : [input['event']];
52
+ const flags = {
53
+ file: optionalString(input['file']),
54
+ readName: optionalString(input['readName']),
55
+ command: optionalString(input['command']),
56
+ };
57
+ const targetPath = realpathOrSelf(doc.path);
58
+ const recordPathCache = new Map();
59
+ const rows = events.map((event) => {
60
+ const plan = planDelivery(snapshot.subject, snapshot.target, event, payloadFor(event, flags, snapshot.target));
61
+ return eventRow(event, findRecord(plan.docs, targetPath, recordPathCache));
62
+ });
63
+ return {
64
+ name: doc.name,
65
+ kind: doc.kind,
66
+ scope: doc.scope,
67
+ store_path: doc.path,
68
+ project_memory_cap: doc.projectMemory,
69
+ subject: subjectSummary(snapshot.subject),
70
+ surfaces: doc.surfaces.map(surfaceRow),
71
+ events: rows,
72
+ };
73
+ },
74
+ render: (result) => renderDoc(result),
75
+ });
76
+ /** The document the name addresses under this snapshot's store chain — the same
77
+ * winner `crtr memory read` would open. A document whose frontmatter does not
78
+ * parse into the substrate schema has no surface behavior to report. */
79
+ function resolveDoc(name, target) {
80
+ const source = resolveMemoryDocForTarget(name, target);
81
+ const doc = parseSubstrateDoc(source);
82
+ if (doc === null) {
83
+ throw new InputError({
84
+ error: 'invalid_field',
85
+ message: `document is not a substrate document: ${source.path}`,
86
+ field: 'name',
87
+ next: 'Give the document valid frontmatter (`kind` and `when-and-why-to-read`) with `crtr memory edit`.',
88
+ });
89
+ }
90
+ return doc;
91
+ }
92
+ /** The plan record for this exact physical document. Identity is the file, not
93
+ * the name: a nearer store may hold a different document under the same name,
94
+ * and that one is the shadower, not this row's subject. */
95
+ function findRecord(records, targetPath, pathCache) {
96
+ return records.find((record) => {
97
+ let recordPath = pathCache.get(record.storePath);
98
+ if (recordPath === undefined) {
99
+ recordPath = realpathOrSelf(record.storePath);
100
+ pathCache.set(record.storePath, recordPath);
101
+ }
102
+ return recordPath === targetPath;
103
+ }) ?? null;
104
+ }
105
+ function surfaceRow(entry) {
106
+ return {
107
+ event: entry.on,
108
+ match: entry.match === undefined ? null : entry.match.join(', '),
109
+ match_frontmatter: entry.matchFrontmatter !== undefined,
110
+ gate: entry.gate !== undefined,
111
+ at: entry.at,
112
+ };
113
+ }
114
+ function eventRow(event, record) {
115
+ if (record === null) {
116
+ return {
117
+ event,
118
+ delivers: false,
119
+ authored_rung: 'none',
120
+ capped_rung: 'none',
121
+ final_rung: 'none',
122
+ doc_gate: null,
123
+ entry_gate: null,
124
+ matched_entry: null,
125
+ winner: false,
126
+ shadowed_by: null,
127
+ excluded: `this event\u2019s corpus does not carry the document (${corpusNote(event)})`,
128
+ };
129
+ }
130
+ return {
131
+ event,
132
+ delivers: delivers(record),
133
+ authored_rung: record.authoredRung,
134
+ capped_rung: record.cappedRung,
135
+ final_rung: record.finalRung,
136
+ doc_gate: gateText(record.docGate),
137
+ entry_gate: record.entryGate === null ? null : gateText(record.entryGate),
138
+ matched_entry: entryText(record.matchedEntry),
139
+ winner: record.winner,
140
+ shadowed_by: record.shadowedBy === null ? null : `${record.shadowedBy.scope}:${record.shadowedBy.path}`,
141
+ excluded: delivers(record) ? null : exclusionReason(record),
142
+ };
143
+ }
144
+ function corpusNote(event) {
145
+ if (event === 'workspace-open')
146
+ return 'workspace-open sees project stores only';
147
+ if (event === 'memory-read')
148
+ return 'the document being read never delivers to itself';
149
+ return 'it is excluded from this event\u2019s document set';
150
+ }
151
+ function renderDoc(result) {
152
+ const lines = [
153
+ `# ${result.name}`,
154
+ '',
155
+ `- kind: ${result.kind}`,
156
+ `- scope: ${result.scope}`,
157
+ `- path: ${result.store_path}`,
158
+ `- project-memory cap: ${result.project_memory_cap} (binds at boot and workspace-open only)`,
159
+ '',
160
+ `subject: ${describeSubject(result.subject)}`,
161
+ '',
162
+ '## Authored surfaces',
163
+ '',
164
+ renderSurfaces(result.surfaces),
165
+ '',
166
+ '## Delivery per event',
167
+ '',
168
+ renderEvents(result.events),
169
+ ];
170
+ return lines.join('\n');
171
+ }
172
+ function renderSurfaces(surfaces) {
173
+ if (surfaces.length === 0) {
174
+ return 'None. The document authors no surface entry, so it only appears in its directory listing.';
175
+ }
176
+ return table(['event', 'at', 'match', 'match-frontmatter', 'gate'], surfaces.map((s) => [s.event, s.at, s.match ?? '—', s.match_frontmatter ? 'yes' : '—', s.gate ? 'yes' : '—']));
177
+ }
178
+ function renderEvents(events) {
179
+ return table(['event', 'delivers', 'authored', 'capped', 'final', 'matched', 'gate', 'why not'], events.map((e) => [
180
+ e.event,
181
+ e.delivers ? 'yes' : 'no',
182
+ e.authored_rung,
183
+ e.capped_rung,
184
+ e.final_rung,
185
+ e.matched_entry ?? '—',
186
+ e.doc_gate ?? '—',
187
+ e.excluded ?? '—',
188
+ ]));
189
+ }
190
+ function table(cols, rows) {
191
+ return [
192
+ `| ${cols.join(' | ')} |`,
193
+ `| ${cols.map(() => '---').join(' | ')} |`,
194
+ ...rows.map((r) => `| ${r.map(cell).join(' | ')} |`),
195
+ ].join('\n');
196
+ }
@@ -0,0 +1 @@
1
+ export declare const promptReviewLeaf: import("../../../core/command.js").LeafDef;
@@ -1,18 +1,18 @@
1
1
  import { mkdirSync, writeFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- import { defineLeaf } from '../../core/command.js';
4
- import { InputError, readStdinRaw } from '../../core/io.js';
5
- import { userScopeRoot } from '../../core/scope.js';
6
- import { ApiError } from '../../api/index.js';
7
- import { cliClient } from '../api-client.js';
8
- import { readMergedLaunchConfig, subKindsAvailableTo } from '../../core/config.js';
9
- import { LAUNCH_SYSTEM_PROMPT } from '../../core/runtime/launch-prompt.js';
3
+ import { defineLeaf } from '../../../core/command.js';
4
+ import { InputError, readStdinRaw } from '../../../core/io.js';
5
+ import { userScopeRoot } from '../../../core/scope.js';
6
+ import { ApiError } from '../../../api/index.js';
7
+ import { cliClient } from '../../api-client.js';
8
+ import { readMergedLaunchConfig, subKindsAvailableTo } from '../../../core/config.js';
9
+ import { LAUNCH_SYSTEM_PROMPT } from '../../../core/runtime/launch-prompt.js';
10
10
  // De-barreled imports: substrate/index.js re-exports subject.js (canvas-db), so
11
11
  // this CLI leaf imports the PURE render fns from render.js and the pure subject
12
12
  // fields from subject-fields.js/schema.js directly, never the barrel. Spine
13
13
  // depth is computed via the API (spineDepthViaApi), not subject.js's spineDepth.
14
- import { renderPreferencesForSubject, renderKnowledgeForSubject } from '../../core/substrate/render.js';
15
- import { scopeForCwd, profileNameFor } from '../../core/substrate/subject-fields.js';
14
+ import { renderPreferencesForSubject, renderKnowledgeForSubject } from '../../../core/substrate/render.js';
15
+ import { scopeForCwd, profileNameFor } from '../../../core/substrate/subject-fields.js';
16
16
  function resolveLifecycle(mode, lifecycle) {
17
17
  if (lifecycle !== undefined)
18
18
  return lifecycle;
@@ -245,7 +245,7 @@ export const promptReviewLeaf = defineLeaf({
245
245
  description: 'inspect an assembled prompt and export review notes',
246
246
  whenToUse: 'you want to inspect a composed persona prompt for a kind/mode combo, compare the space around it, or export review notes back to source files',
247
247
  help: {
248
- name: 'sys prompt-review',
248
+ name: 'sys context prompt-review',
249
249
  summary: 'inspect an assembled prompt and export review notes',
250
250
  params: [
251
251
  { kind: 'positional', name: 'action', type: 'string', required: false, constraint: 'Optional subcommand: export. Omit for the assembled prompt contract.' },
@@ -0,0 +1,55 @@
1
+ import { type MemoryTarget } from '../../../core/memory-resolver.js';
2
+ import { type DeliveryPayload, type DeliveryRecord, type GateOutcome } from '../../../core/substrate/plan.js';
3
+ import { type Rung, type SurfaceEntry, type SurfaceEvent } from '../../../core/substrate/schema.js';
4
+ import { type NodeConfigSubject } from '../../../core/substrate/subject-fields.js';
5
+ export declare const resolveLeaf: import("../../../core/command.js").LeafDef;
6
+ export interface ContextSnapshot {
7
+ subject: NodeConfigSubject;
8
+ target: MemoryTarget;
9
+ /** `--node` named an id the daemon does not have; the subject is synthetic. */
10
+ nodeMissing: boolean;
11
+ }
12
+ /** Build the subject and store target from `--node` and the axis flags. A flag
13
+ * always wins over the value derived from the node, so a live node is a
14
+ * starting point an engineer can dial away from. */
15
+ export declare function buildSnapshot(input: Record<string, unknown>): Promise<ContextSnapshot>;
16
+ /** The three payload-carrying flags, named once so each leaf can map its own
17
+ * flag spelling onto them (`doc` spends its `name` on the document itself). */
18
+ export interface PayloadFlags {
19
+ file: string | null;
20
+ readName: string | null;
21
+ command: string | null;
22
+ }
23
+ /** The event's match payload from the flags that carry it. An event invoked
24
+ * without its payload matches nothing — the planner's own contract. */
25
+ export declare function payloadFor(event: SurfaceEvent, flags: PayloadFlags, target: MemoryTarget): DeliveryPayload;
26
+ export declare function subjectSummary(subject: NodeConfigSubject): Record<string, unknown>;
27
+ export declare function optionalString(value: unknown): string | null;
28
+ export interface DocRow {
29
+ name: string;
30
+ kind: string;
31
+ scope: string;
32
+ store_path: string;
33
+ authored_rung: Rung;
34
+ project_memory_cap: Rung | null;
35
+ capped_rung: Rung;
36
+ final_rung: Rung;
37
+ winner: boolean;
38
+ shadowed_by: string | null;
39
+ doc_gate: string;
40
+ entry_gate: string | null;
41
+ matched_entry: string | null;
42
+ excluded?: string;
43
+ }
44
+ /** A document delivers when it won its canonical name AND resolved to a rung
45
+ * above the no-delivery floor — boot keeps rung-`none` winners in the plan so
46
+ * the catalog can still count them. */
47
+ export declare function delivers(record: DeliveryRecord): boolean;
48
+ export declare function docRow(record: DeliveryRecord, includeAll: boolean): DocRow;
49
+ export declare function gateText(gate: GateOutcome): string;
50
+ export declare function entryText(entry: SurfaceEntry | null): string | null;
51
+ /** Why a considered document delivers nothing, in the order the planner applies
52
+ * its checks — the first failing one is the answer an engineer acts on. */
53
+ export declare function exclusionReason(record: DeliveryRecord): string;
54
+ export declare function describeSubject(subject: Record<string, unknown>): string;
55
+ export declare function cell(v: unknown): string;