@cohortapp/agent-sdk 2.12.0 → 2.14.0

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 (129) hide show
  1. package/bin/maestro.mjs +6 -2
  2. package/docs/guides/front-door-session.md +86 -0
  3. package/lib/cli/design.mjs +185 -0
  4. package/lib/cli/design.test.mjs +270 -0
  5. package/lib/cli/global-setup-extras.mjs +44 -0
  6. package/lib/cli/global-setup-extras.test.mjs +95 -0
  7. package/lib/cli/session.mjs +11 -1
  8. package/lib/cli/session.test.mjs +17 -6
  9. package/lib/collective/global-config.mjs +5 -0
  10. package/lib/collective/global-config.test.mjs +5 -0
  11. package/lib/collective/vendor-skills.mjs +305 -0
  12. package/lib/collective/vendor-skills.test.mjs +306 -0
  13. package/lib/design/design-md.mjs +793 -0
  14. package/lib/design/design-md.test.mjs +318 -0
  15. package/lib/design/fixtures/DESIGN.golden.md +238 -0
  16. package/lib/design/fixtures/PRODUCT.golden.md +67 -0
  17. package/lib/design/fixtures/foundation.json +133 -0
  18. package/lib/design/refresh-gate.mjs +154 -0
  19. package/lib/design/refresh-gate.test.mjs +144 -0
  20. package/lib/design/write.mjs +275 -0
  21. package/lib/design/write.test.mjs +241 -0
  22. package/lib/prompts/parallelism.mjs +79 -0
  23. package/lib/prompts/parallelism.test.mjs +177 -0
  24. package/lib/telemetry/collect.mjs +357 -5
  25. package/lib/telemetry/collect.test.mjs +285 -0
  26. package/package.json +1 -1
  27. package/plugins/maestro-skills/plugin.json +4 -0
  28. package/plugins/maestro-skills/skills/cohort-design.md +153 -0
  29. package/plugins/maestro-skills/vendor/emilkowalski/LICENSE +21 -0
  30. package/plugins/maestro-skills/vendor/emilkowalski/UPSTREAM.json +70 -0
  31. package/plugins/maestro-skills/vendor/emilkowalski/skills/animate/RECIPES.md +324 -0
  32. package/plugins/maestro-skills/vendor/emilkowalski/skills/animate/SKILL.md +199 -0
  33. package/plugins/maestro-skills/vendor/emilkowalski/skills/animation-vocabulary/SKILL.md +173 -0
  34. package/plugins/maestro-skills/vendor/emilkowalski/skills/apple-design/SKILL.md +282 -0
  35. package/plugins/maestro-skills/vendor/emilkowalski/skills/emil-design-eng/SKILL.md +674 -0
  36. package/plugins/maestro-skills/vendor/emilkowalski/skills/find-animation-opportunities/SKILL.md +132 -0
  37. package/plugins/maestro-skills/vendor/emilkowalski/skills/improve-animations/AUDIT.md +115 -0
  38. package/plugins/maestro-skills/vendor/emilkowalski/skills/improve-animations/PLAN-TEMPLATE.md +73 -0
  39. package/plugins/maestro-skills/vendor/emilkowalski/skills/improve-animations/SKILL.md +101 -0
  40. package/plugins/maestro-skills/vendor/emilkowalski/skills/prototype/PICKER.md +197 -0
  41. package/plugins/maestro-skills/vendor/emilkowalski/skills/prototype/SKILL.md +90 -0
  42. package/plugins/maestro-skills/vendor/emilkowalski/skills/review-animations/SKILL.md +112 -0
  43. package/plugins/maestro-skills/vendor/emilkowalski/skills/review-animations/STANDARDS.md +187 -0
  44. package/plugins/maestro-skills/vendor/impeccable/LICENSE +191 -0
  45. package/plugins/maestro-skills/vendor/impeccable/NOTICE.md +11 -0
  46. package/plugins/maestro-skills/vendor/impeccable/SKILL.md +86 -0
  47. package/plugins/maestro-skills/vendor/impeccable/UPSTREAM.json +201 -0
  48. package/plugins/maestro-skills/vendor/impeccable/agents/impeccable-asset-producer.md +42 -0
  49. package/plugins/maestro-skills/vendor/impeccable/agents/impeccable-documenter.md +29 -0
  50. package/plugins/maestro-skills/vendor/impeccable/agents/impeccable-finish-reviewer.md +43 -0
  51. package/plugins/maestro-skills/vendor/impeccable/agents/impeccable-manual-edit-applier.md +97 -0
  52. package/plugins/maestro-skills/vendor/impeccable/reference/adapt.md +312 -0
  53. package/plugins/maestro-skills/vendor/impeccable/reference/adapt.native.md +58 -0
  54. package/plugins/maestro-skills/vendor/impeccable/reference/android.md +46 -0
  55. package/plugins/maestro-skills/vendor/impeccable/reference/animate.md +89 -0
  56. package/plugins/maestro-skills/vendor/impeccable/reference/audit.md +136 -0
  57. package/plugins/maestro-skills/vendor/impeccable/reference/audit.native.md +139 -0
  58. package/plugins/maestro-skills/vendor/impeccable/reference/bolder.md +33 -0
  59. package/plugins/maestro-skills/vendor/impeccable/reference/clarify.md +94 -0
  60. package/plugins/maestro-skills/vendor/impeccable/reference/colorize.md +86 -0
  61. package/plugins/maestro-skills/vendor/impeccable/reference/craft-floor.md +44 -0
  62. package/plugins/maestro-skills/vendor/impeccable/reference/craft.md +5 -0
  63. package/plugins/maestro-skills/vendor/impeccable/reference/critique.md +806 -0
  64. package/plugins/maestro-skills/vendor/impeccable/reference/degraded/asset-producer.md +37 -0
  65. package/plugins/maestro-skills/vendor/impeccable/reference/degraded/documenter.md +24 -0
  66. package/plugins/maestro-skills/vendor/impeccable/reference/degraded/finish-reviewer.md +38 -0
  67. package/plugins/maestro-skills/vendor/impeccable/reference/degraded/manual-edit-applier.md +92 -0
  68. package/plugins/maestro-skills/vendor/impeccable/reference/delight.md +70 -0
  69. package/plugins/maestro-skills/vendor/impeccable/reference/distill.md +111 -0
  70. package/plugins/maestro-skills/vendor/impeccable/reference/doctor.md +54 -0
  71. package/plugins/maestro-skills/vendor/impeccable/reference/document.md +416 -0
  72. package/plugins/maestro-skills/vendor/impeccable/reference/extract.md +69 -0
  73. package/plugins/maestro-skills/vendor/impeccable/reference/harden.md +336 -0
  74. package/plugins/maestro-skills/vendor/impeccable/reference/hooks.md +111 -0
  75. package/plugins/maestro-skills/vendor/impeccable/reference/init.md +131 -0
  76. package/plugins/maestro-skills/vendor/impeccable/reference/ios.md +51 -0
  77. package/plugins/maestro-skills/vendor/impeccable/reference/layout.md +84 -0
  78. package/plugins/maestro-skills/vendor/impeccable/reference/live-setup.md +104 -0
  79. package/plugins/maestro-skills/vendor/impeccable/reference/live.md +325 -0
  80. package/plugins/maestro-skills/vendor/impeccable/reference/new-work.md +147 -0
  81. package/plugins/maestro-skills/vendor/impeccable/reference/onboard.md +234 -0
  82. package/plugins/maestro-skills/vendor/impeccable/reference/operate.md +61 -0
  83. package/plugins/maestro-skills/vendor/impeccable/reference/optimize.md +258 -0
  84. package/plugins/maestro-skills/vendor/impeccable/reference/overdrive.md +127 -0
  85. package/plugins/maestro-skills/vendor/impeccable/reference/polish.md +105 -0
  86. package/plugins/maestro-skills/vendor/impeccable/reference/quieter.md +99 -0
  87. package/plugins/maestro-skills/vendor/impeccable/reference/routing.md +24 -0
  88. package/plugins/maestro-skills/vendor/impeccable/reference/shape.md +59 -0
  89. package/plugins/maestro-skills/vendor/impeccable/reference/typeset.md +80 -0
  90. package/plugins/maestro-skills/vendor/impeccable/reference/visualize.md +46 -0
  91. package/plugins/maestro-skills/vendor/taste-skill/LICENSE +21 -0
  92. package/plugins/maestro-skills/vendor/taste-skill/UPSTREAM.json +37 -0
  93. package/plugins/maestro-skills/vendor/taste-skill/skills/minimalist-skill/SKILL.md +85 -0
  94. package/plugins/maestro-skills/vendor/taste-skill/skills/redesign-skill/SKILL.md +178 -0
  95. package/plugins/maestro-skills/vendor/taste-skill/skills/soft-skill/SKILL.md +98 -0
  96. package/plugins/maestro-skills/vendor/taste-skill/skills/taste-skill/SKILL.md +1206 -0
  97. package/plugins/maestro-skills/vendor/unlazy/LICENSE +21 -0
  98. package/plugins/maestro-skills/vendor/unlazy/SECURITY.md +72 -0
  99. package/plugins/maestro-skills/vendor/unlazy/SKILL.md +104 -0
  100. package/plugins/maestro-skills/vendor/unlazy/UPSTREAM.json +94 -0
  101. package/plugins/maestro-skills/vendor/unlazy/references/dispatch.md +82 -0
  102. package/plugins/maestro-skills/vendor/unlazy/references/gates.md +149 -0
  103. package/plugins/maestro-skills/vendor/unlazy/references/method.md +49 -0
  104. package/plugins/maestro-skills/vendor/unlazy/references/orchestration.md +107 -0
  105. package/plugins/maestro-skills/vendor/unlazy/references/parallel.md +133 -0
  106. package/plugins/maestro-skills/vendor/unlazy/references/token-economy.md +48 -0
  107. package/plugins/maestro-skills/vendor/unlazy/scripts/dispatch-check.mjs +139 -0
  108. package/plugins/maestro-skills/vendor/unlazy/scripts/gate-check.mjs +960 -0
  109. package/plugins/maestro-skills/vendor/unlazy/scripts/gate-lint.mjs +245 -0
  110. package/plugins/maestro-skills/vendor/unlazy/scripts/lib/check-supervisor.mjs +46 -0
  111. package/plugins/maestro-skills/vendor/unlazy/scripts/lib/dispatch.mjs +293 -0
  112. package/plugins/maestro-skills/vendor/unlazy/scripts/lib/gates.mjs +953 -0
  113. package/plugins/maestro-skills/vendor/unlazy/scripts/lib/process-tree.mjs +161 -0
  114. package/plugins/maestro-skills/vendor/unlazy/scripts/lib/regex-worker.mjs +9 -0
  115. package/plugins/maestro-skills/vendor/unlazy/templates/PLAN.md +116 -0
  116. package/plugins/maestro-skills/vendor/unlazy/templates/gates-leaf.md +51 -0
  117. package/plugins/maestro-skills/vendor/unlazy/templates/gates-node.md +51 -0
  118. package/scripts/ci/check-skill-packs.mjs +388 -0
  119. package/scripts/ci/check-skill-packs.test.mjs +495 -0
  120. package/scripts/ci/check.mjs +3 -0
  121. package/scripts/daemon/agent-daemon-design.test.mjs +238 -0
  122. package/scripts/daemon/agent-daemon.mjs +108 -0
  123. package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +61 -2
  124. package/scripts/daemon/cadence-consumer.mjs +46 -22
  125. package/scripts/daemon/prompt-builder.mjs +19 -3
  126. package/scripts/local-triggers/autoupdate.test.mjs +33 -3
  127. package/scripts/vendor/skill-packs.mjs +354 -0
  128. package/scripts/vendor/sync-skill-packs.mjs +242 -0
  129. package/scripts/vendor/sync-skill-packs.test.mjs +103 -0
