@north-light/crouter 0.3.262 → 0.3.269

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 (150) hide show
  1. package/dist/api/client.d.ts +5 -1
  2. package/dist/api/client.js +6 -0
  3. package/dist/api/dto/canvas.d.ts +53 -2
  4. package/dist/api/dto/health.d.ts +2 -0
  5. package/dist/api/dto/nodes.d.ts +6 -2
  6. package/dist/api/routes.d.ts +1 -0
  7. package/dist/api/routes.js +1 -0
  8. package/dist/builtin-memory/05-kinds/advisor/advice-contract.md +2 -2
  9. package/dist/builtin-memory/internal/memory-loading.md +3 -3
  10. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/integration/provider-rotation.test.ts +50 -0
  11. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +46 -6
  12. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +10 -5
  13. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +12 -5
  14. package/dist/clients/attach/input/controller.d.ts +4 -0
  15. package/dist/clients/attach/input/controller.js +8 -0
  16. package/dist/clients/attach/session/bindings.d.ts +3 -0
  17. package/dist/clients/attach/session/bindings.js +7 -0
  18. package/dist/clients/attach/session/keys.d.ts +2 -0
  19. package/dist/clients/attach/session/keys.js +3 -3
  20. package/dist/clients/attach/session/whip-achievements.d.ts +4 -0
  21. package/dist/clients/attach/session/whip-achievements.js +145 -0
  22. package/dist/clients/attach/slash/dispatch.js +5 -3
  23. package/dist/clients/attach/viewer.js +668 -667
  24. package/dist/commands/canvas-history/grep.js +3 -22
  25. package/dist/commands/canvas-history/read.js +5 -3
  26. package/dist/commands/canvas-history/search.js +3 -22
  27. package/dist/commands/canvas-history/shared.d.ts +8 -0
  28. package/dist/commands/canvas-history/shared.js +64 -1
  29. package/dist/commands/canvas-history/stats.d.ts +1 -0
  30. package/dist/commands/canvas-history/stats.js +68 -0
  31. package/dist/commands/canvas-history.js +4 -3
  32. package/dist/commands/memory/read.js +15 -38
  33. package/dist/commands/memory/shared.d.ts +1 -1
  34. package/dist/commands/memory/shared.js +1 -1
  35. package/dist/commands/node/create.js +2 -2
  36. package/dist/commands/sys/config.js +19 -2
  37. package/dist/commands/sys/context/admin/docs-panel.d.ts +3 -3
  38. package/dist/commands/sys/context/admin/model.d.ts +7 -2
  39. package/dist/commands/sys/context/admin/model.js +7 -1
  40. package/dist/commands/sys/context/admin/read-view.d.ts +5 -5
  41. package/dist/commands/sys/context/admin/read-view.js +9 -4
  42. package/dist/commands/sys/context/resolve.d.ts +32 -3
  43. package/dist/commands/sys/context/resolve.js +74 -25
  44. package/dist/commands/sys/panels/models-panel.d.ts +28 -5
  45. package/dist/commands/sys/panels/models-panel.js +560 -75
  46. package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +1 -1
  47. package/dist/core/__tests__/integration/revive.test.js +4 -2
  48. package/dist/core/__tests__/model-pin-durability.test.js +25 -1
  49. package/dist/core/__tests__/model-routes-config.test.d.ts +1 -0
  50. package/dist/core/__tests__/model-routes-config.test.js +142 -0
  51. package/dist/core/__tests__/on-read-nested-store.test.js +8 -0
  52. package/dist/core/__tests__/profile-project-memory-delivery.test.js +264 -1
  53. package/dist/core/__tests__/relaunch-root.test.js +17 -0
  54. package/dist/core/__tests__/seam/broker-provider-retry.test.js +21 -6
  55. package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.js +19 -2
  56. package/dist/core/broker-client/__tests__/transport-relay.test.js +3 -1
  57. package/dist/core/broker-client/transport-relay.js +5 -1
  58. package/dist/core/canvas/__tests__/history-transcript.test.d.ts +1 -0
  59. package/dist/core/canvas/__tests__/history-transcript.test.js +102 -0
  60. package/dist/core/canvas/browse/app.d.ts +1 -1
  61. package/dist/core/canvas/browse/app.js +13 -10
  62. package/dist/core/canvas/browse/model.js +16 -1
  63. package/dist/core/canvas/browse/render.d.ts +23 -3
  64. package/dist/core/canvas/browse/render.js +429 -64
  65. package/dist/core/canvas/canvas.js +12 -8
  66. package/dist/core/canvas/extensions.d.ts +1 -1
  67. package/dist/core/canvas/extensions.js +7 -1
  68. package/dist/core/canvas/history.d.ts +36 -1
  69. package/dist/core/canvas/history.js +328 -7
  70. package/dist/core/canvas/labels.d.ts +2 -2
  71. package/dist/core/canvas/migrations.js +32 -0
  72. package/dist/core/canvas/node-recap.d.ts +23 -0
  73. package/dist/core/canvas/node-recap.js +67 -0
  74. package/dist/core/canvas/render-source.d.ts +34 -2
  75. package/dist/core/canvas/render-source.js +83 -24
  76. package/dist/core/canvas/types.d.ts +9 -0
  77. package/dist/core/config.d.ts +19 -2
  78. package/dist/core/config.js +174 -51
  79. package/dist/core/keybindings/catalog.d.ts +2 -2
  80. package/dist/core/keybindings/catalog.js +11 -8
  81. package/dist/core/model-routes.js +11 -4
  82. package/dist/core/runtime/broker/auth-reload.js +4 -1
  83. package/dist/core/runtime/broker/fault-retry.js +7 -2
  84. package/dist/core/runtime/broker/frame-client.js +4 -0
  85. package/dist/core/runtime/broker/frame-dispatch.js +7 -0
  86. package/dist/core/runtime/broker/inbox.d.ts +12 -2
  87. package/dist/core/runtime/broker/inbox.js +68 -38
  88. package/dist/core/runtime/broker-protocol.d.ts +6 -0
  89. package/dist/core/runtime/broker.js +14 -0
  90. package/dist/core/runtime/canvas-extensions.d.ts +1 -0
  91. package/dist/core/runtime/canvas-extensions.js +2 -0
  92. package/dist/core/runtime/interactive-deliver.d.ts +8 -2
  93. package/dist/core/runtime/interactive-deliver.js +12 -3
  94. package/dist/core/runtime/launch-target.js +1 -1
  95. package/dist/core/runtime/launch.d.ts +12 -6
  96. package/dist/core/runtime/launch.js +19 -17
  97. package/dist/core/runtime/model-swap.d.ts +5 -4
  98. package/dist/core/runtime/model-swap.js +7 -5
  99. package/dist/core/runtime/promote.js +2 -1
  100. package/dist/core/runtime/reset.js +4 -2
  101. package/dist/core/runtime/revive.js +22 -0
  102. package/dist/core/runtime/spawn.d.ts +3 -3
  103. package/dist/core/runtime/spawn.js +20 -3
  104. package/dist/core/runtime/stamp/channel.d.ts +18 -0
  105. package/dist/core/runtime/stamp/channel.js +74 -0
  106. package/dist/core/runtime/stamp/protocol.d.ts +86 -0
  107. package/dist/core/runtime/stamp/protocol.js +113 -0
  108. package/dist/core/runtime/tmux-driver.d.ts +11 -1
  109. package/dist/core/runtime/tmux-driver.js +27 -11
  110. package/dist/core/self-update.js +2 -5
  111. package/dist/core/substrate/listings.d.ts +1 -1
  112. package/dist/core/substrate/listings.js +6 -2
  113. package/dist/core/substrate/on-read.d.ts +61 -5
  114. package/dist/core/substrate/on-read.js +129 -22
  115. package/dist/core/substrate/render.d.ts +3 -2
  116. package/dist/core/substrate/render.js +133 -38
  117. package/dist/core/tui/draw.d.ts +1 -0
  118. package/dist/core/tui/draw.js +5 -1
  119. package/dist/core/tui/markdown.d.ts +17 -0
  120. package/dist/core/tui/markdown.js +407 -0
  121. package/dist/core/user-settings.d.ts +4 -0
  122. package/dist/core/user-settings.js +1 -0
  123. package/dist/daemon/api/__tests__/node-model-validation.test.js +21 -0
  124. package/dist/daemon/api/__tests__/seam/api-server.test.js +34 -0
  125. package/dist/daemon/api/bridge.d.ts +3 -2
  126. package/dist/daemon/api/bridge.js +34 -9
  127. package/dist/daemon/api/handlers/canvas.js +80 -18
  128. package/dist/daemon/api/handlers/health.js +17 -12
  129. package/dist/daemon/api/handlers/messages.js +12 -1
  130. package/dist/daemon/api/handlers/nodes.js +10 -3
  131. package/dist/daemon/api/map.js +2 -0
  132. package/dist/daemon/messaging/node-message.js +15 -1
  133. package/dist/daemon/reconcilers/broker-supervision.js +26 -9
  134. package/dist/daemon/reconcilers/live-obligation.d.ts +8 -1
  135. package/dist/daemon/reconcilers/live-obligation.js +17 -0
  136. package/dist/pi-extensions/__tests__/pre-command-gate.test.js +2 -2
  137. package/dist/pi-extensions/canvas-inbox-watcher.js +27 -2
  138. package/dist/pi-extensions/canvas-recap.js +4 -0
  139. package/dist/pi-extensions/canvas-stamp.d.ts +14 -0
  140. package/dist/pi-extensions/canvas-stamp.js +77 -0
  141. package/dist/pi-extensions/canvas-stophook.js +3 -0
  142. package/dist/shared/__tests__/env-boundary.test.js +4 -2
  143. package/dist/shared/crtr-version.d.ts +2 -0
  144. package/dist/shared/crtr-version.js +18 -0
  145. package/dist/shared/env.d.ts +10 -0
  146. package/dist/shared/env.js +19 -4
  147. package/dist/types.d.ts +14 -2
  148. package/dist/types.js +4 -0
  149. package/package.json +1 -1
  150. package/runtime.lock.json +2 -2
