@north-light/crouter 0.3.227 → 0.3.228

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 (286) hide show
  1. package/dist/api/client.d.ts +5 -5
  2. package/dist/api/client.js +3 -3
  3. package/dist/api/dto/broker-ops.d.ts +8 -0
  4. package/dist/api/dto/memory.d.ts +8 -7
  5. package/dist/api/dto/memory.js +2 -2
  6. package/dist/builtin-memory/05-kinds/design/00-base.md +3 -4
  7. package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +4 -3
  8. package/dist/builtin-memory/05-kinds/design/design-contract.md +3 -6
  9. package/dist/builtin-memory/05-kinds/plan/00-base.md +3 -4
  10. package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +4 -5
  11. package/dist/builtin-memory/05-kinds/plan/plan-contract.md +5 -13
  12. package/dist/builtin-memory/design/guide.md +38 -16
  13. package/dist/builtin-memory/design/roadmap.md +4 -4
  14. package/dist/builtin-memory/insights/init.md +5 -5
  15. package/dist/builtin-memory/internal/INDEX.md +1 -1
  16. package/dist/builtin-memory/internal/examples/INDEX.md +1 -1
  17. package/dist/builtin-memory/internal/memory-loading.md +6 -6
  18. package/dist/builtin-memory/internal/plugins.md +1 -1
  19. package/dist/builtin-memory/plan/guide.md +53 -0
  20. package/dist/builtin-memory/plan/roadmap.md +10 -8
  21. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +50 -37
  22. package/dist/clients/attach/__tests__/pane-tag-successor.test.js +56 -0
  23. package/dist/clients/attach/__tests__/profile-files.test.js +21 -0
  24. package/dist/clients/attach/chrome/inbox-strip.js +11 -7
  25. package/dist/clients/attach/session/pane-tag.d.ts +28 -2
  26. package/dist/clients/attach/session/pane-tag.js +48 -22
  27. package/dist/clients/attach/session/profile-files.d.ts +6 -0
  28. package/dist/clients/attach/session/profile-files.js +44 -11
  29. package/dist/clients/attach/slash/dispatch.js +4 -7
  30. package/dist/clients/attach/viewer.js +588 -596
  31. package/dist/commands/__tests__/surface-reload-target.test.js +21 -0
  32. package/dist/commands/api-client.d.ts +3 -7
  33. package/dist/commands/api-client.js +7 -12
  34. package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +292 -0
  35. package/dist/commands/memory/__tests__/repository-root-lint.test.js +146 -0
  36. package/dist/commands/memory/delete.js +51 -24
  37. package/dist/commands/memory/edit.js +50 -10
  38. package/dist/commands/memory/find.js +105 -56
  39. package/dist/commands/memory/history.js +51 -31
  40. package/dist/commands/memory/lint.d.ts +0 -9
  41. package/dist/commands/memory/lint.js +78 -322
  42. package/dist/commands/memory/list.d.ts +12 -4
  43. package/dist/commands/memory/list.js +64 -25
  44. package/dist/commands/memory/move.js +103 -73
  45. package/dist/commands/memory/origin.js +35 -5
  46. package/dist/commands/memory/read.d.ts +4 -0
  47. package/dist/commands/memory/read.js +132 -80
  48. package/dist/commands/memory/shared.d.ts +115 -25
  49. package/dist/commands/memory/shared.js +331 -74
  50. package/dist/commands/memory/write.js +72 -21
  51. package/dist/commands/memory.js +2 -2
  52. package/dist/commands/node/lifecycle.js +2 -2
  53. package/dist/commands/pkg/market-manage.js +25 -24
  54. package/dist/commands/pkg/plugin-manage.d.ts +22 -6
  55. package/dist/commands/pkg/plugin-manage.js +118 -25
  56. package/dist/commands/pkg/shared.d.ts +8 -0
  57. package/dist/commands/pkg/shared.js +23 -20
  58. package/dist/commands/surface-reload.d.ts +5 -0
  59. package/dist/commands/surface-reload.js +9 -1
  60. package/dist/commands/sys/__tests__/migrate.test.js +1140 -22
  61. package/dist/commands/sys/__tests__/sync-project-guidance.test.js +218 -0
  62. package/dist/commands/sys/migrate.js +126 -140
  63. package/dist/commands/sys/panels/profiles-panel.d.ts +66 -0
  64. package/dist/commands/sys/panels/profiles-panel.js +599 -0
  65. package/dist/commands/sys/settings-shell.d.ts +4 -2
  66. package/dist/commands/sys/settings-shell.js +35 -4
  67. package/dist/commands/sys/settings.js +21 -5
  68. package/dist/commands/sys/sync-deps.d.ts +2 -0
  69. package/dist/commands/sys/sync-deps.js +26 -15
  70. package/dist/commands/sys/sync-project-guidance.js +228 -145
  71. package/dist/commands/sys/sync-skills.js +28 -17
  72. package/dist/commands/sys/update.js +11 -3
  73. package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +15 -2
  74. package/dist/core/__tests__/config-change-delta.test.js +106 -0
  75. package/dist/core/__tests__/context-intro.test.js +13 -9
  76. package/dist/core/__tests__/daemon-boot.test.js +22 -16
  77. package/dist/core/__tests__/fixtures/memory-slash-live-probe.js +5 -1
  78. package/dist/core/__tests__/human-deliver.test.js +19 -0
  79. package/dist/core/__tests__/inline-memory-refs.test.js +50 -46
  80. package/dist/core/__tests__/{serial → integration}/broker-fork-seam.test.js +1 -1
  81. package/dist/core/__tests__/{serial → integration}/broker-sdk-wiring.test.js +1 -1
  82. package/dist/core/__tests__/{serial → integration}/broker-snapshot-history.test.js +1 -1
  83. package/dist/core/__tests__/{serial → integration}/command-plugins.test.js +94 -3
  84. package/dist/core/__tests__/{serial → integration}/deferred-no-wake.test.js +1 -1
  85. package/dist/core/__tests__/{serial → integration}/flagship-lifecycle.test.js +13 -27
  86. package/dist/core/__tests__/{serial → integration}/host-teardown-process-group.test.js +1 -1
  87. package/dist/core/__tests__/{serial → integration}/human-deliver-e2e.test.js +1 -1
  88. package/dist/core/__tests__/{live-mutation-verbs.test.js → integration/live-mutation-verbs.test.js} +22 -33
  89. package/dist/core/__tests__/{serial → integration}/live-mutation.test.js +21 -41
  90. package/dist/core/__tests__/integration/refresh-stall-recycle.test.d.ts +1 -0
  91. package/dist/core/__tests__/{serial → integration}/refresh-stall-recycle.test.js +1 -1
  92. package/dist/core/__tests__/integration/revive.test.d.ts +1 -0
  93. package/dist/core/__tests__/{serial → integration}/revive.test.js +38 -19
  94. package/dist/core/__tests__/integration/spawn-root.test.d.ts +1 -0
  95. package/dist/core/__tests__/{serial → integration}/spawn-root.test.js +104 -1
  96. package/dist/core/__tests__/integration/subscription-delivery.test.d.ts +1 -0
  97. package/dist/core/__tests__/{serial → integration}/subscription-delivery.test.js +1 -1
  98. package/dist/core/__tests__/integration/tmux-surface.test.d.ts +1 -0
  99. package/dist/core/__tests__/{serial → integration}/tmux-surface.test.js +1 -1
  100. package/dist/core/__tests__/integration/worktree-land.test.d.ts +1 -0
  101. package/dist/core/__tests__/integration/worktree-land.test.js +400 -0
  102. package/dist/core/__tests__/integration/worktree-reap.test.d.ts +1 -0
  103. package/dist/core/__tests__/{serial/worktree.test.js → integration/worktree-reap.test.js} +6 -338
  104. package/dist/core/__tests__/kickoff.test.js +16 -5
  105. package/dist/core/__tests__/memory-resolver-precedence.test.js +122 -91
  106. package/dist/core/__tests__/nested-store-discovery.test.js +5 -3
  107. package/dist/core/__tests__/on-read-crouter-home-fence.test.js +2 -1
  108. package/dist/core/__tests__/on-read-dedup-resume.test.js +39 -27
  109. package/dist/core/__tests__/on-read-nested-store.test.js +19 -13
  110. package/dist/core/__tests__/profile-project-memory-delivery.test.js +138 -44
  111. package/dist/core/__tests__/repository-association.test.d.ts +1 -0
  112. package/dist/core/__tests__/repository-association.test.js +153 -0
  113. package/dist/core/__tests__/repository-root-identity.test.d.ts +1 -0
  114. package/dist/core/__tests__/repository-root-identity.test.js +219 -0
  115. package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.js +27 -11
  116. package/dist/core/__tests__/warm-claim-preference-snapshot.test.d.ts +1 -0
  117. package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +40 -0
  118. package/dist/core/canvas/canvas.d.ts +3 -0
  119. package/dist/core/canvas/canvas.js +30 -10
  120. package/dist/core/canvas/db.js +28 -1
  121. package/dist/core/canvas/paths.d.ts +4 -6
  122. package/dist/core/canvas/paths.js +8 -6
  123. package/dist/core/canvas/render-source.js +2 -2
  124. package/dist/core/canvas/types.d.ts +9 -13
  125. package/dist/core/exclusive-lock.d.ts +2 -0
  126. package/dist/core/exclusive-lock.js +21 -0
  127. package/dist/core/git.d.ts +0 -1
  128. package/dist/core/git.js +0 -3
  129. package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +1 -0
  130. package/dist/core/human/feedback-companion.js +3 -0
  131. package/dist/core/human/scan.js +8 -1
  132. package/dist/core/keybindings/catalog.d.ts +2 -2
  133. package/dist/core/keybindings/catalog.js +2 -1
  134. package/dist/core/memory/doc-link-grammar.js +2 -2
  135. package/dist/core/memory/history.d.ts +24 -0
  136. package/dist/core/memory/history.js +66 -1
  137. package/dist/core/memory/identity.d.ts +65 -0
  138. package/dist/core/memory/identity.js +185 -0
  139. package/dist/core/memory/inline-ref-guidance.d.ts +1 -1
  140. package/dist/core/memory/inline-ref-guidance.js +1 -1
  141. package/dist/core/memory/inline-ref-inventory.d.ts +3 -10
  142. package/dist/core/memory/inline-ref-inventory.js +30 -62
  143. package/dist/core/memory/lint.d.ts +129 -0
  144. package/dist/core/memory/lint.js +517 -0
  145. package/dist/core/memory/project-namespace.d.ts +31 -0
  146. package/dist/core/memory/project-namespace.js +85 -0
  147. package/dist/core/memory/repository-association.d.ts +31 -0
  148. package/dist/core/memory/repository-association.js +129 -0
  149. package/dist/core/memory/tree.d.ts +39 -0
  150. package/dist/core/memory/tree.js +93 -0
  151. package/dist/core/memory-resolver.d.ts +193 -90
  152. package/dist/core/memory-resolver.js +461 -375
  153. package/dist/core/nested-stores.js +6 -13
  154. package/dist/core/profiles/select.d.ts +4 -1
  155. package/dist/core/profiles/select.js +108 -48
  156. package/dist/core/review/__tests__/capture-origin.test.js +2 -2
  157. package/dist/core/review/__tests__/stage-identity.test.js +2 -2
  158. package/dist/core/review/companion.js +11 -2
  159. package/dist/core/runtime/bearings.d.ts +2 -2
  160. package/dist/core/runtime/bearings.js +3 -3
  161. package/dist/core/runtime/broker/daemon-ops.d.ts +2 -2
  162. package/dist/core/runtime/broker/rebind.js +5 -0
  163. package/dist/core/runtime/broker-extension-render.d.ts +7 -3
  164. package/dist/core/runtime/broker-extension-render.js +10 -5
  165. package/dist/core/runtime/broker-persona-guidance.d.ts +19 -5
  166. package/dist/core/runtime/broker-persona-guidance.js +122 -27
  167. package/dist/core/runtime/deliver-live.d.ts +16 -4
  168. package/dist/core/runtime/deliver-live.js +29 -15
  169. package/dist/core/runtime/kickoff.d.ts +3 -3
  170. package/dist/core/runtime/kickoff.js +8 -16
  171. package/dist/core/runtime/lifecycle.js +2 -3
  172. package/dist/core/runtime/nodes.d.ts +3 -4
  173. package/dist/core/runtime/nodes.js +3 -4
  174. package/dist/core/runtime/persona.d.ts +8 -12
  175. package/dist/core/runtime/persona.js +24 -96
  176. package/dist/core/runtime/promote.d.ts +2 -2
  177. package/dist/core/runtime/promote.js +8 -21
  178. package/dist/core/runtime/revive.js +20 -17
  179. package/dist/core/runtime/spawn.js +18 -7
  180. package/dist/core/runtime/tmux-bindings.js +2 -3
  181. package/dist/core/runtime/warm-pool.js +3 -4
  182. package/dist/core/scope.js +2 -0
  183. package/dist/core/self-update.d.ts +0 -2
  184. package/dist/core/self-update.js +2 -35
  185. package/dist/core/substrate/__tests__/surface-match-memory-read.test.d.ts +1 -0
  186. package/dist/core/substrate/__tests__/surface-match-memory-read.test.js +28 -0
  187. package/dist/core/substrate/index.d.ts +2 -2
  188. package/dist/core/substrate/index.js +1 -1
  189. package/dist/core/substrate/injected-store.d.ts +43 -27
  190. package/dist/core/substrate/injected-store.js +208 -104
  191. package/dist/core/substrate/listings.d.ts +19 -12
  192. package/dist/core/substrate/listings.js +75 -52
  193. package/dist/core/substrate/on-read-node.d.ts +4 -7
  194. package/dist/core/substrate/on-read-node.js +6 -8
  195. package/dist/core/substrate/on-read.d.ts +22 -25
  196. package/dist/core/substrate/on-read.js +103 -147
  197. package/dist/core/substrate/render-node.d.ts +4 -7
  198. package/dist/core/substrate/render-node.js +5 -7
  199. package/dist/core/substrate/render.d.ts +21 -3
  200. package/dist/core/substrate/render.js +291 -223
  201. package/dist/core/substrate/schema.d.ts +1 -13
  202. package/dist/core/substrate/schema.js +5 -40
  203. package/dist/core/substrate/session-cache.d.ts +14 -4
  204. package/dist/core/substrate/session-cache.js +40 -22
  205. package/dist/core/substrate/surface-match.d.ts +9 -7
  206. package/dist/core/substrate/surface-match.js +26 -25
  207. package/dist/daemon/__tests__/helpers/source-daemon.d.ts +30 -0
  208. package/dist/daemon/__tests__/helpers/source-daemon.js +174 -0
  209. package/dist/daemon/__tests__/integration/migration-startup.test.d.ts +1 -0
  210. package/dist/daemon/__tests__/integration/migration-startup.test.js +97 -0
  211. package/dist/daemon/api/__tests__/bridge-heartbeat.test.js +21 -5
  212. package/dist/daemon/api/handlers/broker-ops.js +21 -16
  213. package/dist/daemon/api/handlers/memory.js +2 -0
  214. package/dist/daemon/crtrd.js +2 -0
  215. package/dist/daemon/human/finish.js +8 -1
  216. package/dist/daemon/manage.d.ts +0 -1
  217. package/dist/daemon/manage.js +7 -16
  218. package/dist/daemon/startup-policy.d.ts +1 -0
  219. package/dist/daemon/startup-policy.js +1 -0
  220. package/dist/migrations/001-surfaces-frontmatter.js +21 -109
  221. package/dist/migrations/002-profile-project-memory.js +1 -0
  222. package/dist/migrations/003-repository-root-memory-identity/front-door.d.ts +26 -0
  223. package/dist/migrations/003-repository-root-memory-identity/front-door.js +231 -0
  224. package/dist/migrations/003-repository-root-memory-identity/index.d.ts +2 -0
  225. package/dist/migrations/003-repository-root-memory-identity/index.js +513 -0
  226. package/dist/migrations/003-repository-root-memory-identity/references.d.ts +95 -0
  227. package/dist/migrations/003-repository-root-memory-identity/references.js +469 -0
  228. package/dist/migrations/003-repository-root-memory-identity/repository-facts.d.ts +39 -0
  229. package/dist/migrations/003-repository-root-memory-identity/repository-facts.js +349 -0
  230. package/dist/migrations/__tests__/activation-concurrency.test.d.ts +1 -0
  231. package/dist/migrations/__tests__/activation-concurrency.test.js +145 -0
  232. package/dist/migrations/__tests__/activation.test.d.ts +1 -0
  233. package/dist/migrations/__tests__/activation.test.js +149 -0
  234. package/dist/migrations/__tests__/deletion-and-root-declaration.test.d.ts +1 -0
  235. package/dist/migrations/__tests__/deletion-and-root-declaration.test.js +149 -0
  236. package/dist/migrations/activation.d.ts +16 -0
  237. package/dist/migrations/activation.js +78 -0
  238. package/dist/migrations/convergent.d.ts +14 -3
  239. package/dist/migrations/convergent.js +21 -10
  240. package/dist/migrations/corpus.d.ts +78 -0
  241. package/dist/migrations/corpus.js +497 -0
  242. package/dist/migrations/frontmatter-splice.d.ts +15 -0
  243. package/dist/migrations/frontmatter-splice.js +176 -0
  244. package/dist/migrations/registry.d.ts +6 -1
  245. package/dist/migrations/registry.js +7 -2
  246. package/dist/migrations/runner.d.ts +41 -0
  247. package/dist/migrations/runner.js +81 -0
  248. package/dist/migrations/types.d.ts +148 -9
  249. package/dist/migrations/types.js +9 -2
  250. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +225 -17
  251. package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +11 -3
  252. package/dist/pi-extensions/canvas-context-intro.d.ts +3 -5
  253. package/dist/pi-extensions/canvas-context-intro.js +50 -46
  254. package/dist/pi-extensions/canvas-doc-substrate.d.ts +1 -8
  255. package/dist/pi-extensions/canvas-doc-substrate.js +55 -122
  256. package/dist/pi-extensions/canvas-stophook.js +6 -13
  257. package/dist/shared/generated-context.d.ts +0 -3
  258. package/dist/shared/generated-context.js +0 -57
  259. package/dist/shared/tool-groups.js +2 -3
  260. package/package.json +5 -4
  261. package/runtime.lock.json +2 -2
  262. /package/dist/api/__tests__/{serial → integration}/client.test.d.ts +0 -0
  263. /package/dist/api/__tests__/{serial → integration}/client.test.js +0 -0
  264. /package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/{serial → integration}/provider-rotation.test.ts +0 -0
  265. /package/dist/clients/{inbox/__tests__/serial/inbox-controller.test.d.ts → attach/__tests__/pane-tag-successor.test.d.ts} +0 -0
  266. /package/dist/clients/{inbox/__tests__/serial/mount-panel.test.d.ts → attach/__tests__/profile-files.test.d.ts} +0 -0
  267. /package/dist/{core/__tests__/live-mutation-verbs.test.d.ts → clients/inbox/__tests__/integration/inbox-controller.test.d.ts} +0 -0
  268. /package/dist/clients/inbox/__tests__/{serial → integration}/inbox-controller.test.js +0 -0
  269. /package/dist/{core/__tests__/serial/broker-fork-seam.test.d.ts → clients/inbox/__tests__/integration/mount-panel.test.d.ts} +0 -0
  270. /package/dist/clients/inbox/__tests__/{serial → integration}/mount-panel.test.js +0 -0
  271. /package/dist/{core/__tests__/serial/broker-sdk-wiring.test.d.ts → commands/__tests__/surface-reload-target.test.d.ts} +0 -0
  272. /package/dist/{core/__tests__/serial/broker-snapshot-history.test.d.ts → commands/memory/__tests__/command-selector-and-mutation-guards.test.d.ts} +0 -0
  273. /package/dist/{core/__tests__/serial/command-plugins.test.d.ts → commands/memory/__tests__/repository-root-lint.test.d.ts} +0 -0
  274. /package/dist/{core/__tests__/serial/deferred-no-wake.test.d.ts → commands/sys/__tests__/sync-project-guidance.test.d.ts} +0 -0
  275. /package/dist/core/__tests__/{serial/flagship-lifecycle.test.d.ts → config-change-delta.test.d.ts} +0 -0
  276. /package/dist/core/__tests__/{serial/host-teardown-process-group.test.d.ts → integration/broker-fork-seam.test.d.ts} +0 -0
  277. /package/dist/core/__tests__/{serial/human-deliver-e2e.test.d.ts → integration/broker-sdk-wiring.test.d.ts} +0 -0
  278. /package/dist/core/__tests__/{serial/live-mutation.test.d.ts → integration/broker-snapshot-history.test.d.ts} +0 -0
  279. /package/dist/core/__tests__/{serial/refresh-stall-recycle.test.d.ts → integration/command-plugins.test.d.ts} +0 -0
  280. /package/dist/core/__tests__/{serial/revive.test.d.ts → integration/deferred-no-wake.test.d.ts} +0 -0
  281. /package/dist/core/__tests__/{serial/spawn-root.test.d.ts → integration/flagship-lifecycle.test.d.ts} +0 -0
  282. /package/dist/core/__tests__/{serial/subscription-delivery.test.d.ts → integration/host-teardown-process-group.test.d.ts} +0 -0
  283. /package/dist/core/__tests__/{serial/tmux-surface.test.d.ts → integration/human-deliver-e2e.test.d.ts} +0 -0
  284. /package/dist/core/__tests__/{serial/worktree.test.d.ts → integration/live-mutation-verbs.test.d.ts} +0 -0
  285. /package/dist/core/{human/__tests__/serial/inbox-core.test.d.ts → __tests__/integration/live-mutation.test.d.ts} +0 -0
  286. /package/dist/core/human/__tests__/{serial → integration}/inbox-core.test.js +0 -0
