create-byan-agent 2.19.1 → 2.20.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 +188 -0
- package/README.md +4 -4
- package/install/src/byan-v2/generation/templates/default-agent.md +1 -1
- package/install/templates/.claude/CLAUDE.md +1 -1
- package/install/templates/.claude/hooks/fd-phase-guard.js +2 -2
- package/install/templates/.claude/hooks/mantra-validate.js +16 -8
- package/install/templates/.claude/hooks/strict-scope-guard.js +25 -7
- package/install/templates/.claude/rules/native-workflows.md +32 -0
- package/install/templates/.claude/skills/byan-byan/SKILL.md +5 -5
- package/install/templates/.claude/skills/byan-mantra-audit/SKILL.md +53 -0
- package/install/templates/.claude/skills/byan-merise-agile/SKILL.md +2 -2
- package/install/templates/.claude/skills/byan-native-dev-story/SKILL.md +83 -0
- package/install/templates/.claude/workflows/INDEX.md +35 -0
- package/install/templates/.claude/workflows/check-implementation-readiness.js +280 -0
- package/install/templates/.claude/workflows/code-review.js +179 -0
- package/install/templates/.claude/workflows/create-excalidraw-dataflow.js +214 -0
- package/install/templates/.claude/workflows/create-excalidraw-diagram.js +188 -0
- package/install/templates/.claude/workflows/create-excalidraw-flowchart.js +225 -0
- package/install/templates/.claude/workflows/create-excalidraw-wireframe.js +192 -0
- package/install/templates/.claude/workflows/create-story.js +216 -0
- package/install/templates/.claude/workflows/dev-story.js +100 -0
- package/install/templates/.claude/workflows/document-project.js +455 -0
- package/install/templates/.claude/workflows/qa-automate.js +169 -0
- package/install/templates/.claude/workflows/quick-dev.js +273 -0
- package/install/templates/.claude/workflows/sprint-planning.js +261 -0
- package/install/templates/.claude/workflows/testarch-atdd.js +287 -0
- package/install/templates/.claude/workflows/testarch-automate.js +229 -0
- package/install/templates/.claude/workflows/testarch-ci.js +184 -0
- package/install/templates/.claude/workflows/testarch-framework.js +267 -0
- package/install/templates/.claude/workflows/testarch-nfr.js +316 -0
- package/install/templates/.claude/workflows/testarch-test-design.js +293 -0
- package/install/templates/.claude/workflows/testarch-test-review.js +321 -0
- package/install/templates/.claude/workflows/testarch-trace.js +316 -0
- package/install/templates/.githooks/pre-commit +49 -15
- package/install/templates/_byan/config.yaml +15 -5
- package/install/templates/_byan/mcp/byan-mcp-server/bin/byan-build-workflows.js +20 -0
- package/install/templates/_byan/mcp/byan-mcp-server/bin/byan-lint-workflows.js +57 -0
- package/install/templates/_byan/mcp/byan-mcp-server/lib/native-loop.js +39 -0
- package/install/templates/_byan/mcp/byan-mcp-server/lib/workflows-generator.js +149 -0
- package/install/templates/_byan/mcp/byan-mcp-server/lib/workflows-lint.js +113 -0
- package/install/templates/_byan/workflow/simple/byan/feature-workflow.md +14 -11
- package/install/templates/docs/native-workflows-contract.md +84 -0
- package/package.json +2 -2
- package/src/byan-v2/data/agent-scopes.json +46 -0
- package/src/byan-v2/data/mantras.json +194 -8
- package/src/byan-v2/generation/mantra-audit.js +147 -0
- package/src/byan-v2/generation/mantra-validator.js +56 -6
- package/src/byan-v2/generation/scope-resolver.js +102 -0
- package/src/byan-v2/generation/templates/default-agent.md +1 -1
- package/update-byan-agent/bin/update-byan-agent.js +67 -72
- package/update-byan-agent/lib/apply-update.js +202 -0
- package/update-byan-agent/package.json +1 -1
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
export const meta = {
|
|
2
|
+
name: 'testarch-test-review',
|
|
3
|
+
description: 'Autonomous TEA test-quality review: load knowledge, discover tests, fan out 5 parallel quality-dimension checks, aggregate a weighted 0-100 score, and produce a report. Returns a verdict for the human gate.',
|
|
4
|
+
phases: [
|
|
5
|
+
{ title: 'LOAD-CONTEXT' },
|
|
6
|
+
{ title: 'DISCOVER-TESTS' },
|
|
7
|
+
{ title: 'QUALITY-EVALUATION' },
|
|
8
|
+
{ title: 'AGGREGATE-SCORES' },
|
|
9
|
+
{ title: 'GENERATE-REPORT' }
|
|
10
|
+
]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// FD/STRICT CONTRACT (re-asserted): this script returns data only. It never
|
|
14
|
+
// imports lib/fd-state.js, never writes fd-state.json, and uses no wall-clock
|
|
15
|
+
// or randomness primitive (those break resume in the Workflow sandbox). Any
|
|
16
|
+
// timestamp/id is passed in via args. The orchestrating skill records FD /
|
|
17
|
+
// strict state via MCP at the human gate that lives OUTSIDE this script.
|
|
18
|
+
// The byan-lint-workflows linter forbids fd-state coupling here.
|
|
19
|
+
|
|
20
|
+
// --- Inputs (mirror workflow.yaml variables; all passed via args, no defaults that touch the clock) ---
|
|
21
|
+
const testDir = (args && args.test_dir) || './tests'
|
|
22
|
+
const reviewScope = (args && args.review_scope) || 'suite' // single | directory | suite
|
|
23
|
+
const targetPath = (args && args.target_path) || testDir // for scope=single/directory
|
|
24
|
+
const usePlaywrightUtils = !!(args && args.tea_use_playwright_utils)
|
|
25
|
+
const timestamp = (args && args.timestamp) || 'run' // passed in by orchestrator; never generated here
|
|
26
|
+
const outputFile = (args && args.output_file) || `_byan-output/test-review-${timestamp}.md`
|
|
27
|
+
const knowledgeIndex = (args && args.knowledge_index) || '_byan/connaissance/testarch/tea-index.csv'
|
|
28
|
+
|
|
29
|
+
// Step 1: Load Context & Knowledge Base (steps-c/step-01-load-context.md)
|
|
30
|
+
phase('LOAD-CONTEXT')
|
|
31
|
+
const context = await agent(
|
|
32
|
+
`You are the Master Test Architect running step 01 (load-context) of the testarch-test-review workflow.
|
|
33
|
+
Read the real source step: _byan/workflow/simple/testarch/test-review/steps-c/step-01-load-context.md.
|
|
34
|
+
Do exactly what it says:
|
|
35
|
+
1. Determine review scope = "${reviewScope}" (single = one file, directory = one folder, suite = whole repo). Target path: ${targetPath}.
|
|
36
|
+
2. Load the TEA knowledge base. Read the index at ${knowledgeIndex} and check tea_use_playwright_utils (=${usePlaywrightUtils}) to pick the fragment set.
|
|
37
|
+
Core fragments: test-quality.md, data-factories.md, test-levels-framework.md, selective-testing.md, test-healing-patterns.md, selector-resilience.md, timing-debugging.md.
|
|
38
|
+
${usePlaywrightUtils
|
|
39
|
+
? 'Playwright Utils ENABLED -> also load: overview.md, api-request.md, network-recorder.md, auth-session.md, intercept-network-call.md, recurse.md, log.md, file-utils.md, burn-in.md, network-error-monitor.md, fixtures-composition.md.'
|
|
40
|
+
: 'Playwright Utils DISABLED -> also load: fixture-architecture.md, network-first.md, playwright-config.md, component-tdd.md, ci-burn-in.md.'}
|
|
41
|
+
3. Gather context artifacts if present: story file (acceptance criteria), test-design doc (priorities), framework config.
|
|
42
|
+
Summarize what was found.`,
|
|
43
|
+
{
|
|
44
|
+
label: 'load-context',
|
|
45
|
+
phase: 'LOAD-CONTEXT',
|
|
46
|
+
schema: {
|
|
47
|
+
type: 'object',
|
|
48
|
+
required: ['scope', 'knowledgeFragmentsLoaded'],
|
|
49
|
+
properties: {
|
|
50
|
+
scope: { type: 'string', enum: ['single', 'directory', 'suite'] },
|
|
51
|
+
knowledgeFragmentsLoaded: { type: 'array', items: { type: 'string' } },
|
|
52
|
+
playwrightUtils: { type: 'boolean' },
|
|
53
|
+
storyFile: { type: 'string' },
|
|
54
|
+
testDesignFile: { type: 'string' },
|
|
55
|
+
frameworkConfig: { type: 'string' },
|
|
56
|
+
summary: { type: 'string' }
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
log(`scope=${context.scope} fragments=${(context.knowledgeFragmentsLoaded || []).length}`)
|
|
62
|
+
|
|
63
|
+
// Step 2: Discover & Parse Tests (steps-c/step-02-discover-tests.md)
|
|
64
|
+
phase('DISCOVER-TESTS')
|
|
65
|
+
const discovery = await agent(
|
|
66
|
+
`Run step 02 (discover-tests) of testarch-test-review.
|
|
67
|
+
Read the real source step: _byan/workflow/simple/testarch/test-review/steps-c/step-02-discover-tests.md.
|
|
68
|
+
1. Discover test files for scope "${context.scope}":
|
|
69
|
+
- single: use the provided file path (${targetPath})
|
|
70
|
+
- directory: glob test files under the selected folder (${targetPath})
|
|
71
|
+
- suite: glob all tests in the repo (root: ${testDir})
|
|
72
|
+
HALT (set halted=true, testFileCount=0) if no test files are found.
|
|
73
|
+
2. Per file, parse metadata: file size & line count, detected framework, describe/test block counts,
|
|
74
|
+
test IDs and priority markers (P0/P1/P2/P3), imports/fixtures/factories/network interception,
|
|
75
|
+
waits/timeouts and control flow (if/try/catch).`,
|
|
76
|
+
{
|
|
77
|
+
label: 'discover-tests',
|
|
78
|
+
phase: 'DISCOVER-TESTS',
|
|
79
|
+
schema: {
|
|
80
|
+
type: 'object',
|
|
81
|
+
required: ['halted', 'testFileCount'],
|
|
82
|
+
properties: {
|
|
83
|
+
halted: { type: 'boolean' },
|
|
84
|
+
testFileCount: { type: 'integer' },
|
|
85
|
+
framework: { type: 'string' },
|
|
86
|
+
testFiles: {
|
|
87
|
+
type: 'array',
|
|
88
|
+
items: {
|
|
89
|
+
type: 'object',
|
|
90
|
+
properties: {
|
|
91
|
+
file: { type: 'string' },
|
|
92
|
+
lineCount: { type: 'integer' },
|
|
93
|
+
describeBlocks: { type: 'integer' },
|
|
94
|
+
testBlocks: { type: 'integer' },
|
|
95
|
+
testIds: { type: 'array', items: { type: 'string' } }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
summary: { type: 'string' }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
log(`discovered ${discovery.testFileCount} test file(s), halted=${discovery.halted}`)
|
|
105
|
+
|
|
106
|
+
// If discovery halted (no tests), short-circuit with a verdict — the human gate decides next move.
|
|
107
|
+
if (discovery.halted || discovery.testFileCount === 0) {
|
|
108
|
+
return {
|
|
109
|
+
workflow: 'testarch-test-review',
|
|
110
|
+
summary: 'No tests found in scope — review halted at discovery (mirrors step-02 HALT condition).',
|
|
111
|
+
steps: 2,
|
|
112
|
+
halted: true,
|
|
113
|
+
scope: context.scope,
|
|
114
|
+
needsHumanGate: true,
|
|
115
|
+
result: { reason: 'no-tests-found', testFileCount: 0 }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Step 3: Orchestrate 5 Parallel Quality-Dimension Subprocesses (steps-c/step-03 + 03a-03e)
|
|
120
|
+
// True fan-out in the source ("Launch FIVE subprocesses in PARALLEL") -> parallel() of 5 thunks.
|
|
121
|
+
// Each subprocess is read-only, checks ONE dimension, and returns a 0-100 score with severity-weighted
|
|
122
|
+
// violations (HIGH 10 / MEDIUM 5 / LOW 2), except coverage which uses its own gap-based penalty.
|
|
123
|
+
phase('QUALITY-EVALUATION')
|
|
124
|
+
|
|
125
|
+
const dimensionSchema = {
|
|
126
|
+
type: 'object',
|
|
127
|
+
required: ['dimension', 'score', 'violations'],
|
|
128
|
+
properties: {
|
|
129
|
+
dimension: { type: 'string' },
|
|
130
|
+
score: { type: 'integer', minimum: 0, maximum: 100 },
|
|
131
|
+
grade: { type: 'string' },
|
|
132
|
+
violations: {
|
|
133
|
+
type: 'array',
|
|
134
|
+
items: {
|
|
135
|
+
type: 'object',
|
|
136
|
+
properties: {
|
|
137
|
+
file: { type: 'string' },
|
|
138
|
+
line: { type: 'integer' },
|
|
139
|
+
severity: { type: 'string', enum: ['HIGH', 'MEDIUM', 'LOW'] },
|
|
140
|
+
category: { type: 'string' },
|
|
141
|
+
description: { type: 'string' },
|
|
142
|
+
suggestion: { type: 'string' }
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
violationSummary: {
|
|
147
|
+
type: 'object',
|
|
148
|
+
properties: {
|
|
149
|
+
HIGH: { type: 'integer' },
|
|
150
|
+
MEDIUM: { type: 'integer' },
|
|
151
|
+
LOW: { type: 'integer' }
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
recommendations: { type: 'array', items: { type: 'string' } },
|
|
155
|
+
summary: { type: 'string' }
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const filesForPrompt = JSON.stringify(discovery.testFiles || [])
|
|
160
|
+
|
|
161
|
+
const [determinism, isolation, maintainability, coverage, performance] = await parallel([
|
|
162
|
+
// 3A — Determinism (steps-c/step-03a-subprocess-determinism.md)
|
|
163
|
+
() => agent(
|
|
164
|
+
`Subprocess 3A (DETERMINISM only). Read the real source: _byan/workflow/simple/testarch/test-review/steps-c/step-03a-subprocess-determinism.md.
|
|
165
|
+
Read-only analysis of these test files: ${filesForPrompt}.
|
|
166
|
+
Flag non-determinism: HIGH = RNG calls, wall-clock reads/a wall-clock read unmocked, setTimeout/setInterval without waits, unmocked external API calls, random-path FS ops, non-deterministic DB ordering;
|
|
167
|
+
MEDIUM = waitForTimeout hard waits, flaky selectors, race conditions, test-order dependencies; LOW = shared state, unfixed timezone.
|
|
168
|
+
Score = max(0, 100 - penalty) with HIGH=10, MEDIUM=5, LOW=2. Check determinism ONLY.`,
|
|
169
|
+
{ label: 'subprocess-determinism', phase: 'QUALITY-EVALUATION', schema: dimensionSchema }
|
|
170
|
+
),
|
|
171
|
+
// 3B — Isolation (steps-c/step-03b-subprocess-isolation.md)
|
|
172
|
+
() => agent(
|
|
173
|
+
`Subprocess 3B (ISOLATION only). Read the real source: _byan/workflow/simple/testarch/test-review/steps-c/step-03b-subprocess-isolation.md.
|
|
174
|
+
Read-only analysis of these test files: ${filesForPrompt}.
|
|
175
|
+
Flag isolation issues: HIGH = global state mutations, test-order dependencies, shared DB records without cleanup, beforeAll/afterAll side effects leaking;
|
|
176
|
+
MEDIUM = missing cleanup, state-mutating shared fixtures, assumed execution order, env vars modified without restore; LOW = shared (non-mutated) data, missing describe grouping.
|
|
177
|
+
Score = max(0, 100 - penalty) with HIGH=10, MEDIUM=5, LOW=2. Check isolation ONLY.`,
|
|
178
|
+
{ label: 'subprocess-isolation', phase: 'QUALITY-EVALUATION', schema: dimensionSchema }
|
|
179
|
+
),
|
|
180
|
+
// 3C — Maintainability (steps-c/step-03c-subprocess-maintainability.md)
|
|
181
|
+
() => agent(
|
|
182
|
+
`Subprocess 3C (MAINTAINABILITY only). Read the real source: _byan/workflow/simple/testarch/test-review/steps-c/step-03c-subprocess-maintainability.md.
|
|
183
|
+
Read-only analysis of these test files: ${filesForPrompt}.
|
|
184
|
+
Flag maintainability issues: HIGH = tests >100 lines, no describe grouping, duplicate/copy-paste logic, unclear names (no Given/When/Then), magic numbers/strings;
|
|
185
|
+
MEDIUM = missing comments on complex logic, inconsistent naming, nesting >3 levels, large setup/teardown; LOW = minor style, helper-extraction opportunities, inconsistent assertion styles.
|
|
186
|
+
Score = max(0, 100 - penalty) with HIGH=10, MEDIUM=5, LOW=2. Check maintainability ONLY.`,
|
|
187
|
+
{ label: 'subprocess-maintainability', phase: 'QUALITY-EVALUATION', schema: dimensionSchema }
|
|
188
|
+
),
|
|
189
|
+
// 3D — Coverage (steps-c/step-03d-subprocess-coverage.md) — distinct gap-based scoring
|
|
190
|
+
() => agent(
|
|
191
|
+
`Subprocess 3D (COVERAGE only). Read the real source: _byan/workflow/simple/testarch/test-review/steps-c/step-03d-subprocess-coverage.md.
|
|
192
|
+
Read-only analysis of these test files: ${filesForPrompt}.
|
|
193
|
+
Flag coverage gaps: HIGH = critical/P0 paths untested, API endpoints without tests, error handling untested, missing auth/authz tests;
|
|
194
|
+
MEDIUM = uncovered edge cases (boundaries, null/empty), only happy path, missing integration layer, weak assertion coverage; LOW = additional cases, minor edges, incomplete docs.
|
|
195
|
+
Scoring is DISTINCT here: if any HIGH gap exists, score = max(0, 50 - highCount*10); else score = max(0, 100 - totalViolations*5). Check coverage ONLY.`,
|
|
196
|
+
{ label: 'subprocess-coverage', phase: 'QUALITY-EVALUATION', schema: dimensionSchema }
|
|
197
|
+
),
|
|
198
|
+
// 3E — Performance (steps-c/step-03e-subprocess-performance.md)
|
|
199
|
+
() => agent(
|
|
200
|
+
`Subprocess 3E (PERFORMANCE only). Read the real source: _byan/workflow/simple/testarch/test-review/steps-c/step-03e-subprocess-performance.md.
|
|
201
|
+
Read-only analysis of these test files: ${filesForPrompt}.
|
|
202
|
+
Flag performance issues: HIGH = unnecessary test.describe.serial (not parallelizable), slow setup/teardown (fresh DB per test), excessive navigation, no fixture reuse;
|
|
203
|
+
MEDIUM = hard waits >2s, inefficient selectors (page.$$ vs locators), large datasets without pagination, missing optimizations; LOW = unused parallelization, minor inefficiencies, excessive logging.
|
|
204
|
+
Score = max(0, 100 - penalty) with HIGH=10, MEDIUM=5, LOW=2. Check performance ONLY.`,
|
|
205
|
+
{ label: 'subprocess-performance', phase: 'QUALITY-EVALUATION', schema: dimensionSchema }
|
|
206
|
+
)
|
|
207
|
+
])
|
|
208
|
+
log(`dimensions: det=${determinism.score} iso=${isolation.score} maint=${maintainability.score} cov=${coverage.score} perf=${performance.score}`)
|
|
209
|
+
|
|
210
|
+
// Step 3F: Aggregate Scores (steps-c/step-03f-aggregate-scores.md)
|
|
211
|
+
// Weighted overall score with the source's exact weights; grade thresholds A>=90 B>=80 C>=70 D>=60 else F.
|
|
212
|
+
phase('AGGREGATE-SCORES')
|
|
213
|
+
const aggregate = await agent(
|
|
214
|
+
`Run step 03F (aggregate-scores) of testarch-test-review.
|
|
215
|
+
Read the real source: _byan/workflow/simple/testarch/test-review/steps-c/step-03f-aggregate-scores.md.
|
|
216
|
+
You are given the 5 quality-dimension results (each already a 0-100 score with violations):
|
|
217
|
+
determinism=${JSON.stringify(determinism)}
|
|
218
|
+
isolation=${JSON.stringify(isolation)}
|
|
219
|
+
maintainability=${JSON.stringify(maintainability)}
|
|
220
|
+
coverage=${JSON.stringify(coverage)}
|
|
221
|
+
performance=${JSON.stringify(performance)}
|
|
222
|
+
|
|
223
|
+
Do NOT re-evaluate quality — only aggregate.
|
|
224
|
+
1. Weighted overall score = round( det*0.25 + iso*0.25 + maint*0.20 + cov*0.15 + perf*0.15 ).
|
|
225
|
+
2. Grade: >=90 A, >=80 B, >=70 C, >=60 D, else F.
|
|
226
|
+
3. Aggregate all violations across dimensions; count by severity (HIGH/MEDIUM/LOW) plus total.
|
|
227
|
+
4. Prioritize recommendations (impact HIGH if a dimension score <70), keep the top 10.`,
|
|
228
|
+
{
|
|
229
|
+
label: 'aggregate-scores',
|
|
230
|
+
phase: 'AGGREGATE-SCORES',
|
|
231
|
+
schema: {
|
|
232
|
+
type: 'object',
|
|
233
|
+
required: ['overallScore', 'overallGrade', 'violationSummary'],
|
|
234
|
+
properties: {
|
|
235
|
+
overallScore: { type: 'integer', minimum: 0, maximum: 100 },
|
|
236
|
+
overallGrade: { type: 'string', enum: ['A', 'B', 'C', 'D', 'F'] },
|
|
237
|
+
dimensionScores: {
|
|
238
|
+
type: 'object',
|
|
239
|
+
properties: {
|
|
240
|
+
determinism: { type: 'integer' },
|
|
241
|
+
isolation: { type: 'integer' },
|
|
242
|
+
maintainability: { type: 'integer' },
|
|
243
|
+
coverage: { type: 'integer' },
|
|
244
|
+
performance: { type: 'integer' }
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
violationSummary: {
|
|
248
|
+
type: 'object',
|
|
249
|
+
required: ['total', 'HIGH', 'MEDIUM', 'LOW'],
|
|
250
|
+
properties: {
|
|
251
|
+
total: { type: 'integer' },
|
|
252
|
+
HIGH: { type: 'integer' },
|
|
253
|
+
MEDIUM: { type: 'integer' },
|
|
254
|
+
LOW: { type: 'integer' }
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
topRecommendations: { type: 'array', items: { type: 'string' } },
|
|
258
|
+
qualityAssessment: { type: 'string' }
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
)
|
|
263
|
+
log(`overall=${aggregate.overallScore}/100 grade=${aggregate.overallGrade} violations=${aggregate.violationSummary.total}`)
|
|
264
|
+
|
|
265
|
+
// Step 4: Generate Report & Validate (steps-c/step-04-generate-report.md)
|
|
266
|
+
phase('GENERATE-REPORT')
|
|
267
|
+
const report = await agent(
|
|
268
|
+
`Run step 04 (generate-report) of testarch-test-review.
|
|
269
|
+
Read the real source: _byan/workflow/simple/testarch/test-review/steps-c/step-04-generate-report.md
|
|
270
|
+
and the template _byan/workflow/simple/testarch/test-review/test-review-template.md.
|
|
271
|
+
Using the aggregated results: ${JSON.stringify(aggregate)}
|
|
272
|
+
and scope=${context.scope}, write the report to ${outputFile} including:
|
|
273
|
+
- Score summary (overall ${aggregate.overallScore}/100, grade ${aggregate.overallGrade})
|
|
274
|
+
- Critical findings with concrete fixes (HIGH-severity violations)
|
|
275
|
+
- Warnings and recommendations (top recommendations)
|
|
276
|
+
- Context references (story / test-design if available)
|
|
277
|
+
Then validate the report against _byan/workflow/simple/testarch/test-review/checklist.md and fix any gaps.
|
|
278
|
+
Report the scope reviewed, overall score, critical blocker count, and the next recommended workflow (e.g. automate or trace).
|
|
279
|
+
Do NOT make the approve/request-changes/block decision — that is the human gate.`,
|
|
280
|
+
{
|
|
281
|
+
label: 'generate-report',
|
|
282
|
+
phase: 'GENERATE-REPORT',
|
|
283
|
+
schema: {
|
|
284
|
+
type: 'object',
|
|
285
|
+
required: ['reportPath', 'checklistValidated'],
|
|
286
|
+
properties: {
|
|
287
|
+
reportPath: { type: 'string' },
|
|
288
|
+
checklistValidated: { type: 'boolean' },
|
|
289
|
+
criticalBlockers: { type: 'integer' },
|
|
290
|
+
nextRecommendedWorkflow: { type: 'string' },
|
|
291
|
+
completionSummary: { type: 'string' }
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
)
|
|
296
|
+
log(`report -> ${report.reportPath} checklistValidated=${report.checklistValidated}`)
|
|
297
|
+
|
|
298
|
+
// Final verdict object. The decision (Approve / Approve-with-comments / Request-changes / Block)
|
|
299
|
+
// stays OUT of this script — the orchestrating skill presents this at the human gate.
|
|
300
|
+
return {
|
|
301
|
+
workflow: 'testarch-test-review',
|
|
302
|
+
summary: `Test-quality review of scope "${context.scope}" across ${discovery.testFileCount} file(s): overall ${aggregate.overallScore}/100 (grade ${aggregate.overallGrade}), ${aggregate.violationSummary.total} violations (${aggregate.violationSummary.HIGH} HIGH).`,
|
|
303
|
+
steps: 5,
|
|
304
|
+
halted: false,
|
|
305
|
+
scope: context.scope,
|
|
306
|
+
testFileCount: discovery.testFileCount,
|
|
307
|
+
overallScore: aggregate.overallScore,
|
|
308
|
+
overallGrade: aggregate.overallGrade,
|
|
309
|
+
dimensionScores: aggregate.dimensionScores,
|
|
310
|
+
violationSummary: aggregate.violationSummary,
|
|
311
|
+
topRecommendations: aggregate.topRecommendations,
|
|
312
|
+
reportPath: report.reportPath,
|
|
313
|
+
criticalBlockers: report.criticalBlockers,
|
|
314
|
+
nextRecommendedWorkflow: report.nextRecommendedWorkflow,
|
|
315
|
+
needsHumanGate: true,
|
|
316
|
+
result: {
|
|
317
|
+
dimensions: { determinism, isolation, maintainability, coverage, performance },
|
|
318
|
+
aggregate,
|
|
319
|
+
report
|
|
320
|
+
}
|
|
321
|
+
}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
export const meta = {
|
|
2
|
+
name: 'testarch-trace',
|
|
3
|
+
description: 'Generate a requirements-to-tests traceability matrix, analyze coverage gaps, and apply deterministic quality-gate logic (PASS/CONCERNS/FAIL/WAIVED). Mirrors the BYAN testarch-trace Create-mode steps; returns a verdict for the human gate.',
|
|
4
|
+
phases: [
|
|
5
|
+
{ title: 'LOAD_CONTEXT' },
|
|
6
|
+
{ title: 'DISCOVER_TESTS' },
|
|
7
|
+
{ title: 'MAP_CRITERIA' },
|
|
8
|
+
{ title: 'ANALYZE_GAPS' },
|
|
9
|
+
{ title: 'GATE_DECISION' }
|
|
10
|
+
]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// FD/STRICT CONTRACT (re-asserted): this script returns data only. It never
|
|
14
|
+
// imports lib/fd-state.js, never writes fd-state.json, and records no platform
|
|
15
|
+
// state. The orchestrating skill presents the gate verdict at the human gate
|
|
16
|
+
// and records FD/strict state via MCP. The byan-lint-workflows linter forbids
|
|
17
|
+
// fd-state coupling, and the sandbox forbids import/require/fs/clock/RNG.
|
|
18
|
+
// Any timestamp or run id must arrive through `args` (no wall-clock / RNG).
|
|
19
|
+
|
|
20
|
+
const source = '/home/yan/BYAN/_byan/workflow/simple/testarch/trace'
|
|
21
|
+
const testDir = (args && args.testDir) || '{project-root}/tests'
|
|
22
|
+
const sourceDir = (args && args.sourceDir) || '{project-root}'
|
|
23
|
+
const story = (args && args.story) || '{project-root} story / inline acceptance criteria'
|
|
24
|
+
const coverageLevels = (args && args.coverageLevels) || 'e2e,api,component,unit'
|
|
25
|
+
const gateType = (args && args.gateType) || 'story'
|
|
26
|
+
const decisionMode = (args && args.decisionMode) || 'deterministic'
|
|
27
|
+
const runId = (args && args.runId) || 'trace-run'
|
|
28
|
+
|
|
29
|
+
// Step 1 (steps-c/step-01-load-context.md): gather acceptance criteria,
|
|
30
|
+
// priorities, knowledge base and supporting artifacts. HALT if AC missing.
|
|
31
|
+
phase('LOAD_CONTEXT')
|
|
32
|
+
const context = await agent(
|
|
33
|
+
[
|
|
34
|
+
'You are the Master Test Architect running Step 1 of the testarch-trace workflow.',
|
|
35
|
+
'Read the real source step at ' + source + '/steps-c/step-01-load-context.md and follow it.',
|
|
36
|
+
'Goal: gather acceptance criteria (AC), their priorities (P0/P1/P2/P3), and supporting artifacts for traceability.',
|
|
37
|
+
'Story / AC input: ' + story + '.',
|
|
38
|
+
'Prerequisite: acceptance criteria MUST be available. If AC are missing, do NOT invent them: set acGate to "HALT".',
|
|
39
|
+
'Load the tea knowledge base index ({project-root}/_byan/connaissance/testarch/tea-index.csv): test-priorities-matrix, risk-governance, probability-impact, test-quality, selective-testing.',
|
|
40
|
+
'Load artifacts if present: story file + AC, test design doc (priorities), tech spec / PRD. Summarize what was found.'
|
|
41
|
+
].join(' '),
|
|
42
|
+
{
|
|
43
|
+
label: 'load-context',
|
|
44
|
+
phase: 'LOAD_CONTEXT',
|
|
45
|
+
schema: {
|
|
46
|
+
type: 'object',
|
|
47
|
+
required: ['acGate', 'criteria'],
|
|
48
|
+
properties: {
|
|
49
|
+
acGate: { type: 'string', enum: ['OK', 'HALT'] },
|
|
50
|
+
haltReason: { type: 'string' },
|
|
51
|
+
criteria: {
|
|
52
|
+
type: 'array',
|
|
53
|
+
items: {
|
|
54
|
+
type: 'object',
|
|
55
|
+
required: ['id', 'priority'],
|
|
56
|
+
properties: {
|
|
57
|
+
id: { type: 'string' },
|
|
58
|
+
description: { type: 'string' },
|
|
59
|
+
priority: { type: 'string', enum: ['P0', 'P1', 'P2', 'P3'] }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
artifactsSummary: { type: 'string' }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
if (context.acGate === 'HALT') {
|
|
70
|
+
// Mirror the source HALT: AC are a hard prerequisite. Return early; the
|
|
71
|
+
// human gate decides how to supply criteria. No state is written here.
|
|
72
|
+
return {
|
|
73
|
+
workflow: 'testarch-trace',
|
|
74
|
+
runId,
|
|
75
|
+
summary: 'Halted at Step 1: acceptance criteria are required and were not provided.',
|
|
76
|
+
steps: 5,
|
|
77
|
+
completedSteps: 1,
|
|
78
|
+
halted: true,
|
|
79
|
+
haltReason: context.haltReason || 'Acceptance criteria missing.',
|
|
80
|
+
needsHumanGate: true,
|
|
81
|
+
result: context
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Step 2 (steps-c/step-02-discover-tests.md): discover tests under test_dir and
|
|
86
|
+
// classify by level (E2E / API / Component / Unit), recording test IDs.
|
|
87
|
+
phase('DISCOVER_TESTS')
|
|
88
|
+
const tests = await agent(
|
|
89
|
+
[
|
|
90
|
+
'Step 2 of testarch-trace. Read ' + source + '/steps-c/step-02-discover-tests.md and follow it.',
|
|
91
|
+
'Search the test directory (' + testDir + ', source: ' + sourceDir + ') for tests relevant to the acceptance criteria from Step 1.',
|
|
92
|
+
'Match by: explicit test IDs (e.g. 1.3-E2E-001), feature-name matches, and spec patterns (*.spec.*, *.test.*).',
|
|
93
|
+
'Categorize each discovered test by level among: ' + coverageLevels + ' (E2E, API, Component, Unit).',
|
|
94
|
+
'Record test IDs, describe blocks, file:line, and any priority markers present.'
|
|
95
|
+
].join(' '),
|
|
96
|
+
{
|
|
97
|
+
label: 'discover-tests',
|
|
98
|
+
phase: 'DISCOVER_TESTS',
|
|
99
|
+
schema: {
|
|
100
|
+
type: 'object',
|
|
101
|
+
required: ['tests', 'levelCounts'],
|
|
102
|
+
properties: {
|
|
103
|
+
tests: {
|
|
104
|
+
type: 'array',
|
|
105
|
+
items: {
|
|
106
|
+
type: 'object',
|
|
107
|
+
required: ['testId', 'level'],
|
|
108
|
+
properties: {
|
|
109
|
+
testId: { type: 'string' },
|
|
110
|
+
level: { type: 'string', enum: ['E2E', 'API', 'Component', 'Unit'] },
|
|
111
|
+
location: { type: 'string' },
|
|
112
|
+
describeBlock: { type: 'string' }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
levelCounts: {
|
|
117
|
+
type: 'object',
|
|
118
|
+
properties: {
|
|
119
|
+
E2E: { type: 'integer' },
|
|
120
|
+
API: { type: 'integer' },
|
|
121
|
+
Component: { type: 'integer' },
|
|
122
|
+
Unit: { type: 'integer' }
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
// Step 3 (steps-c/step-03-map-criteria.md): build the traceability matrix
|
|
131
|
+
// linking each AC to tests with a coverage status; validate P0/P1 coverage and
|
|
132
|
+
// flag unjustified duplicate coverage across levels.
|
|
133
|
+
phase('MAP_CRITERIA')
|
|
134
|
+
const matrix = await agent(
|
|
135
|
+
[
|
|
136
|
+
'Step 3 of testarch-trace. Read ' + source + '/steps-c/step-03-map-criteria.md and follow it.',
|
|
137
|
+
'For EACH acceptance criterion from Step 1, map it to matching tests from Step 2.',
|
|
138
|
+
'Coverage status of each criterion is one of: FULL, PARTIAL, NONE, UNIT-ONLY, INTEGRATION-ONLY.',
|
|
139
|
+
'Record the criterion priority and the levels of the mapped tests.',
|
|
140
|
+
'Validate coverage logic: P0/P1 criteria MUST have coverage; flag duplicate coverage across levels that has no defense-in-depth justification.'
|
|
141
|
+
].join(' '),
|
|
142
|
+
{
|
|
143
|
+
label: 'map-criteria',
|
|
144
|
+
phase: 'MAP_CRITERIA',
|
|
145
|
+
schema: {
|
|
146
|
+
type: 'object',
|
|
147
|
+
required: ['traceabilityMatrix'],
|
|
148
|
+
properties: {
|
|
149
|
+
traceabilityMatrix: {
|
|
150
|
+
type: 'array',
|
|
151
|
+
items: {
|
|
152
|
+
type: 'object',
|
|
153
|
+
required: ['id', 'priority', 'coverage'],
|
|
154
|
+
properties: {
|
|
155
|
+
id: { type: 'string' },
|
|
156
|
+
priority: { type: 'string', enum: ['P0', 'P1', 'P2', 'P3'] },
|
|
157
|
+
coverage: { type: 'string', enum: ['FULL', 'PARTIAL', 'NONE', 'UNIT-ONLY', 'INTEGRATION-ONLY'] },
|
|
158
|
+
tests: { type: 'array', items: { type: 'string' } }
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
coverageLogicIssues: { type: 'array', items: { type: 'string' } }
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
// Step 4 (steps-c/step-04-analyze-gaps.md): Phase 1 final step. Gap analysis by
|
|
169
|
+
// risk priority, recommendations, coverage statistics (overall + per-priority),
|
|
170
|
+
// and the complete coverage matrix. NO gate decision here (that is Step 5).
|
|
171
|
+
phase('ANALYZE_GAPS')
|
|
172
|
+
const coverage = await agent(
|
|
173
|
+
[
|
|
174
|
+
'Step 4 of testarch-trace (Phase 1 final). Read ' + source + '/steps-c/step-04-analyze-gaps.md and follow it.',
|
|
175
|
+
'From the traceability matrix from Step 3, compute the gap analysis and coverage statistics. Do NOT make a gate decision here.',
|
|
176
|
+
'Gaps: uncovered (coverage=NONE) split into critical=P0, high=P1, medium=P2, low=P3; plus partial-coverage and unit-only items.',
|
|
177
|
+
'Recommendations: URGENT atdd for P0 gaps, HIGH automate for P1 gaps, MEDIUM complete partial coverage, LOW test-review for quality.',
|
|
178
|
+
'Statistics: overall coverage percentage = round(fullyCovered / totalRequirements * 100); plus per-priority breakdown (P0/P1/P2/P3 total, covered=FULL, percentage). P0 percentage = round(p0Covered / p0Total * 100).',
|
|
179
|
+
'Produce the complete Phase 1 coverage matrix object (requirements + coverage_statistics + gap_analysis + recommendations).'
|
|
180
|
+
].join(' '),
|
|
181
|
+
{
|
|
182
|
+
label: 'analyze-gaps',
|
|
183
|
+
phase: 'ANALYZE_GAPS',
|
|
184
|
+
schema: {
|
|
185
|
+
type: 'object',
|
|
186
|
+
required: ['phase', 'coverageStatistics', 'gapAnalysis', 'recommendations'],
|
|
187
|
+
properties: {
|
|
188
|
+
phase: { type: 'string', enum: ['PHASE_1_COMPLETE'] },
|
|
189
|
+
coverageStatistics: {
|
|
190
|
+
type: 'object',
|
|
191
|
+
required: ['totalRequirements', 'fullyCovered', 'overallCoveragePercentage', 'p0Percentage'],
|
|
192
|
+
properties: {
|
|
193
|
+
totalRequirements: { type: 'integer' },
|
|
194
|
+
fullyCovered: { type: 'integer' },
|
|
195
|
+
partiallyCovered: { type: 'integer' },
|
|
196
|
+
uncovered: { type: 'integer' },
|
|
197
|
+
overallCoveragePercentage: { type: 'integer' },
|
|
198
|
+
p0Total: { type: 'integer' },
|
|
199
|
+
p0Covered: { type: 'integer' },
|
|
200
|
+
p0Percentage: { type: 'integer' }
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
gapAnalysis: {
|
|
204
|
+
type: 'object',
|
|
205
|
+
properties: {
|
|
206
|
+
criticalGaps: { type: 'array', items: { type: 'string' } },
|
|
207
|
+
highGaps: { type: 'array', items: { type: 'string' } },
|
|
208
|
+
mediumGaps: { type: 'array', items: { type: 'string' } },
|
|
209
|
+
lowGaps: { type: 'array', items: { type: 'string' } }
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
recommendations: {
|
|
213
|
+
type: 'array',
|
|
214
|
+
items: {
|
|
215
|
+
type: 'object',
|
|
216
|
+
required: ['priority', 'action'],
|
|
217
|
+
properties: {
|
|
218
|
+
priority: { type: 'string', enum: ['URGENT', 'HIGH', 'MEDIUM', 'LOW'] },
|
|
219
|
+
action: { type: 'string' },
|
|
220
|
+
requirements: { type: 'array', items: { type: 'string' } }
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
// Phase 1 gate from the source: Step 5 must not run unless Phase 1 is complete.
|
|
230
|
+
if (coverage.phase !== 'PHASE_1_COMPLETE') {
|
|
231
|
+
return {
|
|
232
|
+
workflow: 'testarch-trace',
|
|
233
|
+
runId,
|
|
234
|
+
summary: 'Phase 1 (coverage matrix) did not complete; gate decision cannot proceed.',
|
|
235
|
+
steps: 5,
|
|
236
|
+
completedSteps: 4,
|
|
237
|
+
halted: true,
|
|
238
|
+
haltReason: 'Phase 1 not complete - cannot proceed to gate decision.',
|
|
239
|
+
needsHumanGate: true,
|
|
240
|
+
result: { context, tests, matrix, coverage }
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Step 5 (steps-c/step-05-gate-decision.md): Phase 2. Apply the DETERMINISTIC
|
|
245
|
+
// gate decision tree from the source over the Phase 1 statistics. The
|
|
246
|
+
// rule-based verdict is computed; the human accept/waive decision stays OUT of
|
|
247
|
+
// this script (returned for the gate). decisionMode=manual is surfaced too.
|
|
248
|
+
phase('GATE_DECISION')
|
|
249
|
+
const gate = await agent(
|
|
250
|
+
[
|
|
251
|
+
'Step 5 of testarch-trace (Phase 2 gate). Read ' + source + '/steps-c/step-05-gate-decision.md and follow its deterministic decision tree.',
|
|
252
|
+
'Inputs from Phase 1: p0Coverage = coverage_statistics.p0Percentage, overallCoverage = coverage_statistics.overallCoveragePercentage, criticalGaps = count of P0 uncovered.',
|
|
253
|
+
'Apply EXACTLY these rules in order:',
|
|
254
|
+
'(1) if p0Coverage < 100 -> FAIL (P0 coverage below required 100%, critical requirements uncovered).',
|
|
255
|
+
'(2) else if overallCoverage >= 90 -> PASS (P0 at 100% and overall meets the 90% target).',
|
|
256
|
+
'(3) else if overallCoverage >= 75 -> CONCERNS (P0 at 100% but overall below 90% target).',
|
|
257
|
+
'(4) else -> FAIL (overall below the 75% minimum, significant gaps).',
|
|
258
|
+
'Gate type: ' + gateType + '. Decision mode: ' + decisionMode + '.',
|
|
259
|
+
'WAIVED is a manual stakeholder waiver only and is NOT decided here: leave waiverApplicable as a flag for the human gate. Do not self-apply a waiver.',
|
|
260
|
+
'Return the deterministic decision, the rationale string, and the gate criteria (p0 met?, overall status MET/PARTIAL/NOT MET).'
|
|
261
|
+
].join(' '),
|
|
262
|
+
{
|
|
263
|
+
label: 'gate-decision',
|
|
264
|
+
phase: 'GATE_DECISION',
|
|
265
|
+
schema: {
|
|
266
|
+
type: 'object',
|
|
267
|
+
required: ['decision', 'rationale', 'gateCriteria'],
|
|
268
|
+
properties: {
|
|
269
|
+
decision: { type: 'string', enum: ['PASS', 'CONCERNS', 'FAIL'] },
|
|
270
|
+
rationale: { type: 'string' },
|
|
271
|
+
gateCriteria: {
|
|
272
|
+
type: 'object',
|
|
273
|
+
required: ['p0Status', 'overallStatus'],
|
|
274
|
+
properties: {
|
|
275
|
+
p0CoverageRequired: { type: 'string' },
|
|
276
|
+
p0CoverageActual: { type: 'string' },
|
|
277
|
+
p0Status: { type: 'string', enum: ['MET', 'NOT MET'] },
|
|
278
|
+
overallCoverageTarget: { type: 'string' },
|
|
279
|
+
overallCoverageActual: { type: 'string' },
|
|
280
|
+
overallStatus: { type: 'string', enum: ['MET', 'PARTIAL', 'NOT MET'] }
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
waiverApplicable: { type: 'boolean' },
|
|
284
|
+
uncoveredRequirements: { type: 'array', items: { type: 'string' } }
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
// Single top-level return. The deterministic verdict travels to the human gate;
|
|
291
|
+
// the human (with gate_type=' + gateType + ') may PASS-through, demand fixes, or
|
|
292
|
+
// apply a WAIVED override. None of that is decided in-script.
|
|
293
|
+
return {
|
|
294
|
+
workflow: 'testarch-trace',
|
|
295
|
+
runId,
|
|
296
|
+
gateType,
|
|
297
|
+
decisionMode,
|
|
298
|
+
summary:
|
|
299
|
+
'Traceability matrix built and quality gate evaluated deterministically: ' +
|
|
300
|
+
gate.decision +
|
|
301
|
+
'. ' +
|
|
302
|
+
gate.rationale,
|
|
303
|
+
steps: 5,
|
|
304
|
+
completedSteps: 5,
|
|
305
|
+
halted: false,
|
|
306
|
+
decision: gate.decision,
|
|
307
|
+
waiverApplicable: gate.waiverApplicable === true,
|
|
308
|
+
needsHumanGate: true,
|
|
309
|
+
result: {
|
|
310
|
+
context,
|
|
311
|
+
tests,
|
|
312
|
+
matrix,
|
|
313
|
+
coverage,
|
|
314
|
+
gate
|
|
315
|
+
}
|
|
316
|
+
}
|