@@ -212,7 +212,7 @@ test('C3f — a logical request can reach a non-managed raw-registry fallback wh
212
212
  family: 'custom',
213
213
  credentialSource: 'user',
214
214
  providerId: 'c3prov',
215
- models: { strong: 'c3prov/c3model' },
215
+ models: { ultra: 'c3prov/c3model', strong: 'c3prov/c3model', medium: 'c3prov/c3model', light: 'c3prov/c3model' },
216
216
  },
217
217
  },
218
218
  })}\n`);
@@ -180,7 +180,8 @@ test('a resumed revive replays the persisted LaunchSpec (model, tools, extension
180
180
  pi_session_id: 'uuid-9',
181
181
  pi_session_file: sessionFile,
182
182
  launch: {
183
- model: 'anthropic/sonnet',
183
+ model: 'anthropic/claude-opus-5:high',
184
+ modelExact: true,
184
185
  tools: ['bash', 'read'],
185
186
  extensions: ['/ext/a.js', '/ext/b.js'],
186
187
  systemPrompt: 'You are a developer orchestrator.',
@@ -196,7 +197,8 @@ test('a resumed revive replays the persisted LaunchSpec (model, tools, extension
196
197
  assert.equal(argv[ea - 1], '-e');
197
198
  // Model + tools + system prompt + resume target.
198
199
  const mi = argv.indexOf('--model');
199
- assert.equal(argv[mi + 1], 'anthropic/sonnet');
200
+ assert.equal(argv[mi + 1], 'anthropic/claude-opus-5:high');
201
+ assert.ok(argv.includes('--crtr-model-exact'), 'revive preserves the exact-model contract');
200
202
  const ti = argv.indexOf('--tools');
201
203
  assert.equal(argv[ti + 1], 'bash,read');
202
204
  assert.ok(argv.includes('--append-system-prompt'), 'system prompt replayed');
@@ -20,7 +20,7 @@ import { join } from 'node:path';
20
20
  import { buildLaunchSpec, isProviderPinnedModelToken, normalizeModel } from '../runtime/launch.js';
21
21
  import { defaultModelLaddersConfig } from '../../types.js';
22
22
  import { persistDormantModel } from '../runtime/model-swap.js';
23
- import { promote } from '../runtime/promote.js';
23
+ import { promote, reshapeNode } from '../runtime/promote.js';
24
24
  import { createNode, getNode } from '../canvas/canvas.js';
25
25
  import { closeDb } from '../canvas/db.js';
26
26
  const ladders = defaultModelLaddersConfig();
@@ -86,6 +86,15 @@ test('only concrete provider/id tokens pin; logical family/tier refs remain pref
86
86
  assert.equal(isProviderPinnedModelToken('ultra'), false, 'a bare tier has no `/` and stays unpinned');
87
87
  assert.equal(isProviderPinnedModelToken('opus'), false, 'a bare family alias has no `/` and stays unpinned');
88
88
  });
89
+ test('a new --model override resolves once and freezes portable and concrete requests', () => {
90
+ for (const requested of ['anthropic/opus', 'strong', 'anthropic/claude-fable-5:high']) {
91
+ const launch = buildSpec({ model: requested, modelOverride: true });
92
+ assert.equal(launch.model, normalizeModel(requested, ladders), `${requested} resolves through the configured ladder`);
93
+ assert.equal(launch.modelExact, true, `${requested} is exact after resolution`);
94
+ assert.equal(launch.modelIntent, undefined, `${requested} has no portable route intent`);
95
+ assert.equal(launch.env.CRTR_MODEL_INTENT, undefined, `${requested} cannot re-enable fallback on revive`);
96
+ }
97
+ });
89
98
  // Direction 1: an EXPLICIT concrete pin must SURVIVE a config/lifecycle rebuild
90
99
  // even when the carried override has portablized back to a bare tier.
91
100
  test('an explicit concrete pin survives a config/lifecycle rebuild whose portablized override reads as a bare tier', () => {
@@ -103,6 +112,20 @@ test('an explicit concrete pin survives a config/lifecycle rebuild whose portabl
103
112
  // Direction 2: a deliberately TIER-run (unpinned) node must NOT be wrongly
104
113
  // re-pinned when a subsequent rebuild/persist normalizes its override to the
105
114
  // concrete cell it happens to share bytes with.
115
+ test('reshape resolves and persists a new --model override as the exact revive recipe', () => {
116
+ createNode(node('override-node', {
117
+ model_override: 'strong',
118
+ launch: { extensions: [], env: { CRTR_MODEL_INTENT: 'anthropic/strong' }, model: normalizeModel('strong', ladders), modelIntent: { family: 'anthropic', strength: 'strong' } },
119
+ }));
120
+ reshapeNode('override-node', { model: 'anthropic/opus' });
121
+ const after = getNode('override-node');
122
+ const expected = normalizeModel('anthropic/opus', ladders);
123
+ assert.equal(after?.model_override, expected, 'the raw alias never becomes the durable override');
124
+ assert.equal(after?.launch?.model, expected, 'revive replays the resolved provider/model');
125
+ assert.equal(after?.launch?.modelExact, true, 'retries and revives reject substitutes');
126
+ assert.equal(after?.launch?.modelIntent, undefined);
127
+ assert.equal(after?.launch?.env.CRTR_MODEL_INTENT, undefined);
128
+ });
106
129
  test('promotion preserves the exact-model contract required to revive a private human-work fork', () => {
107
130
  const exactModel = 'openai-codex/gpt-5.6-sol:max';
108
131
  createNode(node('exact-node', {
@@ -134,6 +157,7 @@ test('a deliberately tier-run node is not wrongly re-pinned when a rebuild or pe
134
157
  const after = getNode('tier-node');
135
158
  assert.equal(after?.launch?.model, ULTRA_CELL, 'the concrete spec is persisted as the launch model');
136
159
  assert.equal(after?.model_override, ULTRA_CELL, 'the concrete spec is persisted as the override');
160
+ assert.equal(after?.launch?.modelExact, true, 'an explicit model change remains exact on revive');
137
161
  assert.equal(after?.launch?.modelIntent, undefined, 'an explicit concrete choice clears the prior logical route intent');
138
162
  assert.equal(after?.launch?.env.CRTR_MODEL_INTENT, undefined, 'the revived broker does not inherit the prior logical route intent');
139
163
  assert.equal(after?.launch?.modelProviderPinned, false, 'an explicit bare-tier decision clears a prior pin — the raw-token decision wins over the concrete spec shape');
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,142 @@
1
+ import { after, afterEach, before, beforeEach, test } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ import { persistModelRoute, readConfig, readMergedLaunchConfig } from '../config.js';
7
+ import { expandModelCandidates } from '../model-routes.js';
8
+ import { resetScopeCache } from '../scope.js';
9
+ let home;
10
+ let work;
11
+ let previousHome;
12
+ function route(providerId = 'provider-x', family = 'family-x', credentialSource = 'source-x') {
13
+ return {
14
+ family,
15
+ credentialSource,
16
+ providerId,
17
+ models: {
18
+ ultra: `${providerId}/ultra`,
19
+ strong: `${providerId}/strong`,
20
+ medium: `${providerId}/medium`,
21
+ light: `${providerId}/light`,
22
+ },
23
+ };
24
+ }
25
+ function writeConfig(root, config) {
26
+ mkdirSync(root, { recursive: true });
27
+ writeFileSync(join(root, 'config.json'), JSON.stringify(config));
28
+ }
29
+ before(() => {
30
+ previousHome = process.env.HOME;
31
+ });
32
+ beforeEach(() => {
33
+ home = mkdtempSync(join(tmpdir(), 'crtr-model-routes-home-'));
34
+ work = mkdtempSync(join(tmpdir(), 'crtr-model-routes-work-'));
35
+ process.env.HOME = home;
36
+ resetScopeCache();
37
+ });
38
+ afterEach(() => {
39
+ resetScopeCache();
40
+ rmSync(home, { recursive: true, force: true });
41
+ rmSync(work, { recursive: true, force: true });
42
+ });
43
+ after(() => {
44
+ if (previousHome === undefined)
45
+ delete process.env.HOME;
46
+ else
47
+ process.env.HOME = previousHome;
48
+ resetScopeCache();
49
+ });
50
+ test('complete routes preserve arbitrary names and credential-source ordering', () => {
51
+ writeConfig(join(home, '.crouter'), {
52
+ modelRoutes: {
53
+ personal: route('shared-provider', 'family/one', 'personal source'),
54
+ funded: route('shared-provider', 'family/one', 'funded source'),
55
+ },
56
+ modelRouting: {
57
+ credentialSourceOrder: ['funded source', 'personal source'],
58
+ familyOrder: ['family/one'],
59
+ strengthFallback: ['strong'],
60
+ },
61
+ });
62
+ const config = readMergedLaunchConfig(work, null);
63
+ assert.deepEqual(config.modelRoutes?.personal?.models, route('shared-provider', 'family/one', 'personal source').models);
64
+ assert.deepEqual(expandModelCandidates({ kind: 'logical', family: 'family/one', strength: 'strong', spec: 'family/one/strong' }, work, null).map((candidate) => candidate.routeId), ['funded', 'personal'], 'same provider/model refs remain distinct candidates when routes differ');
65
+ });
66
+ test('a sparse upper-scope route patch overlays a complete lower route', () => {
67
+ writeConfig(join(home, '.crouter'), { modelRoutes: { shared: route() } });
68
+ writeConfig(join(work, '.crouter'), { modelRoutes: { shared: { models: { medium: 'provider-x/project-medium' } } } });
69
+ const config = readMergedLaunchConfig(work, null);
70
+ assert.equal(config.modelRoutes?.shared?.models.medium, 'provider-x/project-medium');
71
+ assert.equal(config.modelRoutes?.shared?.models.ultra, 'provider-x/ultra');
72
+ });
73
+ test('legacy sparse routes remain readable with no invented cells', () => {
74
+ writeConfig(join(home, '.crouter'), {
75
+ modelRoutes: {
76
+ legacy: { family: 'legacy', credentialSource: 'old', providerId: 'legacy-provider', models: { medium: 'legacy-provider/medium' } },
77
+ },
78
+ });
79
+ const config = readMergedLaunchConfig(work, null);
80
+ assert.equal(config.modelRoutes, undefined);
81
+ assert.deepEqual(config.legacyModelRoutes?.legacy?.models, { medium: 'legacy-provider/medium' });
82
+ assert.deepEqual(readConfig('user').legacyModelRoutes?.legacy?.models, { medium: 'legacy-provider/medium' });
83
+ assert.deepEqual(expandModelCandidates({ kind: 'logical', family: 'legacy', strength: 'medium', spec: 'legacy/medium' }, work, null).map((candidate) => candidate.modelId), ['medium']);
84
+ });
85
+ test('route records retain __proto__ ids when read and persisted', () => {
86
+ const root = join(home, '.crouter');
87
+ writeConfig(root, JSON.parse(JSON.stringify({ modelRoutes: { ['__proto__']: route() } })));
88
+ assert.deepEqual(readMergedLaunchConfig(work, null).modelRoutes?.['__proto__'], route());
89
+ persistModelRoute(root, '__proto__', route('persisted-provider'));
90
+ const raw = JSON.parse(readFileSync(join(root, 'config.json'), 'utf8'));
91
+ assert.deepEqual(raw.modelRoutes['__proto__'], route('persisted-provider'));
92
+ });
93
+ test('route candidate identity distinguishes delimiter-containing route and provider ids', () => {
94
+ writeConfig(join(home, '.crouter'), {
95
+ modelRoutes: {
96
+ a: route('b:c', 'family', 'first'),
97
+ 'a:b': route('c', 'family', 'second'),
98
+ },
99
+ modelRouting: {
100
+ credentialSourceOrder: ['first', 'second'],
101
+ familyOrder: ['family'],
102
+ strengthFallback: ['strong'],
103
+ },
104
+ });
105
+ assert.deepEqual(expandModelCandidates({ kind: 'logical', family: 'family', strength: 'strong', spec: 'family/strong' }, work, null).map((candidate) => candidate.routeId), ['a', 'a:b']);
106
+ });
107
+ test('route writes reject missing cells and provider-prefix mismatches with actionable diagnostics', () => {
108
+ const root = join(home, '.crouter');
109
+ const incomplete = { ...route(), models: { strong: 'provider-x/strong' } };
110
+ assert.throws(() => persistModelRoute(root, 'needs-all-rungs', incomplete), /model route "needs-all-rungs" is incomplete; missing strengths: ultra, medium, light/);
111
+ assert.throws(() => persistModelRoute(root, 'wrong-provider', { ...route(), models: { ...route().models, light: 'other/light' } }), /model route "wrong-provider" models.light provider prefix must equal providerId/);
112
+ });
113
+ test('invalid route entries do not block valid routes, and no valid routes falls back to local ladders', () => {
114
+ writeConfig(join(home, '.crouter'), {
115
+ modelRoutes: {
116
+ valid: route('valid-provider', 'valid family', 'valid source'),
117
+ invalid: { family: 'bad', credentialSource: 'bad', providerId: 'one', models: { ultra: 'two/u', strong: 'two/s', medium: 'two/m', light: 'two/l' } },
118
+ },
119
+ });
120
+ assert.deepEqual(Object.keys(readMergedLaunchConfig(work, null).modelRoutes ?? {}), ['valid']);
121
+ writeConfig(join(home, '.crouter'), {
122
+ modelRoutes: {
123
+ invalid: { family: 'bad', credentialSource: 'bad', providerId: 'one', models: { ultra: 'two/u', strong: 'two/s', medium: 'two/m', light: 'two/l' } },
124
+ },
125
+ });
126
+ assert.equal(readMergedLaunchConfig(work, null).modelRoutes, undefined);
127
+ assert.equal(expandModelCandidates({ kind: 'logical', family: 'anthropic', strength: 'strong', spec: 'anthropic/strong' }, work, null)[0]?.routeId, 'anthropic');
128
+ });
129
+ test('persistModelRoute touches only its raw route and deletes by restoring inheritance', () => {
130
+ const root = join(home, '.crouter');
131
+ const other = route('other-provider');
132
+ writeConfig(root, { modelRoutes: { other }, max_panes_per_window: 7 });
133
+ persistModelRoute(root, 'edited', route('edited-provider'));
134
+ let raw = JSON.parse(readFileSync(join(root, 'config.json'), 'utf8'));
135
+ assert.deepEqual(raw.modelRoutes.other, other);
136
+ assert.deepEqual(raw.modelRoutes.edited, route('edited-provider'));
137
+ assert.equal(raw.modelLadders, undefined);
138
+ assert.equal(raw.max_panes_per_window, 7);
139
+ persistModelRoute(root, 'edited', undefined);
140
+ raw = JSON.parse(readFileSync(join(root, 'config.json'), 'utf8'));
141
+ assert.deepEqual(raw.modelRoutes, { other });
142
+ });
@@ -69,6 +69,11 @@ function makeWorkTree() {
69
69
  ' - {on: workspace-open, at: content}\n' +
70
70
  ' - {on: read, match: "./**", at: content}\n---\n' +
71
71
  `${NESTED_BODY}\n`);
72
+ // A bootless neighbour in the same discovered store: it wins no delivery of
73
+ // its own, so only a listing can ever disclose it.
74
+ writeFileSync(join(nestedStore, 'constraint.md'), '---\nkind: knowledge\n' +
75
+ 'when-and-why-to-read: When changing pkg internals, this knowledge should be read because it names the constraint.\n---\n' +
76
+ 'NESTED NEIGHBOUR BODY\n');
72
77
  return work;
73
78
  }
74
79
  test('a nested store front door surfaces on a read beneath its owning dir, not outside it', () => {
@@ -79,6 +84,9 @@ test('a nested store front door surfaces on a read beneath its owning dir, not o
79
84
  writeFileSync(inside, 'export const x = 1;\n');
80
85
  const rendered = renderOnReadDocs(node, inside, freshTranscriptTarget());
81
86
  assert.ok(rendered.includes(NESTED_BODY), `read beneath pkg surfaces the nested front door; got: ${rendered}`);
87
+ // A content delivery is a read: the discovered store's own listing comes
88
+ // with it, answered from the corpus the read walk discovered.
89
+ assert.match(rendered, /\[\[work\/pkg\/constraint\]\]: When changing pkg internals/, `the delivered front door discloses its directory's bootless member; got: ${rendered}`);
82
90
  const outside = join(work, 'docs', 'notes.ts');