@@ -0,0 +1,238 @@
1
+ /**
2
+ * agent-daemon-design.test.mjs — the daemon re-syncs the brand foundation into
3
+ * DESIGN.md + PRODUCT.md, polled every 15 minutes and rewritten when the
4
+ * foundation's version moves (WP-M7 mechanic 4).
5
+ *
6
+ * Drives the REAL `refreshDesignFoundation` with an injected
7
+ * `branding.getFoundation` and a fixed clock under a tmp AGENT_DIR — the same
8
+ * shape as `agent-daemon-board-mine.test.mjs`, including its `imapflow` loader
9
+ * stub, because importing the daemon module pulls the whole seat in.
10
+ *
11
+ * The four claims: the 15-minute gate bounds the reads; a moved version is
12
+ * detected and named on the poll that sees it; an un-enrolled seat writes
13
+ * nothing; and no failure of hq's can blank a DESIGN.md already on disk.
14
+ */
15
+
16
+ import { test, before } from "node:test";
17
+ import assert from "node:assert/strict";
18
+ import { register } from "node:module";
19
+ import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, existsSync, statSync, utimesSync, rmSync } from "node:fs";
20
+ import { join } from "node:path";
21
+ import { tmpdir } from "node:os";
22
+
23
+ const loaderSrc = `
24
+ export async function resolve(specifier, context, next) {
25
+ if (specifier === "imapflow") return { url: "imapflow-stub:main", shortCircuit: true };
26
+ return next(specifier, context);
27
+ }
28
+ export async function load(url, context, next) {
29
+ if (url === "imapflow-stub:main") return { format: "module", shortCircuit: true, source: "export class ImapFlow {}" };
30
+ return next(url, context);
31
+ }
32
+ `;
33
+ register(`data:text/javascript,${encodeURIComponent(loaderSrc)}`);
34
+
35
+ const AGENT_DIR = mkdtempSync(join(tmpdir(), "maestro-daemon-design-"));
36
+ process.env.AGENT_DIR = AGENT_DIR;
37
+ process.env.MAESTRO_DAEMON_NO_AUTOSTART = "1";
38
+
39
+ const NOW = Date.parse("2026-09-08T12:00:00Z");
40
+ const FIFTEEN_MIN = 15 * 60_000;
41
+ const DESIGN = join(AGENT_DIR, "DESIGN.md");
42
+ const PRODUCT = join(AGENT_DIR, "PRODUCT.md");
43
+ const STATE = join(AGENT_DIR, "state", "design", "foundation.json");
44
+
45
+ const FIXTURE = JSON.parse(readFileSync(new URL("../../lib/design/fixtures/foundation.json", import.meta.url), "utf8"));
46
+
47
+ const frame = (over = {}) => ({
48
+ ok: true,
49
+ result: {
50
+ foundation: FIXTURE,
51
+ version: { id: "bv_01HQZ", seq: 7, label: "V2.7" },
52
+ headVersionId: "bv_01HQZ",
53
+ isHead: true,
54
+ unversioned: false,
55
+ history: [],
56
+ ...over,
57
+ },
58
+ });
59
+
60
+ let daemon;
61
+ before(async () => {
62
+ mkdirSync(join(AGENT_DIR, "config"), { recursive: true });
63
+ mkdirSync(join(AGENT_DIR, "state", "sessions", "locks"), { recursive: true });
64
+ writeFileSync(
65
+ join(AGENT_DIR, "config", "org.yaml"),
66
+ "org:\n cohort:\n enabled: true\n base: https://os.example.test\n token: nlk_test_token\n orgId: org_test\n orgName: Northwind\n",
67
+ );
68
+ writeFileSync(join(AGENT_DIR, "config", "agent.json"), JSON.stringify({ firstName: "Nova", lastName: "Reyes" }));
69
+ const url = new URL("./agent-daemon.mjs", import.meta.url);
70
+ daemon = await import(`${url.href}?design=${Math.random()}`);
71
+ });
72
+
73
+ test("the first tick writes DESIGN.md + PRODUCT.md from the seat's own connection", async () => {
74
+ const calls = [];
75
+ const getFoundationImpl = async (params, o) => { calls.push({ params, o }); return frame(); };
76
+
77
+ const first = await daemon.refreshDesignFoundation({ now: NOW, getFoundationImpl });
78
+ assert.equal(first.refreshed, true);
79
+ assert.equal(first.reason, "never", "the gate's own words for a seat that had never synced");
80
+ assert.deepEqual(first.written, ["DESIGN.md", "PRODUCT.md"]);
81
+ assert.equal(first.version, "bv_01HQZ");
82
+
83
+ assert.equal(calls.length, 1);
84
+ assert.deepEqual(calls[0].params, { historyLimit: 0 });
85
+ assert.equal(calls[0].o.base, "https://os.example.test", "the daemon's org.yaml connection is what the read gets");
86
+ assert.equal(calls[0].o.orgId, "org_test");
87
+
88
+ assert.match(readFileSync(DESIGN, "utf8"), /^---\nname: "Northwind"\n/);
89
+ assert.match(readFileSync(PRODUCT, "utf8"), /# Northwind — product voice/);
90
+ assert.equal(statSync(DESIGN).mode & 0o777, 0o644);
91
+ assert.equal(JSON.parse(readFileSync(STATE, "utf8")).version, "bv_01HQZ");
92
+ });
93
+
94
+ test("a tick inside 15 minutes spends no hq call at all", async () => {
95
+ let calls = 0;
96
+ const getFoundationImpl = async () => { calls += 1; return frame(); };
97
+ const soon = await daemon.refreshDesignFoundation({ now: NOW + 14 * 60_000, getFoundationImpl });
98
+ assert.deepEqual(soon, { refreshed: false, reason: "fresh" });
99
+ assert.equal(calls, 0, "the gate is asked BEFORE the read, which is the point of it");
100
+ });
101
+
102
+ test("a tick at 15 minutes reads, finds nothing changed, and touches no file", async () => {
103
+ const old = new Date(NOW - 86_400_000);
104
+ utimesSync(DESIGN, old, old);
105
+ utimesSync(PRODUCT, old, old);
106
+ const before = [statSync(DESIGN).mtimeMs, statSync(PRODUCT).mtimeMs];
107
+
108
+ let calls = 0;
109
+ const at = await daemon.refreshDesignFoundation({ now: NOW + FIFTEEN_MIN, getFoundationImpl: async () => { calls += 1; return frame(); } });
110
+ assert.equal(calls, 1, "the interval is what bounds the reads — hq has no cheap etag for this");
111
+ assert.equal(at.refreshed, false, "an unchanged foundation is not a refresh");
112
+ assert.equal(at.reason, "interval");
113
+ assert.deepEqual([statSync(DESIGN).mtimeMs, statSync(PRODUCT).mtimeMs], before, "not one mtime moved");
114
+ });
115
+
116
+ test("a moved foundation is picked up on the next poll, and named 'version-changed' not 'interval'", async () => {
117
+ // The honest bound: hq exposes no cheap "has the foundation changed?" etag
118
+ // the way `integration.toolsetVersion` does, so a version change can only be
119
+ // SEEN by a read, and the 15-minute gate is what bounds the reads. What the
120
+ // gate's second call buys is a truthful reason — the daemon logs why this
121
+ // tick rewrote the file, and "the palette moved" is not "the timer fired".
122
+ const moved = JSON.parse(JSON.stringify(FIXTURE));
123
+ moved.colors[2].value = "#0f172a";
124
+ const r = await daemon.refreshDesignFoundation({
125
+ now: NOW + 2 * FIFTEEN_MIN,
126
+ getFoundationImpl: async () => frame({ foundation: moved, headVersionId: "bv_02", version: { id: "bv_02", seq: 8, label: "V2.8" } }),
127
+ });
128
+
129
+ assert.equal(r.refreshed, true);
130
+ assert.equal(r.reason, "version-changed", "not 'interval' — the gate's second call names the real cause");
131
+ assert.ok(r.written.includes("DESIGN.md"));
132
+ assert.match(readFileSync(DESIGN, "utf8"), /primary-moss: "#0f172a"/);
133
+ assert.match(readFileSync(DESIGN, "utf8"), /\*\*Version:\*\* V2\.8/);
134
+ assert.equal(JSON.parse(readFileSync(STATE, "utf8")).version, "bv_02");
135
+ });
136
+
137
+ test("a forced tick inside the interval still sees a version change for what it is", async () => {
138
+ const moved = JSON.parse(JSON.stringify(FIXTURE));
139
+ moved.colors[2].value = "#334155";
140
+ const r = await daemon.refreshDesignFoundation({
141
+ now: NOW + 2 * FIFTEEN_MIN + 60_000, // one minute later: the gate would say "fresh"
142
+ force: true,
143
+ getFoundationImpl: async () => frame({ foundation: moved, headVersionId: "bv_03", version: { id: "bv_03", seq: 9, label: "V2.9" } }),
144
+ });
145
+ assert.equal(r.reason, "version-changed");
146
+ assert.ok(r.written.includes("DESIGN.md"));
147
+ });
148
+
149
+ test("a fetch failure keeps the DESIGN.md already on disk — fail-open, never blanked", async () => {
150
+ const good = readFileSync(DESIGN, "utf8");
151
+ const old = new Date(NOW - 86_400_000);
152
+ utimesSync(DESIGN, old, old);
153
+ const mtime = statSync(DESIGN).mtimeMs;
154
+ const now = NOW + 4 * FIFTEEN_MIN;
155
+
156
+ const thrown = await daemon.refreshDesignFoundation({ now, getFoundationImpl: async () => { throw new Error("ECONNREFUSED"); } });
157
+ assert.deepEqual(thrown, { refreshed: false, reason: "fetch-failed" });
158
+
159
+ const errored = await daemon.refreshDesignFoundation({ now, getFoundationImpl: async () => ({ ok: false, error: { code: "UNAUTHORIZED", message: "bad token" } }) });
160
+ assert.deepEqual(errored, { refreshed: false, reason: "error:UNAUTHORIZED" });
161
+
162
+ const empty = await daemon.refreshDesignFoundation({ now, getFoundationImpl: async () => ({ ok: true, result: { foundation: null, headVersionId: null } }) });
163
+ assert.deepEqual(empty, { refreshed: false, reason: "no-foundation" });
164
+
165
+ assert.equal(readFileSync(DESIGN, "utf8"), good, "three failures, and the last-good file is byte-identical");
166
+ assert.equal(statSync(DESIGN).mtimeMs, mtime);
167
+ });
168
+
169
+ test("an org-disabled seat writes nothing and spends no call", async () => {
170
+ const dir = mkdtempSync(join(tmpdir(), "maestro-daemon-design-off-"));
171
+ try {
172
+ mkdirSync(join(dir, "config"), { recursive: true });
173
+ mkdirSync(join(dir, "state", "sessions", "locks"), { recursive: true });
174
+ writeFileSync(join(dir, "config", "org.yaml"), "org:\n cohort:\n enabled: false\n");
175
+ writeFileSync(join(dir, "config", "agent.json"), JSON.stringify({ firstName: "Nova" }));
176
+
177
+ const prev = process.env.AGENT_DIR;
178
+ process.env.AGENT_DIR = dir;
179
+ let off;
180
+ try {
181
+ const url = new URL("./agent-daemon.mjs", import.meta.url);
182
+ off = await import(`${url.href}?designoff=${Math.random()}`);
183
+ } finally {
184
+ process.env.AGENT_DIR = prev;
185
+ }
186
+
187
+ let calls = 0;
188
+ const r = await off.refreshDesignFoundation({ now: NOW, getFoundationImpl: async () => { calls += 1; return frame(); } });
189
+ assert.deepEqual(r, { refreshed: false, reason: "org-disabled" });
190
+ assert.equal(calls, 0);
191
+ assert.ok(!existsSync(join(dir, "DESIGN.md")));
192
+ assert.ok(!existsSync(join(dir, "state", "design", "foundation.json")));
193
+ } finally {
194
+ rmSync(dir, { recursive: true, force: true });
195
+ }
196
+ });
197
+
198
+ test("force skips the gate but nothing else — an unchanged foundation still writes nothing", async () => {
199
+ // Re-establish the on-disk files from the CURRENT head, then force a second
200
+ // read one second later: the gate would have said "fresh", the read happens
201
+ // anyway, and the byte comparison still refuses to touch the files.
202
+ const now = NOW + 5 * FIFTEEN_MIN;
203
+ await daemon.refreshDesignFoundation({ now, force: true, getFoundationImpl: async () => frame() });
204
+ let calls = 0;
205
+ const r = await daemon.refreshDesignFoundation({ now: now + 1_000, force: true, getFoundationImpl: async () => { calls += 1; return frame(); } });
206
+ assert.equal(calls, 1, "a forced tick reads even though the gate would have said 'fresh'");
207
+ assert.deepEqual(r.written, [], "and still writes nothing when the bytes already match");
208
+ assert.equal(r.refreshed, false);
209
+ });
210
+
211
+ test("a workspace that never saved a foundation gets nothing written — not hq's stock defaults", async () => {
212
+ // hq NEVER returns a null foundation: an org with no saved one reads back
213
+ // `dsDefaults()` (a complete, plausible palette that is nobody's brand) with
214
+ // `unversioned: true`. Writing it would ground the seat's design skills in
215
+ // another product's palette, with a generated file's authority behind it.
216
+ const dir = mkdtempSync(join(tmpdir(), "maestro-daemon-design-unver-"));
217
+ const prevAgent = process.env.AGENT_DIR;
218
+ try {
219
+ mkdirSync(join(dir, "config"), { recursive: true });
220
+ writeFileSync(
221
+ join(dir, "config", "org.yaml"),
222
+ "org:\n cohort:\n enabled: true\n base: https://os.example.test\n token: nlk_test_token\n orgId: org_test\n orgName: Northwind\n",
223
+ );
224
+ process.env.AGENT_DIR = dir;
225
+ const mod = await import(`${new URL("./agent-daemon.mjs", import.meta.url).href}?unver=${Math.random()}`);
226
+ const r = await mod.refreshDesignFoundation({
227
+ now: NOW,
228
+ getFoundationImpl: async () => frame({ headVersionId: null, version: null, unversioned: true }),
229
+ });
230
+ assert.deepEqual(r, { refreshed: false, reason: "unversioned" });
231
+ assert.ok(!existsSync(join(dir, "DESIGN.md")), "no DESIGN.md invented from stock defaults");
232
+ assert.ok(!existsSync(join(dir, "PRODUCT.md")));
233
+ assert.ok(!existsSync(join(dir, "state", "design", "foundation.json")));
234
+ } finally {
235
+ process.env.AGENT_DIR = prevAgent;
236
+ rmSync(dir, { recursive: true, force: true });
237
+ }
238
+ });
@@ -98,6 +98,9 @@ import { isEnabled as orgEnabled, loadOrgConfig, configFromAgent, electResponder
98
98
  // read it. The when/what is pure (lib/org/board-mine-cache.mjs); the I/O is here.
99
99
  import { shouldRefresh as boardMineShouldRefresh, renderRefresh as renderBoardMineRefresh, cacheTs as boardMineCacheTs, BOARD_MINE_REL } from "../../lib/org/board-mine-cache.mjs";
100
100
  import { writeJsonAtomic as writeJsonAtomicFile } from "../../lib/fs-atomic.mjs";
101
+ import { brandingGetFoundation } from "../../lib/org/client.mjs";
102
+ import { shouldRefreshDesign, foundationVersion, isUnversionedFoundation, DESIGN_REFRESH_INTERVAL_MS } from "../../lib/design/refresh-gate.mjs";
103
+ import { syncDesign, readFoundationState, workspaceName as designWorkspaceName } from "../../lib/design/write.mjs";
101
104
  import { remember as orgRemember } from "../../lib/org/knowledge.mjs";
102
105
  import { sweepSessionOutcomes, resultTextFromStdout } from "./session-outcomes.mjs";
103
106
  import { recordWorkStep as orgRecordWorkStep, cohortChannelId, cohortMessageId } from "../../lib/org/work-ledger.mjs";
@@ -1794,6 +1797,101 @@ export async function refreshBoardMineCache(deps = {}) {
1794
1797
  return { refreshed: true, reason: doc.stale ? "kept-previous" : "written", count: doc.items.length };
1795
1798
  }
1796
1799
 
1800
+ /**
1801
+ * Re-sync the workspace brand foundation into DESIGN.md + PRODUCT.md (WP-M7
1802
+ * mechanic 4), in the same idiom as `refreshIntegrationTools` in
1803
+ * `lib/org/mesh.mjs`: a cheap gated poll on the beat, fail-open end to end,
1804
+ * keeping the last-good files when hq is unreachable.
1805
+ *
1806
+ * WHY THIS EXISTS AT ALL. Every design skill on the seat is told to read
1807
+ * `$AGENT_ROOT/DESIGN.md` before it picks a colour or a typeface. A designer
1808
+ * working from a palette a human replaced last week is worse than one with no
1809
+ * palette, because the stale file looks authoritative — so the seat follows the
1810
+ * foundation rather than waiting for someone to re-run the CLI.
1811
+ *
1812
+ * THE GATE IS ASKED TWICE (see `lib/design/refresh-gate.mjs`): once before the
1813
+ * read, with the version unknown, to decide whether to spend a call at all
1814
+ * (15-minute floor); once after, with the version hq returned, to say honestly
1815
+ * WHY this tick refreshed. hq exposes no cheap "has the foundation changed"
1816
+ * etag the way `integration.toolsetVersion` does, so the version comparison can
1817
+ * only happen after a read and the interval is what bounds the reads.
1818
+ *
1819
+ * FAIL-OPEN, AND SILENT ABOUT NOTHING. An un-enrolled seat writes nothing and
1820
+ * says so; a fetch failure keeps the files that are already on disk and logs
1821
+ * the reason. The write itself is byte-idempotent (`syncDesign`), so a steady
1822
+ * foundation never touches an mtime — which is what keeps a git tree clean and
1823
+ * an editor's file watcher quiet on a seat that polls every 15 minutes.
1824
+ *
1825
+ * @param {{now?:number, intervalMs?:number, force?:boolean, getFoundationImpl?:Function, fetchImpl?:Function}} [deps]
1826
+ * @returns {Promise<{refreshed:boolean, reason:string, version?:string|null, written?:string[]}>}
1827
+ */
1828
+ export async function refreshDesignFoundation(deps = {}) {
1829
+ const now = Number.isFinite(deps.now) ? deps.now : Date.now();
1830
+ const intervalMs = Number.isFinite(deps.intervalMs) ? deps.intervalMs : DESIGN_REFRESH_INTERVAL_MS;
1831
+ const prior = readFoundationState(AGENT_REPO_DIR);
1832
+ const before = shouldRefreshDesign({
1833
+ lastVersion: prior ? prior.version : null,
1834
+ currentVersion: null,
1835
+ lastAt: prior ? prior.fetchedAt : null,
1836
+ now,
1837
+ intervalMs,
1838
+ });
1839
+ if (!deps.force && !before.refresh) return { refreshed: false, reason: "fresh" };
1840
+
1841
+ const cfg = daemonOrgCfg();
1842
+ if (!orgEnabled(cfg)) return { refreshed: false, reason: "org-disabled" };
1843
+ const conn = configFromAgent(cfg) || {};
1844
+
1845
+ let frame;
1846
+ try {
1847
+ frame = await (deps.getFoundationImpl || brandingGetFoundation)(
1848
+ { historyLimit: 0 },
1849
+ { base: conn.base, token: conn.token, orgId: conn.orgId, fetchImpl: deps.fetchImpl },
1850
+ );
1851
+ } catch (err) {
1852
+ console.warn(`[daemon] design sync: foundation read failed (${err && err.message ? err.message : err}) — keeping the DESIGN.md already on disk`);
1853
+ return { refreshed: false, reason: "fetch-failed" };
1854
+ }
1855
+ if (!frame || !frame.ok || !frame.result || typeof frame.result !== "object" || !frame.result.foundation) {
1856
+ const why = frame && frame.error && frame.error.code ? frame.error.code : "no-foundation";
1857
+ return { refreshed: false, reason: why === "no-foundation" ? "no-foundation" : `error:${why}` };
1858
+ }
1859
+ // A workspace that has never SAVED a foundation reads back hq's own product
1860
+ // defaults with `unversioned: true` — a complete palette that is nobody's
1861
+ // brand. Writing it would ground every design skill on the seat in the wrong
1862
+ // palette, with a generated file's authority behind it. Nothing is written
1863
+ // and nothing already on disk is disturbed; see
1864
+ // refresh-gate.mjs#isUnversionedFoundation.
1865
+ if (isUnversionedFoundation(frame.result)) return { refreshed: false, reason: "unversioned" };
1866
+
1867
+ // The second gate call: now that the version is known, say why this tick ran.
1868
+ const after = shouldRefreshDesign({
1869
+ lastVersion: prior ? prior.version : null,
1870
+ currentVersion: foundationVersion(frame.result),
1871
+ lastAt: prior ? prior.fetchedAt : null,
1872
+ now,
1873
+ intervalMs,
1874
+ });
1875
+
1876
+ let out;
1877
+ try {
1878
+ out = syncDesign({
1879
+ agentRoot: AGENT_REPO_DIR,
1880
+ result: frame.result,
1881
+ now,
1882
+ name: designWorkspaceName(cfg),
1883
+ source: "cohort · branding.getFoundation",
1884
+ });
1885
+ } catch (err) {
1886
+ console.warn(`[daemon] design sync: write failed (${err && err.message ? err.message : err})`);
1887
+ return { refreshed: false, reason: "write-failed" };
1888
+ }
1889
+ if (out.written.length) {
1890
+ console.log(`[daemon] design sync (${after.reason}) → wrote ${out.written.join(", ")}${out.version ? ` — foundation ${out.version}` : ""}`);
1891
+ }
1892
+ return { refreshed: out.written.length > 0, reason: after.reason, version: out.version, written: out.written };
1893
+ }
1894
+
1797
1895
  /** Longest completion note we will put on a board comment. */
1798
1896
  const COMPLETION_NOTE_CHARS = 1200;
1799
1897
 
@@ -2376,6 +2474,16 @@ async function main() {
2376
2474
  }
2377
2475
  }, POLL_INTERVAL);
