bmad-method-test-architecture-enterprise 3.0.1 → 3.1.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/.claude-plugin/marketplace.json +1 -1
- package/cli/lib/evaluate/check.js +44 -5
- package/cli/lib/evaluate/corpus-index.js +10 -1
- package/cli/lib/evaluate/discovery/candidate.js +699 -0
- package/cli/lib/evaluate/discovery/candidate.schema.json +563 -0
- package/cli/lib/evaluate/discovery/generator.js +954 -0
- package/cli/lib/evaluate/discovery/report.js +239 -0
- package/cli/lib/evaluate/discovery/review.js +569 -0
- package/package.json +9 -6
- package/skills/bmod-tea/bmod.toml +1 -1
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"name": "bmad-method-test-architecture-enterprise",
|
|
32
32
|
"source": "./",
|
|
33
33
|
"description": "Master Test Architect module for quality strategy, test generation in red and expand modes with automatic run-and-heal, combined framework and CI setup, CI/CD quality gates, and structured testing education. Existing CI and ATDD customizations can be migrated to the new skill names. Part of the BMad Method ecosystem.",
|
|
34
|
-
"version": "3.0
|
|
34
|
+
"version": "3.1.0",
|
|
35
35
|
"author": {
|
|
36
36
|
"name": "Murat K Ozcan (TEA Creator) & Brian (BMad) Madison"
|
|
37
37
|
},
|
|
@@ -15,6 +15,10 @@
|
|
|
15
15
|
* regular file held by the evaluation folder, its committed bytes disagree with the recorded digest, or
|
|
16
16
|
* they digest to something other than the contract's `sourceSpecDigest` (contract-source freshness, Story
|
|
17
17
|
* 2.2: a requirements change the contract has not absorbed blocks the pull request).
|
|
18
|
+
* - `candidate-placement`: a `*.candidate.json` file sits anywhere in the folder outside its top-level `discovery/` directory
|
|
19
|
+
* (the lane exploratory candidates live in, which no scored artifact reads), so a candidate cannot be digested into the corpus,
|
|
20
|
+
* the evaluator layer, the policy or the baseline. A development check leaves `corpus/held-out/` unopened, as it does for the
|
|
21
|
+
* corpus index.
|
|
18
22
|
* - `runtime-owned-field`: a committed probe carries a field the runtime writes.
|
|
19
23
|
* - `mutation-operator`: a mutation is not `replace-exact` with exactly one occurrence.
|
|
20
24
|
* - `provisioned-target`: a mutation's `targetArtifact` sits inside a provisioned directory, which every
|
|
@@ -195,7 +199,7 @@ const {
|
|
|
195
199
|
|
|
196
200
|
/** The skill runner's infrastructure exit codes (`cli/skill-runner.js`), which a registry entry for it must declare. */
|
|
197
201
|
const SKILL_RUNNER_INFRASTRUCTURE_CODES = [3, 4, 5, 6];
|
|
198
|
-
const { CorpusIndexError, INDEX_NAME, corpusIndexProblem } = require('./corpus-index');
|
|
202
|
+
const { CorpusIndexError, INDEX_NAME, corpusIndexProblem, unreadPaths } = require('./corpus-index');
|
|
199
203
|
|
|
200
204
|
const Ajv = AjvModule.default ?? AjvModule;
|
|
201
205
|
|
|
@@ -221,6 +225,11 @@ const QUALIFICATION_PREFIX = 'baseline/qualification/';
|
|
|
221
225
|
const MUTATION_ID_PATTERN = '^M-[0-9]{3,}$';
|
|
222
226
|
const PROBE_FILE = /^(.+)\.probe\.json$/;
|
|
223
227
|
const MUTATION_FILE = /^(.+)\.mutation\.json$/;
|
|
228
|
+
/** The one directory of an evaluation folder that holds exploratory candidates, and the suffix of their files. */
|
|
229
|
+
const CANDIDATE_LANE = 'discovery';
|
|
230
|
+
const CANDIDATE_SUFFIX = '.candidate.json';
|
|
231
|
+
/** Top-level entries a placement scan leaves out: the lane itself, runtime output and installed packages. */
|
|
232
|
+
const PLACEMENT_SKIPPED = new Set([CANDIDATE_LANE, 'runs', 'node_modules']);
|
|
224
233
|
const WRITTEN_FILE_POINTER = /^\/interactions\/[^/]+\/artifact(?:\/|$)/;
|
|
225
234
|
const INTERACTION_POINTER = /^\/interactions\/([^/]+)(?:\/|$)/;
|
|
226
235
|
/** The step budget a signature's regular-expression operators get while `check` resolves them. */
|
|
@@ -514,6 +523,36 @@ function optionSetsByOperation(contract) {
|
|
|
514
523
|
return sets;
|
|
515
524
|
}
|
|
516
525
|
|
|
526
|
+
/**
|
|
527
|
+
* `candidate-placement`: a candidate file anywhere a scored artifact could read it. Links are not followed, so the scan stays
|
|
528
|
+
* inside the folder the way `listDirectory` does. `unread` is the `unreadPaths` of the directories the scan leaves unopened, the
|
|
529
|
+
* same set the corpus index leaves unread, so a development check never opens a sealed held-out folder in any spelling.
|
|
530
|
+
*/
|
|
531
|
+
function checkCandidatePlacement(report, folder, unread, relativeDirectory = '') {
|
|
532
|
+
let entries;
|
|
533
|
+
try {
|
|
534
|
+
entries = listDirectory(folder, relativeDirectory);
|
|
535
|
+
} catch (error) {
|
|
536
|
+
// A directory this process cannot read is reported by the rule that owns it (the corpus index for corpus/), and a
|
|
537
|
+
// candidate cannot be read from it either.
|
|
538
|
+
if (error.code === 'EACCES' || error.code === 'EPERM') return;
|
|
539
|
+
throw error;
|
|
540
|
+
}
|
|
541
|
+
for (const entry of entries ?? []) {
|
|
542
|
+
const relative = relativeDirectory === '' ? entry.name : `${relativeDirectory}/${entry.name}`;
|
|
543
|
+
if (relativeDirectory === '' && PLACEMENT_SKIPPED.has(entry.name)) continue;
|
|
544
|
+
if (entry.isDirectory && unread.has(relative)) continue;
|
|
545
|
+
if (entry.isDirectory) checkCandidatePlacement(report, folder, unread, relative);
|
|
546
|
+
else if (entry.name.endsWith(CANDIDATE_SUFFIX)) {
|
|
547
|
+
report.add(
|
|
548
|
+
relative,
|
|
549
|
+
'candidate-placement',
|
|
550
|
+
`a candidate file belongs under ${CANDIDATE_LANE}/<campaignId>/, which no scored artifact reads; here it would be digested into the evaluation`,
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
517
556
|
/**
|
|
518
557
|
* The `skill-root` and `skill-runner` rules over the registry, the launch and
|
|
519
558
|
* the contract (AD-4): the runner must run the skill the launch names, declare
|
|
@@ -2482,6 +2521,9 @@ async function checkEvaluation(folder, { platform = process.platform, env = proc
|
|
|
2482
2521
|
const routes = await checkProbes(report, folder, context, behaviors, mutations, registry, compiled, evaluation);
|
|
2483
2522
|
checkHeldOut(report, folder, evaluation);
|
|
2484
2523
|
const openPlan = partition !== 'development';
|
|
2524
|
+
const planFile = evaluation.partitionPlan?.heldOutPlan;
|
|
2525
|
+
const heldOutUnread = openPlan || typeof planFile !== 'string' ? [] : [HELD_OUT_DIRECTORY];
|
|
2526
|
+
checkCandidatePlacement(report, folder, unreadPaths(heldOutUnread));
|
|
2485
2527
|
const heldOutPlan = checkPartitionPlan(report, folder, evaluation, context, { openPlan });
|
|
2486
2528
|
// The answers a gameability probe gives the held-out plan's steps sit beside the plan, so a check that opens a sound plan reads them
|
|
2487
2529
|
// and a development check never does (Story 1.109).
|
|
@@ -2520,10 +2562,7 @@ async function checkEvaluation(folder, { platform = process.platform, env = proc
|
|
|
2520
2562
|
});
|
|
2521
2563
|
|
|
2522
2564
|
try {
|
|
2523
|
-
const
|
|
2524
|
-
const stale = await corpusIndexProblem(folder, {
|
|
2525
|
-
unread: openPlan || typeof planFile !== 'string' ? [] : [HELD_OUT_DIRECTORY],
|
|
2526
|
-
});
|
|
2565
|
+
const stale = await corpusIndexProblem(folder, { unread: heldOutUnread });
|
|
2527
2566
|
if (stale !== null) report.add(INDEX_NAME, 'stale-index', stale);
|
|
2528
2567
|
} catch (error) {
|
|
2529
2568
|
if (!(error instanceof CorpusIndexError)) throw error;
|
|
@@ -221,4 +221,13 @@ async function corpusIndexProblem(folder, { unread = [] } = {}) {
|
|
|
221
221
|
);
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
module.exports = {
|
|
224
|
+
module.exports = {
|
|
225
|
+
CorpusIndexError,
|
|
226
|
+
INDEXED_ROOTS,
|
|
227
|
+
INDEX_NAME,
|
|
228
|
+
buildCorpusIndex,
|
|
229
|
+
corpusDigestOf,
|
|
230
|
+
corpusIndexProblem,
|
|
231
|
+
unreadPaths,
|
|
232
|
+
writeCorpusIndex,
|
|
233
|
+
};
|