83
91
  mkdirSync(join(work, 'docs'), { recursive: true });
84
92
  writeFileSync(outside, 'export const y = 2;\n');
@@ -25,11 +25,18 @@ import { spawnNode } from '../runtime/nodes.js';
25
25
  import { memoryDir } from '../runtime/memory.js';
26
26
  import { clearSessionCache } from '../substrate/session-cache.js';
27
27
  import { renderKnowledgeBlock } from '../substrate/render-node.js';
28
+ import { renderKnowledgeForSubject, renderPreferencesForSubject } from '../substrate/render.js';
29
+ import { assembleNodeSubject } from '../substrate/subject.js';
28
30
  import { renderWorkspaceOpenDocs } from '../substrate/on-read-node.js';
31
+ import { renderWorkspaceOpenDocsForSubject } from '../substrate/on-read.js';
29
32
  import { documentExposedAtOrAbove, emptyContextExposureState, exposedAtOrAbove, exposureTarget, loadContextExposureState, saveContextExposureState, } from '../substrate/injected-store.js';
30
33
  import { readLeaf } from '../../commands/memory/read.js';
31
34
  import { listLeaf } from '../../commands/memory/list.js';
32
35
  import { findLeaf } from '../../commands/memory/find.js';
36
+ import { assembleEventAccounting, docRow, effectivePlan, } from '../../commands/sys/context/resolve.js';
37
+ import { planDelivery } from '../substrate/plan.js';
38
+ import { PlanSet } from '../../commands/sys/context/admin/model.js';
39
+ import { buildRows } from '../../commands/sys/context/admin/docs-panel.js';
33
40
  function freshTranscriptTarget() {
34
41
  return exposureTarget(emptyContextExposureState(), 'transcript');
35
42
  }
