@north-light/crouter 0.3.226 → 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 (288) 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 +37 -9
  212. package/dist/daemon/api/bridge.d.ts +35 -4
  213. package/dist/daemon/api/bridge.js +50 -8
  214. package/dist/daemon/api/handlers/broker-ops.js +21 -16
  215. package/dist/daemon/api/handlers/memory.js +2 -0
  216. package/dist/daemon/crtrd.js +2 -0
  217. package/dist/daemon/human/finish.js +8 -1
  218. package/dist/daemon/manage.d.ts +0 -1
  219. package/dist/daemon/manage.js +7 -16
  220. package/dist/daemon/startup-policy.d.ts +1 -0
  221. package/dist/daemon/startup-policy.js +1 -0
  222. package/dist/migrations/001-surfaces-frontmatter.js +21 -109
  223. package/dist/migrations/002-profile-project-memory.js +1 -0
  224. package/dist/migrations/003-repository-root-memory-identity/front-door.d.ts +26 -0
  225. package/dist/migrations/003-repository-root-memory-identity/front-door.js +231 -0
  226. package/dist/migrations/003-repository-root-memory-identity/index.d.ts +2 -0
  227. package/dist/migrations/003-repository-root-memory-identity/index.js +513 -0
  228. package/dist/migrations/003-repository-root-memory-identity/references.d.ts +95 -0
  229. package/dist/migrations/003-repository-root-memory-identity/references.js +469 -0
  230. package/dist/migrations/003-repository-root-memory-identity/repository-facts.d.ts +39 -0
  231. package/dist/migrations/003-repository-root-memory-identity/repository-facts.js +349 -0
  232. package/dist/migrations/__tests__/activation-concurrency.test.d.ts +1 -0
  233. package/dist/migrations/__tests__/activation-concurrency.test.js +145 -0
  234. package/dist/migrations/__tests__/activation.test.d.ts +1 -0
  235. package/dist/migrations/__tests__/activation.test.js +149 -0
  236. package/dist/migrations/__tests__/deletion-and-root-declaration.test.d.ts +1 -0
  237. package/dist/migrations/__tests__/deletion-and-root-declaration.test.js +149 -0
  238. package/dist/migrations/activation.d.ts +16 -0
  239. package/dist/migrations/activation.js +78 -0
  240. package/dist/migrations/convergent.d.ts +14 -3
  241. package/dist/migrations/convergent.js +21 -10
  242. package/dist/migrations/corpus.d.ts +78 -0
  243. package/dist/migrations/corpus.js +497 -0
  244. package/dist/migrations/frontmatter-splice.d.ts +15 -0
  245. package/dist/migrations/frontmatter-splice.js +176 -0
  246. package/dist/migrations/registry.d.ts +6 -1
  247. package/dist/migrations/registry.js +7 -2
  248. package/dist/migrations/runner.d.ts +41 -0
  249. package/dist/migrations/runner.js +81 -0
  250. package/dist/migrations/types.d.ts +148 -9
  251. package/dist/migrations/types.js +9 -2
  252. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +225 -17
  253. package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +11 -3
  254. package/dist/pi-extensions/canvas-context-intro.d.ts +3 -5
  255. package/dist/pi-extensions/canvas-context-intro.js +50 -46
  256. package/dist/pi-extensions/canvas-doc-substrate.d.ts +1 -8
  257. package/dist/pi-extensions/canvas-doc-substrate.js +55 -122
  258. package/dist/pi-extensions/canvas-stophook.js +6 -13
  259. package/dist/shared/generated-context.d.ts +0 -3
  260. package/dist/shared/generated-context.js +0 -57
  261. package/dist/shared/tool-groups.js +2 -3
  262. package/package.json +5 -4
  263. package/runtime.lock.json +2 -2
  264. /package/dist/api/__tests__/{serial → integration}/client.test.d.ts +0 -0
  265. /package/dist/api/__tests__/{serial → integration}/client.test.js +0 -0
  266. /package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/{serial → integration}/provider-rotation.test.ts +0 -0
  267. /package/dist/clients/{inbox/__tests__/serial/inbox-controller.test.d.ts → attach/__tests__/pane-tag-successor.test.d.ts} +0 -0
  268. /package/dist/clients/{inbox/__tests__/serial/mount-panel.test.d.ts → attach/__tests__/profile-files.test.d.ts} +0 -0
  269. /package/dist/{core/__tests__/live-mutation-verbs.test.d.ts → clients/inbox/__tests__/integration/inbox-controller.test.d.ts} +0 -0
  270. /package/dist/clients/inbox/__tests__/{serial → integration}/inbox-controller.test.js +0 -0
  271. /package/dist/{core/__tests__/serial/broker-fork-seam.test.d.ts → clients/inbox/__tests__/integration/mount-panel.test.d.ts} +0 -0
  272. /package/dist/clients/inbox/__tests__/{serial → integration}/mount-panel.test.js +0 -0
  273. /package/dist/{core/__tests__/serial/broker-sdk-wiring.test.d.ts → commands/__tests__/surface-reload-target.test.d.ts} +0 -0
  274. /package/dist/{core/__tests__/serial/broker-snapshot-history.test.d.ts → commands/memory/__tests__/command-selector-and-mutation-guards.test.d.ts} +0 -0
  275. /package/dist/{core/__tests__/serial/command-plugins.test.d.ts → commands/memory/__tests__/repository-root-lint.test.d.ts} +0 -0
  276. /package/dist/{core/__tests__/serial/deferred-no-wake.test.d.ts → commands/sys/__tests__/sync-project-guidance.test.d.ts} +0 -0
  277. /package/dist/core/__tests__/{serial/flagship-lifecycle.test.d.ts → config-change-delta.test.d.ts} +0 -0
  278. /package/dist/core/__tests__/{serial/host-teardown-process-group.test.d.ts → integration/broker-fork-seam.test.d.ts} +0 -0
  279. /package/dist/core/__tests__/{serial/human-deliver-e2e.test.d.ts → integration/broker-sdk-wiring.test.d.ts} +0 -0
  280. /package/dist/core/__tests__/{serial/live-mutation.test.d.ts → integration/broker-snapshot-history.test.d.ts} +0 -0
  281. /package/dist/core/__tests__/{serial/refresh-stall-recycle.test.d.ts → integration/command-plugins.test.d.ts} +0 -0
  282. /package/dist/core/__tests__/{serial/revive.test.d.ts → integration/deferred-no-wake.test.d.ts} +0 -0
  283. /package/dist/core/__tests__/{serial/spawn-root.test.d.ts → integration/flagship-lifecycle.test.d.ts} +0 -0
  284. /package/dist/core/__tests__/{serial/subscription-delivery.test.d.ts → integration/host-teardown-process-group.test.d.ts} +0 -0
  285. /package/dist/core/__tests__/{serial/tmux-surface.test.d.ts → integration/human-deliver-e2e.test.d.ts} +0 -0
  286. /package/dist/core/__tests__/{serial/worktree.test.d.ts → integration/live-mutation-verbs.test.d.ts} +0 -0
  287. /package/dist/core/{human/__tests__/serial/inbox-core.test.d.ts → __tests__/integration/live-mutation.test.d.ts} +0 -0
  288. /package/dist/core/human/__tests__/{serial → integration}/inbox-core.test.js +0 -0
