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.
- package/CHANGELOG.md +74 -0
- package/README.md +113 -487
- package/SECURITY.md +71 -0
- package/THIRD-PARTY-LICENSES.md +349 -0
- package/osint-agent-package/README.md +1 -1
- package/package.json +14 -3
- package/readme-international/README.de.md +18 -8
- package/readme-international/README.es.md +19 -9
- package/readme-international/README.fr.md +18 -8
- package/src/bmad-plus/agents/agent-architect-dev/SKILL.md +11 -13
- package/src/bmad-plus/agents/agent-orchestrator/SKILL.md +148 -9
- package/src/bmad-plus/agents/agent-quality/SKILL.md +41 -11
- package/src/bmad-plus/data/role-triggers.yaml +19 -0
- package/src/bmad-plus/module-help.csv +1 -0
- package/src/bmad-plus/module.yaml +1 -0
- package/src/bmad-plus/packs/pack-dev-studio/README.md +133 -141
- package/src/bmad-plus/packs/pack-dev-studio/SKILL.md +49 -0
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/analyst-agent.md +35 -60
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/document-project.md +59 -59
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/domain-research.md +55 -93
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/market-research.md +58 -93
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/prfaq.md +55 -132
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/product-brief.md +63 -78
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/tech-writer-agent.md +54 -69
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/technical-research.md +54 -93
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/architect-agent.md +32 -60
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/create-architecture.md +67 -71
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/create-epics-stories.md +61 -90
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/generate-project-context.md +56 -78
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/implementation-readiness.md +55 -88
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-01-init.md +20 -153
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-01b-continue.md +20 -173
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-02-context.md +14 -220
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-03-starter.md +20 -329
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-04-decisions.md +15 -314
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-05-patterns.md +15 -355
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-06-structure.md +15 -375
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-07-validation.md +14 -357
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-08-complete.md +13 -78
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/checkpoint-preview.md +52 -65
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-01-gather-context.md +14 -81
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-02-review.md +14 -31
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-03-triage.md +14 -45
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-04-present.md +13 -128
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review.md +61 -87
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/correct-course.md +55 -298
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/create-story.md +54 -426
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-agent.md +48 -69
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story-checklist.md +24 -80
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story.md +64 -482
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/investigate.md +50 -184
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/qa-e2e-tests.md +57 -173
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/quick-dev.md +56 -108
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/retrospective.md +54 -1509
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/sprint-planning.md +54 -296
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/sprint-status.md +41 -283
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/create-prd.md +58 -18
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/create-ux-design.md +103 -72
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/edit-prd.md +55 -27
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/pm-agent.md +34 -60
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/prd.md +46 -87
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/steps/step-01-init.md +10 -0
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/ux-designer-agent.md +30 -60
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/validate-prd.md +57 -27
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/advanced-elicitation.md +47 -138
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/adversarial-review.md +48 -34
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/bmad-help.md +51 -68
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/brainstorming.md +46 -3
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/customize.md +68 -109
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/distillator.md +53 -174
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/edge-case-hunter.md +39 -53
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/editorial-review-prose.md +45 -83
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/editorial-review-structure.md +45 -176
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/index-docs.md +45 -63
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/party-mode.md +53 -124
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/shard-doc.md +44 -100
- package/src/bmad-plus/packs/pack-dev-studio/dev-studio-orchestrator.md +56 -116
- package/src/bmad-plus/packs/pack-dev-studio/shared/architecture-decision-template.md +49 -12
- package/src/bmad-plus/packs/pack-dev-studio/shared/bwml-spec.md +51 -328
- package/src/bmad-plus/packs/pack-dev-studio/shared/catalog.json +489 -0
- package/src/bmad-plus/packs/pack-dev-studio/shared/execution.md +69 -0
- package/src/bmad-plus/packs/pack-dev-studio/shared/module-help.csv +39 -32
- package/src/bmad-plus/packs/pack-dev-studio/upstream-sync.yaml +85 -14
- package/src/bmad-plus/packs/pack-memory/README.md +35 -4
- package/src/bmad-plus/packs/pack-memory/memory-orchestrator.md +33 -6
- package/src/bmad-plus/packs/pack-memory/shared/karpathy-guardrails.md +3 -3
- package/src/bmad-plus/packs/pack-memory/shared/memory-protocol.md +27 -3
- package/src/bmad-plus/packs/pack-memory/zecher-agent.md +18 -2
- package/src/bmad-plus/packs/pack-seo/SKILL.md +27 -1
- package/src/bmad-plus/packs/pack-seo/seo-chief.md +16 -1
- package/src/bmad-plus/packs/pack-seo/seo-judge.md +12 -0
- package/src/bmad-plus/packs/pack-seo/seo-scout.md +12 -0
- package/src/bmad-plus/skills/bmad-plus-autopilot/SKILL.md +49 -12
- package/src/bmad-plus/skills/bmad-plus-parallel/SKILL.md +17 -3
- package/src/bmad-plus/skills/bmad-plus-sync/SKILL.md +76 -65
- package/src/bmad-plus/skills/bmad-plus-uat/SKILL.md +144 -0
- package/src/bmad-plus/skills/bmad-plus-uat/ref/uat-results.schema.json +60 -0
- package/src/bmad-plus/skills/bmad-plus-uat/ref/uat-spec.schema.json +121 -0
- package/src/bmad-plus/skills/bmad-plus-uat/ref/uat-triage.schema.json +60 -0
- package/src/bmad-plus/skills/bmad-plus-uat/template/page.html +552 -0
- package/src/bmad-plus/skills/bmad-plus-uat/template/strings.json +362 -0
- package/src/bmad-plus/skills/dev-studio/SKILL.md +19 -0
- package/tools/build/check-install-contract.js +367 -17
- package/tools/build/generate.js +229 -40
- package/tools/build/generated-adapters/.codex/AGENTS.md +1 -1
- package/tools/build/generated-adapters/.cursor/rules/bmad-plus.mdc +1 -1
- package/tools/build/generated-adapters/.opencode/AGENTS.md +1 -1
- package/tools/build/generated-adapters/AGENTS.md +1 -1
- package/tools/build/generated-adapters/CLAUDE.md +1 -1
- package/tools/build/generated-adapters/CONVENTIONS.md +1 -1
- package/tools/build/generated-adapters/GEMINI.md +1 -1
- package/tools/build/pack-delivery.js +78 -0
- package/tools/cli/bmad-plus-cli.js +15 -12
- package/tools/cli/commands/doctor.js +50 -189
- package/tools/cli/commands/install.js +22 -3
- package/tools/cli/commands/memory-journal-cmd.js +119 -19
- package/tools/cli/commands/nexus.js +111 -0
- package/tools/cli/commands/studio.js +68 -0
- package/tools/cli/commands/uat.js +389 -0
- package/tools/cli/lib/README-memory-journal.md +19 -8
- package/tools/cli/lib/installation-health.js +366 -0
- package/tools/cli/lib/memory-journal.js +0 -0
- package/tools/cli/lib/memory-outcomes.js +293 -0
- package/tools/cli/lib/memory-store.js +139 -0
- package/tools/cli/lib/nexus-process.js +377 -0
- package/tools/cli/lib/nexus.js +1532 -0
- package/tools/cli/lib/pack-copy.js +39 -11
- package/tools/cli/lib/packs.js +134 -11
- package/tools/cli/lib/python-health.js +233 -0
- package/tools/cli/lib/python-provision.js +2 -2
- package/tools/cli/lib/studio.js +310 -0
- package/tools/cli/lib/uat.js +869 -0
- package/tools/maintain/upstream-candidate.js +456 -0
- package/tools/release/publication-content.js +903 -0
- 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(
|
|
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(
|
|
101
|
-
|
|
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
|
|
104
|
-
|
|
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
|
|
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 !==
|
|
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
|
-
|
|
168
|
-
|
|
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
|
-
|
|
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',
|
|
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
|
-
|
|
225
|
-
|
|
495
|
+
const expectedExitCode = 0;
|
|
496
|
+
const diagnosis = run(
|
|
497
|
+
process.execPath,
|
|
498
|
+
[
|
|
226
499
|
path.join(packageRoot, 'tools', 'cli', 'bmad-plus-cli.js'),
|
|
227
|
-
'
|
|
228
|
-
|
|
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(
|
|
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 {
|