@@ -48,6 +55,14 @@ const B = {
48
55
  dupFar: 'BODY-T9-DUP-FROM-FAR',
49
56
  kindSplit: 'BODY-T9-KIND-SPLIT',
50
57
  documentGateWins: 'BODY-T9-DOCUMENT-GATE-WINS',
58
+ codeCraftIndex: 'BODY-T9-CODE-CRAFT-INDEX',
59
+ companionSource: 'BODY-T9-COMPANION-SOURCE',
60
+ companionA: 'BODY-T9-COMPANION-A',
61
+ companionB: 'BODY-T9-COMPANION-B',
62
+ prefRouter: 'BODY-T9-PREF-ROUTER',
63
+ crossPartition: 'BODY-T9-CROSS-PARTITION',
64
+ outsideGuide: 'BODY-T9-OUTSIDE-GUIDE',
65
+ pnameNeighbour: 'BODY-T9-NEIGHBOUR-IN-PNAMEDIR',
51
66
  };
52
67
  let home;
53
68
  let canvasHome;
@@ -58,6 +73,7 @@ let prevProfile;
58
73
  let prevNode;
59
74
  let prevCwd;
60
75
  let cwdProject;
76
+ let outsideProject;
61
77
  let ppreviewDocPath;
62
78
  /** A doc delivered automatically at the top rung by both automatic channels —
63
79
  * every cap observed below is the relationship's, never the author's. */
