bmad-plus 0.13.0 → 0.16.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 (135) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/README.md +113 -487
  3. package/SECURITY.md +71 -0
  4. package/THIRD-PARTY-LICENSES.md +349 -0
  5. package/osint-agent-package/README.md +1 -1
  6. package/package.json +14 -3
  7. package/readme-international/README.de.md +18 -8
  8. package/readme-international/README.es.md +19 -9
  9. package/readme-international/README.fr.md +18 -8
  10. package/src/bmad-plus/agents/agent-architect-dev/SKILL.md +11 -13
  11. package/src/bmad-plus/agents/agent-orchestrator/SKILL.md +148 -9
  12. package/src/bmad-plus/agents/agent-quality/SKILL.md +41 -11
  13. package/src/bmad-plus/data/role-triggers.yaml +19 -0
  14. package/src/bmad-plus/module-help.csv +1 -0
  15. package/src/bmad-plus/module.yaml +1 -0
  16. package/src/bmad-plus/packs/pack-dev-studio/README.md +133 -141
  17. package/src/bmad-plus/packs/pack-dev-studio/SKILL.md +49 -0
  18. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/analyst-agent.md +35 -60
  19. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/document-project.md +59 -59
  20. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/domain-research.md +55 -93
  21. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/market-research.md +58 -93
  22. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/prfaq.md +55 -132
  23. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/product-brief.md +63 -78
  24. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/tech-writer-agent.md +54 -69
  25. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/technical-research.md +54 -93
  26. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/architect-agent.md +32 -60
  27. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/create-architecture.md +67 -71
  28. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/create-epics-stories.md +61 -90
  29. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/generate-project-context.md +56 -78
  30. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/implementation-readiness.md +55 -88
  31. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-01-init.md +20 -153
  32. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-01b-continue.md +20 -173
  33. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-02-context.md +14 -220
  34. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-03-starter.md +20 -329
  35. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-04-decisions.md +15 -314
  36. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-05-patterns.md +15 -355
  37. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-06-structure.md +15 -375
  38. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-07-validation.md +14 -357
  39. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-08-complete.md +13 -78
  40. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/checkpoint-preview.md +52 -65
  41. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-01-gather-context.md +14 -81
  42. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-02-review.md +14 -31
  43. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-03-triage.md +14 -45
  44. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-04-present.md +13 -128
  45. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review.md +61 -87
  46. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/correct-course.md +55 -298
  47. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/create-story.md +54 -426
  48. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-agent.md +48 -69
  49. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story-checklist.md +24 -80
  50. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story.md +64 -482
  51. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/investigate.md +50 -184
  52. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/qa-e2e-tests.md +57 -173
  53. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/quick-dev.md +56 -108
  54. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/retrospective.md +54 -1509
  55. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/sprint-planning.md +54 -296
  56. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/sprint-status.md +41 -283
  57. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/create-prd.md +58 -18
  58. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/create-ux-design.md +103 -72
  59. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/edit-prd.md +55 -27
  60. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/pm-agent.md +34 -60
  61. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/prd.md +46 -87
  62. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/steps/step-01-init.md +10 -0
  63. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/ux-designer-agent.md +30 -60
  64. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/validate-prd.md +57 -27
  65. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/advanced-elicitation.md +47 -138
  66. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/adversarial-review.md +48 -34
  67. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/bmad-help.md +51 -68
  68. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/brainstorming.md +46 -3
  69. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/customize.md +68 -109
  70. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/distillator.md +53 -174
  71. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/edge-case-hunter.md +39 -53
  72. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/editorial-review-prose.md +45 -83
  73. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/editorial-review-structure.md +45 -176
  74. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/index-docs.md +45 -63
  75. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/party-mode.md +53 -124
  76. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/shard-doc.md +44 -100
  77. package/src/bmad-plus/packs/pack-dev-studio/dev-studio-orchestrator.md +56 -116
  78. package/src/bmad-plus/packs/pack-dev-studio/shared/architecture-decision-template.md +49 -12
  79. package/src/bmad-plus/packs/pack-dev-studio/shared/bwml-spec.md +51 -328
  80. package/src/bmad-plus/packs/pack-dev-studio/shared/catalog.json +489 -0
  81. package/src/bmad-plus/packs/pack-dev-studio/shared/execution.md +69 -0
  82. package/src/bmad-plus/packs/pack-dev-studio/shared/module-help.csv +39 -32
  83. package/src/bmad-plus/packs/pack-dev-studio/upstream-sync.yaml +85 -14
  84. package/src/bmad-plus/packs/pack-memory/README.md +35 -4
  85. package/src/bmad-plus/packs/pack-memory/memory-orchestrator.md +33 -6
  86. package/src/bmad-plus/packs/pack-memory/shared/karpathy-guardrails.md +3 -3
  87. package/src/bmad-plus/packs/pack-memory/shared/memory-protocol.md +27 -3
  88. package/src/bmad-plus/packs/pack-memory/zecher-agent.md +18 -2
  89. package/src/bmad-plus/packs/pack-seo/SKILL.md +27 -1
  90. package/src/bmad-plus/packs/pack-seo/seo-chief.md +16 -1
  91. package/src/bmad-plus/packs/pack-seo/seo-judge.md +12 -0
  92. package/src/bmad-plus/packs/pack-seo/seo-scout.md +12 -0
  93. package/src/bmad-plus/skills/bmad-plus-autopilot/SKILL.md +49 -12
  94. package/src/bmad-plus/skills/bmad-plus-parallel/SKILL.md +17 -3
  95. package/src/bmad-plus/skills/bmad-plus-sync/SKILL.md +76 -65
  96. package/src/bmad-plus/skills/bmad-plus-uat/SKILL.md +144 -0
  97. package/src/bmad-plus/skills/bmad-plus-uat/ref/uat-results.schema.json +60 -0
  98. package/src/bmad-plus/skills/bmad-plus-uat/ref/uat-spec.schema.json +121 -0
  99. package/src/bmad-plus/skills/bmad-plus-uat/ref/uat-triage.schema.json +60 -0
  100. package/src/bmad-plus/skills/bmad-plus-uat/template/page.html +552 -0
  101. package/src/bmad-plus/skills/bmad-plus-uat/template/strings.json +362 -0
  102. package/src/bmad-plus/skills/dev-studio/SKILL.md +19 -0
  103. package/tools/build/check-install-contract.js +367 -17
  104. package/tools/build/generate.js +229 -40
  105. package/tools/build/generated-adapters/.codex/AGENTS.md +1 -1
  106. package/tools/build/generated-adapters/.cursor/rules/bmad-plus.mdc +1 -1
  107. package/tools/build/generated-adapters/.opencode/AGENTS.md +1 -1
  108. package/tools/build/generated-adapters/AGENTS.md +1 -1
  109. package/tools/build/generated-adapters/CLAUDE.md +1 -1
  110. package/tools/build/generated-adapters/CONVENTIONS.md +1 -1
  111. package/tools/build/generated-adapters/GEMINI.md +1 -1
  112. package/tools/build/pack-delivery.js +78 -0
  113. package/tools/cli/bmad-plus-cli.js +15 -12
  114. package/tools/cli/commands/doctor.js +50 -189
  115. package/tools/cli/commands/install.js +22 -3
  116. package/tools/cli/commands/memory-journal-cmd.js +119 -19
  117. package/tools/cli/commands/nexus.js +111 -0
  118. package/tools/cli/commands/studio.js +68 -0
  119. package/tools/cli/commands/uat.js +389 -0
  120. package/tools/cli/lib/README-memory-journal.md +19 -8
  121. package/tools/cli/lib/installation-health.js +366 -0
  122. package/tools/cli/lib/memory-journal.js +0 -0
  123. package/tools/cli/lib/memory-outcomes.js +293 -0
  124. package/tools/cli/lib/memory-store.js +139 -0
  125. package/tools/cli/lib/nexus-process.js +377 -0
  126. package/tools/cli/lib/nexus.js +1532 -0
  127. package/tools/cli/lib/pack-copy.js +39 -11
  128. package/tools/cli/lib/packs.js +134 -11
  129. package/tools/cli/lib/python-health.js +233 -0
  130. package/tools/cli/lib/python-provision.js +2 -2
  131. package/tools/cli/lib/studio.js +310 -0
  132. package/tools/cli/lib/uat.js +869 -0
  133. package/tools/maintain/upstream-candidate.js +456 -0
  134. package/tools/release/publication-content.js +903 -0
  135. package/tools/release/supply-chain.js +282 -0
