@planu/cli 4.10.5 → 4.10.7
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 +20 -0
- package/dist/engine/compact/tool-list-compactor.d.ts +1 -1
- package/dist/engine/evidence-gates/lifecycle-gate.js +17 -1
- package/dist/engine/evidence-index/done-drift.js +4 -1
- package/dist/engine/local-first/tool-classification.d.ts +7 -0
- package/dist/engine/local-first/tool-classification.js +313 -0
- package/dist/engine/qa-gate.js +13 -1
- package/dist/engine/scope-boundaries/scope-validator.js +14 -2
- package/dist/engine/validator/validation-report-writer.js +3 -4
- package/dist/tools/audit.js +1 -1
- package/dist/tools/challenge-spec.js +52 -2
- package/dist/tools/check-readiness.js +15 -8
- package/dist/tools/data-governance/audit-handler.js +21 -6
- package/dist/tools/data-governance/detect-handler.js +32 -15
- package/dist/tools/define-ui-contract.js +1 -4
- package/dist/tools/design-schema.js +1 -4
- package/dist/tools/detect-drift.js +48 -28
- package/dist/tools/flag-spec-gap.js +47 -12
- package/dist/tools/generate-sub-agent.js +2 -4
- package/dist/tools/list-specs.js +15 -59
- package/dist/tools/orchestrate-locking.js +15 -4
- package/dist/tools/output-compressor.d.ts +14 -1
- package/dist/tools/output-compressor.js +121 -2
- package/dist/tools/package-handoff.js +136 -12
- package/dist/tools/reverse-engineer/handler.js +9 -10
- package/dist/tools/rollback-release.js +20 -2
- package/dist/tools/spec-diff-handler.js +3 -3
- package/dist/tools/status-handler.js +6 -1
- package/dist/tools/suggest-mcp-server.js +2 -4
- package/dist/tools/token-recording.d.ts +2 -1
- package/dist/tools/token-recording.js +21 -2
- package/dist/tools/update-status/dod-gates.d.ts +2 -2
- package/dist/tools/update-status/dod-gates.js +47 -52
- package/dist/tools/update-status/evidence-gate.js +6 -4
- package/dist/tools/update-status/index.js +10 -3
- package/dist/tools/update-status/qa-gate.d.ts +5 -0
- package/dist/tools/update-status/qa-gate.js +50 -0
- package/dist/tools/update-status/response-builder.js +96 -2
- package/dist/tools/update-status/transition-guard.js +33 -11
- package/dist/tools/update-status-convention-gate.js +22 -6
- package/dist/tools/validate-team-results.js +23 -1
- package/dist/tools/validate.js +137 -16
- package/dist/transports/http-transport.js +13 -0
- package/dist/transports/transport-factory.js +13 -0
- package/dist/types/qa-gate.d.ts +3 -0
- package/dist/types/tool-classification.d.ts +8 -0
- package/dist/types/tool-classification.js +2 -0
- package/package.json +10 -10
- package/planu-native.json +1 -1
- package/planu-plugin.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
## [4.10.7] - 2026-07-07
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
- fix(SPEC-1114): reduce MCP token waste
|
|
5
|
+
|
|
6
|
+
### Chores
|
|
7
|
+
- chore(deps): update patch/minor dependencies
|
|
8
|
+
- chore(planu): clear released pending specs
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## [4.10.6] - 2026-07-07
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
- fix(SPEC-1111): stabilize update_status done gates and evidence state transitions
|
|
15
|
+
- fix(SPEC-1112): preserve validation-report lint evidence during done gate
|
|
16
|
+
|
|
17
|
+
### Improvements
|
|
18
|
+
- refactor(SPEC-1110): reduce MCP token payloads with local-first tool classification
|
|
19
|
+
|
|
20
|
+
|
|
1
21
|
## [4.10.5] - 2026-07-07
|
|
2
22
|
|
|
3
23
|
### Bug Fixes
|
|
@@ -3,5 +3,5 @@ export declare function compactToolList(tools: unknown[]): Record<string, unknow
|
|
|
3
3
|
/** Returns true when an outgoing JSON-RPC message is a tools/list result. */
|
|
4
4
|
export declare function isToolsListResponse(message: unknown): boolean;
|
|
5
5
|
/** Compact a tools/list JSON-RPC response message. Returns a new object. */
|
|
6
|
-
export declare function compactToolsListMessage
|
|
6
|
+
export declare function compactToolsListMessage<T extends Record<string, unknown>>(message: T): T;
|
|
7
7
|
//# sourceMappingURL=tool-list-compactor.d.ts.map
|
|
@@ -114,9 +114,25 @@ export function checkDoneEvidenceGate(spec, criteria, artifacts) {
|
|
|
114
114
|
!hasText(row.validationEvidence) ||
|
|
115
115
|
!hasText(row.reviewerEvidence));
|
|
116
116
|
if (incompleteRows.length > 0) {
|
|
117
|
+
const rowDetails = incompleteRows.map((row) => {
|
|
118
|
+
const missing = [];
|
|
119
|
+
if (!rowHasEvidence(row)) {
|
|
120
|
+
missing.push('scenario/testEvidence/contractEvidence/manualEvidence');
|
|
121
|
+
}
|
|
122
|
+
if (row.changedFiles.length === 0) {
|
|
123
|
+
missing.push('changedFiles');
|
|
124
|
+
}
|
|
125
|
+
if (!hasText(row.validationEvidence)) {
|
|
126
|
+
missing.push('validationEvidence');
|
|
127
|
+
}
|
|
128
|
+
if (!hasText(row.reviewerEvidence)) {
|
|
129
|
+
missing.push('reviewerEvidence');
|
|
130
|
+
}
|
|
131
|
+
return `${row.acceptanceCriterion}: missing ${missing.join(', ')}`;
|
|
132
|
+
});
|
|
117
133
|
issues.push({
|
|
118
134
|
code: 'traceability_incomplete_rows',
|
|
119
|
-
message:
|
|
135
|
+
message: `Traceability rows must include evidence, changed files, validation evidence, and reviewer evidence. Incomplete rows: ${rowDetails.join('; ')}`,
|
|
120
136
|
});
|
|
121
137
|
}
|
|
122
138
|
}
|
|
@@ -33,9 +33,12 @@ export function checkDoneDriftContract(args) {
|
|
|
33
33
|
.map((record) => `${entry.criterion}: ${record.value}`);
|
|
34
34
|
});
|
|
35
35
|
if (unapproved.length > 0) {
|
|
36
|
+
const groundedHint = args.groundedTechnicalPaths.length > 0
|
|
37
|
+
? ` Grounded technical paths: ${args.groundedTechnicalPaths.join(', ')}.`
|
|
38
|
+
: ' No grounded technical paths were found in the approved spec.';
|
|
36
39
|
issues.push({
|
|
37
40
|
code: 'done_drift_unapproved_scope',
|
|
38
|
-
message: `Done drift check found changed files outside grounded spec scope: ${unapproved.join('; ')}
|
|
41
|
+
message: `Done drift check found changed files outside grounded spec scope: ${unapproved.join('; ')}.${groundedHint} Add the file to approved technical references or include reviewerEvidence with an approved scope amendment.`,
|
|
39
42
|
});
|
|
40
43
|
}
|
|
41
44
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ToolClassification } from '../../types/tool-classification.js';
|
|
2
|
+
export declare function getToolClassification(toolName: string): ToolClassification;
|
|
3
|
+
export declare function hasExplicitToolClassification(toolName: string): boolean;
|
|
4
|
+
export declare function isLocalFirstTool(toolName: string): boolean;
|
|
5
|
+
export declare function requiresLlmByDefault(toolName: string): boolean;
|
|
6
|
+
export declare function listToolClassifications(): ToolClassification[];
|
|
7
|
+
//# sourceMappingURL=tool-classification.d.ts.map
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
const REGISTERED_TOOL_NAMES = [
|
|
2
|
+
'agent_run_history',
|
|
3
|
+
'analyze_spec_dependencies',
|
|
4
|
+
'approval_status',
|
|
5
|
+
'approve_spec',
|
|
6
|
+
'assess_merge_risk',
|
|
7
|
+
'assign_role',
|
|
8
|
+
'audit',
|
|
9
|
+
'audit_claude_config',
|
|
10
|
+
'audit_log_export',
|
|
11
|
+
'audit_schema_type_parity',
|
|
12
|
+
'audit_specs_drift',
|
|
13
|
+
'bump_spec_version',
|
|
14
|
+
'challenge_spec',
|
|
15
|
+
'check_compliance',
|
|
16
|
+
'check_config_health',
|
|
17
|
+
'check_spec_accuracy',
|
|
18
|
+
'check_versions',
|
|
19
|
+
'clarify_requirements',
|
|
20
|
+
'code_graph_status',
|
|
21
|
+
'compliance_coverage_report',
|
|
22
|
+
'compliance_gap_analysis',
|
|
23
|
+
'compliance_gate_status',
|
|
24
|
+
'compliance_score_report',
|
|
25
|
+
'config_health',
|
|
26
|
+
'configure_approval_policy',
|
|
27
|
+
'configure_code_graph',
|
|
28
|
+
'configure_compliance',
|
|
29
|
+
'configure_compliance_gate',
|
|
30
|
+
'configure_memory',
|
|
31
|
+
'configure_roles',
|
|
32
|
+
'configure_squad',
|
|
33
|
+
'configure_workers',
|
|
34
|
+
'consult_docs',
|
|
35
|
+
'context_budget',
|
|
36
|
+
'coverage_gap_analyzer',
|
|
37
|
+
'create_spec',
|
|
38
|
+
'data_retention_policy',
|
|
39
|
+
'define_spec_lint_rule',
|
|
40
|
+
'define_ui_contract',
|
|
41
|
+
'delete_spec_lint_rule',
|
|
42
|
+
'design_schema',
|
|
43
|
+
'detect_agent',
|
|
44
|
+
'detect_drift',
|
|
45
|
+
'discover_docs_url',
|
|
46
|
+
'discover_mcps',
|
|
47
|
+
'discover_registry',
|
|
48
|
+
'ears_lint',
|
|
49
|
+
'ecosystem_status',
|
|
50
|
+
'estimate',
|
|
51
|
+
'eval_rule',
|
|
52
|
+
'eval_skill',
|
|
53
|
+
'export_audit_trail',
|
|
54
|
+
'expose_spec_as_prompt',
|
|
55
|
+
'fix_schema_type_parity',
|
|
56
|
+
'free_tool',
|
|
57
|
+
'generate_adr',
|
|
58
|
+
'generate_api_client',
|
|
59
|
+
'generate_checklist',
|
|
60
|
+
'generate_compliance_report',
|
|
61
|
+
'generate_compliance_tests',
|
|
62
|
+
'generate_docs',
|
|
63
|
+
'generate_docs_site',
|
|
64
|
+
'generate_execution_plan',
|
|
65
|
+
'generate_orchestration_script',
|
|
66
|
+
'generate_rules',
|
|
67
|
+
'generate_skill',
|
|
68
|
+
'generate_sub_agent',
|
|
69
|
+
'generate_tests',
|
|
70
|
+
'generate_validation_schema',
|
|
71
|
+
'graph_specs',
|
|
72
|
+
'guided_tour',
|
|
73
|
+
'init_constitution',
|
|
74
|
+
'init_project',
|
|
75
|
+
'integrate_pm',
|
|
76
|
+
'issue_reviewer_token',
|
|
77
|
+
'learn_pattern',
|
|
78
|
+
'list_spec_lint_rules',
|
|
79
|
+
'list_spec_prompts',
|
|
80
|
+
'list_specs',
|
|
81
|
+
'manage_context',
|
|
82
|
+
'manage_git',
|
|
83
|
+
'manage_plugins',
|
|
84
|
+
'memory_status',
|
|
85
|
+
'oauth_status',
|
|
86
|
+
'onboarding_checklist',
|
|
87
|
+
'onboarding_progress',
|
|
88
|
+
'orchestrate',
|
|
89
|
+
'paradigm_report',
|
|
90
|
+
'pin_version',
|
|
91
|
+
'pro_tool',
|
|
92
|
+
'publish_registry',
|
|
93
|
+
'quick_start',
|
|
94
|
+
'reconcile_hooks',
|
|
95
|
+
'reconcile_rules',
|
|
96
|
+
'reconcile_skills',
|
|
97
|
+
'reconcile_spec',
|
|
98
|
+
'red_team',
|
|
99
|
+
'registry_install',
|
|
100
|
+
'registry_login',
|
|
101
|
+
'registry_logout',
|
|
102
|
+
'registry_publish',
|
|
103
|
+
'registry_search',
|
|
104
|
+
'registry_whoami',
|
|
105
|
+
'request_changes',
|
|
106
|
+
'reverse_engineer',
|
|
107
|
+
'rewrite_criteria_ears',
|
|
108
|
+
'run_mutation_hints',
|
|
109
|
+
'run_spec_lint',
|
|
110
|
+
'scaffold_plugin',
|
|
111
|
+
'scan_orphan_spec_refs',
|
|
112
|
+
'search_all_projects',
|
|
113
|
+
'set_locale',
|
|
114
|
+
'set_work_mode',
|
|
115
|
+
'skill_install',
|
|
116
|
+
'skill_search',
|
|
117
|
+
'spec_obesity_healer',
|
|
118
|
+
'squad_status',
|
|
119
|
+
'ssr_back_migration',
|
|
120
|
+
'start_oauth_flow',
|
|
121
|
+
'start_onboarding',
|
|
122
|
+
'suggest_mcp_server',
|
|
123
|
+
'suggest_mcps',
|
|
124
|
+
'suggest_stack',
|
|
125
|
+
'suggest_token_optimizer',
|
|
126
|
+
'suggest_tooling',
|
|
127
|
+
'summarize_spec',
|
|
128
|
+
'sync_ai_configs',
|
|
129
|
+
'tdd_scaffold',
|
|
130
|
+
'token_optimizer_status',
|
|
131
|
+
'tool',
|
|
132
|
+
'type_safety_gate',
|
|
133
|
+
'update_registry',
|
|
134
|
+
'update_status',
|
|
135
|
+
'update_status_batch',
|
|
136
|
+
'validate',
|
|
137
|
+
'validate_api_contract',
|
|
138
|
+
'validate_criteria_quality',
|
|
139
|
+
'validate_docs_registry',
|
|
140
|
+
'verify_spec_compliance',
|
|
141
|
+
'worker_status',
|
|
142
|
+
];
|
|
143
|
+
const LEGACY_OR_RUNTIME_TOOL_NAMES = [
|
|
144
|
+
'multi_agent_review',
|
|
145
|
+
'orchestrate_runtime',
|
|
146
|
+
'package_handoff',
|
|
147
|
+
'recommend_model',
|
|
148
|
+
];
|
|
149
|
+
const LLM_REQUIRED_TOOL_NAMES = ['create_spec'];
|
|
150
|
+
const HYBRID_TOOL_NAMES = [
|
|
151
|
+
'challenge_spec',
|
|
152
|
+
'clarify_requirements',
|
|
153
|
+
'consult_docs',
|
|
154
|
+
'define_ui_contract',
|
|
155
|
+
'design_schema',
|
|
156
|
+
'estimate',
|
|
157
|
+
'eval_skill',
|
|
158
|
+
'generate_adr',
|
|
159
|
+
'generate_api_client',
|
|
160
|
+
'generate_checklist',
|
|
161
|
+
'generate_compliance_report',
|
|
162
|
+
'generate_compliance_tests',
|
|
163
|
+
'generate_docs',
|
|
164
|
+
'generate_docs_site',
|
|
165
|
+
'generate_execution_plan',
|
|
166
|
+
'generate_orchestration_script',
|
|
167
|
+
'generate_rules',
|
|
168
|
+
'generate_skill',
|
|
169
|
+
'generate_sub_agent',
|
|
170
|
+
'generate_tests',
|
|
171
|
+
'generate_validation_schema',
|
|
172
|
+
'multi_agent_review',
|
|
173
|
+
'paradigm_report',
|
|
174
|
+
'reconcile_spec',
|
|
175
|
+
'red_team',
|
|
176
|
+
'reverse_engineer',
|
|
177
|
+
'rewrite_criteria_ears',
|
|
178
|
+
'spec_obesity_healer',
|
|
179
|
+
'ssr_back_migration',
|
|
180
|
+
'suggest_mcp_server',
|
|
181
|
+
'suggest_mcps',
|
|
182
|
+
'suggest_stack',
|
|
183
|
+
'suggest_token_optimizer',
|
|
184
|
+
'suggest_tooling',
|
|
185
|
+
'tdd_scaffold',
|
|
186
|
+
];
|
|
187
|
+
const NO_LLM_LOCAL_TOOL_NAMES = [
|
|
188
|
+
'approval_status',
|
|
189
|
+
'approve_spec',
|
|
190
|
+
'check_versions',
|
|
191
|
+
'code_graph_status',
|
|
192
|
+
'config_health',
|
|
193
|
+
'configure_approval_policy',
|
|
194
|
+
'configure_code_graph',
|
|
195
|
+
'configure_compliance',
|
|
196
|
+
'configure_compliance_gate',
|
|
197
|
+
'configure_memory',
|
|
198
|
+
'configure_roles',
|
|
199
|
+
'configure_squad',
|
|
200
|
+
'configure_workers',
|
|
201
|
+
'context_budget',
|
|
202
|
+
'detect_agent',
|
|
203
|
+
'discover_docs_url',
|
|
204
|
+
'discover_mcps',
|
|
205
|
+
'discover_registry',
|
|
206
|
+
'ears_lint',
|
|
207
|
+
'free_tool',
|
|
208
|
+
'issue_reviewer_token',
|
|
209
|
+
'list_spec_lint_rules',
|
|
210
|
+
'list_spec_prompts',
|
|
211
|
+
'list_specs',
|
|
212
|
+
'manage_context',
|
|
213
|
+
'manage_git',
|
|
214
|
+
'manage_plugins',
|
|
215
|
+
'memory_status',
|
|
216
|
+
'oauth_status',
|
|
217
|
+
'orchestrate',
|
|
218
|
+
'orchestrate_runtime',
|
|
219
|
+
'package_handoff',
|
|
220
|
+
'pin_version',
|
|
221
|
+
'pro_tool',
|
|
222
|
+
'registry_install',
|
|
223
|
+
'registry_login',
|
|
224
|
+
'registry_logout',
|
|
225
|
+
'registry_publish',
|
|
226
|
+
'registry_search',
|
|
227
|
+
'registry_whoami',
|
|
228
|
+
'run_spec_lint',
|
|
229
|
+
'scan_orphan_spec_refs',
|
|
230
|
+
'search_all_projects',
|
|
231
|
+
'set_locale',
|
|
232
|
+
'set_work_mode',
|
|
233
|
+
'skill_install',
|
|
234
|
+
'skill_search',
|
|
235
|
+
'squad_status',
|
|
236
|
+
'start_oauth_flow',
|
|
237
|
+
'summarize_spec',
|
|
238
|
+
'token_optimizer_status',
|
|
239
|
+
'tool',
|
|
240
|
+
'update_registry',
|
|
241
|
+
'update_status',
|
|
242
|
+
'update_status_batch',
|
|
243
|
+
'validate',
|
|
244
|
+
'worker_status',
|
|
245
|
+
];
|
|
246
|
+
const EXPLICIT_TOOL_NAMES = new Set([
|
|
247
|
+
...REGISTERED_TOOL_NAMES,
|
|
248
|
+
...LEGACY_OR_RUNTIME_TOOL_NAMES,
|
|
249
|
+
]);
|
|
250
|
+
const LLM_REQUIRED_TOOLS = new Set(LLM_REQUIRED_TOOL_NAMES);
|
|
251
|
+
const HYBRID_TOOLS = new Set(HYBRID_TOOL_NAMES);
|
|
252
|
+
const NO_LLM_LOCAL_TOOLS = new Set(NO_LLM_LOCAL_TOOL_NAMES);
|
|
253
|
+
function executionClassFor(toolName) {
|
|
254
|
+
if (LLM_REQUIRED_TOOLS.has(toolName)) {
|
|
255
|
+
return 'llm-required';
|
|
256
|
+
}
|
|
257
|
+
if (HYBRID_TOOLS.has(toolName)) {
|
|
258
|
+
return 'hybrid';
|
|
259
|
+
}
|
|
260
|
+
return 'local';
|
|
261
|
+
}
|
|
262
|
+
function reasonFor(toolName, executionClass) {
|
|
263
|
+
if (executionClass === 'llm-required') {
|
|
264
|
+
return 'Creates the primary spec artifact and requires language synthesis from user intent.';
|
|
265
|
+
}
|
|
266
|
+
if (executionClass === 'hybrid') {
|
|
267
|
+
return 'Runs local discovery, validation, or scaffolding first; only synthesis/review steps should use LLM tokens.';
|
|
268
|
+
}
|
|
269
|
+
if (NO_LLM_LOCAL_TOOLS.has(toolName)) {
|
|
270
|
+
return 'Uses deterministic local project state and should not call an LLM by default.';
|
|
271
|
+
}
|
|
272
|
+
return 'Processes local project artifacts and returns bounded summaries before any model-facing handoff.';
|
|
273
|
+
}
|
|
274
|
+
function tokenPolicyFor(toolName, executionClass) {
|
|
275
|
+
if (executionClass === 'llm-required') {
|
|
276
|
+
return 'llm-product';
|
|
277
|
+
}
|
|
278
|
+
if (executionClass === 'local' && NO_LLM_LOCAL_TOOLS.has(toolName)) {
|
|
279
|
+
return 'no-llm';
|
|
280
|
+
}
|
|
281
|
+
return 'summarize-first';
|
|
282
|
+
}
|
|
283
|
+
function buildClassification(toolName) {
|
|
284
|
+
const executionClass = executionClassFor(toolName);
|
|
285
|
+
return {
|
|
286
|
+
toolName,
|
|
287
|
+
executionClass,
|
|
288
|
+
tokenPolicy: tokenPolicyFor(toolName, executionClass),
|
|
289
|
+
reason: reasonFor(toolName, executionClass),
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
const TOOL_CLASSIFICATIONS = new Map([...EXPLICIT_TOOL_NAMES].map((toolName) => [toolName, buildClassification(toolName)]));
|
|
293
|
+
export function getToolClassification(toolName) {
|
|
294
|
+
return (TOOL_CLASSIFICATIONS.get(toolName) ?? {
|
|
295
|
+
toolName,
|
|
296
|
+
executionClass: 'hybrid',
|
|
297
|
+
tokenPolicy: 'summarize-first',
|
|
298
|
+
reason: 'Unregistered tools default to local preprocessing with bounded summaries.',
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
export function hasExplicitToolClassification(toolName) {
|
|
302
|
+
return TOOL_CLASSIFICATIONS.has(toolName);
|
|
303
|
+
}
|
|
304
|
+
export function isLocalFirstTool(toolName) {
|
|
305
|
+
return getToolClassification(toolName).executionClass !== 'llm-required';
|
|
306
|
+
}
|
|
307
|
+
export function requiresLlmByDefault(toolName) {
|
|
308
|
+
return getToolClassification(toolName).executionClass === 'llm-required';
|
|
309
|
+
}
|
|
310
|
+
export function listToolClassifications() {
|
|
311
|
+
return [...TOOL_CLASSIFICATIONS.values()].sort((a, b) => a.toolName.localeCompare(b.toolName));
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=tool-classification.js.map
|
package/dist/engine/qa-gate.js
CHANGED
|
@@ -20,7 +20,19 @@ function runCheck(name, command, args, cwd) {
|
|
|
20
20
|
const durationMs = Date.now() - start;
|
|
21
21
|
const passed = result.status === 0 && !result.error;
|
|
22
22
|
const output = [result.stdout, result.stderr].join('\n').trim().slice(0, 2000);
|
|
23
|
-
|
|
23
|
+
const errorCode = result.error && 'code' in result.error
|
|
24
|
+
? String(result.error.code)
|
|
25
|
+
: undefined;
|
|
26
|
+
const timedOut = errorCode === 'ETIMEDOUT' || result.signal === 'SIGTERM';
|
|
27
|
+
return {
|
|
28
|
+
name,
|
|
29
|
+
passed,
|
|
30
|
+
output,
|
|
31
|
+
durationMs,
|
|
32
|
+
command: [command, ...args].join(' '),
|
|
33
|
+
timedOut,
|
|
34
|
+
errorMessage: result.error?.message,
|
|
35
|
+
};
|
|
24
36
|
}
|
|
25
37
|
export function runQaGate(spec, projectPath) {
|
|
26
38
|
const coverageThreshold = extractCoverageThreshold(spec);
|
|
@@ -11,11 +11,23 @@ function normalize(text) {
|
|
|
11
11
|
/**
|
|
12
12
|
* Extract keywords from an out-of-scope item phrase (>3 chars).
|
|
13
13
|
*/
|
|
14
|
-
const STOP_WORDS = new Set(['the', 'and', 'for', 'with', 'that', 'this', 'from', 'will']);
|
|
14
|
+
const STOP_WORDS = new Set(['the', 'and', 'for', 'with', 'that', 'this', 'from', 'will', 'every']);
|
|
15
|
+
const GENERIC_SCOPE_TOKENS = new Set([
|
|
16
|
+
'code',
|
|
17
|
+
'file',
|
|
18
|
+
'files',
|
|
19
|
+
'module',
|
|
20
|
+
'modules',
|
|
21
|
+
'mcp',
|
|
22
|
+
'response',
|
|
23
|
+
'responses',
|
|
24
|
+
'tool',
|
|
25
|
+
'tools',
|
|
26
|
+
]);
|
|
15
27
|
function keywords(phrase) {
|
|
16
28
|
return normalize(phrase)
|
|
17
29
|
.split(' ')
|
|
18
|
-
.filter((w) => w.length >
|
|
30
|
+
.filter((w) => w.length > 2 && !STOP_WORDS.has(w) && !GENERIC_SCOPE_TOKENS.has(w));
|
|
19
31
|
}
|
|
20
32
|
/**
|
|
21
33
|
* Determine whether a file path matches an out-of-scope item.
|
|
@@ -9,7 +9,7 @@ export async function writeImplementationReviewReport(input) {
|
|
|
9
9
|
specCompliance,
|
|
10
10
|
minimalityReport: input.minimalityReport,
|
|
11
11
|
});
|
|
12
|
-
const mergedGates = await
|
|
12
|
+
const mergedGates = await preserveUnobservedGates(input, gates);
|
|
13
13
|
const passed = mergedGates.every((gate) => gate.passed);
|
|
14
14
|
const reviewer = {
|
|
15
15
|
kind: 'implementation-review-agent',
|
|
@@ -56,7 +56,7 @@ export async function writeImplementationReviewReport(input) {
|
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
async function
|
|
59
|
+
async function preserveUnobservedGates(input, nextGates) {
|
|
60
60
|
const currentGateNames = new Set(nextGates.map((gate) => gate.name));
|
|
61
61
|
const explicitlyObserved = new Set([
|
|
62
62
|
...(input.lintPassed !== undefined ? ['lint'] : []),
|
|
@@ -71,8 +71,7 @@ async function preserveUnobservedBlockingGates(input, nextGates) {
|
|
|
71
71
|
if (!existing?.ok) {
|
|
72
72
|
return nextGates;
|
|
73
73
|
}
|
|
74
|
-
const preserved = existing.payload.gates.filter((gate) =>
|
|
75
|
-
currentGateNames.has(gate.name) &&
|
|
74
|
+
const preserved = existing.payload.gates.filter((gate) => currentGateNames.has(gate.name) &&
|
|
76
75
|
!explicitlyObserved.has(gate.name) &&
|
|
77
76
|
(gate.name === 'lint' ||
|
|
78
77
|
gate.name === 'convention-regression' ||
|
package/dist/tools/audit.js
CHANGED
|
@@ -139,8 +139,8 @@ export async function handleAudit(args) {
|
|
|
139
139
|
findingsCount: String(result.findings.length),
|
|
140
140
|
}),
|
|
141
141
|
},
|
|
142
|
-
{ type: 'text', text: JSON.stringify(output, null, 2) },
|
|
143
142
|
],
|
|
143
|
+
structuredContent: output,
|
|
144
144
|
};
|
|
145
145
|
}
|
|
146
146
|
function scoreToGrade(score) {
|
|
@@ -32,6 +32,7 @@ const ALL_FOCUS_AREAS = [
|
|
|
32
32
|
'security',
|
|
33
33
|
'data-consistency',
|
|
34
34
|
];
|
|
35
|
+
const ACTIONABLE_RELEVANCE_MIN = 15;
|
|
35
36
|
/**
|
|
36
37
|
* SPEC-615 AC3: detect contradictions between spec criteria and prior decisions.
|
|
37
38
|
* Mutates failureScenarios in place. Best-effort — never throws.
|
|
@@ -242,8 +243,16 @@ export async function handleChallengeSpec(args, server) {
|
|
|
242
243
|
const match = prioritized.find((p) => p.scenario === s.scenario);
|
|
243
244
|
return match !== undefined ? { ...s, relevanceScore: match.relevanceScore } : s;
|
|
244
245
|
});
|
|
246
|
+
const prioritizedScenarios = new Set(prioritized.map((scenario) => scenario.scenario));
|
|
247
|
+
const actionableFailureScenarios = failureScenariosScored.filter((scenario) => prioritizedScenarios.has(scenario.scenario) ||
|
|
248
|
+
scenario.impact === 'critical' ||
|
|
249
|
+
scenario.impact === 'high' ||
|
|
250
|
+
isDomainRelevantScenario(scenario, specContent, knowledge) ||
|
|
251
|
+
(scenario.relevanceScore ?? computeScenarioRelevanceFallback(scenario)) >=
|
|
252
|
+
ACTIONABLE_RELEVANCE_MIN);
|
|
253
|
+
const suppressedScenarioCount = failureScenariosScored.length - actionableFailureScenarios.length;
|
|
245
254
|
const analysis = {
|
|
246
|
-
failureScenarios:
|
|
255
|
+
failureScenarios: actionableFailureScenarios,
|
|
247
256
|
concurrencyAnalysis,
|
|
248
257
|
scalabilityAssessment,
|
|
249
258
|
overallRisk,
|
|
@@ -262,6 +271,8 @@ export async function handleChallengeSpec(args, server) {
|
|
|
262
271
|
},
|
|
263
272
|
summary: {
|
|
264
273
|
totalScenarios: failureScenarios.length,
|
|
274
|
+
actionableScenarios: actionableFailureScenarios.length,
|
|
275
|
+
suppressedLowRelevanceScenarios: suppressedScenarioCount,
|
|
265
276
|
shownByDefault: 3,
|
|
266
277
|
mustAddressBeforeCoding: prioritizedSummary,
|
|
267
278
|
criticalImpact: failureScenarios.filter((s) => s.impact === 'critical').length,
|
|
@@ -354,9 +365,48 @@ export async function handleChallengeSpec(args, server) {
|
|
|
354
365
|
scenarioCount: String(failureScenarios.length),
|
|
355
366
|
}),
|
|
356
367
|
},
|
|
357
|
-
{
|
|
368
|
+
{
|
|
369
|
+
type: 'text',
|
|
370
|
+
text: `Actionable scenarios: ${String(actionableFailureScenarios.length)}` +
|
|
371
|
+
` | Suppressed low-relevance: ${String(suppressedScenarioCount)}` +
|
|
372
|
+
` | Overall risk: ${overallRisk}`,
|
|
373
|
+
},
|
|
358
374
|
{ type: 'text', text: humanSummary },
|
|
359
375
|
],
|
|
376
|
+
structuredContent: analysisPayload,
|
|
360
377
|
};
|
|
361
378
|
}
|
|
379
|
+
function computeScenarioRelevanceFallback(scenario) {
|
|
380
|
+
if (scenario.impact === 'critical') {
|
|
381
|
+
return 100;
|
|
382
|
+
}
|
|
383
|
+
if (scenario.impact === 'high') {
|
|
384
|
+
return 70;
|
|
385
|
+
}
|
|
386
|
+
if (scenario.probability === 'high') {
|
|
387
|
+
return 35;
|
|
388
|
+
}
|
|
389
|
+
return 0;
|
|
390
|
+
}
|
|
391
|
+
function isDomainRelevantScenario(scenario, specContent, knowledge) {
|
|
392
|
+
const haystack = `${scenario.scenario} ${scenario.currentHandling} ${scenario.requiredHandling}`
|
|
393
|
+
.toLowerCase()
|
|
394
|
+
.trim();
|
|
395
|
+
const specText = specContent.toLowerCase();
|
|
396
|
+
const framework = knowledge.framework?.toLowerCase() ?? '';
|
|
397
|
+
const isGameProject = knowledge.projectCategory === 'game' ||
|
|
398
|
+
['unity', 'godot', 'unreal', 'bevy', 'phaser', 'pygame'].includes(framework);
|
|
399
|
+
if (isGameProject && /\b(game|physics|cheat|balance|save|hud|player)\b/.test(haystack)) {
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
if (/\b(database|query|index|pagination|transaction|cache)\b/.test(haystack) &&
|
|
403
|
+
/\b(database|query|index|pagination|transaction|cache|sql|postgres|sqlite)\b/.test(specText)) {
|
|
404
|
+
return true;
|
|
405
|
+
}
|
|
406
|
+
if (/\b(api|auth|jwt|session|csrf|injection|tenant|bola)\b/.test(haystack) &&
|
|
407
|
+
/\b(api|auth|jwt|session|csrf|injection|tenant|bola)\b/.test(specText)) {
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
362
412
|
//# sourceMappingURL=challenge-spec.js.map
|
|
@@ -6,6 +6,9 @@ import { buildCheckReadinessSummary } from '../engine/human-summary.js';
|
|
|
6
6
|
import { validateSpecFormat } from '../core/spec-validator.js';
|
|
7
7
|
import { resolveProjectId } from './resolve-project-id.js';
|
|
8
8
|
// ── Formatting helpers ───────────────────────────────────────────────────────
|
|
9
|
+
const MAX_VISIBLE_BLOCKERS = 8;
|
|
10
|
+
const MAX_VISIBLE_WARNINGS = 8;
|
|
11
|
+
const MAX_VISIBLE_RECOMMENDATIONS = 5;
|
|
9
12
|
function formatScore(score) {
|
|
10
13
|
if (score >= 90) {
|
|
11
14
|
return `${score}/100 (Excellent)`;
|
|
@@ -50,34 +53,38 @@ function formatReport(report) {
|
|
|
50
53
|
if (report.issues.blockers.length > 0) {
|
|
51
54
|
lines.push('## Blockers');
|
|
52
55
|
lines.push('');
|
|
53
|
-
for (const blocker of report.issues.blockers) {
|
|
56
|
+
for (const blocker of report.issues.blockers.slice(0, MAX_VISIBLE_BLOCKERS)) {
|
|
54
57
|
lines.push(`- BLOCKER: ${blocker}`);
|
|
55
58
|
}
|
|
59
|
+
if (report.issues.blockers.length > MAX_VISIBLE_BLOCKERS) {
|
|
60
|
+
lines.push(`- ...and ${String(report.issues.blockers.length - MAX_VISIBLE_BLOCKERS)} more`);
|
|
61
|
+
}
|
|
56
62
|
lines.push('');
|
|
57
63
|
}
|
|
58
64
|
// Warnings
|
|
59
65
|
if (report.issues.warnings.length > 0) {
|
|
60
66
|
lines.push('## Warnings');
|
|
61
67
|
lines.push('');
|
|
62
|
-
for (const warning of report.issues.warnings) {
|
|
68
|
+
for (const warning of report.issues.warnings.slice(0, MAX_VISIBLE_WARNINGS)) {
|
|
63
69
|
lines.push(`- WARNING: ${warning}`);
|
|
64
70
|
}
|
|
71
|
+
if (report.issues.warnings.length > MAX_VISIBLE_WARNINGS) {
|
|
72
|
+
lines.push(`- ...and ${String(report.issues.warnings.length - MAX_VISIBLE_WARNINGS)} more`);
|
|
73
|
+
}
|
|
65
74
|
lines.push('');
|
|
66
75
|
}
|
|
67
76
|
// Recommendations
|
|
68
77
|
if (report.recommendations.length > 0) {
|
|
69
78
|
lines.push('## Recommendations');
|
|
70
79
|
lines.push('');
|
|
71
|
-
for (const rec of report.recommendations) {
|
|
80
|
+
for (const rec of report.recommendations.slice(0, MAX_VISIBLE_RECOMMENDATIONS)) {
|
|
72
81
|
lines.push(`- ${rec}`);
|
|
73
82
|
}
|
|
83
|
+
if (report.recommendations.length > MAX_VISIBLE_RECOMMENDATIONS) {
|
|
84
|
+
lines.push(`- ...and ${String(report.recommendations.length - MAX_VISIBLE_RECOMMENDATIONS)} more`);
|
|
85
|
+
}
|
|
74
86
|
lines.push('');
|
|
75
87
|
}
|
|
76
|
-
// SPEC-042: Config health hint
|
|
77
|
-
lines.push('## Config Health');
|
|
78
|
-
lines.push('');
|
|
79
|
-
lines.push('- Run `check_config_health` to detect silent misconfigurations (tsconfig coverage, CI script references, build artifact gaps) that block DoR.');
|
|
80
|
-
lines.push('');
|
|
81
88
|
return lines.join('\n');
|
|
82
89
|
}
|
|
83
90
|
// ── Handler ──────────────────────────────────────────────────────────────────
|