2378
2476
 
2477
+ // Brand foundation → DESIGN.md + PRODUCT.md, every 15 min (WP-M7). Fired once
2478
+ // at boot so a seat that has never synced has its design files before the
2479
+ // first session opens, then on its own interval. Fail-open and never awaited:
2480
+ // the design docs are guidance, and no failure to fetch them may hold up the
2481
+ // poll loop.
2482
+ refreshDesignFoundation().catch((err) => console.error("[daemon] design sync error:", err.message));
2483
+ setInterval(() => {
2484
+ refreshDesignFoundation().catch((err) => console.error("[daemon] design sync error:", err.message));
2485
+ }, DESIGN_REFRESH_INTERVAL_MS);
2486
+
2379
2487
  // Fast reactive cohort scan — drains org-push-fetched DMs in ~2s instead of
2380
2488
  // waiting up to 60s for the full poll. Local YAML only; no API/rate-limit cost.
2381
2489
  setInterval(() => {
@@ -26,7 +26,8 @@ import { join, dirname } from "node:path";
26
26
 
27
27
  import { enqueueTick, listInbox, busDepth, getBusPaths } from "../../lib/cadence-bus.mjs";
28
28
  import { listHandoffs, handoffPaths, writeHandoff, ackHandoff, DEFAULT_HANDOFF_DEADLINE_MS, DEFAULT_HANDOFF_RETENTION_MS } from "../../lib/session/handoffs.mjs";
29
- import { startConsumer as startConsumerRaw } from "./cadence-consumer.mjs";
29
+ import { startConsumer as startConsumerRaw, renderCadencePromptBody } from "./cadence-consumer.mjs";
30
+ import { withParallelism, countParallelism, PARALLELISM_DIRECTIVE } from "../../lib/prompts/parallelism.mjs";
30
31
 
31
32
  const ADMIT_ALL = { admit: () => ({ decision: "ADMIT", reason: "test", snapshot: {} }), defaultDeps: () => ({}) };
32
33
  const RATE_OK = { checkRateLimit: () => ({ allowed: true, retryAt: null }), classifyStderr: () => false, recordRateLimit: () => {}, recordSuccess: () => {} };
@@ -85,7 +86,11 @@ test("live session: an escalate tick is handed to the session — handoff + rend
85
86
  assert.equal(open[0].mode, "escalate");
86
87
  assert.ok(open[0].deadlineAt, "carries a deadline");
87
88
  assert.ok(existsSync(open[0].promptPath), "the rendered prompt is on disk");
88
- assert.equal(readFileSync(open[0].promptPath, "utf-8"), "# daily-morning-brief for Nova", "identity tokens rendered");
89
+ assert.equal(
90
+ readFileSync(open[0].promptPath, "utf-8"),
91
+ withParallelism("# daily-morning-brief for Nova"),
92
+ "identity tokens rendered, and the handed prompt is byte-identical to what a sub-session would have been given",
93
+ );
89
94
  assert.equal(busDepth(root).inbox, 0);
90
95
  assert.equal(busDepth(root).claimed, 0);
91
96
  const done = processedEvents(root).find((e) => e.id === t.id);
@@ -332,3 +337,57 @@ test("handoff ledger hygiene: the sweep prunes done/ records and prompts past re
332
337
  assert.equal(existsSync(join(paths.prompts, "evt-done.md")), false);
333
338
  } finally { await consumer.stop(); await rmRoot(root); }
334
339
  });
340
+
341
+ // ── the parallelism directive on the escalate/guarded lane (WP-M7 mechanic 5) ──
342
+ //
343
+ // The spec puts the directive on "every escalate/guarded sub-session prompt".
344
+ // Cadence trigger prompts never pass through `prompt-builder.mjs#buildPrompt`
345
+ // — they are read straight off schedules/triggers/<name>.md — so this lane has
346
+ // its own seam (`renderCadencePromptBody`) and needs its own pins. Without it
347
+ // an `escalate` tick whose trigger asks for six independent audits spawns a
348
+ // sub-session with no standing permission to fan out.
349
+
350
+ test("escalate: the spawned sub-session prompt leads with the parallelism directive, exactly once", async () => {
351
+ const root = await makeAgentRoot();
352
+ plantPrompt(root, "daily-morning-brief", "# daily-morning-brief for {{agent.firstName}}\n\nAudit six things.");
353
+ const spawns = [];
354
+ const consumer = startConsumer({
355
+ agentRoot: root,
356
+ frontDoorState: NOT_LIVE,
357
+ spawnSession: async (o) => { spawns.push(o); return { ok: true, exit_code: 0, duration_ms: 1 }; },
358
+ });
359
+ try {
360
+ enqueueTick({ cadence: "daily-morning-brief", source: "launchd", agentRoot: root });
361
+ await consumer.tickOnce();
362
+ assert.equal(spawns.length, 1);
363
+ // The injected spawner is handed the promptPath, not the body — so assert
364
+ // on the seam the real spawner uses to turn that path into argv.
365
+ const body = renderCadencePromptBody(root, readFileSync(join(root, spawns[0].promptPath), "utf-8"));
366
+ assert.ok(body.startsWith(PARALLELISM_DIRECTIVE), "the directive leads");
367
+ assert.equal(countParallelism(body), 1);
368
+ assert.ok(body.endsWith("# daily-morning-brief for Nova\n\nAudit six things."), "and the trigger body follows, tokens rendered");
369
+ } finally { await consumer.stop(); await rmRoot(root); }
370
+ });
371
+
372
+ test("a trigger that already carries the directive is not given a second copy", () => {
373
+ const body = renderCadencePromptBody("/nonexistent-agent-root", withParallelism("# already-primed"));
374
+ assert.equal(countParallelism(body), 1, "idempotent — a repeated directive reads as emphasis and erodes the file-scope rule");
375
+ assert.equal(body, withParallelism("# already-primed"));
376
+ });
377
+
378
+ test("the handed-off prompt and the spawned prompt are the same bytes", async () => {
379
+ const root = await makeAgentRoot();
380
+ const raw = "# weekly-strategic-memo for {{agent.firstName}}";
381
+ plantPrompt(root, "weekly-strategic-memo", raw);
382
+ const consumer = startConsumer({ agentRoot: root, frontDoorState: LIVE, spawnSession: async () => ({ ok: true, exit_code: 0 }) });
383
+ try {
384
+ enqueueTick({ cadence: "weekly-strategic-memo", source: "launchd", agentRoot: root });
385
+ await consumer.tickOnce();
386
+ const open = listHandoffs(root);
387
+ assert.equal(open.length, 1);
388
+ const handed = readFileSync(open[0].promptPath, "utf-8");
389
+ assert.equal(handed, renderCadencePromptBody(root, raw), "one seam, two lanes");
390
+ assert.equal(countParallelism(handed), 1);
391
+ assert.ok(handed.startsWith(PARALLELISM_DIRECTIVE));
392
+ } finally { await consumer.stop(); await rmRoot(root); }
393
+ });
@@ -100,6 +100,7 @@ import { obligationAllowedUnderPosture } from "../../lib/plan/compile.mjs";
100
100
  import { isHumanLaneCadence } from "../../lib/cadences.mjs";
101
101
  import { sessionPermissionArgs } from "../../lib/session-permissions.mjs";
102
102
  import { renderTemplate, buildContext } from "../../lib/render.mjs";
103
+ import { withParallelism } from "../../lib/prompts/parallelism.mjs";
103
104
  // Model router (opt-in). A `schema_version: 2` config routes each cadence
104
105
  // sub-session's model through resolveChain (SPEC §6.2): the RouteDecision
105
106
  // supplies the --model flag, the session-retarget env (third-party backends),
@@ -315,6 +316,43 @@ export function routeCadenceSpawn(agentRoot, cadence) {
315
316
  }
316
317
  }