@@ -47,7 +47,10 @@ function sameSelection(actual, expected, label) {
47
47
  }
48
48
 
49
49
  /** Compare the installed bytes, normalizing only Windows/Unix line endings. */
50
- function verifyInstallation(projectDir, { packs, tools, derived, ideConfigs, renderFiles, requireInventory = false }) {
50
+ function verifyInstallation(
51
+ projectDir,
52
+ { packs, tools, derived, ideConfigs, renderFiles, requireInventory = false }
53
+ ) {
51
54
  const manifest = JSON.parse(
52
55
  fs.readFileSync(path.join(projectDir, '_bmad', '.bmad-plus-install.json'), 'utf8')
53
56
  );
@@ -97,17 +100,88 @@ function verifyInstallation(projectDir, { packs, tools, derived, ideConfigs, ren
97
100
  assert.equal(manifest.fileInventory?.schemaVersion, 1, 'Missing file inventory');
98
101
  const { collectManagedFiles } = require('../cli/lib/update-transaction');
99
102
  const managedFiles = collectManagedFiles({ projectDir, packs });
100
- assert.deepEqual(Object.keys(manifest.fileInventory.files).sort(), managedFiles.map(entry => entry.file).sort(),
101
- 'Inventory does not cover exactly the selected framework files');
103
+ assert.deepEqual(
104
+ Object.keys(manifest.fileInventory.files).sort(),
105
+ managedFiles.map((entry) => entry.file).sort(),
106
+ 'Inventory does not cover exactly the selected framework files'
107
+ );
102
108
  for (const { file } of managedFiles) {
103
- const actualHash = crypto.createHash('sha256').update(fs.readFileSync(resolveWithin(projectDir, file))).digest('hex');
104
- assert.equal(manifest.fileInventory.files[file], actualHash, `Inventory differs from installed bytes: ${file}`);
109
+ const actualHash = crypto
110
+ .createHash('sha256')
111
+ .update(fs.readFileSync(resolveWithin(projectDir, file)))
112
+ .digest('hex');
113
+ assert.equal(
114
+ manifest.fileInventory.files[file],
115
+ actualHash,
116
+ `Inventory differs from installed bytes: ${file}`
117
+ );
105
118
  }
106
119
  }
107
120
  return files.length;
108
121
  }
109
122
 
110
- function run(command, args, { cwd, env, timeout = 60000 }) {
123
+ function verifyDiagnostics(report, { packs, tools, derived }) {
124
+ assert.equal(report.schemaVersion, 1, 'Unsupported diagnostic report');
125
+ assert.equal(report.versions.installed, derived.product.version);
126
+ assert.equal(report.versions.cli, derived.product.version);
127
+ assert.equal(report.versions.published, null, 'Doctor must not invent npm freshness evidence');
128
+ sameSelection(
129
+ report.packs.map(({ id }) => id),
130
+ packs,
131
+ 'diagnostic packs'
132
+ );
133
+ sameSelection(
134
+ report.integrations.map(({ tool }) => tool),
135
+ tools,
136
+ 'diagnostic integrations'
137
+ );
138
+ for (const integration of report.integrations) {
139
+ assert.deepEqual(
140
+ integration,
141
+ {
142
+ tool: integration.tool,
143
+ ...derived.diagnostics.integrations[integration.tool],
144
+ ownership: 'unchanged',
145
+ hostVerified: false,
146
+ },
147
+ 'An installed adapter must not imply verified host execution'
148
+ );
149
+ }
150
+ const expectedErrors = [];
151
+ assert.deepEqual(
152
+ report.checks
153
+ .filter(({ status }) => status === 'error')
154
+ .map(({ code, path: file }) => `${code}:${file}`)
155
+ .sort(),
156
+ expectedErrors.sort(),
157
+ 'Every advertised pack must install without delivery errors'
158
+ );
159
+ assert.equal(report.summary.errors, expectedErrors.length);
160
+ assert.equal(report.inventory.status, 'complete');
161
+ assert(report.inventory.trackedFiles > 0, 'Doctor did not inspect the ownership inventory');
162
+ assert.equal(
163
+ report.checks.filter(({ code }) => code === 'file.unchanged').length,
164
+ report.inventory.trackedFiles,
165
+ 'Every freshly installed managed file must have matching ownership'
166
+ );
167
+ assert(!report.checks.some(({ code }) => ['file.modified', 'file.unowned'].includes(code)));
168
+ for (const runtime of report.runtimes.filter(({ runtime }) => runtime === 'python')) {
169
+ assert.equal(
170
+ runtime.imports,
171
+ 'not-verified',
172
+ 'Presence of requirements cannot prove Python imports'
173
+ );
174
+ assert.equal(runtime.environment, 'missing', 'Contract checks must not provision Python');
175
+ }
176
+ const expectedStatus = expectedErrors.length
177
+ ? 'error'
178
+ : report.summary.warnings
179
+ ? 'warning'
180
+ : 'ok';
181
+ assert.equal(report.status, expectedStatus);
182
+ }
183
+
184
+ function run(command, args, { cwd, env, timeout = 60000, expectedExitCode = 0 }) {
111
185
  const result = spawnSync(command, args, {
112
186
  cwd,
113
187
  env,
@@ -117,7 +191,7 @@ function run(command, args, { cwd, env, timeout = 60000 }) {
117
191
  windowsHide: true,
118
192
  stdio: ['ignore', 'pipe', 'pipe'],
119
193
  });
120
- if (result.error || result.status !== 0) {
194
+ if (result.error || result.status !== expectedExitCode) {
121
195
  const reason = result.error
122
196
  ? result.error.message
123
197
  : `exit ${result.status}, signal ${result.signal || 'none'}`;
@@ -164,24 +238,221 @@ function packAndExtract(tempRoot, env) {
164
238
  ],
165
239
  { cwd: REPO_ROOT, env, timeout: 120000 }
166
240
  );
167
- const packed = JSON.parse(output);
168
- assert(Array.isArray(packed) && packed.length === 1, 'npm pack must produce exactly one package');
241
+ // npm 11 reports a list, npm 12 an object keyed by package name.
242
+ const reported = JSON.parse(output);
243
+ const packed = Array.isArray(reported) ? reported : Object.values(reported);
244
+ assert(
245
+ packed.length === 1 && typeof packed[0]?.filename === 'string',
246
+ 'npm pack must produce exactly one package'
247
+ );
169
248
  const archive = resolveWithin(destination, packed[0].filename);
170
- const entries = run('tar', ['-tzf', archive], { cwd: destination, env }).trim().split(/\r?\n/);
249
+ // The archive already sits in cwd, and it is named relatively on purpose: GNU tar
250
+ // reads a Windows "C:\..." argument as a remote host and refuses to open it.
251
+ const archiveName = path.basename(archive);
252
+ const entries = run('tar', ['-tzf', archiveName], { cwd: destination, env })
253
+ .trim()
254
+ .split(/\r?\n/);
171
255
  for (const entry of entries) {
172
256
  assert(entry.startsWith('package/'), `Unexpected npm archive entry: ${entry}`);
173
257
  resolveWithin(destination, entry);
174
258
  }
175
- run('tar', ['-xzf', archive, '-C', destination], { cwd: destination, env });
259
+ run('tar', ['-xzf', archiveName, '-C', '.'], { cwd: destination, env });
176
260
  const packageRoot = path.join(destination, 'package');
177
261
  assert(
178
262
  !fs.existsSync(path.join(packageRoot, 'registry.yaml')),
179
263
  'npm contract must work without registry.yaml'
180
264
  );
181
265
  assert(fs.statSync(packageRoot).isDirectory(), 'npm package directory is missing');
266
+ for (const file of ['LICENSE', 'THIRD-PARTY-LICENSES.md', 'SECURITY.md']) {
267
+ assert(fs.statSync(path.join(packageRoot, file)).isFile(), `npm archive is missing ${file}`);
268
+ }
269
+ assert(
270
+ !fs.existsSync(path.join(packageRoot, 'src/bmad-plus/packs/pack-seo/scripts/__pycache__')),
271
+ 'npm archive must exclude local Python caches'
272
+ );
182
273
  return packageRoot;
183
274
  }
184
275
 
276
+ function verifyNexusConsumer(packageRoot, projectDir, env) {
277
+ const invoke = (args) =>
278
+ JSON.parse(
279
+ run(
280
+ process.execPath,
281
+ [
282
+ path.join(packageRoot, 'tools/cli/bmad-plus-cli.js'),
283
+ 'nexus',
284
+ ...args,
285
+ '--directory',
286
+ projectDir,
287
+ '--json',
288
+ ],
289
+ { cwd: projectDir, env }
290
+ )
291
+ );
292
+ const input = (name, value) => {
293
+ const file = path.join(projectDir, name);
294
+ fs.writeFileSync(file, JSON.stringify(value));
295
+ return file;
296
+ };
297
+ const instruction = '.agents/skills/agent-orchestrator/SKILL.md';
298
+ assert(
299
+ fs
300
+ .readFileSync(path.join(projectDir, instruction), 'utf8')
301
+ .includes('Durable host-managed attempts')
302
+ );
303
+ fs.mkdirSync(path.join(projectDir, 'checks'));
304
+ fs.mkdirSync(path.join(projectDir, 'result'));
305
+ fs.writeFileSync(
306
+ path.join(projectDir, 'checks/accepted.cjs'),
307
+ "const fs=require('node:fs');const assert=require('node:assert/strict');assert.equal(fs.readFileSync('result/value.txt','utf8'),'accepted');\n"
308
+ );
309
+ const plan = input('nexus-plan.json', {
310
+ id: 'consumer',
311
+ tasks: [
312
+ {
313
+ id: 'work',
314
+ objective: 'Verify a real relocated npm/source consumer',
315
+ scope: ['result'],
316
+ artifacts: ['result/value.txt'],
317
+ resources: [instruction, 'checks/accepted.cjs'],
318
+ checks: [{ id: 'actual-output', command: 'node', args: ['checks/accepted.cjs'] }],
319
+ },
320
+ ],
321
+ });
322
+ invoke(['create', '--plan', plan]);
323
+ const backend = { kind: 'host', id: 'consumer-check', sessionId: 'observed-attempt' };
324
+ const started = invoke([
325
+ 'start',
326
+ 'consumer',
327
+ 'work',
328
+ '--input',
329
+ input('nexus-host.json', { backend }),
330
+ ]);
331
+ const attemptId = started.tasks[0].attempts[0].id;
332
+ fs.writeFileSync(path.join(projectDir, 'result/value.txt'), 'accepted');
333
+ // A separate CLI process reconciles an interrupted host without repeating its work.
334
+ invoke([
335
+ 'reconcile',
336
+ 'consumer',
337
+ 'work',
338
+ '--input',
339
+ input('nexus-observation.json', {
340
+ attemptId,
341
+ backend,
342
+ ownerStopped: true,
343
+ outcome: 'completed',
344
+ summary: 'Test host stopped after writing its artifact; independent checker follows.',
345
+ }),
346
+ ]);
347
+ const checked = invoke(['verify', 'consumer', 'work']);
348
+ assert.equal(checked.tasks[0].attempts[0].verification.status, 'passed');
349
+ const accepted = invoke(['accept', 'consumer', 'work']);
350
+ assert.equal(accepted.tasks[0].integration, 'accepted');
351
+ assert.equal(accepted.tasks[0].integrationReceipt.gitOperation, null);
352
+ const observed = invoke(['inspect', 'consumer']);
353
+ assert.equal(observed.observations[0].evidenceEligible, true);
354
+ assert.equal(observed.observations[0].stale, false);
355
+
356
+ fs.mkdirSync(path.join(projectDir, 'processed'));
357
+ fs.writeFileSync(path.join(projectDir, 'checks/task.txt'), 'Write the supervised result.\n');
358
+ fs.writeFileSync(
359
+ path.join(projectDir, 'checks/worker.cjs'),
360
+ "require('node:fs').writeFileSync('processed/value.txt','supervised');\n"
361
+ );
362
+ fs.writeFileSync(
363
+ path.join(projectDir, 'checks/process.cjs'),
364
+ "require('node:assert/strict').equal(require('node:fs').readFileSync('processed/value.txt','utf8'),'supervised');\n"
365
+ );
366
+ invoke([
367
+ 'create',
368
+ '--plan',
369
+ input('process-plan.json', {
370
+ id: 'process-consumer',
371
+ tasks: [
372
+ {
373
+ id: 'work',
374
+ objective: 'Verify the packaged foreground process backend',
375
+ scope: ['processed'],
376
+ artifacts: ['processed/value.txt'],
377
+ resources: [instruction, 'checks/process.cjs'],
378
+ checks: [{ id: 'process-output', command: 'node', args: ['checks/process.cjs'] }],
379
+ execution: {
380
+ adapter: 'command',
381
+ command: 'node',
382
+ args: ['checks/worker.cjs'],
383
+ resources: ['checks/worker.cjs'],
384
+ input: 'checks/task.txt',
385
+ },
386
+ },
387
+ ],
388
+ }),
389
+ ]);
390
+ const launched = invoke(['launch', 'process-consumer', 'work']);
391
+ const processAttempt = launched.tasks[0].attempts[0];
392
+ assert.equal(processAttempt.process.receipt.outcome, 'completed');
393
+ invoke([
394
+ 'collect',
395
+ 'process-consumer',
396
+ 'work',
397
+ '--input',
398
+ input('collect.json', {
399
+ attemptId: processAttempt.id,
400
+ backend: processAttempt.backend,
401
+ }),
402
+ ]);
403
+ assert.equal(
404
+ invoke(['verify', 'process-consumer', 'work']).tasks[0].attempts[0].verification.status,
405
+ 'passed'
406
+ );
407
+ assert.equal(invoke(['accept', 'process-consumer', 'work']).tasks[0].integration, 'accepted');
408
+
409
+ const mem = (args) =>
410
+ JSON.parse(
411
+ run(
412
+ process.execPath,
413
+ [
414
+ path.join(packageRoot, 'tools/cli/bmad-plus-cli.js'),
415
+ 'mem',
416
+ ...args,
417
+ '--directory',
418
+ projectDir,
419
+ '--json',
420
+ ],
421
+ { cwd: projectDir, env }
422
+ )
423
+ );
424
+ const memoryFile = '.agents/memory/patterns.md';
425
+ fs.mkdirSync(path.dirname(path.join(projectDir, memoryFile)), { recursive: true });
426
+ fs.appendFileSync(
427
+ path.join(projectDir, memoryFile),
428
+ '\n### Consumer supervision evidence\nVerify the supervised artifact before accepting process work.\n'
429
+ );
430
+ input('memory-observation.json', {
431
+ runId: 'process-consumer',
432
+ taskId: 'work',
433
+ memory: { file: memoryFile, heading: 'Consumer supervision evidence' },
434
+ scope: ['processed'],
435
+ interpretation:
436
+ 'Consumer fixture links this note to the accepted result; causality is unmeasured.',
437
+ });
438
+ mem(['observe', '--input', 'memory-observation.json']);
439
+ const recall = () =>
440
+ mem([
441
+ 'recall',
442
+ 'Consumer supervision evidence',
443
+ '--ranking',
444
+ 'evidence',
445
+ '--context-scope',
446
+ 'processed',
447
+ ]);
448
+ assert(recall().results.some((entry) => entry.ref === 'Consumer supervision evidence'));
449
+ fs.writeFileSync(path.join(projectDir, 'processed/value.txt'), 'changed after acceptance');
450
+ assert(
451
+ !recall().results.some((entry) => entry.ref === 'Consumer supervision evidence'),
452
+ 'Packaged memory must reject evidence after its accepted artifact changes'
453
+ );
454
+ }
455
+
185
456
  function runMatrix(packageRoot, variant, tempRoot, env, reference) {
186
457
  const allPacks = Object.keys(reference.packDefinitions).filter(
187
458
  (id) => !reference.packDefinitions[id].disabled
@@ -221,13 +492,82 @@ function runMatrix(packageRoot, variant, tempRoot, env, reference) {
221
492
  { cwd: projectDir, env }
222
493
  );
223
494
  files += verifyInstallation(projectDir, { ...reference, packs: selection.packs, tools });
224
- if (tools.length === allTools.length) {
225
- const output = run(process.execPath, [
495
+ const expectedExitCode = 0;
496
+ const diagnosis = run(
497
+ process.execPath,
498
+ [
226
499
  path.join(packageRoot, 'tools', 'cli', 'bmad-plus-cli.js'),
227
- 'update-check', '--offline', '--json', '--directory', projectDir,
228
- ], { cwd: projectDir, env });
500
+ 'doctor',
501
+ '--json',
502
+ '--directory',
503
+ projectDir,
504
+ ],
505
+ { cwd: projectDir, env, expectedExitCode }
506
+ );
507
+ verifyDiagnostics(JSON.parse(diagnosis), {
508
+ packs: selection.packs,
509
+ tools,
510
+ derived: reference.derived,
511
+ variant,
512
+ });
513
+ if (installations === 1) verifyNexusConsumer(packageRoot, projectDir, env);
514
+ if (selection.packs.includes('dev-studio')) {
515
+ const list = JSON.parse(
516
+ run(
517
+ process.execPath,
518
+ [
519
+ path.join(packageRoot, 'tools/cli/bmad-plus-cli.js'),
520
+ 'studio',
521
+ 'list',
522
+ '--directory',
523
+ projectDir,
524
+ '--json',
525
+ ],
526
+ { cwd: projectDir, env }
527
+ )
528
+ );
529
+ assert.equal(list.workflows.length, reference.derived.packs['dev-studio'].workflowCount);
530
+ const prepared = JSON.parse(
531
+ run(
532
+ process.execPath,
533
+ [
534
+ path.join(packageRoot, 'tools/cli/bmad-plus-cli.js'),
535
+ 'studio',
536
+ 'prepare',
537
+ 'create-ux-design',
538
+ '--request',
539
+ 'Design the sign-in flow',
540
+ '--directory',
541
+ projectDir,
542
+ '--json',
543
+ ],
544
+ { cwd: projectDir, env }
545
+ )
546
+ );
547
+ assert.equal(prepared.status, 'ready');
548
+ assert.equal(prepared.executed, false);
549
+ assert(prepared.instructions.some((item) => item.path.endsWith('/create-ux-design.md')));
550
+ assert(!fs.existsSync(path.join(projectDir, prepared.outputPath)));
551
+ }
552
+ if (tools.length === allTools.length) {
553
+ const output = run(
554
+ process.execPath,
555
+ [
556
+ path.join(packageRoot, 'tools', 'cli', 'bmad-plus-cli.js'),
557
+ 'update-check',
558
+ '--offline',
559
+ '--json',
560
+ '--directory',
561
+ projectDir,
562
+ ],
563
+ { cwd: projectDir, env }
564
+ );
229
565
  const checked = JSON.parse(output);
230
- assert.equal(checked.status, 'unknown', 'Offline check must not claim the latest release');
566
+ assert.equal(
567
+ checked.status,
568
+ 'unknown',
569
+ 'Offline check must not claim the latest release'
570
+ );
231
571
  assert.equal(checked.updateAvailable, null);
232
572
  assert.equal(checked.canAutoApply, false);
233
573
  }
@@ -271,9 +611,19 @@ function main() {
271
611
  };
272
612
  const source = runMatrix(REPO_ROOT, 'source', tempRoot, env, reference);
273
613
  const packageRoot = packAndExtract(tempRoot, env);
614
+ const { checkPythonDelivery } = require('./pack-delivery');
615
+ const { loadRegistry } = require('./generate');
616
+ assert.deepEqual(
617
+ checkPythonDelivery(loadRegistry(), {
618
+ packageRoot,
619
+ packageJson: JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8')),
620
+ }),
621
+ [],
622
+ 'Declared Python resources must exist in the real npm archive'
623
+ );
274
624
  const packaged = runMatrix(packageRoot, 'npm', tempRoot, env, reference);
275
625
  console.log(
276
- `Install contract passed: ${source.installations + packaged.installations} installs, ${source.files + packaged.files} files compared (source + npm without registry.yaml).`
626
+ `Install contract passed: ${source.installations + packaged.installations} installs and doctor JSON reports, ${source.files + packaged.files} files compared (source + npm without registry.yaml).`
277
627
  );
278
628
  return 0;
279
629
  } finally {