@north-light/crouter 0.3.220 → 0.3.222

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 (271) hide show
  1. package/dist/api/client.d.ts +29 -0
  2. package/dist/api/client.js +44 -0
  3. package/dist/api/dto/chat-inventory.d.ts +56 -0
  4. package/dist/api/dto/chat-inventory.js +11 -0
  5. package/dist/api/dto/human-requests.d.ts +88 -0
  6. package/dist/api/dto/human-requests.js +4 -0
  7. package/dist/api/dto/human.d.ts +3 -0
  8. package/dist/api/dto/profiles.d.ts +19 -5
  9. package/dist/api/dto/profiles.js +2 -1
  10. package/dist/api/dto/reviews.d.ts +2 -0
  11. package/dist/api/index.d.ts +2 -0
  12. package/dist/api/index.js +2 -0
  13. package/dist/api/routes.d.ts +8 -0
  14. package/dist/api/routes.js +11 -0
  15. package/dist/build-root.d.ts +2 -6
  16. package/dist/build-root.js +51 -4
  17. package/dist/builtin-memory/00-runtime-base/00-authoring.md +31 -0
  18. package/dist/builtin-memory/00-runtime-base/01-escalation.md +14 -0
  19. package/dist/builtin-memory/{insights/listen.md → 00-runtime-base/02-insight-capture.md} +1 -0
  20. package/dist/builtin-memory/02-turn-lifecycle/00-ending-a-turn.md +27 -0
  21. package/dist/builtin-memory/{02-lifecycle/01-resident.md → 02-turn-lifecycle/02-resident.md} +5 -0
  22. package/dist/builtin-memory/04-base-worker.md +4 -8
  23. package/dist/builtin-memory/04-orchestration-kernel.md +1 -1
  24. package/dist/builtin-memory/05-kinds/advisor/01-orchestrator.md +1 -0
  25. package/dist/builtin-memory/05-kinds/advisor/advice-contract.md +1 -0
  26. package/dist/builtin-memory/05-kinds/design/00-base.md +2 -1
  27. package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +2 -1
  28. package/dist/builtin-memory/05-kinds/design/design-contract.md +19 -0
  29. package/dist/builtin-memory/05-kinds/developer/00-base.md +1 -0
  30. package/dist/builtin-memory/05-kinds/developer/01-orchestrator.md +1 -0
  31. package/dist/builtin-memory/05-kinds/explore/00-base.md +1 -0
  32. package/dist/builtin-memory/05-kinds/explore/01-orchestrator.md +1 -0
  33. package/dist/builtin-memory/05-kinds/general/00-base.md +1 -0
  34. package/dist/builtin-memory/05-kinds/plan/00-base.md +2 -1
  35. package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +2 -1
  36. package/dist/builtin-memory/05-kinds/plan/plan-contract.md +28 -0
  37. package/dist/builtin-memory/05-kinds/plan/reviewers/architecture-fit.md +1 -0
  38. package/dist/builtin-memory/05-kinds/plan/reviewers/code-smells.md +1 -0
  39. package/dist/builtin-memory/05-kinds/plan/reviewers/lens-contract.md +1 -0
  40. package/dist/builtin-memory/05-kinds/plan/reviewers/pattern-consistency.md +1 -0
  41. package/dist/builtin-memory/05-kinds/plan/reviewers/requirements-coverage.md +1 -0
  42. package/dist/builtin-memory/05-kinds/plan/reviewers/security.md +1 -0
  43. package/dist/builtin-memory/05-kinds/review/00-base.md +1 -0
  44. package/dist/builtin-memory/05-kinds/review/01-orchestrator.md +1 -0
  45. package/dist/builtin-memory/05-kinds/review/companion/00-base.md +1 -0
  46. package/dist/builtin-memory/05-kinds/review/security-findings.md +1 -0
  47. package/dist/builtin-memory/05-kinds/spec/00-base.md +4 -3
  48. package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +1 -0
  49. package/dist/builtin-memory/05-kinds/spec/requirements.md +1 -0
  50. package/dist/builtin-memory/design/guide.md +35 -0
  51. package/dist/builtin-memory/design/roadmap.md +21 -0
  52. package/dist/builtin-memory/insights/capture.md +1 -1
  53. package/dist/builtin-memory/internal/agent-shaping.md +3 -1
  54. package/dist/builtin-memory/internal/memory-loading.md +4 -0
  55. package/dist/builtin-memory/internal/plugins.md +10 -1
  56. package/dist/builtin-memory/internal/storage-tiers.md +1 -1
  57. package/dist/builtin-memory/plan/roadmap.md +6 -22
  58. package/dist/builtin-memory/spec/guide.md +23 -6
  59. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/claude-plugin-commands.ts +1 -1
  60. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/crtr-commands/index.ts +7 -1
  61. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +34 -16
  62. package/dist/clients/attach/__tests__/ref-autocomplete.test.js +1 -1
  63. package/dist/clients/attach/__tests__/titled-editor-preview.test.js +1 -1
  64. package/dist/clients/attach/overlays/file-review.js +2 -2
  65. package/dist/clients/attach/render/markdown-source.js +106 -1
  66. package/dist/clients/attach/session/file-links.d.ts +13 -4
  67. package/dist/clients/attach/session/file-links.js +54 -58
  68. package/dist/clients/attach/session/keys.d.ts +1 -1
  69. package/dist/clients/attach/session/profile-files.js +1 -1
  70. package/dist/clients/attach/viewer.js +698 -696
  71. package/dist/clients/inbox/controller.js +1 -1
  72. package/dist/clients/inbox/resolve.d.ts +1 -0
  73. package/dist/clients/inbox/review/launch.d.ts +8 -4
  74. package/dist/clients/inbox/review/launch.js +55 -5
  75. package/dist/clients/inbox/review/review-client.d.ts +1 -0
  76. package/dist/clients/inbox/review/review-client.js +7 -1
  77. package/dist/clients/inbox/review-adapter.d.ts +1 -8
  78. package/dist/clients/inbox/review-adapter.js +4 -52
  79. package/dist/commands/__tests__/human.test.js +2 -2
  80. package/dist/commands/human/request.d.ts +2 -0
  81. package/dist/commands/human/request.js +281 -0
  82. package/dist/commands/human.js +5 -2
  83. package/dist/commands/memory/lint.js +2 -1
  84. package/dist/commands/memory/read.js +1 -0
  85. package/dist/commands/memory.js +1 -1
  86. package/dist/commands/pkg/market-manage.js +165 -75
  87. package/dist/commands/pkg/plugin-inspect.js +19 -2
  88. package/dist/commands/pkg/plugin-manage.d.ts +8 -3
  89. package/dist/commands/pkg/plugin-manage.js +72 -24
  90. package/dist/commands/profile/default.js +6 -10
  91. package/dist/commands/profile/list.js +5 -3
  92. package/dist/commands/profile/new.js +21 -8
  93. package/dist/commands/profile/project.js +25 -19
  94. package/dist/commands/profile/show.js +3 -3
  95. package/dist/commands/surface-inbox.js +1 -0
  96. package/dist/commands/sys/__tests__/migrate.test.js +16 -5
  97. package/dist/commands/sys/config.js +2 -2
  98. package/dist/commands/sys/doctor.js +87 -5
  99. package/dist/commands/sys/migrate.js +38 -19
  100. package/dist/commands/sys/setup-core.js +1 -1
  101. package/dist/commands/sys/sync-project-guidance.js +1 -1
  102. package/dist/core/__tests__/broker-extension-canvas-db-boundary.test.js +7 -4
  103. package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +1 -1
  104. package/dist/core/__tests__/fixtures/c5-command-boundary-ext.js +24 -0
  105. package/dist/core/__tests__/fixtures/fake-engine.d.ts +24 -18
  106. package/dist/core/__tests__/fixtures/fake-engine.js +8 -1
  107. package/dist/core/__tests__/fixtures/memory-slash-live-probe.js +71 -0
  108. package/dist/core/__tests__/human-action-delivery.test.d.ts +1 -0
  109. package/dist/core/__tests__/human-action-delivery.test.js +140 -0
  110. package/dist/core/__tests__/human-actions.test.d.ts +1 -0
  111. package/dist/core/__tests__/human-actions.test.js +116 -0
  112. package/dist/core/__tests__/inline-memory-refs.test.js +36 -2
  113. package/dist/core/__tests__/profile-project-memory-delivery.test.d.ts +1 -0
  114. package/dist/core/__tests__/profile-project-memory-delivery.test.js +217 -0
  115. package/dist/core/__tests__/prospective-inventory-capability-parity.test.d.ts +1 -0
  116. package/dist/core/__tests__/prospective-inventory-capability-parity.test.js +91 -0
  117. package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.d.ts +1 -0
  118. package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.js +127 -0
  119. package/dist/core/__tests__/seam/prospective-inventory-stdout.test.d.ts +1 -0
  120. package/dist/core/__tests__/seam/prospective-inventory-stdout.test.js +31 -0
  121. package/dist/core/__tests__/serial/broker-sdk-wiring.test.js +102 -2
  122. package/dist/core/bootstrap.js +6 -0
  123. package/dist/core/canvas/browse/app.js +5 -2
  124. package/dist/core/canvas/browse/model.d.ts +25 -15
  125. package/dist/core/canvas/browse/model.js +86 -65
  126. package/dist/core/canvas/db.js +23 -0
  127. package/dist/core/canvas/human-deliveries.d.ts +53 -0
  128. package/dist/core/canvas/human-deliveries.js +75 -0
  129. package/dist/core/canvas/render-source.d.ts +6 -0
  130. package/dist/core/canvas/render-source.js +7 -1
  131. package/dist/core/canvas/render.js +10 -2
  132. package/dist/core/command-hooks/artifact.d.ts +10 -0
  133. package/dist/core/command-hooks/artifact.js +129 -0
  134. package/dist/core/command-hooks/catalog.d.ts +14 -0
  135. package/dist/core/command-hooks/catalog.js +38 -0
  136. package/dist/core/command-hooks/compose.d.ts +15 -0
  137. package/dist/core/command-hooks/compose.js +99 -0
  138. package/dist/core/command-hooks/discovery.d.ts +87 -0
  139. package/dist/core/command-hooks/discovery.js +174 -0
  140. package/dist/core/command-hooks/help.d.ts +5 -0
  141. package/dist/core/command-hooks/help.js +18 -0
  142. package/dist/core/command-hooks/index.d.ts +6 -0
  143. package/dist/core/command-hooks/index.js +6 -0
  144. package/dist/core/command-hooks/report.d.ts +23 -0
  145. package/dist/core/command-hooks/report.js +19 -0
  146. package/dist/core/command-hooks/schema.d.ts +27 -0
  147. package/dist/core/command-hooks/schema.js +68 -0
  148. package/dist/core/command-hooks/transport/exec-invoke.d.ts +22 -0
  149. package/dist/core/command-hooks/transport/exec-invoke.js +274 -0
  150. package/dist/core/command-plugins/presence.d.ts +2 -0
  151. package/dist/core/command-plugins/presence.js +17 -0
  152. package/dist/core/command-plugins/transport/exec-invoke.d.ts +5 -0
  153. package/dist/core/command-plugins/transport/exec-invoke.js +58 -5
  154. package/dist/core/command.d.ts +8 -1
  155. package/dist/core/command.js +12 -10
  156. package/dist/core/config.d.ts +13 -1
  157. package/dist/core/config.js +51 -1
  158. package/dist/core/feed/inbox.d.ts +6 -0
  159. package/dist/core/feed/inbox.js +9 -1
  160. package/dist/core/help.d.ts +7 -1
  161. package/dist/core/human/action-binding.d.ts +21 -0
  162. package/dist/core/human/action-binding.js +40 -0
  163. package/dist/core/human/completion.d.ts +38 -0
  164. package/dist/core/human/completion.js +27 -0
  165. package/dist/core/human/convention.d.ts +2 -0
  166. package/dist/core/human/convention.js +2 -0
  167. package/dist/core/human/tickets.d.ts +25 -6
  168. package/dist/core/human/tickets.js +19 -13
  169. package/dist/core/human/types.d.ts +5 -0
  170. package/dist/core/human-actions.d.ts +25 -0
  171. package/dist/core/human-actions.js +101 -0
  172. package/dist/core/io.d.ts +9 -1
  173. package/dist/core/io.js +44 -2
  174. package/dist/core/memory/inline-ref-inventory.d.ts +2 -1
  175. package/dist/core/memory/inline-ref-inventory.js +15 -8
  176. package/dist/core/memory-resolver.d.ts +13 -1
  177. package/dist/core/memory-resolver.js +26 -20
  178. package/dist/core/profiles/manifest.d.ts +13 -2
  179. package/dist/core/profiles/manifest.js +84 -18
  180. package/dist/core/profiles/select.d.ts +2 -0
  181. package/dist/core/profiles/select.js +29 -12
  182. package/dist/core/render.js +11 -0
  183. package/dist/core/runtime/advertised-command-invocation.d.ts +20 -0
  184. package/dist/core/runtime/advertised-command-invocation.js +233 -0
  185. package/dist/core/runtime/bearings.js +1 -1
  186. package/dist/core/runtime/broker/event-projection.js +7 -0
  187. package/dist/core/runtime/broker/frame-dispatch.d.ts +1 -1
  188. package/dist/core/runtime/broker/frame-dispatch.js +13 -11
  189. package/dist/core/runtime/broker/read-ops.d.ts +4 -0
  190. package/dist/core/runtime/broker/read-ops.js +6 -2
  191. package/dist/core/runtime/broker-extension-render.js +1 -1
  192. package/dist/core/runtime/broker-inventory.d.ts +4 -0
  193. package/dist/core/runtime/broker-inventory.js +116 -0
  194. package/dist/core/runtime/broker-persona-guidance.js +1 -1
  195. package/dist/core/runtime/broker-protocol.d.ts +9 -2
  196. package/dist/core/runtime/broker.js +10 -1
  197. package/dist/core/runtime/chat-inventory-rows.d.ts +8 -0
  198. package/dist/core/runtime/chat-inventory-rows.js +105 -0
  199. package/dist/core/runtime/command-surface.d.ts +38 -0
  200. package/dist/core/runtime/command-surface.js +117 -0
  201. package/dist/core/runtime/launch-target.d.ts +25 -0
  202. package/dist/core/runtime/launch-target.js +54 -0
  203. package/dist/core/runtime/node-read.js +5 -0
  204. package/dist/core/runtime/persona.js +3 -3
  205. package/dist/core/runtime/prospective-inventory-cli.d.ts +1 -0
  206. package/dist/core/runtime/prospective-inventory-cli.js +61 -0
  207. package/dist/core/runtime/prospective-inventory.d.ts +10 -0
  208. package/dist/core/runtime/prospective-inventory.js +88 -0
  209. package/dist/core/runtime/spawn.d.ts +3 -1
  210. package/dist/core/runtime/spawn.js +5 -3
  211. package/dist/core/scope.d.ts +26 -1
  212. package/dist/core/scope.js +52 -12
  213. package/dist/core/substrate/on-read.d.ts +7 -1
  214. package/dist/core/substrate/on-read.js +30 -32
  215. package/dist/core/substrate/render-node.d.ts +3 -2
  216. package/dist/core/substrate/render-node.js +3 -2
  217. package/dist/core/substrate/render.js +65 -24
  218. package/dist/core/substrate/schema.d.ts +16 -2
  219. package/dist/core/substrate/schema.js +14 -5
  220. package/dist/core/user-settings.d.ts +4 -0
  221. package/dist/core/user-settings.js +1 -0
  222. package/dist/daemon/api/__tests__/profile-launch-gates.test.js +56 -7
  223. package/dist/daemon/api/handlers/chat-inventory.d.ts +2 -0
  224. package/dist/daemon/api/handlers/chat-inventory.js +25 -0
  225. package/dist/daemon/api/handlers/human-requests.d.ts +2 -0
  226. package/dist/daemon/api/handlers/human-requests.js +409 -0
  227. package/dist/daemon/api/handlers/human.js +3 -0
  228. package/dist/daemon/api/handlers/inbox.js +3 -0
  229. package/dist/daemon/api/handlers/nodes.d.ts +1 -3
  230. package/dist/daemon/api/handlers/nodes.js +11 -46
  231. package/dist/daemon/api/handlers/profiles.js +7 -1
  232. package/dist/daemon/api/handlers/prospective-chat-inventory.d.ts +2 -0
  233. package/dist/daemon/api/handlers/prospective-chat-inventory.js +59 -0
  234. package/dist/daemon/api/handlers/reviews.js +10 -2
  235. package/dist/daemon/api/map.d.ts +2 -1
  236. package/dist/daemon/api/map.js +3 -2
  237. package/dist/daemon/api/server.js +6 -0
  238. package/dist/daemon/crtrd.js +6 -0
  239. package/dist/daemon/human/deliver-action.d.ts +16 -0
  240. package/dist/daemon/human/deliver-action.js +168 -0
  241. package/dist/daemon/human/finish.d.ts +8 -5
  242. package/dist/daemon/human/finish.js +45 -6
  243. package/dist/daemon/human/sweep.js +4 -1
  244. package/dist/daemon/reconcilers/human-delivery-lane.d.ts +10 -0
  245. package/dist/daemon/reconcilers/human-delivery-lane.js +41 -0
  246. package/dist/daemon/review/finish.d.ts +8 -3
  247. package/dist/daemon/review/finish.js +19 -1
  248. package/dist/hook-authoring.d.ts +75 -0
  249. package/dist/hook-authoring.js +358 -0
  250. package/dist/hook-process.d.ts +7 -0
  251. package/dist/hook-process.js +34 -0
  252. package/dist/index.d.ts +2 -0
  253. package/dist/index.js +2 -0
  254. package/dist/migrations/002-profile-project-memory.d.ts +2 -0
  255. package/dist/migrations/002-profile-project-memory.js +71 -0
  256. package/dist/migrations/profile-manifests.d.ts +30 -0
  257. package/dist/migrations/profile-manifests.js +70 -0
  258. package/dist/migrations/registry.js +10 -5
  259. package/dist/migrations/types.d.ts +28 -1
  260. package/dist/migrations/types.js +15 -9
  261. package/dist/pi-extensions/__tests__/canvas-structured-output.test.js +21 -4
  262. package/dist/pi-extensions/canvas-structured-output.js +85 -2
  263. package/dist/types.d.ts +23 -6
  264. package/dist/types.js +1 -0
  265. package/package.json +1 -1
  266. package/runtime.lock.json +2 -2
  267. package/dist/builtin-memory/00-runtime-base.md +0 -55
  268. package/dist/builtin-memory/design.md +0 -55
  269. package/dist/clients/attach/__tests__/file-review-focus.test.js +0 -49
  270. /package/dist/builtin-memory/{02-lifecycle/00-terminal.md → 02-turn-lifecycle/01-terminal.md} +0 -0
  271. /package/dist/{clients/attach/__tests__/file-review-focus.test.d.ts → core/__tests__/fixtures/memory-slash-live-probe.d.ts} +0 -0