317
318
 
319
+ /**
320
+ * A cadence trigger prompt, as the sub-session actually receives it.
321
+ *
322
+ * TWO transforms, and BOTH lanes must apply them or the two lanes disagree
323
+ * about what the same tick asked for:
324
+ *
325
+ * 1. `{{agent.*}}` / `{{company.*}}` identity tokens are rendered from
326
+ * config/agent.json + config/company.json (lib/render.mjs), so a trigger
327
+ * shipped generically in the package resolves to THIS seat. No-op on
328
+ * token-free text.
329
+ * 2. The fleet parallelism directive LEADS the body (WP-M7 mechanic 5). An
330
+ * escalate/guarded tick is exactly the sub-session the directive exists
331
+ * for — a trigger that asks for six independent audits has, without it,
332
+ * no standing permission to fan out, and runs them one at a time. The
333
+ * inbox/backlog lane gets this from `prompt-builder.mjs#buildPrompt`;
334
+ * cadence prompts never pass through that builder, which is how this lane
335
+ * was missed. `withParallelism` is idempotent, so a trigger file that
336
+ * already carries the text keeps exactly one copy.
337
+ *
338
+ * Pure apart from the two config reads; a missing/invalid config/agent.json
339
+ * leaves the body's tokens verbatim rather than failing the tick.
340
+ *
341
+ * @param {string} agentRoot
342
+ * @param {string} body the raw trigger prompt as read from disk
343
+ * @returns {string}
344
+ */
345
+ export function renderCadencePromptBody(agentRoot, body) {
346
+ let out = typeof body === "string" ? body : "";
347
+ try {
348
+ const agentCfg = JSON.parse(readFileSync(join(agentRoot, "config/agent.json"), "utf-8"));
349
+ let companyCfg = {};
350
+ try { companyCfg = JSON.parse(readFileSync(join(agentRoot, "config/company.json"), "utf-8")); } catch { /* optional */ }
351
+ out = renderTemplate(out, buildContext(agentCfg, null, companyCfg));
352
+ } catch { /* no/invalid config/agent.json — leave body verbatim */ }
353
+ return withParallelism(out);
354
+ }
355
+
318
356
  /**
319
357
  * Spawn a sub-session running the cadence's trigger prompt and resolve
320
358
  * with { exit_code, durationMs, stderr_tail }. Reads the prompt at call
@@ -339,17 +377,9 @@ function realSpawnSession({ agentRoot, cadence, promptPath, timeoutMs, log }) {
339
377
  return;
340
378
  }
341
379
 
342
- // Render {{agent.*}} / {{company.*}} identity tokens at spawn time so framework
343
- // triggers stay generic in the package and resolve to THIS agent + company
344
- // (see lib/render.mjs). Synchronous, from config/agent.json + config/company.json.
345
- // No-op on token-free text, so this is behaviour-preserving for any not-yet-
346
- // tokenised trigger.
347
- try {
348
- const agentCfg = JSON.parse(readFileSync(join(agentRoot, "config/agent.json"), "utf-8"));
349
- let companyCfg = {};
350
- try { companyCfg = JSON.parse(readFileSync(join(agentRoot, "config/company.json"), "utf-8")); } catch { /* optional */ }
351
- body = renderTemplate(body, buildContext(agentCfg, null, companyCfg));
352
- } catch { /* no/invalid config/agent.json — leave body verbatim */ }
380
+ // Identity tokens + the parallelism directive, at spawn time — one seam,
381
+ // shared with the front-door handoff lane. See renderCadencePromptBody.
382
+ body = renderCadencePromptBody(agentRoot, body);
353
383
 