@@ -1,30 +1,21 @@
1
- // Broker-local persona transition guidance. Canvas state is supplied by crtrd;
2
- // this module reads only the node's own roadmap and durable memory files.
3
- import { resolveMemoryDoc } from '../memory-resolver.js';
4
1
  import { contextDir } from '../canvas/paths.js';
2
+ import { emitEvent } from '../events/emit.js';
3
+ import { brokerExtensionState, commitBrokerPersonaAck } from './broker/daemon-ops.js';
5
4
  import { readRoadmap, roadmapPath } from './roadmap.js';
6
- function loadMemoryBody(name) {
7
- try {
8
- return resolveMemoryDoc(name).body.trim();
9
- }
10
- catch {
11
- return '';
12
- }
13
- }
5
+ import { buildSubPersonaMenu, renderConfigChangeDelta } from '../substrate/render.js';
6
+ import { cloneContextExposureState, contextExposureStatesEqual, exposureTarget, loadContextExposureState, mergeContextExposureState, replaceContextExposureState, saveContextExposureState, sharedContextExposureState, } from '../substrate/injected-store.js';
7
+ import { formatCard } from '../../shared/generated-context.js';
14
8
  function orchestratorContextNote(nodeId) {
15
9
  return `Because you persist across refresh cycles, your context directory (${contextDir(nodeId)}) is also where a future cycle of you resumes the work — keep the working notes and decisions a refreshed you would need there, alongside the docs you share with the nodes you spawn.`;
16
10
  }