@@ -0,0 +1,129 @@
1
+ import { existsSync, statSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
4
+ import { gitSync } from '../git.js';
5
+ import { realpathOrSelf } from '../fs-utils.js';
6
+ import { CRTR_DIR_NAME } from '../../types.js';
7
+ import { INDEX_SEGMENT, slugCanonicalSegment, validateNamespace } from './identity.js';
8
+ const associationCache = new Map();
9
+ const GIT_LOOKUP_TIMEOUT_MS = 5000;
10
+ /** Normalize a POSIX-slash relative owner path into canonical segments. */
11
+ export function normalizeOwnerRelativePath(relPosix) {
12
+ const normalizedSeparators = relPosix.replace(/\\/g, '/');
13
+ if (normalizedSeparators === '' || normalizedSeparators === '.')
14
+ return { ok: true, path: '' };
15
+ const rawSegments = normalizedSeparators.split('/');
16
+ for (const segment of rawSegments) {
17
+ if (segment === '' || segment === '.' || segment === '..') {
18
+ return { ok: false, reason: `owner path contains invalid segment \`${segment}\`` };
19
+ }
20
+ }
21
+ const segments = rawSegments.map(slugCanonicalSegment);
22
+ for (let i = 0; i < segments.length; i++) {
23
+ if (segments[i] === '') {
24
+ return { ok: false, reason: `directory \`${rawSegments[i]}\` normalizes to an empty name segment` };
25
+ }
26
+ }
27
+ const path = segments.join('/');
28
+ if (segments[segments.length - 1].toLowerCase() === INDEX_SEGMENT.toLowerCase()) {
29
+ return { ok: false, reason: `owner path ends in reserved segment \`${INDEX_SEGMENT}\`` };
30
+ }
31
+ const check = validateNamespace(path);
32
+ return check.ok ? { ok: true, path } : { ok: false, reason: check.reason };
33
+ }
34
+ /** Associate one physical project-store owner with its repository identity. */
35
+ export function associateRepository(ownerDir) {
36
+ const owner = realpathOrSelf(resolve(ownerDir));
37
+ const cached = associationCache.get(owner);
38
+ if (cached !== undefined)
39
+ return { ok: true, association: cached };
40
+ const git = gitSync(['rev-parse', '--path-format=absolute', '--show-toplevel', '--git-common-dir'], owner, undefined, { timeoutMs: GIT_LOOKUP_TIMEOUT_MS });
41
+ if (git.status === 0 || git.timedOut === true || !/not a git repository/i.test(git.stderr)) {
42
+ if (git.timedOut === true) {
43
+ return failure('git-lookup-failed', owner, `git lookup timed out after ${GIT_LOOKUP_TIMEOUT_MS}ms`);
44
+ }
45
+ if (git.status !== 0) {
46
+ const detail = git.stderr.trim() || `git rev-parse exited with status ${git.status}`;
47
+ return failure('git-lookup-failed', owner, `git lookup failed: ${detail}`);
48
+ }
49
+ const lines = git.stdout.split(/\r?\n/);
50
+ const rootLine = lines[0]?.trim() ?? '';
51
+ const commonLine = lines[1]?.trim() ?? '';
52
+ if (rootLine === '' || commonLine === '') {
53
+ return failure('git-lookup-failed', owner, 'git lookup returned an empty repository root or common directory');
54
+ }
55
+ const repositoryRoot = realpathOrSelf(resolve(rootLine));
56
+ const repositoryKey = `git:${realpathOrSelf(resolve(commonLine))}`;
57
+ const rel = relative(repositoryRoot, owner).split(sep).join('/');
58
+ if (isAbsolute(rel) || rel === '..' || rel.startsWith('../')) {
59
+ return failure('owner-outside-root', owner, `owner ${owner} is outside repository root ${repositoryRoot}`, repositoryRoot);
60
+ }
61
+ const ownerPath = normalizeOwnerRelativePath(rel);
62
+ if (!ownerPath.ok)
63
+ return failure('owner-path-invalid', owner, ownerPath.reason, repositoryRoot);
64
+ return remember({
65
+ kind: 'git',
66
+ repositoryRoot,
67
+ repositoryKey,
68
+ ownerRelativePath: ownerPath.path,
69
+ declarationPath: join(repositoryRoot, CRTR_DIR_NAME, 'memory', 'INDEX.md'),
70
+ }, owner);
71
+ }
72
+ const repositoryRoot = nonGitRepositoryRoot(owner);
73
+ if (repositoryRoot === null)
74
+ return failure('no-project-root', owner, `no ancestor ${CRTR_DIR_NAME}/memory store found for ${owner}`);
75
+ const ownerPath = normalizeOwnerRelativePath(relative(repositoryRoot, owner).split(sep).join('/'));
76
+ if (!ownerPath.ok)
77
+ return failure('owner-path-invalid', owner, ownerPath.reason, repositoryRoot);
78
+ return remember({
79
+ kind: 'non-git',
80
+ repositoryRoot,
81
+ repositoryKey: `path:${repositoryRoot}`,
82
+ ownerRelativePath: ownerPath.path,
83
+ declarationPath: join(repositoryRoot, CRTR_DIR_NAME, 'memory', 'INDEX.md'),
84
+ }, owner);
85
+ }
86
+ /** Clear successful associations; failures are deliberately never cached. */
87
+ export function resetRepositoryAssociationCache() {
88
+ associationCache.clear();
89
+ }
90
+ function remember(association, owner) {
91
+ associationCache.set(owner, association);
92
+ return { ok: true, association };
93
+ }
94
+ function failure(code, ownerDir, reason, repositoryRoot) {
95
+ return repositoryRoot === undefined
96
+ ? { ok: false, code, ownerDir, reason }
97
+ : { ok: false, code, ownerDir, repositoryRoot, reason };
98
+ }
99
+ function nonGitRepositoryRoot(owner) {
100
+ const home = realpathOrSelf(homedir());
101
+ const ownerIsUnderHome = isWithin(owner, home);
102
+ let dir = owner;
103
+ let outermost = null;
104
+ while (true) {
105
+ // The user's own .crouter/memory is a separate scope, never a project root.
106
+ if ((!ownerIsUnderHome || dir !== home) && hasProjectMemoryStore(dir))
107
+ outermost = dir;
108
+ if (ownerIsUnderHome && dir === home)
109
+ break;
110
+ const parent = dirname(dir);
111
+ if (parent === dir)
112
+ break;
113
+ dir = parent;
114
+ }
115
+ return outermost;
116
+ }
117
+ function hasProjectMemoryStore(owner) {
118
+ const memory = join(owner, CRTR_DIR_NAME, 'memory');
119
+ try {
120
+ return existsSync(memory) && statSync(memory).isDirectory();
121
+ }
122
+ catch {
123
+ return false;
124
+ }
125
+ }
126
+ function isWithin(child, parent) {
127
+ const rel = relative(parent, child);
128
+ return rel === '' || (rel !== '..' && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
129
+ }
@@ -0,0 +1,39 @@
1
+ import type { MemoryDoc } from '../memory-resolver.js';
2
+ /** One canonical directory row: its own document (when it has one) plus its
3
+ * immediate members. */
4
+ export interface MemoryTreeNode {
5
+ /** Exact canonical identity. `''` is the corpus root, which holds no
6
+ * document — every store's documents merge beneath it. */
7
+ name: string;
8
+ /** Sort key among siblings: the stored physical path segment, `NN-` ordering
9
+ * prefix retained, or this node's canonical segment when no physical
10
+ * segment aligns. */
11
+ order: string;
12
+ /** The winning document attached to this row, or null for a directory that
13
+ * carries none. */
14
+ document: MemoryDoc | null;
15
+ /** Every physical document sharing this identity, in source order. */
16
+ documentCandidates: readonly MemoryDoc[];
17
+ /** Immediate children that have members of their own, ordered. Each may also
18
+ * carry its own `document`. */
19
+ childDirectories: readonly MemoryTreeNode[];
20
+ /** Immediate childless members, ordered. A node's own document never appears
21
+ * among its children. */
22
+ childDocuments: readonly MemoryTreeDocumentNode[];
23
+ }
24
+ /** A childless node: it exists only because a document landed on it. */
25
+ export interface MemoryTreeDocumentNode extends MemoryTreeNode {
26
+ document: MemoryDoc;
27
+ }
28
+ export interface MemoryTree {
29
+ root: MemoryTreeNode;
30
+ /** The node at an exact canonical name, or null when nothing lives there. */
31
+ node(name: string): MemoryTreeNode | null;
32
+ /** Does this canonical name have members? True for the corpus root when the
33
+ * corpus is non-empty. */
34
+ isDirectory(name: string): boolean;
35
+ }
36
+ /** Merge a precedence-ordered corpus into the canonical tree. Documents sharing
37
+ * one identity stay in source order behind that node, so its `document` is the
38
+ * address winner. */
39
+ export declare function buildMemoryTree(docs: readonly MemoryDoc[]): MemoryTree;
@@ -0,0 +1,93 @@
1
+ // tree.ts — the ONE canonical directory model. A canonical name is a path in a
2
+ // tree whose nodes merge across stores: a node holds the winning document at
3
+ // that exact identity (with every physical candidate behind it) AND its
4
+ // immediate children, so a directory that carries its own document is one row,
5
+ // never a document plus a separate directory of the same name. Listing
6
+ // generation, boot render, inline inventory, and lint all read structure from
7
+ // here instead of scanning name prefixes.
8
+ import { canonicalSegments, normalizeNameSegment } from './identity.js';
9
+ /** The stored physical segments a document supplies as ordering keys for itself
10
+ * and its canonical ancestors. Both names are walked from the TAIL and stop at
11
+ * the first mismatch, because no canonical segment index addresses a physical
12
+ * one: a store's namespace has no physical counterpart at all, and an explicit
13
+ * `name:` detaches identity from the path entirely. */
14
+ function physicalOrderSegments(doc) {
15
+ const canonical = canonicalSegments(doc.name);
16
+ const physical = canonicalSegments(doc.physicalRelativePath.replace(/\.md$/i, ''));
17
+ // The INDEX filename names its directory, not a segment of its own.
18
+ if (doc.representation === 'directory-index')
19
+ physical.pop();
20
+ const keys = [];
21
+ for (let i = canonical.length - 1, j = physical.length - 1; i >= 0 && j >= 0; i--, j--) {
22
+ const segment = physical[j];
23
+ if (normalizeNameSegment(segment) !== canonical[i])
24
+ break;
25
+ keys.push([canonical.slice(0, i + 1).join('/'), segment]);
26
+ }
27
+ return keys;
28
+ }
29
+ function hasMembers(node) {
30
+ return node.childDirectories.length > 0 || node.childDocuments.length > 0;
31
+ }
32
+ function compareNodes(a, b) {
33
+ return a.order.localeCompare(b.order) || a.name.localeCompare(b.name);
34
+ }
35
+ function asDocumentNode(node) {
36
+ if (node.document === null)
37
+ throw new Error(`memory tree: ${node.name} has neither a document nor members`);
38
+ return node;
39
+ }
40
+ function finalize(pending, order, index) {
41
+ const children = [...pending.children.values()]
42
+ .map((child) => finalize(child, order, index))
43
+ .sort(compareNodes);
44
+ const segments = canonicalSegments(pending.name);
45
+ const node = {
46
+ name: pending.name,
47
+ order: order.get(pending.name) ?? segments[segments.length - 1] ?? '',
48
+ document: pending.documents[0] ?? null,
49
+ documentCandidates: pending.documents,
50
+ childDirectories: children.filter(hasMembers),
51
+ childDocuments: children.filter((child) => !hasMembers(child)).map(asDocumentNode),
52
+ };
53
+ index.set(node.name, node);
54
+ return node;
55
+ }
56
+ /** Merge a precedence-ordered corpus into the canonical tree. Documents sharing
57
+ * one identity stay in source order behind that node, so its `document` is the
58
+ * address winner. */
59
+ export function buildMemoryTree(docs) {
60
+ const root = { name: '', documents: [], children: new Map() };
61
+ const order = new Map();
62
+ for (const doc of docs) {
63
+ const segments = canonicalSegments(doc.name);
64
+ if (segments.length === 0)
65
+ continue;
66
+ let node = root;
67
+ for (let i = 0; i < segments.length; i++) {
68
+ const segment = segments[i];
69
+ let child = node.children.get(segment);
70
+ if (child === undefined) {
71
+ child = { name: segments.slice(0, i + 1).join('/'), documents: [], children: new Map() };
72
+ node.children.set(segment, child);
73
+ }
74
+ node = child;
75
+ }
76
+ node.documents.push(doc);
77
+ for (const [name, segment] of physicalOrderSegments(doc)) {
78
+ if (!order.has(name))
79
+ order.set(name, segment);
80
+ }
81
+ }
82
+ const index = new Map();
83
+ const rootNode = finalize(root, order, index);
84
+ const lookup = (name) => index.get(canonicalSegments(name).join('/')) ?? null;
85
+ return {
86
+ root: rootNode,
87
+ node: lookup,
88
+ isDirectory: (name) => {
89
+ const node = lookup(name);
90
+ return node !== null && hasMembers(node);
91
+ },
92
+ };
93
+ }
@@ -1,79 +1,144 @@
1
1
  import { type InstalledPlugin, type Scope } from '../types.js';
2
2
  import type { ProfileProjectMemory } from '../api/dto/profiles.js';
3
+ import { type DocRepresentation } from './memory/identity.js';
3
4
  /**
4
- * Thin memory-document resolver for the document substrate. Resolution has
5
- * two passes. First, scopes/sources are tried NEAREST-FIRST (node > project
6
- * stack nearest > ... > profile > user > builtin) for strong matches: (1) exact
7
- * substrate identity (`doc.name === query` — the explicit frontmatter `name`,
8
- * or its path-derived fallback), then (2) direct `memory/<name>.md` physical
9
- * path. A directory name resolves to NO doc — the read leaf answers it with
10
- * the directory's listing. Only when no source has a strong
11
- * match does a second nearest-first pass try bare leaf-name fallback (final
12
- * path segment only). Thus scope precedence resolves competing strong matches,
13
- * while a nearer nested doc's coincidental leaf can never shadow a farther
14
- * canonical identity. It returns the raw parsed frontmatter + body;
15
- * it does NOT interpret the schema, kind, gate, or rungs — that is the
16
- * schema/gate layer's job (callers filter by `frontmatter.kind`). Project
17
- * resolution is a nearest-first stack of every ancestor `.crouter/` (widened by
18
- * a selected profile's `projects` manifest entries — see `findProjectScopeRoots`
19
- * in scope.ts), profile is the selected profile's own singleton `memory/` store
20
- * (from `CRTR_PROFILE_ID`), and user/builtin remain singleton scopes.
5
+ * Memory-document loading and resolution for the document substrate.
21
6
  *
22
- * Every doc also carries its project store's profile relationship ceiling
23
- * (`projectMemory`) for the automatic delivery paths to clamp against.
24
- * Resolution itself never consults it: the full corpus resolves and lists
25
- * regardless of the ceiling.
7
+ * Every document belongs to exactly one physical STORE. A store declares a
8
+ * namespace — a project's `namespace:` on its root `INDEX.md`, an installed
9
+ * plugin's mount prefix, or empty for user/profile/node/builtin — and every
10
+ * document's CANONICAL name is that namespace composed with the document's
11
+ * store-local name (see memory/identity.ts). The canonical name is the only
12
+ * address: resolution compares the normalized query with canonical identities
13
+ * and nothing else. There is no physical-path pass, no bare-leaf pass, no
14
+ * scope-prefixed spelling, and no `<dir>/INDEX` address — a trailing `INDEX`
15
+ * segment collapses, so a directory's own document answers at the directory
16
+ * name and `memory find` is the discovery surface when the name is unknown.
17
+ *
18
+ * A project store whose repository root carries no valid `namespace:` does not mount: its
19
+ * documents are absent from every target view, and its descriptor carries the
20
+ * `crtr sys migrate --dir <repository root>` remedy. Exact-store inspection
21
+ * (`openProjectMemoryStore` + `loadMemoryStoreView`) still loads it, which is
22
+ * what lint and migration diagnose an unmounted store through.
23
+ *
24
+ * Two views exist over the same loader:
25
+ * - a TARGET view — the ordered source stack of one cwd/profile/node
26
+ * (node > project stores nearest-first > profile > user > builtin, native
27
+ * documents before enabled-plugin documents within each source). Alternate
28
+ * physical stores legitimately expose the same canonical identity (a linked
29
+ * worktree, a generated copy), so a name yields an ordered CANDIDATE set:
30
+ * source order picks the address winner, every later candidate stays
31
+ * inspectable.
32
+ * - an exact STORE view — one physical project store selected by owner
33
+ * directory, which still requires the full canonical query.
34
+ *
35
+ * Resolution never consults a project store's profile memory ceiling
36
+ * (`projectMemory`); the full corpus resolves and lists regardless, and only
37
+ * automatic delivery clamps against it.
26
38
  */
39
+ /** The memory-only scope union: the global `Scope` (`user|project|builtin`)
40
+ * plus `profile` and `node`. Confined to the memory resolver/list/read/write/
41
+ * render paths — deliberately NOT folded into the global `Scope` union, because
42
+ * that would leak these into config, plugin, marketplace, and sys-config
43
+ * surfaces the spec does not extend. `node` is the this-node store
44
+ * (`nodes/<id>/context/memory/`), available only inside a running node
45
+ * (`CRTR_NODE_ID`), and it is the NEAREST scope — a node doc overrides a
46
+ * same-named doc at any wider scope. */
47
+ export type MemoryScope = Scope | 'profile' | 'node';
48
+ /** Whether a store contributes to target views.
49
+ * `absent` — no `memory/` dir on disk (silent, the ordinary case).
50
+ * `undeclared`/`invalid` — a project store the runtime refuses to mount until
51
+ * its namespace is declared; `diagnostic` carries the migration remedy. */
52
+ export type MemoryStoreMountStatus = 'ready' | 'absent' | 'undeclared' | 'invalid';
53
+ /** One physical memory store: where its documents live, what namespace their
54
+ * canonical names compose under, and whether it mounts. */
55
+ export interface MemoryStoreDescriptor {
56
+ scope: MemoryScope;
57
+ /** Absolute path of the physical `memory/` dir. Store identity (and the
58
+ * dedup of one real store reached twice) uses its realpath. */
59
+ storeRoot: string;
60
+ /** The project directory containing `.crouter/`, for project stores only. */
61
+ ownerDir?: string;
62
+ /** Effective project prefix, repository-root namespace composed with the
63
+ * owner-relative path; installed-plugin mount prefix, or `''`. */
64
+ namespace: string;
65
+ /** The repository-root `INDEX.md` a project namespace is declared on, when
66
+ * association succeeds — present even for an undeclared/invalid store so
67
+ * lint and migration can point at it. */
68
+ namespacePath?: string;
69
+ /** Absolute realpath of the associated repository root, for project stores. */
70
+ repositoryRoot?: string;
71
+ /** Linked-worktree/non-Git grouping key, for project stores. */
72
+ repositoryKey?: string;
73
+ /** Valid namespace read from the repository-root declaration. */
74
+ repositoryNamespace?: string;
75
+ /** Canonical owner path relative to the repository root, empty at the root. */
76
+ ownerRelativePath?: string;
77
+ /** Structured cause for a project store that does not mount. */
78
+ mountFailure?: 'association' | 'collision' | 'undeclared-root' | 'invalid-root';
79
+ /** The owning plugin's name for a plugin-mounted store. Plugin docs surface
80
+ * within a source scope but live under the plugins dir and are managed by
81
+ * `crtr pkg`, so consumers that mutate the store gate on this. */
82
+ plugin?: string;
83
+ mountStatus: MemoryStoreMountStatus;
84
+ /** Why a non-`ready` store does not mount, with its actionable remedy. */
85
+ diagnostic?: string;
86
+ /** The selected profile relationship's ceiling for this store: the maximum
87
+ * rung AUTOMATIC boot and workspace-open delivery may disclose it at.
88
+ * Neutral (`content`) for every non-project store. */
89
+ projectMemory: ProfileProjectMemory;
90
+ }
27
91
  export interface MemoryDoc {
28
- /** Resolver identity: the doc's explicit frontmatter `name` when present,
29
- * otherwise its normalized path under the scope's memory/ root — e.g.
30
- * memory/taste/foo.md → "taste/foo". */
92
+ /** Canonical identity — the store's namespace composed with `localName`, and
93
+ * the only address resolution accepts. */
31
94
  name: string;
95
+ /** Store-local identity: the explicit frontmatter `name` when present, else
96
+ * derived from the store-relative path with ordering prefixes stripped and
97
+ * one trailing `INDEX` collapsed. Empty for a store's root document. */
98
+ localName: string;
32
99
  scope: MemoryScope;
33
100
  /** Absolute path to the resolved .md file. */
34
101
  path: string;
35
102
  /** Absolute path of the `memory/` dir this doc loaded from — the store root
36
- * its identity is relative to. Sidecar trees that mirror the doc tree (the
37
- * revision-history log) are derived from `path` relative to this. For a
38
- * plugin doc it is that plugin's own memory dir. */
103
+ * its physical path is relative to. Sidecar trees that mirror the doc tree
104
+ * (the revision-history log) are derived from `path` relative to this. */
39
105
  root: string;
106
+ /** Store-relative markdown path, kept exactly: placement, ordering, origin,
107
+ * and history depend on the physical form, never on segment counts of the
108
+ * canonical name. */
109
+ physicalRelativePath: string;
110
+ /** Which physical form owns this identity — `foo.md` or `foo/INDEX.md`. */
111
+ representation: DocRepresentation;
112
+ /** The canonical directory this doc's `./` memory-read entries match against:
113
+ * its own name for a directory document, its parent for a leaf. */
114
+ routingAnchor: string;
115
+ store: MemoryStoreDescriptor;
40
116
  /** Raw, uncoerced frontmatter record (null when the doc has no frontmatter). */
41
117
  frontmatter: Record<string, unknown> | null;
42
118
  /** Document body, with the frontmatter block stripped. */
43
119
  body: string;
44
- /** Set to the owning plugin's name when this doc is mounted from an installed
45
- * plugin (under `<pluginName>/`), undefined for a native scope doc. Plugin
46
- * docs surface within a source scope but live under the plugins dir and are
47
- * managed by `crtr pkg`, not writable/deletable as scope docs — consumers
48
- * that mutate the store gate on this. */
49
120
  plugin?: string;
50
- /** The selected profile relationship's ceiling for the project store this doc
51
- * came from: the maximum rung AUTOMATIC boot and workspace-open delivery may
52
- * disclose it at. Always present; `content` (neutral, i.e. uncapped) for
53
- * every non-project scope and for a project root no profile entry
54
- * contributes. Resolution, listing, and targeted reads ignore it entirely. */
55
121
  projectMemory: ProfileProjectMemory;
56
122
  }
57
- /** The memory-only scope union: the global `Scope` (`user|project|builtin`)
58
- * plus `profile` and `node`. Confined to the memory resolver/list/read/write/
59
- * render paths — deliberately NOT folded into the global `Scope` union, because
60
- * that would leak these into config, plugin, marketplace, and sys-config
61
- * surfaces the spec does not extend. `node` is the this-node store
62
- * (`nodes/<id>/context/memory/`), available only inside a running node
63
- * (`CRTR_NODE_ID`), and it is the NEAREST scope — a node doc overrides a
64
- * same-named doc at any wider scope. */
65
- export type MemoryScope = Scope | 'profile' | 'node';
66
- export interface MemoryResolutionOpts {
67
- /** Restrict resolution to a single scope. Conflicts with a scope prefix on
68
- * the identifier (e.g. `user/foo` with scope=project) throw. */
69
- scope?: MemoryScope;
70
- /** Restrict resolution to docs whose resolved kind matches. Threaded INTO
71
- * precedence, not applied as a post-resolution filter: within each source,
72
- * a doc whose kind doesn't match is skipped (not returned), so a nearer
73
- * source's wrong-kind doc can no longer shadow a farther source's
74
- * matching-kind doc into a false not_found — resolution simply continues
75
- * to the next source. See `effectiveDocKind` for the kind coercion. */
123
+ /** Every physical document sharing one canonical identity in a view, in source
124
+ * order. `winner` is the address a deliberate read resolves to; automatic
125
+ * delivery filters the ordered candidates for its own event eligibility. */
126
+ export interface MemoryCandidateSet {
127
+ name: string;
128
+ winner: MemoryDoc;
129
+ candidates: readonly MemoryDoc[];
130
+ }
131
+ /** Restrict resolution to docs whose resolved kind matches. Threaded INTO
132
+ * candidate selection, not applied as a post-filter: a wrong-kind candidate is
133
+ * skipped so a nearer one cannot shadow a farther matching-kind document into
134
+ * a false not_found. */
135
+ export interface MemoryCandidateOpts {
76
136
  kind?: string;
137
+ }
138
+ export interface MemoryResolutionOpts extends MemoryCandidateOpts {
139
+ /** Restrict resolution to a single scope. Narrows the source stack only — the
140
+ * query is still the full canonical name. */
141
+ scope?: MemoryScope;
77
142
  /** Also consult `.crouter/memory` stores discovered BELOW the mounted
78
143
  * project roots (bounded discovery — see nested-stores.ts). Opt-in for the
79
144
  * addressing surfaces (`memory read/list/find/lint/delete/origin`); OFF for
@@ -81,17 +146,26 @@ export interface MemoryResolutionOpts {
81
146
  * persona resolution, which stay on the flat ancestor+profile stack. */
82
147
  includeDescendants?: boolean;
83
148
  }
84
- /** The native (non-plugin) store dirs in resolution precedence — node >
85
- * project stack > profile > user, builtin excluded as a read-only corpus.
86
- * Addresses a store directly when the document itself may be absent, which is
87
- * how a DELETED doc's revision log is still found: history outlives the doc,
88
- * so its lookup cannot go through document resolution. */
89
- export declare function nativeMemoryStoresInPrecedence(scope?: MemoryScope, includeDescendants?: boolean): {
90
- scope: MemoryScope;
91
- memoryDir: string;
92
- }[];
93
- /** Canonical, unambiguous identifier for a memory document: `<scope>/<name>`. */
94
- export declare function memoryDocId(doc: MemoryDoc): string;
149
+ /** A loaded corpus and the exact-identity queries over it. */
150
+ export interface MemoryView {
151
+ /** Every store the view discovered, mounted or not, in precedence order. */
152
+ stores: readonly MemoryStoreDescriptor[];
153
+ /** Every document of every MOUNTED store, in source order — candidates
154
+ * included, so a first-wins dedup by name yields the winners. */
155
+ docs: readonly MemoryDoc[];
156
+ /** The ordered candidate set for one exact canonical name, or null. */
157
+ candidates(name: string, opts?: MemoryCandidateOpts): MemoryCandidateSet | null;
158
+ /** One entry per canonical identity, sorted by name. */
159
+ candidateSets(opts?: MemoryCandidateOpts): MemoryCandidateSet[];
160
+ /** The address winner for one exact canonical name; throws not_found. */
161
+ resolve(name: string, opts?: MemoryCandidateOpts): MemoryDoc;
162
+ }
163
+ export interface MemoryTargetView extends MemoryView {
164
+ target: MemoryTarget;
165
+ }
166
+ export interface MemoryStoreView extends MemoryView {
167
+ store: MemoryStoreDescriptor;
168
+ }
95
169
  /** Whose memory view is being resolved: the workspace dir, profile, and node
96
170
  * id that decide which stores exist and in what order. Ambient resolution
97
171
  * builds this from the process (`process.cwd()`, `CRTR_PROFILE_ID`,
@@ -110,16 +184,41 @@ export interface MemoryTarget {
110
184
  * or null outside a node. */
111
185
  nodeId: string | null;
112
186
  }
113
- /** All native memory docs for a scope. Project scope is a nearest-first stack of
114
- * every ancestor `.crouter/memory/` (widened by a selected profile's project
115
- * stack); profile is the selected profile's own singleton store, resolved
116
- * through `loadProfileManifest`; user and builtin are singleton stores. */
117
- export declare function listMemoryDocs(scope: MemoryScope, quiet?: boolean): MemoryDoc[];
118
- /** All of one plugin's substrate docs, mounted under the virtual `<pluginName>/`
119
- * namespace. Walks `pluginMemoryDir(plugin)` recursively for *.md, deriving each
120
- * doc's name exactly as `listMemoryDocs` does (path-relative, no extension,
121
- * slash-separated) then prefixing the plugin name. Builtin has no plugins. */
187
+ /** The exact project store owned by `ownerDir` — the `--dir` selector's store,
188
+ * loaded whatever its mount status so lint and migration can inspect a store
189
+ * the runtime refuses to mount. */
190
+ export declare function openProjectMemoryStore(ownerDir: string): MemoryStoreDescriptor;
191
+ /** Every store a target sees, in precedence order: node, each project store
192
+ * nearest-first (its enabled plugins after it), then profile, user, builtin.
193
+ * One real store reached twice contributes once, at its first position. */
194
+ export declare function memoryStoresInPrecedence(target: MemoryTarget, scope?: MemoryScope, includeDescendants?: boolean): MemoryStoreDescriptor[];
195
+ /** The native (non-plugin) MOUNTED store dirs in resolution precedence.
196
+ * Addresses a store directly when the document itself may be absent, which is
197
+ * how a DELETED doc's revision log is still found: history outlives the doc,
198
+ * so its lookup cannot go through document resolution. */
199
+ export declare function nativeMemoryStoresInPrecedence(scope?: MemoryScope, includeDescendants?: boolean): {
200
+ scope: MemoryScope;
201
+ memoryDir: string;
202
+ store: MemoryStoreDescriptor;
203
+ }[];
204
+ /** Every document in one store, sorted by canonical identity. SKILL.md bundles
205
+ * are Agent Skills, not memory docs. Enumeration stops at a nested `.crouter/`:
206
+ * that is another store's mount point, never this store's content. */
207
+ export declare function loadStoreMemoryDocs(store: MemoryStoreDescriptor, quiet?: boolean): MemoryDoc[];
208
+ /** All of one plugin's substrate docs, mounted under the plugin's own name:
209
+ * each doc's store-local name (explicit or path-derived) composes under that
210
+ * mount prefix. */
122
211
  export declare function listPluginMemoryDocs(plugin: InstalledPlugin, scope: MemoryScope, quiet?: boolean, projectMemory?: ProfileProjectMemory): MemoryDoc[];
212
+ /** Load one target's whole corpus: every mounted store's documents in source
213
+ * order. A project store that does not mount is reported once (unless quiet)
214
+ * with its migration remedy and contributes nothing. */
215
+ export declare function loadMemoryTargetView(target: MemoryTarget, opts?: MemoryResolutionOpts & {
216
+ quiet?: boolean;
217
+ }): MemoryTargetView;
218
+ /** Load ONE exact physical store. An undeclared/invalid project store loads
219
+ * under the empty namespace so lint and migration can inspect what it holds;
220
+ * no target view ever sees those documents. */
221
+ export declare function loadMemoryStoreView(store: MemoryStoreDescriptor, quiet?: boolean): MemoryStoreView;
123
222
  /** All project-scoped docs visible from an explicit node workspace/profile — the
124
223
  * project-only slice of a `MemoryTarget` view, used where only workspace docs
125
224
  * are wanted (a workspace-open render). For a full-precedence target-addressed
@@ -128,31 +227,22 @@ export declare function listProjectMemoryDocs(startDir?: string, profileId?: str
128
227
  /** All memory docs across the resolved sources, in precedence order: each
129
228
  * ancestor project `.crouter/` from nearest to farthest, then the selected
130
229
  * profile's memory (if any), then user, then builtin. Within each source,
131
- * native docs are emitted before enabled-plugin docs, so native wins on the
132
- * caller's first-wins dedup. */
230
+ * native docs are emitted before enabled-plugin docs, and equal-canonical
231
+ * candidates stay in source order, so a caller's first-wins dedup yields the
232
+ * address winners. */
133
233
  export declare function listAllMemoryDocs(scope?: MemoryScope, quiet?: boolean, includeDescendants?: boolean): MemoryDoc[];
134
- /** Resolve a document NAME against a `.history` tree, which mirrors the doc
135
- * tree segment for segment with `.jsonl` in place of `.md`. Reuses the doc
136
- * resolution rules so a log outlives its doc under the same name the doc had:
137
- * numeric prefixes stay prefix-blind (`00-topic.md` logged at
138
- * `.history/00-topic.jsonl` still answers to `topic`). Returns null when
139
- * nothing resolves. */
140
- export declare function resolveHistoryLogPath(historyDir: string, segments: string[]): string | null;
141
234
  export interface MemoryDocSnapshot {
142
235
  /** Every document in default scope precedence order, loaded once. */
143
236
  docs: readonly MemoryDoc[];
144
237
  /** Resolve names against that source snapshot; failures are omitted so
145
238
  * callers can retain their own per-document error behavior. */
146
239
  resolve(names: readonly string[]): Map<string, MemoryDoc>;
147
- /** Frozen pre-cut resolution used only by the surfaces migration: return the
148
- * explicit INDEX spelling when a bare directory/plugin ref and that spelling
149
- * resolve to the same file in this target corpus. */
150
- legacyDirectoryIndex(name: string): string | null;
151
240
  }
152
241
  export declare function createMemoryDocSnapshot(target?: MemoryTarget): MemoryDocSnapshot;
153
- /** Resolve several unqualified names over one memory-source snapshot. Failed
242
+ /** Resolve several canonical names over one memory-source snapshot. Failed
154
243
  * names are omitted so callers can preserve their per-document error behavior. */
155
244
  export declare function resolveMemoryDocs(names: readonly string[]): Map<string, MemoryDoc>;
245
+ /** The address winner for an exact canonical name in the ambient target. */
156
246
  export declare function resolveMemoryDoc(rawName: string, opts?: MemoryResolutionOpts): MemoryDoc;
157
247
  /** Resolve a memory document as ANOTHER node would see it — the same precedence
158
248
  * chain (node-local > project stack > profile > user > builtin), read from the
@@ -160,3 +250,16 @@ export declare function resolveMemoryDoc(rawName: string, opts?: MemoryResolutio
160
250
  * resolves a `[[name]]` link through: the daemon's own cwd and env name no
161
251
  * node, and the same name can be a different document for two nodes. */
162
252
  export declare function resolveMemoryDocForTarget(rawName: string, target: MemoryTarget, opts?: MemoryResolutionOpts): MemoryDoc;
253
+ /** Every physical candidate for an exact canonical name in the ambient target,
254
+ * in source order — what path-aware inventory, diagnostics, and automatic
255
+ * event eligibility consume instead of the winner alone. */
256
+ export declare function resolveMemoryCandidates(rawName: string, opts?: MemoryResolutionOpts): MemoryCandidateSet;
257
+ export declare function resolveMemoryCandidatesForTarget(rawName: string, target: MemoryTarget, opts?: MemoryResolutionOpts): MemoryCandidateSet;
258
+ /** The address winner for an exact canonical name inside ONE project store —
259
+ * the `--dir` selector's resolution. The query is still the full canonical
260
+ * name, including that store's namespace. */
261
+ export declare function resolveMemoryDocInStore(rawName: string, store: MemoryStoreDescriptor, opts?: MemoryCandidateOpts): MemoryDoc;
262
+ /** Resolve PHYSICAL path segments against a `.history` tree, which mirrors the
263
+ * doc tree segment for segment with `.jsonl` in place of `.md`. A log outlives
264
+ * its document, so this lookup cannot go through document resolution. */
265
+ export declare function resolveHistoryLogPath(historyDir: string, segments: string[]): string | null;