@@ -6,6 +6,9 @@ import { assetRoot } from './asset-root.js';
6
6
  import { usage } from './errors.js';
7
7
  import { loadProfileManifest } from './profiles/manifest.js';
8
8
  import { realpathOrSelf } from './fs-utils.js';
9
+ /** The ceiling a project root carries when no profile relationship contributes
10
+ * it — the top rung, i.e. no cap at all. */
11
+ export const NEUTRAL_PROJECT_MEMORY = 'content';
9
12
  // Cache of resolved project-scope-root STACKS, keyed by (resolved cwd, profile
10
13
  // id). A profile widens the pointer set beyond the bare cwd (see
11
14
  // findProjectScopeRoots below), so the cache key must carry both — the same
@@ -13,7 +16,8 @@ import { realpathOrSelf } from './fs-utils.js';
13
16
  // `resetScopeCache()` (called by every profile mutation in
14
17
  // src/core/profiles/manifest.ts) clears the whole cache; there is no
15
18
  // per-entry invalidation because a profile edit can change which pointer set
16
- // any cached cwd belongs to.
19
+ // any cached cwd belongs to. Path-only callers read `roots`, which is the same
20
+ // array in the same order as `annotated`.
17
21
  const scopeRootsCache = new Map();
18
22
  function scopeRootsCacheKey(resolvedStartDir, profileId) {
19
23
  return `${resolvedStartDir}\u0000${profileId ?? ''}`;
@@ -94,19 +98,40 @@ function collectAncestorScopeRoots(startDir, userRoot) {
94
98
  * selected profile (`CRTR_PROFILE_ID`); a null/unresolvable profile collapses
95
99
  * to the single `[startDir]` pointer, reproducing today's behavior exactly.
96
100
  * Results are cached per (resolved startDir, profileId) — `resetScopeCache()`
97
- * invalidates on any profile mutation that can change project dirs or names. */
101
+ * invalidates on any profile mutation that can change project dirs or names.
102
+ *
103
+ * Each root also carries the selected profile relationship's memory ceiling
104
+ * (`findProjectScopeRootsWithMemory`), a projection layered onto this order
105
+ * rather than part of it: discovery provenance decides precedence, the
106
+ * relationship decides the ceiling, and the two never influence each other. */
98
107
  export function findProjectScopeRoots(startDir = process.cwd(), profileId = process.env['CRTR_PROFILE_ID'] || null) {
108
+ return resolveProjectScopeRoots(startDir, profileId).roots;
109
+ }
110
+ /** `findProjectScopeRoots` with each root's profile memory ceiling attached —
111
+ * same roots, same order. A root contributed by a profile entry's ancestor
112
+ * walk takes that entry's `memory` (first entry in manifest order wins, even
113
+ * when cwd also reaches the root); every other root takes the neutral
114
+ * `content` ceiling. */
115
+ export function findProjectScopeRootsWithMemory(startDir = process.cwd(), profileId = process.env['CRTR_PROFILE_ID'] || null) {
116
+ return resolveProjectScopeRoots(startDir, profileId).annotated;
117
+ }
118
+ function resolveProjectScopeRoots(startDir, profileId) {
99
119
  const resolvedStart = resolve(startDir);
100
120
  const key = scopeRootsCacheKey(resolvedStart, profileId);
101
121
  const cached = scopeRootsCache.get(key);
102
122
  if (cached !== undefined)
103
123
  return cached;
104
124
  const userRoot = userScopeRoot();
105
- const pointers = [resolvedStart];
125
+ // A null `memory` marks the cwd pointer: it decides precedence like any
126
+ // other pointer but contributes no ceiling.
127
+ const pointers = [
128
+ { dir: resolvedStart, memory: null },
129
+ ];
106
130
  if (profileId !== null && profileId !== '') {
107
131
  try {
108
132
  const { manifest } = loadProfileManifest(profileId);
109
- pointers.push(...manifest.projects);
133
+ for (const project of manifest.projects)
134
+ pointers.push({ dir: project.path, memory: project.memory });
110
135
  }
111
136
  catch {
112
137
  // Missing/deleted/invalid profile: this is a hot path every memory and
@@ -116,16 +141,23 @@ export function findProjectScopeRoots(startDir = process.cwd(), profileId = proc
116
141
  }
117
142
  const seen = new Set();
118
143
  const roots = [];
144
+ const caps = new Map();
119
145
  for (const pointer of pointers) {
120
- for (const root of collectAncestorScopeRoots(pointer, userRoot)) {
121
- if (seen.has(root))
122
- continue;
123
- seen.add(root);
124
- roots.push(root);
146
+ for (const root of collectAncestorScopeRoots(pointer.dir, userRoot)) {
147
+ if (!seen.has(root)) {
148
+ seen.add(root);
149
+ roots.push(root);
150
+ }
151
+ // Independent of the order dedupe above: a root already discovered from
152
+ // cwd still takes the first profile entry's ceiling.
153
+ if (pointer.memory !== null && !caps.has(root))
154
+ caps.set(root, pointer.memory);
125
155
  }
126
156
  }
127
- scopeRootsCache.set(key, roots);
128
- return roots;
157
+ const annotated = roots.map((root) => ({ root, memory: caps.get(root) ?? NEUTRAL_PROJECT_MEMORY }));
158
+ const entry = { roots, annotated };
159
+ scopeRootsCache.set(key, entry);
160
+ return entry;
129
161
  }
130
162
  export function projectScopeRoot(startDir) {
131
163
  return findProjectScopeRoot(startDir);
@@ -133,6 +165,9 @@ export function projectScopeRoot(startDir) {
133
165
  export function projectScopeRoots(startDir, profileId) {
134
166
  return findProjectScopeRoots(startDir, profileId);
135
167
  }
168
+ export function projectScopeRootsWithMemory(startDir, profileId) {
169
+ return findProjectScopeRootsWithMemory(startDir, profileId);
170
+ }
136
171
  export function scopeRoot(scope) {
137
172
  // The builtin scope has no scope-root dir; its content lives in dedicated
138
173
  // package directories such as builtinMemoryRoot().
@@ -160,7 +195,12 @@ export function ensureProjectScopeRoot(startDir = process.cwd()) {
160
195
  const resolvedStart = resolve(startDir);
161
196
  const root = join(resolvedStart, CRTR_DIR_NAME);
162
197
  const profileId = process.env['CRTR_PROFILE_ID'] || null;
163
- scopeRootsCache.set(scopeRootsCacheKey(resolvedStart, profileId), [root]);
198
+ // Neutral ceiling: the empty cached stack proves no profile entry's ancestor
199
+ // walk reached this root, since it did not exist to be walked into.
200
+ scopeRootsCache.set(scopeRootsCacheKey(resolvedStart, profileId), {
201
+ roots: [root],
202
+ annotated: [{ root, memory: NEUTRAL_PROJECT_MEMORY }],
203
+ });
164
204
  return root;
165
205
  }
166
206
  export function pluginsDir(scope) {
@@ -15,7 +15,13 @@ export declare function renderOnReadDocsForSubject(subject: NodeConfigSubject, r
15
15
  /** Workspace boot docs using the snapshot's cwd/profile instead of a canvas
16
16
  * lookup. `seen` is the transcript-scoped dedup set shared with the on-read
17
17
  * hook, so a front door delivered here never re-delivers on a later read
18
- * beneath its store. */
18
+ * beneath its store.
19
+ *
20
+ * Each candidate's authored rung is clamped to its project's profile
21
+ * relationship cap BEFORE the `none` filter, the ordering, the render, and the
22
+ * seen-map recording — so a capped doc never renders above its cap, a `none`
23
+ * project contributes nothing, and a later explicit `crtr memory read` can
24
+ * still upgrade what workspace-open disclosed at a lower rung. */
19
25
  export declare function renderWorkspaceOpenDocsForSubject(subject: NodeConfigSubject, cwd: string, profileId: string | null, seen?: InjectedDocs): string;
20
26
  /** Inner `<memory>` blocks for the memory-read event: docs whose
21
27
  * `memory-read` entries match the doc a `crtr memory read` just resolved
@@ -33,11 +33,11 @@ import { CRTR_DIR_NAME } from '../../types.js';
33
33
  import { pathExists, readText, walkFiles } from '../fs-utils.js';
34
34
  import { parseFrontmatterGeneric } from '../frontmatter.js';
35
35
  import { listAllMemoryDocs, listProjectMemoryDocs } from '../memory-resolver.js';
36
- import { userScopeRoot } from '../scope.js';
36
+ import { NEUTRAL_PROJECT_MEMORY, userScopeRoot } from '../scope.js';
37
37
  import { gatePasses } from './gate.js';
38
38
  import { commandDeliveryRung, memoryReadDeliveryRung, owningRootOf, readDeliveryRung, workspaceOpenRung } from './surface-match.js';
39
39
  import { deliveredAtOrAbove, recordDelivery } from './injected-store.js';
40
- import { normalizeDocName, parseSubstrateDoc, parseSubstrateFrontmatter, previewLine, resolveDocName } from './schema.js';
40
+ import { minRung, normalizeDocName, parseSubstrateDoc, parseSubstrateFrontmatter, previewLine, resolveDocName } from './schema.js';
41
41
  import { cachedSubstrateDocsInclusive } from './session-cache.js';
42
42
  import { realpathOrSelf } from '../fs-utils.js';
43
43
  const JUNK_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.next', '.cache', '.yalc']);
@@ -48,27 +48,6 @@ function attr(s) {
48
48
  .replace(/</g, '&lt;')
49
49
  .replace(/>/g, '&gt;');
50
50
  }
51
- function gitRootOf(p) {
52
- let d = p;
53
- const root = parse(d).root;
54
- while (true) {
55
- if (pathExists(`${d}${sep}.git`))
56
- return d;
57
- if (d === root)
58
- return null;
59
- const parent = dirname(d);
60
- if (parent === d)
61
- return null;
62
- d = parent;
63
- }
64
- }
65
- function disp(p) {
66
- const root = gitRootOf(p);
67
- if (!root)
68
- return p;
69
- const rel = relative(root, p);
70
- return rel === '' ? '.' : rel;
71
- }
72
51
  function isJunkAncestor(dir) {
73
52
  return dir.split(sep).some((segment) => JUNK_DIRS.has(segment));
74
53
  }
@@ -91,6 +70,9 @@ function loadProjectDoc(file, memoryDir) {
91
70
  scope: 'project',
92
71
  path: file,
93
72
  body,
73
+ // The read event is uncapped by design: this walk is reached only by an
74
+ // explicit file read, never by automatic boot/workspace-open delivery.
75
+ projectMemory: NEUTRAL_PROJECT_MEMORY,
94
76
  };
95
77
  }
96
78
  catch {
@@ -181,16 +163,26 @@ function docContentKey(doc) {
181
163
  return null;
182
164
  return `content-sha256:${createHash('sha256').update(body).digest('hex')}`;
183
165
  }
166
+ // Two shapes: element-with-body means the content is here; self-closing means
167
+ // it is not — a preview carries its routing line as `readWhen` metadata so the
168
+ // one-liner can never be mistaken for the document's content. No disk path:
169
+ // agents address memory by name through the command surface, and the explicit
170
+ // read leaf already returns `path`.
184
171
  function renderDocEnvelope(doc, rung) {
185
172
  if (rung === 'none')
186
173
  return null;
187
- let body = '';
188
- if (rung === 'content')
189
- body = doc.body.trim();
190
- else if (rung === 'preview')
191
- body = previewLine(doc);
192
- const attrs = `kind="${attr(doc.kind)}" name="${attr(doc.name)}" src="${attr(disp(doc.path))}"`;
193
- return body === '' ? `<memory ${attrs} />` : `<memory ${attrs}>\n${body}\n</memory>`;
174
+ let attrs = `kind="${attr(doc.kind)}" name="${attr(doc.name)}"`;
175
+ if (rung === 'content') {
176
+ const body = doc.body.trim();
177
+ if (body !== '')
178
+ return `<memory ${attrs}>\n${body}\n</memory>`;
179
+ }
180
+ else if (rung === 'preview') {
181
+ const pl = previewLine(doc);
182
+ if (pl !== '')
183
+ attrs += ` readWhen="${attr(pl)}"`;
184
+ }
185
+ return `<memory ${attrs} />`;
194
186
  }
195
187
  /** Render candidates at their matched rungs, gate-checked and deduped against
196
188
  * the transcript set (higher rungs pierce lower; a delivery records at its
@@ -244,7 +236,13 @@ export function renderOnReadDocsForSubject(subject, readFilePath, seen = new Map
244
236
  /** Workspace boot docs using the snapshot's cwd/profile instead of a canvas
245
237
  * lookup. `seen` is the transcript-scoped dedup set shared with the on-read
246
238
  * hook, so a front door delivered here never re-delivers on a later read
247
- * beneath its store. */
239
+ * beneath its store.
240
+ *
241
+ * Each candidate's authored rung is clamped to its project's profile
242
+ * relationship cap BEFORE the `none` filter, the ordering, the render, and the
243
+ * seen-map recording — so a capped doc never renders above its cap, a `none`
244
+ * project contributes nothing, and a later explicit `crtr memory read` can
245
+ * still upgrade what workspace-open disclosed at a lower rung. */
248
246
  export function renderWorkspaceOpenDocsForSubject(subject, cwd, profileId, seen = new Map()) {
249
247
  let docs;
250
248
  try {
@@ -256,7 +254,7 @@ export function renderWorkspaceOpenDocsForSubject(subject, cwd, profileId, seen
256
254
  return '';
257
255
  }
258
256
  const candidates = dedupeByPhysicalPath(docs)
259
- .map((doc) => ({ doc, rung: workspaceOpenRung(doc) }))
257
+ .map((doc) => ({ doc, rung: minRung(workspaceOpenRung(doc), doc.projectMemory) }))
260
258
  .filter(({ rung }) => rung !== 'none')
261
259
  .sort((a, b) => {
262
260
  const aRoot = owningRootOf(a.doc) ?? '';
@@ -1,6 +1,7 @@
1
1
  import type { InjectedDocs } from './injected-store.js';
2
- /** The system-prompt `<memory kind="preference">` block for `nodeId`: assemble
3
- * the node's subject from the canvas-db, render the pure block, then swap the
2
+ /** The system-prompt substrate splice for `nodeId` — shipped-layer protocol
3
+ * prose followed by the `<memory kind="preference">` block: assemble
4
+ * the node's subject from the canvas-db, render the pure splice, then swap the
4
5
  * literal `$CRTR_CONTEXT_DIR` env-var token for the node's real absolute path
5
6
  * so agents don't paste it into the non-expanding Write tool (see
6
7
  * interpolateNodePaths). A null subject (unknown id) still renders the
@@ -8,8 +8,9 @@
8
8
  import { interpolateNodePaths } from '../canvas/paths.js';
9
9
  import { assembleNodeSubject } from './subject.js';
10
10
  import { renderPreferencesForSubject, renderKnowledgeForSubject } from './render.js';
11
- /** The system-prompt `<memory kind="preference">` block for `nodeId`: assemble
12
- * the node's subject from the canvas-db, render the pure block, then swap the
11
+ /** The system-prompt substrate splice for `nodeId` — shipped-layer protocol
12
+ * prose followed by the `<memory kind="preference">` block: assemble
13
+ * the node's subject from the canvas-db, render the pure splice, then swap the
13
14
  * literal `$CRTR_CONTEXT_DIR` env-var token for the node's real absolute path
14
15
  * so agents don't paste it into the non-expanding Write tool (see
15
16
  * interpolateNodePaths). A null subject (unknown id) still renders the
@@ -57,14 +57,14 @@ import { subKindsAvailableTo } from '../config.js';
57
57
  import { parseFrontmatterGeneric } from '../frontmatter.js';
58
58
  import { pathExists, readText, realpathOrSelf, walkFiles } from '../fs-utils.js';
59
59
  import { memoryDir } from '../runtime/memory.js';
60
- import { projectScopeRoots } from '../scope.js';
60
+ import { NEUTRAL_PROJECT_MEMORY, projectScopeRoots } from '../scope.js';
61
61
  import { gatePasses } from './gate.js';
62
62
  // De-barreled to LEAF modules on purpose: importing from './index.js' would
63
63
  // pull subject.js (canvas-db) transitively, re-tainting every CLI consumer of
64
64
  // the pure render fns. The canvas-db wrappers (renderPreferencesSection /
65
65
  // renderKnowledgeBlock) live in render-node.js instead.
66
66
  import { recordDelivery } from './injected-store.js';
67
- import { bootRung, normalizeDocName, parseSubstrateDoc, parseSubstrateFrontmatter, previewLine, resolveDocName, rungRank, } from './schema.js';
67
+ import { bootRung, minRung, normalizeDocName, parseSubstrateDoc, parseSubstrateFrontmatter, previewLine, resolveDocName, rungRank, } from './schema.js';
68
68
  import { cachedSubstrateDocs } from './session-cache.js';
69
69
  /** First-wins dedup by (already-normalized) name, preserving the input's
70
70
  * precedence order. The array's ORDER is the precedence signal — callers must
@@ -89,7 +89,13 @@ function dedupeFirstWins(docs) {
89
89
  * selection ONLY; display ordering is a separate, later step (see
90
90
  * renderGrouped) so a builtin doc can never mask a project/user override by
91
91
  * virtue of sorting first. Uses the per-session cache so the full corpus is
92
- * scanned + parsed at most once per session across the boot-render calls. */
92
+ * scanned + parsed at most once per session across the boot-render calls.
93
+ *
94
+ * The profile relationship's cap applies HERE, before dedup: a `none` project
95
+ * leaves the corpus entirely (it contributes nothing, not even a `[+N more]`
96
+ * count, and so cannot shadow a wider-scope automatic winner), and every
97
+ * surviving doc's authored boot rung is clamped to its cap before dedup, the
98
+ * session cache, and delivery seeding see it. */
93
99
  function selectWinners(subject, kind) {
94
100
  let docs;
95
101
  try {
@@ -100,8 +106,9 @@ function selectWinners(subject, kind) {
100
106
  }
101
107
  const eligible = docs
102
108
  .filter((d) => d.kind === kind)
109
+ .filter((d) => d.projectMemory !== 'none')
103
110
  .filter((d) => gatePasses(d, subject))
104
- .map((d) => ({ ...d, bootRung: bootRung(d) }));
111
+ .map((d) => ({ ...d, bootRung: minRung(bootRung(d), d.projectMemory) }));
105
112
  return dedupeFirstWins(eligible);
106
113
  }
107
114
  // ---------------------------------------------------------------------------
@@ -176,9 +183,16 @@ function compareTreePosition(a, b) {
176
183
  }
177
184
  return as.length - bs.length;
178
185
  }
186
+ /** A doc the running agent can revise through `crtr memory edit`: any native
187
+ * scope-store doc (user/profile/project/node). Shipped docs — builtin and
188
+ * plugin-mounted — are not editable from a node; the preference render treats
189
+ * them as operating protocol, not memory (see buildPreferencesBlock). */
190
+ function isAgentEditable(doc) {
191
+ return doc.scope !== 'builtin' && doc.plugin === undefined;
192
+ }
179
193
  /** STEP 2 — split deduped `winners` by rung and render each group in its
180
194
  * display order: `content` docs first, as clean prose (general→specific,
181
- * then tree position, then filename — no tree chrome, no name label); every
195
+ * then tree position, then filename — no tree chrome); every
182
196
  * other winner (preview/name/hidden-bootless) folds into ONE catalog tree
183
197
  * exactly as before. `nodeLocalSet` marks which winners came from the
184
198
  * node-local store (ranked most-specific in content ordering); omit it for a
@@ -238,7 +252,9 @@ function nodeLocalDocs(nodeId, subject) {
238
252
  // node-local is NOT a resolver scope; `scope` is a placeholder never read
239
253
  // by gate eval (keyed off the NODE subject, not the doc) nor by the
240
254
  // renderers (keyed off name / body / rung).
241
- out.push({ ...schema, name, scope: 'user', path: file, body });
255
+ // Node-local is not a project store: no profile relationship names it,
256
+ // so it carries the neutral ceiling.
257
+ out.push({ ...schema, name, scope: 'user', path: file, body, projectMemory: NEUTRAL_PROJECT_MEMORY });
242
258
  }
243
259
  catch {
244
260
  // A single malformed file is skipped, never fatal to the render.
@@ -480,37 +496,62 @@ function buildSubPersonaMenu(kind) {
480
496
  lines.join('\n'));
481
497
  }
482
498
  // ---------------------------------------------------------------------------
483
- // 1. Preferences — `<memory kind="preference">` (system prompt).
499
+ // 1. Preferences — protocol prose + `<memory kind="preference">` (system prompt).
484
500
  // ---------------------------------------------------------------------------
485
- /** The system-prompt memory block: the always-present memory-usage guidance,
486
- * then (when any are eligible) the `kind: preference` docs GROUPED by rung —
487
- * content-rung docs first as clean prose (general→specific), then the
488
- * preview/name catalog tree. ALWAYS returns a non-empty `<memory
489
- * kind="preference">` block — the guidance frame is unconditional, so the
490
- * system-prompt splice is never empty even when no preference is eligible. */
501
+ /** The system-prompt substrate splice, TWO parts in one string.
502
+ *
503
+ * 1. The operating protocol: shipped (builtin/plugin) content-rung bodies as
504
+ * bare prose, plus the sub-persona menu. No memory envelope — the protocol
505
+ * is the rules of the node's world, and wrapping it as recalled "memory:
506
+ * preference" content gave it softer epistemic status than the
507
+ * instructions it is; the intro/outro sentences ("how the user wants you
508
+ * to behave", "update the preference") were also false of it.
509
+ * 2. The `<memory kind="preference">` block: the always-present memory-usage
510
+ * guidance, then (when any are eligible) each agent-editable doc wrapped
511
+ * `<preference name="…">…</preference>` — the name is the edit handle the
512
+ * outro's update directive needs — then the preview/name catalog tree.
513
+ * Every frame sentence is true of everything inside it.
514
+ *
515
+ * Ordering across the two parts is unchanged from the single-block render
516
+ * (shipped→editable was already the scope sort), so the shared cache prefix
517
+ * across nodes is preserved. ALWAYS returns a non-empty splice — the guidance
518
+ * frame is unconditional, so it is never empty even when no preference is
519
+ * eligible. */
491
520
  function buildPreferencesBlock(subject, seen) {
492
- let body = MEMORY_USAGE_GUIDANCE;
493
521
  let contentDocs = [];
494
522
  let tree = '';
523
+ let protocol = '';
524
+ let memBody = MEMORY_USAGE_GUIDANCE;
495
525
  if (subject !== null) {
496
526
  const winners = selectWinners(subject, 'preference');
497
527
  seedBootDelivery(seen, winners);
498
528
  const grouped = renderGrouped(winners, 'preferences');
499
529
  contentDocs = grouped.contentDocs;
500
530
  tree = grouped.tree;
531
+ const shipped = contentDocs
532
+ .filter((d) => !isAgentEditable(d))
533
+ .map((d) => d.body.trim())
534
+ .filter((b) => b !== '');
535
+ const editable = contentDocs
536
+ .filter((d) => isAgentEditable(d))
537
+ .map((d) => {
538
+ const b = d.body.trim();
539
+ return b === '' ? '' : `<preference name="${d.name}">\n${b}\n</preference>`;
540
+ })
541
+ .filter((b) => b !== '');
501
542
  const subPersonaMenu = buildSubPersonaMenu(subject.kind);
502
- if (grouped.contentProse !== '' || grouped.tree !== '' || subPersonaMenu !== '') {
503
- body += `\n\n${PREFERENCES_INTRO}`;
504
- if (grouped.contentProse !== '')
505
- body += `\n\n${grouped.contentProse}`;
506
- if (subPersonaMenu !== '')
507
- body += `\n\n${subPersonaMenu}`;
508
- if (grouped.tree !== '')
509
- body += `\n\n${grouped.tree}`;
510
- body += `\n\n${PREFERENCES_OUTRO}`;
543
+ protocol = [...shipped, ...(subPersonaMenu === '' ? [] : [subPersonaMenu])].join('\n\n');
544
+ if (editable.length > 0 || tree !== '') {
545
+ memBody += `\n\n${PREFERENCES_INTRO}`;
546
+ if (editable.length > 0)
547
+ memBody += `\n\n${editable.join('\n\n')}`;
548
+ if (tree !== '')
549
+ memBody += `\n\n${tree}`;
550
+ memBody += `\n\n${PREFERENCES_OUTRO}`;
511
551
  }
512
552
  }
513
- return { block: `<memory kind="preference">\n${body}\n</memory>`, contentDocs, tree };
553
+ const memoryBlock = `<memory kind="preference">\n${memBody}\n</memory>`;
554
+ return { block: protocol === '' ? memoryBlock : `${protocol}\n\n${memoryBlock}`, contentDocs, tree };
514
555
  }
515
556
  export function renderPreferencesForSubject(subject, seen) {
516
557
  return buildPreferencesBlock(subject, seen);
@@ -1,16 +1,20 @@
1
+ import { type ProfileProjectMemory } from '../../api/dto/profiles.js';
1
2
  import type { MemoryDoc, MemoryScope } from '../memory-resolver.js';
2
3
  export declare const KINDS: readonly ["knowledge", "preference"];
3
4
  export type DocKind = (typeof KINDS)[number];
4
5
  /** Is `v` one of the two valid document kinds? */
5
6
  export declare function isDocKind(v: unknown): v is DocKind;
6
7
  export declare const RUNGS: readonly ["none", "name", "preview", "content"];
7
- export type Rung = (typeof RUNGS)[number];
8
+ export type Rung = ProfileProjectMemory;
8
9
  /** Ordinal of a rung on the ladder (none=0 … content=3). */
9
10
  export declare function rungRank(r: Rung): number;
10
11
  /** Does rung `r` disclose at least as much as `min`? */
11
12
  export declare function rungAtLeast(r: Rung, min: Rung): boolean;
13
+ /** The lower of two rungs — the clamp automatic delivery applies to an
14
+ * authored rung against its project relationship's cap. */
15
+ export declare function minRung(a: Rung, b: Rung): Rung;
12
16
  /** Strip an optional `NN-` ordering prefix from ONE path segment (file or
13
- * directory display name). `00-runtime-base` -> `runtime-base`; `spine` (no
17
+ * directory display name). `00-runtime-base/00-authoring` -> `runtime-base/authoring`; `spine` (no
14
18
  * prefix) is unchanged. */
15
19
  export declare function normalizeNameSegment(segment: string): string;
16
20
  /** Normalize a full slash-separated path-derived doc name by stripping the
@@ -107,10 +111,20 @@ export interface SubstrateDoc extends SubstrateSchema {
107
111
  name: string;
108
112
  /** The scope this doc resolved from. */
109
113
  scope: MemoryScope;
114
+ /** Owning plugin name when the doc is mounted from an installed plugin;
115
+ * undefined for a native scope doc. Shipped (builtin/plugin) docs and
116
+ * agent-editable scope docs render differently at boot (see renderGrouped). */
117
+ plugin?: string;
110
118
  /** Absolute path to the source .md. */
111
119
  path: string;
112
120
  /** Document body, frontmatter stripped. */
113
121
  body: string;
122
+ /** The selected profile relationship's cap on the project store this doc
123
+ * resolved from — the maximum rung automatic boot and workspace-open
124
+ * delivery may disclose it at. `content` is the neutral ceiling every
125
+ * non-project doc and every uncapped project root carries. Targeted reads,
126
+ * command routing, and directory listings ignore it. */
127
+ projectMemory: ProfileProjectMemory;
114
128
  }
115
129
  /** Parse a raw frontmatter record (from `parseFrontmatterGeneric`, via the
116
130
  * resolver) into a typed schema with defaults applied. Returns `null` when the
@@ -7,6 +7,7 @@
7
7
  // The runtime parser is tolerant by contract (it maps over many docs and must
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
+ import { PROFILE_PROJECT_MEMORY_VALUES } from '../../api/dto/profiles.js';
10
11
  // ---------------------------------------------------------------------------
11
12
  // Kinds — the two semantic kinds (design §3): `knowledge` (consult — procedural
12
13
  // playbooks + factual references merged) vs `preference` (behave — standing
@@ -22,7 +23,10 @@ export function isDocKind(v) {
22
23
  // each rung is a superset of disclosure over the one before:
23
24
  // none < name < preview < content
24
25
  // ---------------------------------------------------------------------------
25
- export const RUNGS = ['none', 'name', 'preview', 'content'];
26
+ // The ladder is ONE union, owned by the public API DTO (`ProfileProjectMemory`)
27
+ // so a profile relationship's cap and a doc's authored rung are the same values
28
+ // and clamp against each other without a conversion.
29
+ export const RUNGS = PROFILE_PROJECT_MEMORY_VALUES;
26
30
  /** Ordinal of a rung on the ladder (none=0 … content=3). */
27
31
  export function rungRank(r) {
28
32
  return RUNGS.indexOf(r);
@@ -31,21 +35,26 @@ export function rungRank(r) {
31
35
  export function rungAtLeast(r, min) {
32
36
  return rungRank(r) >= rungRank(min);
33
37
  }
38
+ /** The lower of two rungs — the clamp automatic delivery applies to an
39
+ * authored rung against its project relationship's cap. */
40
+ export function minRung(a, b) {
41
+ return rungRank(a) <= rungRank(b) ? a : b;
42
+ }
34
43
  // ---------------------------------------------------------------------------
35
44
  // Display-name normalization — the optional `NN-` ordering pin. A doc's
36
45
  // physical path may carry a two-digit numeric prefix on a file or directory
37
- // segment (`00-runtime-base.md`, `01-spine/`) purely to pin structural order
46
+ // segment (`00-runtime-base/`, `01-spine/`) purely to pin structural order
38
47
  // against natural sort (design: builtin prompt corpus, used sparingly). The
39
48
  // prefix is NEVER part of the doc's identity: every identity-facing surface —
40
49
  // MemoryDoc.name, direct lookup, leaf fallback, `crtr memory read`/`list`,
41
50
  // prompt render, on-read display — derives from the NORMALIZED segments, so
42
- // `00-runtime-base` displays/dedups/resolves as `runtime-base`. When a doc
51
+ // `00-runtime-base/00-authoring` displays/dedups/resolves as `runtime-base/authoring`. When a doc
43
52
  // sets an explicit frontmatter `name`, that normalized name wins instead of
44
53
  // the path-derived fallback. Only the physical path keeps the prefix.
45
54
  // ---------------------------------------------------------------------------
46
55
  const NUMERIC_PREFIX_RE = /^\d{2}-/;
47
56
  /** Strip an optional `NN-` ordering prefix from ONE path segment (file or
48
- * directory display name). `00-runtime-base` -> `runtime-base`; `spine` (no
57
+ * directory display name). `00-runtime-base/00-authoring` -> `runtime-base/authoring`; `spine` (no
49
58
  * prefix) is unchanged. */
50
59
  export function normalizeNameSegment(segment) {
51
60
  return segment.replace(NUMERIC_PREFIX_RE, '');
@@ -133,7 +142,7 @@ export function parseSubstrateDoc(doc) {
133
142
  const schema = parseSubstrateFrontmatter(doc.frontmatter);
134
143
  if (schema === null)
135
144
  return null;
136
- return { ...schema, name: doc.name, scope: doc.scope, path: doc.path, body: doc.body };
145
+ return { ...schema, name: doc.name, scope: doc.scope, plugin: doc.plugin, path: doc.path, body: doc.body, projectMemory: doc.projectMemory };
137
146
  }
138
147
  /** The kind a doc resolves to for `--kind` filtering/matching: the substrate
139
148
  * kind when the frontmatter parses as a valid substrate doc, else the raw
@@ -326,6 +326,10 @@ export declare const CONFIG_SETTINGS_COVERAGE: {
326
326
  surface: "specialized";
327
327
  owner: string;
328
328
  };
329
+ humanActions: {
330
+ surface: "specialized";
331
+ owner: string;
332
+ };
329
333
  };
330
334
  /** The exhaustive top-level key list used by config persistence and help. */
331
335
  export declare const SCOPE_CONFIG_KEYS: (keyof ScopeConfig)[];
@@ -389,6 +389,7 @@ export const CONFIG_SETTINGS_COVERAGE = {
389
389
  remoteCanvas: { surface: 'specialized', owner: 'canvas config commands' },
390
390
  spawnEnv: { surface: 'specialized', owner: 'spawn environment allow-list' },
391
391
  bin: { surface: 'specialized', owner: 'bare-binary contributions on every node PATH' },
392
+ humanActions: { surface: 'specialized', owner: 'scope-configured human completion actions' },
392
393
  };
393
394
  /** The exhaustive top-level key list used by config persistence and help. */
394
395
  export const SCOPE_CONFIG_KEYS = Object.keys(CONFIG_SETTINGS_COVERAGE);