17
11
  function orchestrationGuidance(node) {
18
- const kernel = loadMemoryBody('orchestration-kernel');
19
12
  const roadmap = readRoadmap(node.node_id) ?? '(no roadmap yet)';
20
13
  const rmPath = roadmapPath(node.node_id);
21
14
  return [
22
15
  `You are now a ${node.kind.toUpperCase()} ORCHESTRATOR. Your scarce resource is your own context window.`,
23
16
  'Your job is to manage context and delegate — not to do the goal yourself.',
24
17
  '',
25
- kernel,
26
- '',
27
- `Your roadmap scaffold (\`${rmPath}\`) — author it now: state the goal, exit criteria, and the phase skeleton, using the approach above. Current contents:`,
18
+ `Your roadmap scaffold (\`${rmPath}\`) — author it now: state the goal, exit criteria, and the phase skeleton, using the approach below. Current contents:`,
28
19
  '',
29
20
  roadmap,
30
21
  '',
@@ -38,21 +29,125 @@ function orchestrationGuidance(node) {
38
29
  function baseModeGuidance() {
39
30
  return 'You are HANDS-ON again — base mode. Do the work yourself in THIS window and finish it here; stop delegating by default. You no longer drive a roadmap, so `crtr node yield` is not your exit. Spawn a child only for a cleanly separable unit, never as your first move.';
40
31
  }
41
- export function transitionGuidanceFromBrokerState(node, from, to) {
32
+ function subPersonaGuidance(from, to) {
33
+ if (from.kind === to.kind)
34
+ return '';
35
+ const before = buildSubPersonaMenu(from.kind);
36
+ const after = buildSubPersonaMenu(to.kind);
37
+ if (before === after)
38
+ return '';
39
+ return after === ''
40
+ ? `The ${to.kind} kind has no specialist sub-personas available.`
41
+ : after;
42
+ }
43
+ function transitionGuidance(state, from, to, contextExposure) {
42
44
  const sections = [];
43
45
  if (from.mode !== to.mode) {
44
- sections.push(to.mode === 'orchestrator' ? orchestrationGuidance(node) : baseModeGuidance());
45
- }
46
- if (from.lifecycle !== to.lifecycle) {
47
- sections.push(loadMemoryBody(to.lifecycle === 'resident' ? 'turn-lifecycle/resident' : 'turn-lifecycle/terminal'));
46
+ sections.push(to.mode === 'orchestrator' ? orchestrationGuidance(state.node) : baseModeGuidance());
48
47
  }
49
- return sections.join('\n\n');
48
+ sections.push(subPersonaGuidance(from, to));
49
+ sections.push(renderConfigChangeDelta(state.subject, state.node.node_id, exposureTarget(contextExposure, 'transcript')));
50
+ return sections.filter((section) => section !== '').join('\n\n');
50
51
  }
51
- /** Return an unacknowledged persona transition from crtrd's fixed extension state. */
52
- export function personaDriftFromBrokerState(node) {
53
- const to = { mode: node.mode, lifecycle: node.lifecycle };
54
- const from = node.persona_ack ?? to;
55
- if (from.mode === to.mode && from.lifecycle === to.lifecycle)
52
+ export function personaDriftFromBrokerState(state) {
53
+ const node = state.node;
54
+ const to = { kind: node.kind, mode: node.mode, lifecycle: node.lifecycle };
55
+ const ack = node.persona_ack;
56
+ const from = ack === undefined
57
+ ? to
58
+ : { kind: ack.kind ?? node.kind, mode: ack.mode, lifecycle: ack.lifecycle };
59
+ if (from.kind === to.kind && from.mode === to.mode && from.lifecycle === to.lifecycle)
56
60
  return null;
57
- return { from, to, guidance: transitionGuidanceFromBrokerState(node, from, to) };
61
+ return { from, to };
62
+ }
63
+ /** A new context's current snapshots make a rank delta unnecessary. */
64
+ export async function acknowledgePersonaBaseline(state) {
65
+ const drift = personaDriftFromBrokerState(state);
66
+ if (drift === null)
67
+ return false;
68
+ const result = await commitBrokerPersonaAck(state.node.node_id, drift);
69
+ if (!result.applied)
70
+ throw new Error(`persona baseline acknowledgment was rejected for ${state.node.node_id}`);
71
+ return true;
72
+ }
73
+ /** Append pending transitions until the delivered persona matches current canvas state. */
74
+ export async function deliverPendingPersonaTransition(initialState, deliver) {
75
+ const nodeId = initialState.node.node_id;
76
+ const live = sharedContextExposureState(nodeId);
77
+ const persisted = loadContextExposureState(nodeId);
78
+ mergeContextExposureState(live, persisted);
79
+ if (live.preferenceSnapshot === null) {
80
+ throw new Error(`context preference snapshot is missing for ${nodeId}`);
81
+ }
82
+ if (!contextExposureStatesEqual(live, persisted))
83
+ saveContextExposureState(nodeId, live);
84
+ let state = initialState;
85
+ let daemonDrift = personaDriftFromBrokerState(state);
86
+ if (daemonDrift === null)
87
+ return false;
88
+ const next = cloneContextExposureState(live);
89
+ let represented = daemonDrift.from;
90
+ for (;;) {
91
+ const current = {
92
+ kind: state.node.kind,
93
+ mode: state.node.mode,
94
+ lifecycle: state.node.lifecycle,
95
+ };
96
+ if (represented.kind !== current.kind
97
+ || represented.mode !== current.mode
98
+ || represented.lifecycle !== current.lifecycle) {
99
+ const guidance = transitionGuidance(state, represented, current, next);
100
+ if (guidance !== '')
101
+ await deliver(formatCard('persona-transition', {}, guidance));
102
+ represented = current;
103
+ replaceContextExposureState(live, next);
104
+ saveContextExposureState(nodeId, live);
105
+ }
106
+ daemonDrift = personaDriftFromBrokerState(state);
107
+ if (daemonDrift === null)
108
+ return true;
109
+ const result = await commitBrokerPersonaAck(nodeId, daemonDrift);
110
+ if (result.applied)
111
+ return true;
112
+ state = await brokerExtensionState(nodeId);
113
+ }
114
+ }
115
+ /** Gate each provider request on durable delivery of the current persona delta. */
116
+ export function installPersonaTransitionGate(nodeId, session) {
117
+ const { agent } = session;
118
+ const previousTransform = agent.transformContext;
119
+ agent.transformContext = async (messages, signal) => {
120
+ const transformed = previousTransform === undefined
121
+ ? messages
122
+ : await previousTransform(messages, signal);
123
+ try {
124
+ const state = await brokerExtensionState(nodeId);
125
+ await deliverPendingPersonaTransition(state, (content) => {
126
+ const details = { nodeId };
127
+ const message = {
128
+ role: 'custom',
129
+ customType: 'crtr-persona-transition',
130
+ content,
131
+ display: true,
132
+ details,
133
+ timestamp: Date.now(),
134
+ };
135
+ session.sessionManager.appendCustomMessageEntry('crtr-persona-transition', content, true, details);
136
+ const active = agent.state.messages;
137
+ for (const context of new Set([active, messages, transformed]))
138
+ context.push(message);
139
+ });
140
+ return transformed;
141
+ }
142
+ catch (error) {
143
+ emitEvent({
144
+ event: 'broker.daemon.operation.failed',
145
+ level: 'error',
146
+ node_id: nodeId,
147
+ error,
148
+ fields: { operation: 'persona_transition' },
149
+ });
150
+ throw error;
151
+ }
152
+ };
58
153
  }
@@ -15,10 +15,22 @@ export interface LiveCustomMessage {
15
15
  * warm claim it is fatal: a node wearing the wrong bearings is worse than a
16
16
  * slow one, so the spare is abandoned and the create goes cold). */
17
17
  export declare function deliverCustomMessageLive(nodeId: string, message: LiveCustomMessage): Promise<void>;
18
- /** Build and deliver `nodeId`'s bearings into its live session. Called by the
19
- * warm-pool claim once the row already wears the claimant's kind/mode/name, so
20
- * the identity assertion, the knowledge catalog, and the `<project_context>`
21
- * environment snapshot are all built for the node being handed out.
18
+ /** Build and deliver `nodeId`'s bearings into its live session, and freeze the
19
+ * preference snapshot that goes with them. Called by the warm-pool claim once
20
+ * the row already wears the claimant's kind/mode/name, so the identity
21
+ * assertion, the knowledge catalog, the `<project_context>` environment
22
+ * snapshot, and the preference block are all built for the node being handed
23
+ * out.
24
+ *
25
+ * The snapshot is frozen HERE for the same reason bearings are built here: a
26
+ * spare boots with no claimant, so `canvas-context-intro` skips warm spares
27
+ * and the block would otherwise assert the SPARE's preferences. Claim is the
28
+ * first moment the real persona, profile, and cwd exist, and it is the last
29
+ * moment before the claimant's first turn — which reaches the provider through
30
+ * the persona gate, and which need not be a typed prompt. A slash command
31
+ * opens its turn through `sendMessage({ triggerTurn: true })`, and Pi emits
32
+ * `before_agent_start` only on the typed-prompt path, so the broker's own
33
+ * freeze in `canvas-doc-substrate` never runs for it.
22
34
  *
23
35
  * The `details.nodeId` stamp is what the context-intro extension's idempotency
24
36
  * guard matches on, so a LATER revive of this node sees its own bearings
@@ -21,7 +21,9 @@ import { getNode } from '../canvas/index.js';
21
21
  import { oneShotControllerRequest } from './broker-request.js';
22
22
  import { isBrokerLive } from './model-swap.js';
23
23
  import { buildContextBearings, CONTEXT_INTRO_CUSTOM_TYPE } from './bearings.js';
24
- import { loadInjectedDocs, saveInjectedDocs } from '../substrate/injected-store.js';
24
+ import { renderPreferencesForNodeSubject } from './broker-extension-render.js';
25
+ import { assembleNodeSubject } from '../substrate/subject.js';
26
+ import { exposureTarget, freezePreferenceSnapshot, loadContextExposureState, saveContextExposureState, } from '../substrate/injected-store.js';
25
27
  import { formatCard } from '../../shared/generated-context.js';
26
28
  /** Deliver `message` into `nodeId`'s live session. Resolves without doing
27
29
  * anything when the node has no live broker. Rejects when a live broker cannot
@@ -48,27 +50,39 @@ export async function deliverCustomMessageLive(nodeId, message) {
48
50
  result: () => undefined,
49
51
  });
50
52
  }
51
- /** Build and deliver `nodeId`'s bearings into its live session. Called by the
52
- * warm-pool claim once the row already wears the claimant's kind/mode/name, so
53
- * the identity assertion, the knowledge catalog, and the `<project_context>`
54
- * environment snapshot are all built for the node being handed out.
53
+ /** Build and deliver `nodeId`'s bearings into its live session, and freeze the
54
+ * preference snapshot that goes with them. Called by the warm-pool claim once
55
+ * the row already wears the claimant's kind/mode/name, so the identity
56
+ * assertion, the knowledge catalog, the `<project_context>` environment
57
+ * snapshot, and the preference block are all built for the node being handed
58
+ * out.
59
+ *
60
+ * The snapshot is frozen HERE for the same reason bearings are built here: a
61
+ * spare boots with no claimant, so `canvas-context-intro` skips warm spares
62
+ * and the block would otherwise assert the SPARE's preferences. Claim is the
63
+ * first moment the real persona, profile, and cwd exist, and it is the last
64
+ * moment before the claimant's first turn — which reaches the provider through
65
+ * the persona gate, and which need not be a typed prompt. A slash command
66
+ * opens its turn through `sendMessage({ triggerTurn: true })`, and Pi emits
67
+ * `before_agent_start` only on the typed-prompt path, so the broker's own
68
+ * freeze in `canvas-doc-substrate` never runs for it.
55
69
  *
56
70
  * The `details.nodeId` stamp is what the context-intro extension's idempotency
57
71
  * guard matches on, so a LATER revive of this node sees its own bearings
58
72
  * already on the branch and does not inject a second copy. */
59
73
  export async function deliverBearingsLive(nodeId) {
60
- // This runs in the DAEMON while the spare's pi process is already alive, so
61
- // the disk file — not any in-memory set — is the dedup medium: load the
62
- // node's persisted set, grow it through the workspace-open render, and
63
- // persist it after successful delivery. The pi process's on-read hook merges
64
- // the on-disk set before each render, so docs delivered here never
65
- // re-deliver on a later read. Plain load/save (not the process-shared
66
- // cache): the daemon is long-lived and serves many nodes.
67
- const seen = loadInjectedDocs(nodeId);
74
+ // Claim runs in the daemon while the spare's Pi process is already alive.
75
+ // Persist transcript registrations after Pi accepts the bearings so the
76
+ // broker merges the same context state before later event deliveries.
77
+ const contextExposure = loadContextExposureState(nodeId);
68
78
  await deliverCustomMessageLive(nodeId, {
69
79
  customType: CONTEXT_INTRO_CUSTOM_TYPE,
70
- content: formatCard('bearings', {}, buildContextBearings(nodeId, seen)),
80
+ content: formatCard('bearings', {}, buildContextBearings(nodeId, exposureTarget(contextExposure, 'transcript'))),
71
81
  details: { nodeId },
72
82
  });
73
- saveInjectedDocs(nodeId, seen);
83
+ const subject = assembleNodeSubject(nodeId);
84
+ if (subject === null)
85
+ throw new Error(`deliverBearingsLive: no document subject for ${nodeId}`);
86
+ freezePreferenceSnapshot(contextExposure, renderPreferencesForNodeSubject(nodeId, subject, exposureTarget(contextExposure, 'system')));
87
+ saveContextExposureState(nodeId, contextExposure);
74
88
  }
@@ -32,12 +32,12 @@ export interface ReviveBearings {
32
32
  /** Coalesced digest of unread reports, or null when the feed was empty. The
33
33
  * cursor now records the last physically covered entry. */
34
34
  unreadDigest: string | null;
35
- /** Persona-transition guidance to surface when the node's role was changed
36
- * while it was away (its ack has already been committed), else null. */
35
+ /** Persona-transition framing to surface when the node's role changed while
36
+ * it was away. The drift remains pending until context-intro completes. */
37
37
  driftGuidance: string | null;
38
38
  }
39
39
  /** Capture revive bearings for `meta`: read the pending yield note, advance the
40
- * feed cursor past unread reports, and capture+commit external persona drift.
40
+ * feed cursor past unread reports, and capture external persona framing.
41
41
  * Revive paths call it once, then pass the result to the pure kickoff builder. */
42
42
  export declare function drainBearings(meta: NodeMeta): ReviveBearings;
43
43
  /** Assemble the auto-injected first message for a FRESH revive of `meta` from its
@@ -24,7 +24,7 @@ import { contextDir, reportsDir, getNode, subscriptionsOf, subscribersOf, } from
24
24
  import { activeBackgroundBashJobs, bashJobCommandLine, formatBashElapsed } from '../bash-jobs.js';
25
25
  import { readRoadmap, roadmapPath } from './roadmap.js';
26
26
  import { buildWakeBearings } from './bearings.js';
27
- import { personaDrift, commitPersonaAck } from './persona.js';
27
+ import { personaDrift } from './persona.js';
28
28
  import { readInboxSince, readCursor, writeCursor, coalesce, } from '../feed/inbox.js';
29
29
  import { formatCard } from '../../shared/generated-context.js';
30
30
  /** The runtime-restart continuation exactly as delivered. `revive.ts` injects
@@ -102,7 +102,7 @@ export function listContextDir(nodeId) {
102
102
  return readdirSync(dir).sort();
103
103
  }
104
104
  /** Capture revive bearings for `meta`: read the pending yield note, advance the
105
- * feed cursor past unread reports, and capture+commit external persona drift.
105
+ * feed cursor past unread reports, and capture external persona framing.
106
106
  * Revive paths call it once, then pass the result to the pure kickoff builder. */
107
107
  export function drainBearings(meta) {
108
108
  const nodeId = meta.node_id;
@@ -119,15 +119,10 @@ export function drainBearings(meta) {
119
119
  // join back into this block's body; the kickoff is one whole envelope.
120
120
  unreadDigest = coalesce(entries).map((delivery) => delivery.text).join('\n\n');
121
121
  }
122
- // Capture + commit any external persona drift (the second of the two delivery
123
- // sites). Committing the ack here is the mutation; the guidance is surfaced by
124
- // the pure builder from this captured value.
125
122
  const drift = personaDrift(nodeId);
126
- let driftGuidance = null;
127
- if (drift !== null) {
128
- driftGuidance = drift.guidance;
129
- commitPersonaAck(nodeId, drift.to);
130
- }
123
+ const driftGuidance = drift?.guidance === undefined || drift.guidance === ''
124
+ ? null
125
+ : drift.guidance;
131
126
  return { yieldMsg, unreadDigest, driftGuidance };
132
127
  }
133
128
  // Fresh-revive catch-up bug: on a refresh-yield (resume:false) the old
@@ -309,12 +304,9 @@ export function buildReviveKickoff(meta, bearings, wakeReason) {
309
304
  : 'If there is work to do, perform it. Otherwise end your turn — you are working ' +
310
305
  'directly with the user, so stay available and continue the conversation when they ' +
311
306
  'write back.');
312
- // Persona-transition catch-up. If the node's mode/lifecycle was changed
313
- // EXTERNALLY while it was dormant (e.g. a human ran `crtr node config --lifecycle` /
314
- // `node promote --node` on it), it never saw the turn_end injector. drainBearings
315
- // captured the guidance for its new persona and committed the ack (the second
316
- // and only other delivery site); we just surface it. A clean fresh revive has
317
- // no drift, so this is empty unless a real external change happened.
307
+ // Non-document framing for a persona change made while the node was away.
308
+ // The replacement context's current snapshots carry all eligible documents;
309
+ // context-intro acknowledges the transition after those snapshots persist.
318
310
  if (bearings.driftGuidance !== null) {
319
311
  parts.push(`<persona-transition>\nYour role was changed while you were away. ${bearings.driftGuidance}\n</persona-transition>`);
320
312
  }
@@ -7,9 +7,8 @@
7
7
  // writes both fields in ONE atomic statement (on the WAL'd row setters) so the
8
8
  // two can never disagree.
9
9
  //
10
- // This mirrors persona.ts: persona.ts is the single source of transition PROSE;
11
- // lifecycle.ts is the single source of which status/intent move is LEGAL. Two
12
- // parallel, legible state machines instead of scattered enactment.
10
+ // Persona modules own context framing; this file owns which status/intent move
11
+ // is legal. The two state machines stay independent.
13
12
  //
14
13
  // Crash-safety invariant: flip status to a non-supervised value + clear intent
15
14
  // BEFORE killing the host (tmux pane or headless broker) — the daemon only ever
@@ -65,11 +65,10 @@ export interface SpawnNodeOpts {
65
65
  * `parent` — e.g. an independent root (parent=null) still records its
66
66
  * spawner. Defaults to `parent`. */
67
67
  spawnedBy?: string | null;
68
- /** Fork provenance — the `--fork-from` reference this node was forked from (a
69
- * node id / session path / pi session uuid). Persisted to `meta.fork_from` so
70
- * the boot intro can re-assert this node's OWN identity over the source's
71
- * copied-in conversation. Omit for a fresh node. */
68
+ /** Raw `--fork-from` operand retained for identity framing and audit. */
72
69
  forkFrom?: string | null;
70
+ /** Resolved absolute source session file retained for pre-bind retries. */
71
+ forkSourceFile?: string | null;
73
72
  /** Suppress the spawn-time parent→child ACTIVE subscription. Default true
74
73
  * (every existing caller keeps its edge). False gives ordinary canvas
75
74
  * placement with no wake path back to the parent. */
@@ -237,14 +237,13 @@ export function spawnNode(opts) {
237
237
  kind: opts.kind,
238
238
  mode,
239
239
  lifecycle,
240
- // Born already acked to its initial persona: a fresh node has been "given
241
- // guidance" for the state it starts in (its bearings carry it), so the
242
- // persona injector sees no drift on its first turn boundary.
243
- persona_ack: { mode, lifecycle },
240
+ // Birth snapshots already represent this persona, so no transition is pending.
241
+ persona_ack: { kind: opts.kind, mode, lifecycle },
244
242
  status: 'active',
245
243
  parent,
246
244
  spawned_by: spawnedBy,
247
245
  fork_from: opts.forkFrom,
246
+ fork_source_file: opts.forkSourceFile,
248
247
  review_binding: opts.reviewBinding,
249
248
  model_override: opts.modelOverride ?? null,
250
249
  passive_default: opts.passiveDefault ?? false,
@@ -1,25 +1,21 @@
1
1
  import { type Mode, type Lifecycle } from '../canvas/index.js';
2
- /** The two-axis persona state the injector keys on. */
2
+ /** The three-axis persona transition key. */
3
3
  export interface Persona {
4
+ kind: string;
4
5
  mode: Mode;
5
6
  lifecycle: Lifecycle;
6
7
  }
7
8
  export interface PersonaDriftResult {
8
9
  from: Persona;
9
10
  to: Persona;
10
- /** The built transition guidance to inject for `to`. */
11
+ /** Fresh-context framing for `to`; its snapshots carry document context. */
11
12
  guidance: string;
12
13
  }
13
- /** Build the injected transition prompt for a `from → to` persona change.
14
- * Concatenates the relevant section per changed axis (both when both changed).
15
- * Pure read of the node's roadmap/memory for the base→orchestrator case. */
14
+ /** Build only the non-document framing for a fresh-context transition. */
16
15
  export declare function transitionGuidance(nodeId: string, from: Persona, to: Persona): string;
17
- /** Compare a node's live {mode,lifecycle} against its `persona_ack` (the last
18
- * state it was given guidance for). Returns the transition + built guidance
19
- * when they differ, else null. Does NOT mutate — the caller delivers the
20
- * guidance, then `commitPersonaAck`s the new state. An unset `persona_ack`
21
- * (legacy node) defaults to the current persona, so it reads as no drift and
22
- * never fabricates spurious guidance. */
16
+ /** Compare the live persona with the last acknowledged context delivery.
17
+ * `guidance` contains only fresh-context framing; current snapshots supply its
18
+ * documents. An absent or pre-kind acknowledgment cannot fabricate drift. */
23
19
  export declare function personaDrift(nodeId: string): PersonaDriftResult | null;
24
- /** Commit the persona state the node has now been given guidance for. */
20
+ /** Commit the persona state whose context delivery is complete. */
25
21
  export declare function commitPersonaAck(nodeId: string, to: Persona): void;
@@ -1,75 +1,25 @@
1
- // persona.ts — the CENTRALIZED persona-transition injector.
2
- //
3
- // A node has two orthogonal, independently switchable axes:
4
- // • mode — base (hands-on; yields mode-preservingly when another window is needed) ↔ orchestrator
5
- // (delegates, holds a roadmap, survives refresh cycles + yields)
6
- // • lifecycle — terminal (owes a final up the spine, reaps when done) ↔
7
- // resident (interactable, never forced to submit; unattended
8
- // obligation-free conversations complete after the daemon grace)
9
- //
10
- // Whenever EITHER axis changes from the value the node was last GIVEN guidance
11
- // for, the node must be prompt-injected with guidance for its new state —
12
- // automatically, here, not by each state-changing command. Commands just call
13
- // `updateNode({ mode|lifecycle })`; this module is the single source of the
14
- // transition prose, delivered from exactly two sites:
15
- // • the stophook turn_end hook (self-changes this turn + external changes
16
- // while the node is active), and
17
- // • the revive kickoff (external changes made while the node was dormant).
18
- //
19
- // The `persona_ack` meta field records the last {mode,lifecycle} the node was
20
- // given guidance for (born equal to its initial persona at spawn, so a fresh
21
- // worker never gets spurious guidance). `personaDrift` compares live meta to it;
22
- // the caller delivers the guidance, then commits the ack.
1
+ // Persona drift and the non-document framing carried by a fresh-revive kickoff.
2
+ // The new context's system and bearings snapshots supply its memory baseline;
3
+ // canvas-context-intro acknowledges the drift after both snapshots persist.
23
4
  import { getNode, updateNode } from '../canvas/index.js';
24
- import { resolveMemoryDoc } from '../memory-resolver.js';
25
5
  import { readRoadmap, roadmapPath } from './roadmap.js';
26
6
  import { orchestratorContextNote } from './bearings.js';
27
7
  // ---------------------------------------------------------------------------
28
- // base→orchestrator guidance (the roadmap-shaping dump) — MOVED here from
29
- // promote.ts so the injector is the one place that builds it.
8
+ // Base → orchestrator guidance
30
9
  // ---------------------------------------------------------------------------
31
- /** Load a builtin/user/project memory doc's body by its normalized name, or ''
32
- * if it can't be resolved. The single source for the lifecycle/spine/
33
- * orchestration-kernel fragments this injector re-delivers on a transition —
34
- * the SAME docs under `src/builtin-memory/`, so static
35
- * (system-prompt splice) and transition prose can never drift. */
36
- function loadMemoryBody(name) {
37
- try {
38
- return resolveMemoryDoc(name).body.trim();
39
- }
40
- catch {
41
- return '';
42
- }
43
- }
44
- /** The base→orchestrator guidance dump, specialized to the node's kind: the
45
- * shared orchestration-kernel doc + the roadmap scaffold the node must author +
46
- * the orchestrator context-dir framing + a short pointer at the substrate
47
- * memory flow (promotion re-informs of nothing about the stores, it only adds
48
- * the across-cycles relevance). The node is now a delegator whose scarce
49
- * resource is its own context window. Roadmap-shaping guidance is just another orchestrator-gated memory
50
- * doc the node already gets via the boot-render splice (kinds/<kind>/orchestrator
51
- * gated `{kind, mode: orchestrator}`). (Lifecycle is left to its own section —
52
- * promotion does not force resident, so this never asserts residency.) */
10
+ /** Fresh-context framing for a base → orchestrator change. */
53
11
  function orchestrationGuidance(nodeId, kind) {
54
- const kernel = loadMemoryBody('orchestration-kernel');
55
12
  const roadmap = readRoadmap(nodeId) ?? '(no roadmap yet)';
56
13
  const rmPath = roadmapPath(nodeId);
57
14
  const parts = [
58
15
  `You are now a ${kind.toUpperCase()} ORCHESTRATOR. Your scarce resource is your own context window.`,
59
16
  'Your job is to manage context and delegate — not to do the goal yourself.',
60
- '',
61
- kernel,
62
17
  ];
63
- parts.push('', `Your roadmap scaffold (\`${rmPath}\`) — author it now: state the goal, exit criteria, and the phase skeleton, using the approach above. Current contents:`, '', roadmap, '',
64
- // The orchestrator framing for the context dir — the missing guidance a
65
- // promoted node never got at spawn (it spawned as a base worker). Same note
66
- // a born-orchestrator gets in its <crtr-bearings> block.
67
- orchestratorContextNote(nodeId), '', 'Your three long-term memory scopes (user-global, project, node-local) already surface in your bearings at boot. What promotion changes is USE, not access: you now persist across refresh cycles, so node-local carries what THIS goal needs between your refreshes, and durable facts you commit to user-global or project outlive this node. Use `crtr memory write` to save a document; `crtr memory list` to browse; `crtr memory find` to search (see "Your long-term memory" above for the full substrate flow).', '', 'Then delegate each phase with `crtr node new --kind <kind>`. When your context fills, run `crtr node yield` to refresh against this roadmap.');
18
+ parts.push('', `Your roadmap scaffold (\`${rmPath}\`) — author it now: state the goal, exit criteria, and the phase skeleton, using your orchestration protocol. Current contents:`, '', roadmap, '', orchestratorContextNote(nodeId), '', 'Your three long-term memory scopes (user-global, project, node-local) already surface in your bearings at boot. What promotion changes is USE, not access: you now persist across refresh cycles, so node-local carries what THIS goal needs between your refreshes, and durable facts you commit to user-global or project outlive this node. Use `crtr memory write` to save a document; `crtr memory list` to browse; `crtr memory find` to search (see "Your long-term memory" above for the full substrate flow).', '', 'Then delegate each phase with `crtr node new --kind <kind>`. When your context fills, run `crtr node yield` to refresh against this roadmap.');
68
19
  return parts.join('\n');
69
20
  }
70
21
  // ---------------------------------------------------------------------------
71
- // The other three transitions — short, prescriptive, audience = the node's
72
- // agent (decision-first; one well-placed "don't").
22
+ // Orchestrator → base guidance
73
23
  // ---------------------------------------------------------------------------
74
24
  /** orchestrator → base (demote): hands-on again, finish in-window. */
75
25
  function baseModeGuidance() {
@@ -77,57 +27,35 @@ function baseModeGuidance() {
77
27
  'stop delegating by default. You no longer drive a roadmap, so `crtr node yield` is not your exit. ' +
78
28
  'Spawn a child only for a cleanly separable unit, never as your first move.');
79
29
  }
80
- // The lifecycle transition prose is the SAME contract that's baked into the
81
- // static system prompt at birth — so both load from the one source
82
- // (`turn-lifecycle/{terminal,resident}.md`) and can never drift. The flip
83
- // only re-delivers that fragment as the node's new-state steer.
84
- /** terminal → resident: interactable, never forced to submit. */
85
- function residentLifecycleGuidance() {
86
- return loadMemoryBody('turn-lifecycle/resident');
87
- }
88
- /** resident → terminal: owes a final, reaps when done. */
89
- function terminalLifecycleGuidance() {
90
- return loadMemoryBody('turn-lifecycle/terminal');
91
- }
92
- /** Build the injected transition prompt for a `from → to` persona change.
93
- * Concatenates the relevant section per changed axis (both when both changed).
94
- * Pure read of the node's roadmap/memory for the base→orchestrator case. */
30
+ /** Build only the non-document framing for a fresh-context transition. */
95
31
  export function transitionGuidance(nodeId, from, to) {
96
- const sections = [];
97
- if (from.mode !== to.mode) {
98
- if (to.mode === 'orchestrator') {
99
- const node = getNode(nodeId);
100
- sections.push(orchestrationGuidance(nodeId, node?.kind ?? 'general'));
101
- }
102
- else {
103
- sections.push(baseModeGuidance());
104
- }
105
- }
106
- if (from.lifecycle !== to.lifecycle) {
107
- sections.push(to.lifecycle === 'resident' ? residentLifecycleGuidance() : terminalLifecycleGuidance());
108
- }
109
- return sections.join('\n\n');
32
+ if (from.mode === to.mode)
33
+ return '';
34
+ if (to.mode === 'base')
35
+ return baseModeGuidance();
36
+ const node = getNode(nodeId);
37
+ return orchestrationGuidance(nodeId, node?.kind ?? 'general');
110
38
  }
111
39
  // ---------------------------------------------------------------------------
112
40
  // Detector + ack commit.
113
41
  // ---------------------------------------------------------------------------
114
- /** Compare a node's live {mode,lifecycle} against its `persona_ack` (the last
115
- * state it was given guidance for). Returns the transition + built guidance
116
- * when they differ, else null. Does NOT mutate — the caller delivers the
117
- * guidance, then `commitPersonaAck`s the new state. An unset `persona_ack`
118
- * (legacy node) defaults to the current persona, so it reads as no drift and
119
- * never fabricates spurious guidance. */
42
+ /** Compare the live persona with the last acknowledged context delivery.
43
+ * `guidance` contains only fresh-context framing; current snapshots supply its
44
+ * documents. An absent or pre-kind acknowledgment cannot fabricate drift. */
120
45
  export function personaDrift(nodeId) {
121
46
  const meta = getNode(nodeId);
122
47
  if (meta === null)
123
48
  return null;
124
- const to = { mode: meta.mode, lifecycle: meta.lifecycle };
125
- const from = meta.persona_ack ?? { mode: meta.mode, lifecycle: meta.lifecycle };
126
- if (from.mode === to.mode && from.lifecycle === to.lifecycle)
49
+ const to = { kind: meta.kind, mode: meta.mode, lifecycle: meta.lifecycle };
50
+ const ack = meta.persona_ack;
51
+ const from = ack === undefined
52
+ ? to
53
+ : { kind: ack.kind ?? meta.kind, mode: ack.mode, lifecycle: ack.lifecycle };
54
+ if (from.kind === to.kind && from.mode === to.mode && from.lifecycle === to.lifecycle)
127
55
  return null;
128
56
  return { from, to, guidance: transitionGuidance(nodeId, from, to) };
129
57
  }
130
- /** Commit the persona state the node has now been given guidance for. */
58
+ /** Commit the persona state whose context delivery is complete. */
131
59
  export function commitPersonaAck(nodeId, to) {
132
60
  updateNode(nodeId, { persona_ack: to });
133
61
  }
@@ -30,8 +30,8 @@ export declare function reshapeNodeAsync(nodeId: string, opts?: ReshapeOptions,
30
30
  * `developer.orchestrator`) and optionally also making it resident. Idempotent:
31
31
  * re-promoting just rewrites the spec. Seeds a roadmap SCAFFOLD if absent (a
32
32
  * boss with no map is a failure mode) — no goal is forced here; authoring the
33
- * goal + roadmap is the node's next act. The transition guidance is injected
34
- * centrally by the persona injector at the next turn boundary, not returned. */
33
+ * goal + roadmap is the node's next act. Pending context delivery is owned by
34
+ * the broker provider gate, not this command. */
35
35
  export declare function promote(nodeId: string, opts?: {
36
36
  kind?: string;
37
37
  resident?: boolean;