@planu/cli 5.7.0 → 5.7.1
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 +21 -0
- package/dist/.planu-build.json +1 -1
- package/dist/engine/detectors/cache-db-detector.js +2 -2
- package/dist/engine/detectors/newsql-db-detector.js +2 -2
- package/dist/engine/detectors/search-engine-detector.js +2 -2
- package/dist/engine/detectors/vector-db-detector.js +2 -2
- package/dist/engine/detectors/widecolumn-db-detector.js +2 -2
- package/dist/engine/validator/reliability-gate.d.ts +4 -0
- package/dist/engine/validator/reliability-gate.js +93 -0
- package/dist/storage/migrations/canonical-storage.js +22 -9
- package/dist/tools/validate.js +36 -1
- package/dist/types/validation-evidence.d.ts +12 -0
- package/package.json +1 -1
- package/planu-plugin.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
## [5.7.1] - 2026-08-31
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
- fix(release): classify cherry-pick abort cleanup for reliability gate
|
|
5
|
+
- fix(test): serialize validate-execute.branches and git-setup suites to avoid full-load spawn flakes
|
|
6
|
+
- fix(test): pin reliability-gate suite in serial inventory snapshot
|
|
7
|
+
- fix(test): mock reliability policies output in lifecycle e2e and serialize reliability-gate suite
|
|
8
|
+
- fix: ignore and untrack runtime index backup files
|
|
9
|
+
- fix: untrack runtime housekeeping history leaked by runtime migration
|
|
10
|
+
- fix(test-infra): address SPEC-1707 review findings in spawn retry helper and lint
|
|
11
|
+
- fix(detectors): resolve datastore claims from runtime dependencies only (SPEC-1700)
|
|
12
|
+
- fix(test-infra): allowlist SPEC-1706 release harness spawner in child_process lint
|
|
13
|
+
- fix(test-infra): add transient spawn retry classifier and child_process lint (SPEC-1707)
|
|
14
|
+
- fix: address SPEC-1706 review findings in release.sh --abort repair
|
|
15
|
+
- fix: repair buried phantom bump commits in release.sh --abort (SPEC-1706)
|
|
16
|
+
- fix(validate): audit spec-declared files and dedupe reliability allowlist
|
|
17
|
+
- fix(validate): run reliability gate on spec-listed files before done (SPEC-1705)
|
|
18
|
+
- fix(storage): exclude atomic-write temp files and tolerate vanish races in inventoryStorageRoot
|
|
19
|
+
- fix: retire completed canonical migration journal on unchanged roots (SPEC-1711)
|
|
20
|
+
|
|
21
|
+
|
|
1
22
|
## [5.7.0] - 2026-08-31
|
|
2
23
|
|
|
3
24
|
### Features
|
package/dist/.planu-build.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"schemaVersion":1,"commit":"
|
|
1
|
+
{"schemaVersion":1,"commit":"04661e53067d139151a01671e92fec0f4f2ddfc1"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { technologyValue } from '../technology-registry.js';
|
|
2
|
-
import {
|
|
2
|
+
import { collectRuntimeDependencies, hasAnyFile } from '../detection-utils.js';
|
|
3
3
|
const VALKEY_DEPS = ['valkey', '@valkey/client', 'iovalkey'];
|
|
4
4
|
const REDIS_DEPS = [
|
|
5
5
|
technologyValue('technology-redis-34fb46'),
|
|
@@ -17,7 +17,7 @@ const REDIS_DEPS = [
|
|
|
17
17
|
];
|
|
18
18
|
const MEMCACHED_DEPS = ['memcached', 'memcachier', 'pymemcache', 'gomemcache', 'memcache', 'dalli'];
|
|
19
19
|
export async function detectCacheEngine(projectPath) {
|
|
20
|
-
const deps = await
|
|
20
|
+
const deps = await collectRuntimeDependencies(projectPath);
|
|
21
21
|
// Valkey first (Redis fork — more specific)
|
|
22
22
|
if (VALKEY_DEPS.some((d) => deps.has(d))) {
|
|
23
23
|
return 'valkey';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { collectRuntimeDependencies } from '../detection-utils.js';
|
|
2
2
|
const PLANETSCALE_DEPS = ['@planetscale/database', 'planetscale-go'];
|
|
3
3
|
const NEON_DEPS = ['@neondatabase/serverless', 'neon-serverless'];
|
|
4
4
|
const SPANNER_DEPS = [
|
|
@@ -10,7 +10,7 @@ const ALLOYDB_DEPS = ['@google-cloud/alloydb', 'google-cloud-alloydb-connector']
|
|
|
10
10
|
const COCKROACH_DEPS = ['cockroachdb', '@cockroachlabs/serverless'];
|
|
11
11
|
const TIDB_DEPS = ['tidb', 'tidb-serverless', '@tidbcloud/serverless'];
|
|
12
12
|
export async function detectNewSqlDb(projectPath) {
|
|
13
|
-
const deps = await
|
|
13
|
+
const deps = await collectRuntimeDependencies(projectPath);
|
|
14
14
|
// PlanetScale (specific SDK)
|
|
15
15
|
if (PLANETSCALE_DEPS.some((d) => deps.has(d))) {
|
|
16
16
|
return 'planetscale';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { collectRuntimeDependencies } from '../detection-utils.js';
|
|
2
2
|
const ALGOLIA_DEPS = [
|
|
3
3
|
'algoliasearch',
|
|
4
4
|
'@algolia/client-search',
|
|
@@ -25,7 +25,7 @@ const ELASTICSEARCH_DEPS = [
|
|
|
25
25
|
'chewy',
|
|
26
26
|
];
|
|
27
27
|
export async function detectSearchEngine(projectPath) {
|
|
28
|
-
const deps = await
|
|
28
|
+
const deps = await collectRuntimeDependencies(projectPath);
|
|
29
29
|
// Algolia (most specific deps)
|
|
30
30
|
if (ALGOLIA_DEPS.some((d) => deps.has(d))) {
|
|
31
31
|
return 'algolia';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { collectRuntimeDependencies } from '../detection-utils.js';
|
|
2
2
|
const PINECONE_DEPS = ['@pinecone-database/pinecone', 'pinecone-client', 'pinecone'];
|
|
3
3
|
const WEAVIATE_DEPS = ['weaviate-client', 'weaviate-ts-client', 'weaviate'];
|
|
4
4
|
const QDRANT_DEPS = ['@qdrant/js-client-rest', 'qdrant-client', 'qdrant_client'];
|
|
@@ -6,7 +6,7 @@ const MILVUS_DEPS = ['@zilliz/milvus2-sdk-node', 'pymilvus', 'milvus'];
|
|
|
6
6
|
const CHROMA_DEPS = ['chromadb', 'chromadb-client'];
|
|
7
7
|
const PGVECTOR_DEPS = ['pgvector', 'pgvector-node', 'pgvector-python'];
|
|
8
8
|
export async function detectVectorDb(projectPath) {
|
|
9
|
-
const deps = await
|
|
9
|
+
const deps = await collectRuntimeDependencies(projectPath);
|
|
10
10
|
if (PINECONE_DEPS.some((d) => deps.has(d))) {
|
|
11
11
|
return 'pinecone';
|
|
12
12
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { collectRuntimeDependencies } from '../detection-utils.js';
|
|
2
2
|
const SCYLLA_DEPS = ['scylladb', '@scylladb/driver', 'scylla-driver'];
|
|
3
3
|
const CASSANDRA_DEPS = [
|
|
4
4
|
'cassandra-driver',
|
|
@@ -16,7 +16,7 @@ const CLICKHOUSE_DEPS = [
|
|
|
16
16
|
];
|
|
17
17
|
const HBASE_DEPS = ['hbase', 'hbase-client', 'happybase', 'hbase-rpc-client'];
|
|
18
18
|
export async function detectWideColumnDb(projectPath) {
|
|
19
|
-
const deps = await
|
|
19
|
+
const deps = await collectRuntimeDependencies(projectPath);
|
|
20
20
|
// ScyllaDB (Cassandra-compatible but more specific)
|
|
21
21
|
if (SCYLLA_DEPS.some((d) => deps.has(d))) {
|
|
22
22
|
return 'scylladb';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ReliabilityGateResult, ReliabilityGateViolation } from '../../types/index.js';
|
|
2
|
+
export type { ReliabilityGateResult, ReliabilityGateViolation };
|
|
3
|
+
export declare function runReliabilityGate(projectPath: string, specFiles: string[]): ReliabilityGateResult;
|
|
4
|
+
//# sourceMappingURL=reliability-gate.d.ts.map
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { relative, resolve } from 'node:path';
|
|
3
|
+
let cachedProtectedDirectories;
|
|
4
|
+
function fetchProtectedDirectories() {
|
|
5
|
+
if (cachedProtectedDirectories !== undefined) {
|
|
6
|
+
return cachedProtectedDirectories;
|
|
7
|
+
}
|
|
8
|
+
const stdout = execFileSync(process.execPath, ['scripts/check-reliability-policies.mjs', '--print-protected-dirs'], { cwd: process.cwd(), encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
9
|
+
const parsed = JSON.parse(stdout);
|
|
10
|
+
if (!Array.isArray(parsed) || !parsed.every((entry) => typeof entry === 'string')) {
|
|
11
|
+
throw new Error('reliability gate protected-directories output is not a string array');
|
|
12
|
+
}
|
|
13
|
+
cachedProtectedDirectories = parsed;
|
|
14
|
+
return parsed;
|
|
15
|
+
}
|
|
16
|
+
function normalizeToProjectRelative(projectPath, file) {
|
|
17
|
+
return relative(projectPath, resolve(projectPath, file));
|
|
18
|
+
}
|
|
19
|
+
function isProtected(protectedDirectories, file) {
|
|
20
|
+
return protectedDirectories.some((directory) => file === directory || file.startsWith(`${directory}/`));
|
|
21
|
+
}
|
|
22
|
+
function parseViolations(stdout) {
|
|
23
|
+
const parsed = JSON.parse(stdout);
|
|
24
|
+
if (!Array.isArray(parsed)) {
|
|
25
|
+
throw new Error('reliability gate output is not a JSON array');
|
|
26
|
+
}
|
|
27
|
+
return parsed.map((entry) => {
|
|
28
|
+
if (typeof entry !== 'object' ||
|
|
29
|
+
entry === null ||
|
|
30
|
+
typeof entry.file !== 'string' ||
|
|
31
|
+
typeof entry.line !== 'number' ||
|
|
32
|
+
typeof entry.rule !== 'string') {
|
|
33
|
+
throw new Error('reliability gate output entry is malformed');
|
|
34
|
+
}
|
|
35
|
+
return entry;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
export function runReliabilityGate(projectPath, specFiles) {
|
|
39
|
+
const normalizedSpecFiles = new Set(specFiles.map((file) => normalizeToProjectRelative(projectPath, file)));
|
|
40
|
+
if (normalizedSpecFiles.size === 0) {
|
|
41
|
+
return { skipped: true, passed: true, failures: [], warnings: [] };
|
|
42
|
+
}
|
|
43
|
+
let protectedDirectories;
|
|
44
|
+
try {
|
|
45
|
+
protectedDirectories = fetchProtectedDirectories();
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
return {
|
|
49
|
+
skipped: false,
|
|
50
|
+
passed: false,
|
|
51
|
+
failures: [],
|
|
52
|
+
warnings: [],
|
|
53
|
+
executionError: `could not resolve protected-directory allowlist: ${error instanceof Error ? error.message : String(error)}`,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (![...normalizedSpecFiles].some((file) => isProtected(protectedDirectories, file))) {
|
|
57
|
+
return { skipped: true, passed: true, failures: [], warnings: [] };
|
|
58
|
+
}
|
|
59
|
+
let stdout;
|
|
60
|
+
try {
|
|
61
|
+
stdout = execFileSync(process.execPath, ['scripts/check-reliability-policies.mjs', '--root', projectPath, '--json'], { cwd: process.cwd(), encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
const stdoutFromError = error.stdout;
|
|
65
|
+
if (typeof stdoutFromError !== 'string' || stdoutFromError.trim().length === 0) {
|
|
66
|
+
return {
|
|
67
|
+
skipped: false,
|
|
68
|
+
passed: false,
|
|
69
|
+
failures: [],
|
|
70
|
+
warnings: [],
|
|
71
|
+
executionError: error instanceof Error ? error.message : String(error),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
stdout = stdoutFromError;
|
|
75
|
+
}
|
|
76
|
+
let violations;
|
|
77
|
+
try {
|
|
78
|
+
violations = parseViolations(stdout);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
return {
|
|
82
|
+
skipped: false,
|
|
83
|
+
passed: false,
|
|
84
|
+
failures: [],
|
|
85
|
+
warnings: [],
|
|
86
|
+
executionError: `unparseable reliability gate output: ${error instanceof Error ? error.message : String(error)}`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const failures = violations.filter((violation) => normalizedSpecFiles.has(violation.file));
|
|
90
|
+
const warnings = violations.filter((violation) => !normalizedSpecFiles.has(violation.file));
|
|
91
|
+
return { skipped: false, passed: failures.length === 0, failures, warnings };
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=reliability-gate.js.map
|
|
@@ -61,6 +61,7 @@ async function listFiles(root, current = root) {
|
|
|
61
61
|
}
|
|
62
62
|
return files;
|
|
63
63
|
}
|
|
64
|
+
const ATOMIC_WRITE_TEMP_SUFFIX = /\.tmp\.\d+\.[0-9a-f]{8}$/;
|
|
64
65
|
export async function inventoryStorageRoot(rootInput) {
|
|
65
66
|
const root = assertAbsolute(rootInput, 'inventory root');
|
|
66
67
|
const info = await lstat(root);
|
|
@@ -70,7 +71,19 @@ export async function inventoryStorageRoot(rootInput) {
|
|
|
70
71
|
const files = [];
|
|
71
72
|
let totalBytes = 0;
|
|
72
73
|
for (const path of await listFiles(root)) {
|
|
73
|
-
|
|
74
|
+
if (ATOMIC_WRITE_TEMP_SUFFIX.test(path)) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
let content;
|
|
78
|
+
try {
|
|
79
|
+
content = await readFile(path);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
74
87
|
const relativePath = relative(root, path).split(sep).join('/');
|
|
75
88
|
files.push({ relativePath, size: content.byteLength, sha256: sha256(content) });
|
|
76
89
|
totalBytes += content.byteLength;
|
|
@@ -218,17 +231,17 @@ async function retireCompletedJournalForNewSources(journalPath, expected) {
|
|
|
218
231
|
}
|
|
219
232
|
try {
|
|
220
233
|
const previous = validateJournal(parsed, { ...expected, legacyRoots: previousRoots });
|
|
221
|
-
if (
|
|
234
|
+
if (previous.phase === 'completed') {
|
|
235
|
+
if (rootsChanged) {
|
|
236
|
+
await inventoryStorageRoot(expected.destinationRoot);
|
|
237
|
+
}
|
|
238
|
+
await rm(journalPath, { force: true });
|
|
222
239
|
return;
|
|
223
240
|
}
|
|
224
|
-
if (
|
|
225
|
-
|
|
241
|
+
if (!rootsChanged) {
|
|
242
|
+
return;
|
|
226
243
|
}
|
|
227
|
-
|
|
228
|
-
// canonical records. Re-inventory its real directory instead of requiring
|
|
229
|
-
// the historical generation digest to remain frozen forever.
|
|
230
|
-
await inventoryStorageRoot(expected.destinationRoot);
|
|
231
|
-
await rm(journalPath, { force: true });
|
|
244
|
+
throw new Error('[Planu] Active migration journal does not match requested roots');
|
|
232
245
|
}
|
|
233
246
|
catch (error) {
|
|
234
247
|
if (!isTerminalPhaseValue(record.phase)) {
|
package/dist/tools/validate.js
CHANGED
|
@@ -58,6 +58,7 @@ import { compactObj } from '../engine/compact-obj.js';
|
|
|
58
58
|
import { resolveProjectId, missingProjectIdError } from './resolve-project-id.js';
|
|
59
59
|
import { specStore, knowledgeStore } from '../storage/index.js';
|
|
60
60
|
import { validateSpec, generateDoR, generateDoD } from '../engine/validator.js';
|
|
61
|
+
import { scanCodeForSpec } from '../engine/validator/analyzer.js';
|
|
61
62
|
import { isBlockingQualitySeverity } from '../engine/validator/dor-dod.js';
|
|
62
63
|
import { readVerifiedImplementationReview } from '../engine/handoff-artifacts/implementation-review-reader.js';
|
|
63
64
|
import { calcQualityScore } from '../engine/auditor-scoring.js';
|
|
@@ -69,6 +70,7 @@ import { parseConventions, scanConventions } from '../engine/convention-scanner/
|
|
|
69
70
|
import { validateScopeCompliance } from '../engine/scope-boundaries/index.js';
|
|
70
71
|
import { compareWithBaseline } from '../storage/convention-baseline.js';
|
|
71
72
|
import { writeImplementationReviewReport } from '../engine/validator/validation-report-writer.js';
|
|
73
|
+
import { runReliabilityGate } from '../engine/validator/reliability-gate.js';
|
|
72
74
|
import { buildGraphCoverageReport, formatGraphCoverageText } from './validate-graph-coverage.js';
|
|
73
75
|
import { buildMinimalityReport } from './validate-minimality.js';
|
|
74
76
|
import { buildValidateFailureFallback, validateStrictLayoutOrError } from './validate-helpers.js';
|
|
@@ -85,7 +87,7 @@ import { getRuntimePolicy } from '../engine/runtime-policy.js';
|
|
|
85
87
|
import { resolveValidationWorktree, ValidationWorktreeError, } from '../engine/validation/validation-worktree.js';
|
|
86
88
|
// Re-export for external use (SPEC-018)
|
|
87
89
|
export { validateContractCompliance };
|
|
88
|
-
const VALIDATION_GATE_TOTAL =
|
|
90
|
+
const VALIDATION_GATE_TOTAL = 9;
|
|
89
91
|
const GATE_DEADLINE_EXEMPT_PHASES = new Set(['lint', 'compliance']);
|
|
90
92
|
async function runWithGateDeadline(phase, command, work) {
|
|
91
93
|
if (GATE_DEADLINE_EXEMPT_PHASES.has(phase)) {
|
|
@@ -360,6 +362,10 @@ export async function executeValidate(args, server, onProgress) {
|
|
|
360
362
|
});
|
|
361
363
|
const implementationQualityScore = calcQualityScore(result.qualityIssues);
|
|
362
364
|
const auditedFiles = [...new Set(result.qualityIssues.map((i) => i.file))];
|
|
365
|
+
const reliabilityGate = await runStage('reliability', 'scripts/check-reliability-policies.mjs --json', async () => {
|
|
366
|
+
const codeState = await scanCodeForSpec(executionSpec, projectPath);
|
|
367
|
+
return runReliabilityGate(projectPath, codeState.affectedFiles);
|
|
368
|
+
});
|
|
363
369
|
const { conventionViolations, regressionDetected } = await runStage('conventions', 'validate project conventions', () => scanProjectConventions(projectId, projectPath));
|
|
364
370
|
const lintCheck = await runStage('lint', knowledge.lintCommand ?? 'pnpm lint', () => runLintCheck(projectPath, knowledge.lintCommand ?? null, {
|
|
365
371
|
projectId,
|
|
@@ -397,6 +403,7 @@ export async function executeValidate(args, server, onProgress) {
|
|
|
397
403
|
lintPassed: lintCheck.passed,
|
|
398
404
|
assurancePassed: assuranceGates.newCode.passed,
|
|
399
405
|
minimalityBlocked: minimalityReport?.blocked === true,
|
|
406
|
+
reliabilityPassed: reliabilityGate.passed,
|
|
400
407
|
score: effectiveResult.score,
|
|
401
408
|
});
|
|
402
409
|
const output = {
|
|
@@ -492,6 +499,26 @@ export async function executeValidate(args, server, onProgress) {
|
|
|
492
499
|
});
|
|
493
500
|
}
|
|
494
501
|
}
|
|
502
|
+
for (const failure of reliabilityGate.failures) {
|
|
503
|
+
output.qualityIssues.push({
|
|
504
|
+
file: failure.file,
|
|
505
|
+
line: failure.line,
|
|
506
|
+
severity: 'critical',
|
|
507
|
+
rule: failure.rule,
|
|
508
|
+
message: `Reliability gate violation: ${failure.rule}`,
|
|
509
|
+
suggestion: 'Fix the release-gate reliability violation before marking this spec done.',
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
for (const warning of reliabilityGate.warnings) {
|
|
513
|
+
output.qualityIssues.push({
|
|
514
|
+
file: warning.file,
|
|
515
|
+
line: warning.line,
|
|
516
|
+
severity: 'warning',
|
|
517
|
+
rule: warning.rule,
|
|
518
|
+
message: `Reliability gate violation in an unrelated file: ${warning.rule}`,
|
|
519
|
+
suggestion: 'Not blocking this spec — file is outside the spec Files section.',
|
|
520
|
+
});
|
|
521
|
+
}
|
|
495
522
|
// Auto-suggest corrective actions based on validation results
|
|
496
523
|
// score is null when no criteria can be extracted — treat as 0 for comparisons.
|
|
497
524
|
const scoreValue = effectiveResult.score ?? 0;
|
|
@@ -523,6 +550,12 @@ export async function executeValidate(args, server, onProgress) {
|
|
|
523
550
|
if (!lintCheck.passed) {
|
|
524
551
|
suggestions.push(`Lint check failed: ${lintCheck.issueCount} issue(s) found via \`${lintCheck.command}\`. Fix before marking as done.`);
|
|
525
552
|
}
|
|
553
|
+
if (reliabilityGate.executionError !== undefined) {
|
|
554
|
+
suggestions.push(`Reliability gate could not run: ${reliabilityGate.executionError}. Fix the gate before marking as done.`);
|
|
555
|
+
}
|
|
556
|
+
else if (!reliabilityGate.passed) {
|
|
557
|
+
suggestions.push(`Reliability gate failed: ${String(reliabilityGate.failures.length)} violation(s) in this spec's files. Fix before marking as done.`);
|
|
558
|
+
}
|
|
526
559
|
if (!assuranceGates.newCode.passed) {
|
|
527
560
|
suggestions.push(`Assurance gate failed: ${String(assuranceGates.newCode.findings.length)} new-code domain inference issue(s) found.`);
|
|
528
561
|
}
|
|
@@ -673,6 +706,7 @@ function buildQualityGateSummary(input) {
|
|
|
673
706
|
...(!input.lintPassed ? ['lint'] : []),
|
|
674
707
|
...(!input.assurancePassed ? ['assurance'] : []),
|
|
675
708
|
...(input.minimalityBlocked ? ['minimality'] : []),
|
|
709
|
+
...(!input.reliabilityPassed ? ['reliability'] : []),
|
|
676
710
|
];
|
|
677
711
|
return {
|
|
678
712
|
passed: failures.length === 0,
|
|
@@ -683,6 +717,7 @@ function buildQualityGateSummary(input) {
|
|
|
683
717
|
lintPassed: input.lintPassed,
|
|
684
718
|
assurancePassed: input.assurancePassed,
|
|
685
719
|
minimalityPassed: !input.minimalityBlocked,
|
|
720
|
+
reliabilityPassed: input.reliabilityPassed,
|
|
686
721
|
};
|
|
687
722
|
}
|
|
688
723
|
function buildExecutableCoverage(specCompliance) {
|
|
@@ -110,4 +110,16 @@ export interface ExecutableEvidence {
|
|
|
110
110
|
unmappedCriteria: string[];
|
|
111
111
|
ignoredCriteriaTitles: string[];
|
|
112
112
|
}
|
|
113
|
+
export interface ReliabilityGateViolation {
|
|
114
|
+
file: string;
|
|
115
|
+
line: number;
|
|
116
|
+
rule: string;
|
|
117
|
+
}
|
|
118
|
+
export interface ReliabilityGateResult {
|
|
119
|
+
skipped: boolean;
|
|
120
|
+
passed: boolean;
|
|
121
|
+
failures: ReliabilityGateViolation[];
|
|
122
|
+
warnings: ReliabilityGateViolation[];
|
|
123
|
+
executionError?: string;
|
|
124
|
+
}
|
|
113
125
|
//# sourceMappingURL=validation-evidence.d.ts.map
|
package/package.json
CHANGED
package/planu-plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dev.planu.cli",
|
|
3
3
|
"displayName": "Planu — Spec Driven Development",
|
|
4
4
|
"description": "Manage software specs, estimations, and autonomous SDD workflows. Language-agnostic MCP server for Claude Code.",
|
|
5
|
-
"version": "5.7.
|
|
5
|
+
"version": "5.7.1",
|
|
6
6
|
"icon": "assets/plugin/icon.svg",
|
|
7
7
|
"command": ["npx", "@planu/cli@latest"],
|
|
8
8
|
"packageName": "@planu/cli",
|