@@ -157,6 +173,60 @@ before(() => {
157
173
  B.kindSplit,
158
174
  '',
159
175
  ].join('\n'));
176
+ writeDoc(user, 'dev/code-craft/INDEX', [
177
+ '---',
178
+ 'kind: knowledge',
179
+ 'when-and-why-to-read: "When planning code work, this knowledge should be read because it routes the applicable craft guidance"',
180
+ 'surfaces:',
181
+ ' - {on: boot, gate: {kind: {in: [design, spec, plan, developer]}}, at: content}',
182
+ '---',
183
+ '',
184
+ B.codeCraftIndex,
185
+ '',
186
+ ].join('\n'));
187
+ for (let i = 1; i <= 10; i++) {
188
+ writeDoc(user, `dev/code-craft/member-${i}`, bootlessDoc(`When code-craft member ${i} applies`, `BODY-T9-CODE-CRAFT-MEMBER-${i}`));
189
+ }
190
+ // The ANCESTOR half of a content delivery's listings: a bootless sibling of
191
+ // the `dev/code-craft` directory, disclosed because `dev/` is the enclosing
192
+ // directory of the doc whose body delivered.
193
+ writeDoc(user, 'dev/sibling', bootlessDoc('When the dev sibling applies', 'BODY-T9-DEV-SIBLING'));
194
+ writeDoc(user, 't9-companion-source', [
195
+ '---',
196
+ 'kind: knowledge',
197
+ 'when-and-why-to-read: "When companion delivery starts, this knowledge should be read because it proves content reads cascade"',
198
+ 'surfaces:',
199
+ ' - {on: boot, at: content}',
200
+ '---',
201
+ '',
202
+ B.companionSource,
203
+ '',
204
+ ].join('\n'));
205
+ // A companion that FRONTS a directory: its content delivery discloses its own
206
+ // members exactly as the source doc's did, one level deeper in the fixpoint.
207
+ writeDoc(user, 't9-companion-a/INDEX', [
208
+ '---',
209
+ 'kind: knowledge',
210
+ 'when-and-why-to-read: "When the source is read, this knowledge should be read because it proves the first companion route"',
211
+ 'surfaces:',
212
+ ' - {on: memory-read, match: t9-companion-source, at: content}',
213
+ '---',
214
+ '',
215
+ B.companionA,
216
+ '',
217
+ ].join('\n'));
218
+ writeDoc(user, 't9-companion-a/member-1', bootlessDoc('When the companion member applies', 'BODY-T9-COMPANION-A-MEMBER-1'));
219
+ writeDoc(user, 't9-companion-b', [
220
+ '---',
221
+ 'kind: knowledge',
222
+ 'when-and-why-to-read: "When the first companion is read, this knowledge should be read because it proves the route reaches a fixpoint"',
223
+ 'surfaces:',
224
+ ' - {on: memory-read, match: t9-companion-a, at: content}',
225
+ '---',
226
+ '',
227
+ B.companionB,
228
+ '',
229
+ ].join('\n'));
160
230
  writeDoc(user, 't9-document-gate-wins', [
161
231
  '---',
162
232
  'kind: knowledge',
@@ -169,6 +239,78 @@ before(() => {
169
239
  B.documentGateWins,
170
240
  '',
171
241
  ].join('\n'));
242
+ // Cross-partition single emission: a preference body routes a KNOWLEDGE doc
243
+ // that carries boot content of its own. Both partitions render against one
244
+ // exposure state, so that body must be delivered exactly once.
245
+ writeDoc(user, 't9-pref-router', [
246
+ '---',
247
+ 'kind: preference',
248
+ 'when-and-why-to-read: "When the preference partition renders, this preference should be read because it routes a knowledge companion"',
249
+ 'surfaces:',
250
+ ' - {on: boot, at: content}',
251
+ '---',
252
+ '',
253
+ B.prefRouter,
254
+ '',
255
+ ].join('\n'));
256
+ writeDoc(user, 't9-cross-partition', [
257
+ '---',
258
+ 'kind: knowledge',
259
+ 'when-and-why-to-read: "When the preference routes here, this knowledge should be read because it proves one body crosses both partitions once"',
260
+ 'surfaces:',
261
+ ' - {on: boot, at: content}',
262
+ ' - {on: memory-read, match: t9-pref-router, at: content}',
263
+ '---',
264
+ '',
265
+ B.crossPartition,
266
+ '',
267
+ ].join('\n'));
268
+ // Cap safety: a name-tree directory merges across stores, so this user doc
269
+ // sits in the SAME canonical directory as the name-capped project's docs. Its
270
+ // content delivery discloses that directory — and must not raise a capped
271
+ // neighbour above the bare name its relationship allows.
272
+ writeDoc(user, 'pname/user-guide', [
273
+ '---',
274
+ 'kind: knowledge',
275
+ 'when-and-why-to-read: "When the merged directory renders, this knowledge should be read because it discloses that directory"',
276
+ 'surfaces:',
277
+ ' - {on: boot, at: content}',
278
+ '---',
279
+ '',
280
+ B.pnameNeighbour,
281
+ '',
282
+ ].join('\n'));
283
+ // A project OUTSIDE the profile manifest: its store mounts only for a target
284
+ // whose cwd sits inside it, so a workspace-open render of it proves the
285
+ // listings answer from the opened workspace rather than the ambient one.
286
+ const poutside = store('poutside', 'poutside');
287
+ outsideProject = join(home, 'poutside');
288
+ writeDoc(poutside, 't9-ws-guide/INDEX', [
289
+ '---',
290
+ 'kind: knowledge',
291
+ 'when-and-why-to-read: "When that workspace opens, this knowledge should be read because it fronts its guide directory"',
292
+ 'surfaces:',
293
+ ' - {on: workspace-open, at: content}',
294
+ '---',
295
+ '',
296
+ B.outsideGuide,
297
+ '',
298
+ ].join('\n'));
299
+ writeDoc(poutside, 't9-ws-guide/member-1', bootlessDoc('When the outside guide member applies', 'BODY-T9-OUTSIDE-MEMBER-1'));
300
+ // Workspace-open plans project stores only, but a content delivery's
301
+ // read-equivalent attachments answer from the whole opened target.
302
+ writeDoc(user, 'poutside/listing-member', bootlessDoc('When the workspace listing member applies', 'BODY-T9-WORKSPACE-LISTING-MEMBER'));
303
+ writeDoc(user, 't9-workspace-companion', [
304
+ '---',
305
+ 'kind: knowledge',
306
+ 'when-and-why-to-read: "When the workspace guide is read, this companion proves full-view accounting"',
307
+ 'surfaces:',
308
+ ' - {on: memory-read, match: poutside/t9-ws-guide, at: content}',
309
+ '---',
310
+ '',
311
+ 'BODY-T9-WORKSPACE-COMPANION',
312
+ '',
313
+ ].join('\n'));
172
314
  // Hidden-count pair: a top-level dir always renders, so a dir holding only a
173
315
  // bootless doc shows as `<dir>/` + `[+1 more]` — unless its project is `none`.
174
316
  writeDoc(pnone, join('t9-none-hidden', 'probe'), bootlessDoc('When counted', 'HIDDEN-NONE'));
@@ -219,7 +361,10 @@ test('boot delivery caps every profile project at its relationship, including th
219
361
  // does, and the relationship still decides the ceiling.
220
362
  assert.ok(!boot.includes(B.ppreview), `the cwd project is capped at preview — its body must not render; got:\n${boot}`);
221
363
  assert.match(boot, /t9-ppreview-doc {2}# read when: When the cwd project delivers\./, `the cwd project's doc discloses at preview; got:\n${boot}`);
222
- // `name` discloses the bare identifier and nothing else.
364
+ // `name` discloses the bare identifier and nothing else — including when a
365
+ // content delivery in the same merged directory (the user-store neighbour)
366
+ // discloses that directory's listing.
367
+ assert.ok(boot.includes(B.pnameNeighbour), `the merged directory's uncapped document delivers; got:\n${boot}`);
223
368
  assert.ok(!boot.includes(B.pname), 'a name-capped project must not render a body');
224
369
  assert.match(boot, /[├└]─ t9-pname-doc$/m, `the name-capped doc discloses as a bare name; got:\n${boot}`);
225
370
  assert.ok(!boot.includes('When the name project delivers'), 'a name-capped doc discloses no preview line');
@@ -242,6 +387,88 @@ test('surface-entry gates select the rung by node kind while the document gate r
242
387
  const developer = renderKnowledgeBlock(newNode('developer'), freshTranscriptTarget());
243
388
  assert.ok(!developer.includes('t9-document-gate-wins'), `a failing document gate excludes every entry; got:\n${developer}`);
244
389
  assert.ok(!developer.includes(B.documentGateWins), 'a failing document gate excludes its content');
390
+ assert.ok(developer.includes(B.codeCraftIndex), 'the directory document delivers its boot content');
391
+ for (let i = 1; i <= 10; i++) {
392
+ assert.match(developer, new RegExp(`member-${i} {2}# read when: When code-craft member ${i} applies`), `content delivery exposes code-craft member ${i} at preview; got:\n${developer}`);
393
+ }
394
+ // The ancestor listing, not just the doc's own directory.
395
+ assert.match(developer, /sibling {2}# read when: When the dev sibling applies\./, `content delivery exposes the enclosing directory's members too; got:\n${developer}`);
396
+ });
397
+ test('context docs accounting reports content attachment raises from the renderer', () => {
398
+ const node = newNode('developer');
399
+ const subject = assembleNodeSubject(node);
400
+ assert.ok(subject !== null, 'the fixture node assembles a subject');
401
+ const target = { cwd: cwdProject, profileId, nodeId: node };
402
+ const direct = planDelivery(subject, target, 'boot');
403
+ const rendered = assembleEventAccounting({ subject, target, nodeMissing: false }, 'boot', {});
404
+ const rows = effectivePlan(direct, rendered.accounting.deliveries).docs.map((record) => docRow(record, true, subject));
405
+ for (let i = 1; i <= 10; i++) {
406
+ const row = rows.find((candidate) => candidate.name === `dev/code-craft/member-${i}`);
407
+ assert.equal(row?.final_rung, 'preview', `member ${i} is raised to the listing preview rung`);
408
+ assert.equal(row?.delivery_cause, 'directory listing attached to content delivery');
409
+ }
410
+ const companion = rows.find((row) => row.name === 't9-companion-a');
411
+ assert.equal(companion?.final_rung, 'content', 'a memory-read companion reaches its rendered rung');
412
+ assert.equal(companion?.delivery_cause, 'memory-read companion of content delivery');
413
+ const plans = new PlanSet({
414
+ kind: 'developer',
415
+ mode: 'base',
416
+ lifecycle: 'terminal',
417
+ hasManager: true,
418
+ profileId,
419
+ cwd: cwdProject,
420
+ nodeId: node,
421
+ depth: 0,
422
+ event: 'boot',
423
+ file: '',
424
+ docName: '',
425
+ command: '',
426
+ });
427
+ plans.plan('boot');
428
+ const viewerMember = buildRows(plans, 'boot').find((row) => row.name === 'dev/code-craft/member-1');
429
+ const viewerRecord = viewerMember?.records.get('boot');
430
+ assert.equal(viewerRecord?.finalRung, 'preview', 'the admin row reads the renderer-raised rung');
431
+ assert.equal(viewerRecord?.deliveryCause, 'directory listing attached to content delivery');
432
+ plans.dispose();
433
+ });
434
+ test('context docs accounting adds full-view workspace content attachments', () => {
435
+ const node = newNode();
436
+ const subject = assembleNodeSubject(node);
437
+ assert.ok(subject !== null, 'the fixture node assembles a subject');
438
+ const target = { cwd: outsideProject, profileId, nodeId: node };
439
+ const direct = planDelivery(subject, target, 'workspace-open');
440
+ const rendered = assembleEventAccounting({ subject, target, nodeMissing: false }, 'workspace-open', {});
441
+ const rows = effectivePlan(direct, rendered.accounting.deliveries).docs.map((record) => docRow(record, true, subject));
442
+ const listing = rows.find((row) => row.name === 'poutside/listing-member');
443
+ assert.equal(listing?.final_rung, 'preview');
444
+ assert.equal(listing?.delivery_cause, 'directory listing attached to content delivery');
445
+ const companion = rows.find((row) => row.name === 't9-workspace-companion');
446
+ assert.equal(companion?.final_rung, 'content');
447
+ assert.equal(companion?.delivery_cause, 'memory-read companion of content delivery');
448
+ });
449
+ test('boot content triggers memory-read companions to a deduplicated fixpoint', () => {
450
+ const boot = renderKnowledgeBlock(newNode(), freshTranscriptTarget());
451
+ for (const body of [B.companionSource, B.companionA, B.companionB]) {
452
+ assert.equal(boot.split(body).length - 1, 1, `content companion ${body} delivers once; got:\n${boot}`);
453
+ }
454
+ // A companion is read-equivalent in its own right: the directory it fronts
455
+ // discloses its members exactly as the doc that routed to it did.
456
+ assert.match(boot, /member-1 {2}# read when: When the companion member applies\./, `a content companion exposes its own directory's members; got:\n${boot}`);
457
+ });
458
+ test('one body delivered across both boot partitions renders exactly once', () => {
459
+ const node = newNode();
460
+ const subject = assembleNodeSubject(node);
461
+ assert.ok(subject !== null, 'the fixture node assembles a subject');
462
+ const state = emptyContextExposureState();
463
+ // The live boot order: the knowledge block and the preference splice share
464
+ // ONE exposure state, so a knowledge body already delivered as a preference's
465
+ // companion must not render again as knowledge prose.
466
+ const preferences = renderPreferencesForSubject(subject, exposureTarget(state, 'system')).block;
467
+ const knowledge = renderKnowledgeForSubject(subject, node, exposureTarget(state, 'transcript'));
468
+ assert.ok(preferences.includes(B.prefRouter), `the routing preference delivers its body; got:\n${preferences}`);
469
+ assert.ok(preferences.includes(B.crossPartition), `its companion delivers with it; got:\n${preferences}`);
470
+ const occurrences = (preferences.split(B.crossPartition).length - 1) + (knowledge.split(B.crossPartition).length - 1);
471
+ assert.equal(occurrences, 1, `the companion body renders once across both partitions; got:\n${preferences}\n---\n${knowledge}`);
245
472
  });
246
473
  test('a node-local doc floors to name only without boot entries', () => {
247
474
  const node = newNode('explore');
@@ -312,6 +539,17 @@ test('workspace-open clamps each project store the same way boot does', () => {
312
539
  assert.ok(!opened.includes('t9-pnone-doc'), 'the none-capped project delivers nothing at workspace-open');
313
540
  assert.ok(!opened.includes(B.sharedFromPnone), 'nor its side of a name collision');
314
541
  });
542
+ test('workspace-open answers its listings from the workspace it opened, not the ambient one', () => {
543
+ const node = newNode();
544
+ const subject = assembleNodeSubject(node);
545
+ assert.ok(subject !== null, 'the fixture node assembles a subject');
546
+ // The process cwd is `ppreview`; this workspace is outside the profile
547
+ // manifest, so ONLY the opened target's corpus knows its store at all.
548
+ const opened = renderWorkspaceOpenDocsForSubject(subject, join(home, 'poutside'), profileId, freshTranscriptTarget());
549
+ assert.ok(opened.includes(B.outsideGuide), `the opened workspace's guide delivers its body; got:\n${opened}`);
550
+ assert.match(opened, /<memory-listing dir="poutside\/t9-ws-guide">/, `its directory listing renders from that workspace's corpus; got:\n${opened}`);
551
+ assert.match(opened, /\[\[poutside\/t9-ws-guide\/member-1\]\]: When the outside guide member applies\./, `the listing carries that directory's own members; got:\n${opened}`);
552
+ });
315
553
  test('memory list and find record synthetic pages without crediting matched documents', async () => {
316
554
  const node = newNode();
317
555
  process.env['CRTR_NODE_ID'] = node;
@@ -350,6 +588,31 @@ test('memory list and find record synthetic pages without crediting matched docu
350
588
  delete process.env['CRTR_NODE_ID'];
351
589
  }
352
590
  });
591
+ test('an explicit read after boot content adds no duplicate directory attachments', async () => {
592
+ const node = newNode('developer');
593
+ process.env['CRTR_NODE_ID'] = node;
594
+ try {
595
+ const state = loadContextExposureState(node);
596
+ renderKnowledgeBlock(node, exposureTarget(state, 'transcript'));
597
+ saveContextExposureState(node, state);
598
+ const result = (await readLeaf.run({
599
+ name: 'dev/code-craft',
600
+ kind: undefined,
601
+ frontmatter: false,
602
+ scope: undefined,
603
+ dir: undefined,
604
+ }));
605
+ assert.equal(result.content.trim(), B.codeCraftIndex, 'the repeated read returns its body without a new attachment block');
606
+ assert.equal(result.listing.length, 10, 'the deliberate directory read still returns its immediate members');
607
+ const afterRead = loadContextExposureState(node);
608
+ for (let i = 1; i <= 10; i++) {
609
+ assert.ok(documentExposedAtOrAbove(afterRead, join(home, '.crouter', 'memory', 'dev', 'code-craft', `member-${i}.md`), `BODY-T9-CODE-CRAFT-MEMBER-${i}`, 'preview'), `boot content registered member ${i} at preview before the explicit read`);
610
+ }
611
+ }
612
+ finally {
613
+ delete process.env['CRTR_NODE_ID'];
614
+ }
615
+ });
353
616
  test('an explicit `crtr memory read` returns and records the full body a cap withheld', async () => {
354
617
  const node = newNode();
355
618
  process.env['CRTR_NODE_ID'] = node;
@@ -18,6 +18,7 @@ import { createNode, getNode, subscribe } from '../canvas/canvas.js';
18
18
  import { closeDb } from '../canvas/db.js';
19
19
  import { apiSocketPath } from '../canvas/paths.js';
20
20
  import { brokerCapReached, resetFleetForTests } from '../runtime/fleet.js';
21
+ import { buildPiArgv } from '../runtime/launch.js';
21
22
  import { relaunchRoot } from '../runtime/reset.js';
22
23
  import { bindTestFleet } from './helpers/fleet.js';
23
24
  let home;
@@ -106,6 +107,22 @@ test('relaunchRoot parks the old root done and mints a fresh active root, reapin
106
107
  assert.equal(getNode('child')?.status, 'canceled', 'descendant child canceled');
107
108
  assert.equal(getNode('grand')?.status, 'canceled', 'descendant grandchild canceled');
108
109
  });
110
+ test('relaunchRoot preserves an explicit model override as an exact replay recipe', async () => {
111
+ const exactModel = 'anthropic/claude-opus-5:high';
112
+ createNode(node('root', {
113
+ parent: null,
114
+ lifecycle: 'resident',
115
+ model_override: exactModel,
116
+ launch: { extensions: [], env: {}, model: exactModel, modelExact: true },
117
+ }));
118
+ const { deps } = makeDeps(4242);
119
+ const result = await relaunchRoot('root', deps);
120
+ assert.ok(result);
121
+ const replacement = getNode(result.newNodeId);
122
+ assert.equal(replacement?.launch?.model, exactModel);
123
+ assert.equal(replacement?.launch?.modelExact, true, 'the replacement cannot route away from the override');
124
+ assert.ok(buildPiArgv(replacement, {}).argv.includes('--crtr-model-exact'), 'broker replay preserves the exact-model contract');
125
+ });
109
126
  test('relaunchRoot on a boot failure leaves the old root + descendants fully intact', async () => {
110
127
  // The mint-and-boot-FIRST ordering: a launch failure (pid=null) must occur
111
128
  // BEFORE the old root is parked or its descendants reaped.