354
384
  const bin = resolveClaudeBin();
355
385
  // Permission scoping (audit H1). sessionPermissionArgs() defaults to the
@@ -592,20 +622,14 @@ export function startConsumer(opts = {}) {
592
622
  }
593
623
 
594
624
  /**
595
- * Render a cadence prompt the way realSpawnSession does ({{agent.*}} /
596
- * {{company.*}} tokens from config/agent.json + config/company.json) and
597
- * land it under state/session/handoffs/prompts/<tickId>.md so the session
598
- * reads exactly what a sub-session would have been given.
625
+ * Render a cadence prompt the way realSpawnSession does — the SAME
626
+ * `renderCadencePromptBody`, so the handed-off prompt is byte-identical to
627
+ * what a sub-session would have been given, parallelism directive included —
628
+ * and land it under state/session/handoffs/prompts/<tickId>.md.
599
629
  */
600
630
  function renderHandoffPrompt(tickId, promptPath) {
601
631
  const fullPrompt = join(agentRoot, promptPath);
602
- let body = readFileSync(fullPrompt, "utf-8");
603
- try {
604
- const agentCfg = JSON.parse(readFileSync(join(agentRoot, "config/agent.json"), "utf-8"));
605
- let companyCfg = {};
606
- try { companyCfg = JSON.parse(readFileSync(join(agentRoot, "config/company.json"), "utf-8")); } catch { /* optional */ }
607
- body = renderTemplate(body, buildContext(agentCfg, null, companyCfg));
608
- } catch { /* no/invalid config/agent.json — leave body verbatim */ }
632
+ const body = renderCadencePromptBody(agentRoot, readFileSync(fullPrompt, "utf-8"));
609
633
  const out = join(handoffPaths(agentRoot).prompts, `${tickId}.md`);
610
634
  writeFileAtomic(out, body);
611
635
  return out;
@@ -12,6 +12,7 @@ import { wrapExternalContent } from "../../lib/security/external-content.mjs";
12
12
  import { isEnabled as orgEnabled } from "../../lib/org/client.mjs";
13
13
  import { recall as orgRecall } from "../../lib/org/knowledge.mjs";
14
14
  import { outcomeSourceShareable } from "./session-outcomes.mjs";
15
+ import { withParallelism } from "../../lib/prompts/parallelism.mjs";
15
16
 
16
17
  const AGENT_REPO_DIR = process.env.AGENT_DIR || join(new URL(".", import.meta.url).pathname, "../..");
17
18
 
@@ -852,16 +853,23 @@ export async function buildPrompt(item, classResult, options = {}) {
852
853
  const parts = [];
853
854
 
854
855
  // 1. Persona block (cached) — rendered from config/agent.json, which the
855
- // Cohort enrollment pull keeps current. Leads the prompt and outranks the
856
+ // Cohort enrollment pull keeps current. Leads the BODY and outranks the
856
857
  // CLAUDE.md preamble below, because a scaffolded CLAUDE.md carries
857
858
  // "identity not yet configured" placeholder prose that would otherwise
858
859
  // contradict a perfectly good org record.
860
+ //
861
+ // THE PRECEDENCE SENTENCE NAMES ITSELF, NOT A POSITION. It used to say "the
862
+ // section above … anything below", which was true only while the persona was
863
+ // the first thing in the prompt. The parallelism directive is prepended at
864
+ // the end of this function (§10) and now leads, so "above" had two candidate
865
+ // referents and the directive sat outside the rule entirely — above it, not
866
+ // below. A self-naming sentence stays true wherever the block ends up.
859
867
  const persona = loadPersona();
860
868
  if (persona) {
861
869
  parts.push(persona);
862
870
  parts.push("");
863
871
  parts.push(
864
- "The section above is your identity, resolved from your organisation's record of you. Where anything below conflicts with it, the section above wins.",
872
+ "This section is your identity, resolved from your organisation's record of you. Where anything else in this prompt conflicts with it, this section wins.",
865
873
  );
866
874
  parts.push("");
867
875
  }
@@ -1025,5 +1033,13 @@ export async function buildPrompt(item, classResult, options = {}) {
1025
1033
  // 9. Audit instruction
1026
1034
  parts.push(`Log all actions taken to logs/audit/${date}-actions.jsonl as a JSON line with: timestamp, action_type, target, result, session context.`);
1027
1035
 
1028
- return parts.join("\n");
1036
+ // 10. The parallelism directive (WP-M7 mechanic 5). Every prompt this builder
1037
+ // produces is handed to a sub-session that may plan and dispatch work of its
1038
+ // own, and nothing else in the prompt ever says that fanning out is allowed —
1039
+ // which is why sessions handed six independent tasks ran them one at a time.
1040
+ // It LEADS the prompt because it governs how the session plans, before it has
1041
+ // read a word of the item. `withParallelism` is idempotent, so a prompt that
1042
+ // already carries it (composed by a caller, or handed on to `session spawn`,
1043
+ // which applies the same seam) is returned with exactly one copy.
1044
+ return withParallelism(parts.join("\n"));
1029
1045
  }