@planu/cli 4.7.1 → 4.7.3

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/config/minimal-implementation-gate.json +110 -0
  3. package/dist/config/token-waste-autopilot.json +16 -0
  4. package/dist/engine/compact/compact-middleware.d.ts +2 -2
  5. package/dist/engine/compact/compact-middleware.js +68 -7
  6. package/dist/engine/context-artifacts/index.d.ts +2 -0
  7. package/dist/engine/context-artifacts/index.js +2 -0
  8. package/dist/engine/context-artifacts/store.d.ts +5 -0
  9. package/dist/engine/context-artifacts/store.js +176 -0
  10. package/dist/engine/handoff-artifacts/schemas.d.ts +112 -0
  11. package/dist/engine/handoff-artifacts/schemas.js +40 -0
  12. package/dist/engine/minimality/analyzer.d.ts +3 -0
  13. package/dist/engine/minimality/analyzer.js +140 -0
  14. package/dist/engine/minimality/formatter.d.ts +3 -0
  15. package/dist/engine/minimality/formatter.js +25 -0
  16. package/dist/engine/minimality/index.d.ts +4 -0
  17. package/dist/engine/minimality/index.js +4 -0
  18. package/dist/engine/minimality/policy-loader.d.ts +3 -0
  19. package/dist/engine/minimality/policy-loader.js +133 -0
  20. package/dist/engine/token-optimizer/content-aware-compactor.d.ts +4 -0
  21. package/dist/engine/token-optimizer/content-aware-compactor.js +230 -0
  22. package/dist/engine/token-optimizer/index.d.ts +1 -0
  23. package/dist/engine/token-optimizer/index.js +1 -0
  24. package/dist/engine/token-optimizer/output-filter.js +18 -2
  25. package/dist/engine/token-optimizer/policy-loader.js +12 -0
  26. package/dist/engine/token-optimizer/reporter.d.ts +4 -0
  27. package/dist/engine/token-optimizer/reporter.js +14 -1
  28. package/dist/engine/validator/validation-report-writer.d.ts +2 -0
  29. package/dist/engine/validator/validation-report-writer.js +19 -0
  30. package/dist/engine/web-fetcher/docs-fetcher.js +5 -1
  31. package/dist/tools/challenge-spec.js +25 -0
  32. package/dist/tools/package-handoff.js +23 -1
  33. package/dist/tools/safe-handler.js +4 -1
  34. package/dist/tools/token-usage-handler.js +5 -3
  35. package/dist/tools/update-status/dod-gates.js +9 -0
  36. package/dist/tools/validate.js +34 -0
  37. package/dist/types/compact/compact-mode.d.ts +5 -0
  38. package/dist/types/context-artifacts.d.ts +96 -0
  39. package/dist/types/context-artifacts.js +2 -0
  40. package/dist/types/handoff-artifacts.d.ts +2 -0
  41. package/dist/types/index.d.ts +2 -0
  42. package/dist/types/index.js +2 -0
  43. package/dist/types/minimal-implementation-gate.d.ts +92 -0
  44. package/dist/types/minimal-implementation-gate.js +2 -0
  45. package/dist/types/token-optimization.d.ts +2 -0
  46. package/dist/types/token-waste-autopilot.d.ts +15 -0
  47. package/package.json +17 -17
  48. package/planu-native.json +1 -1
  49. package/planu-plugin.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## [4.7.3] - 2026-06-19
2
+
3
+ ### Features
4
+ - feat: add reversible context compaction
5
+
6
+ ### Chores
7
+ - chore(deps): update patch and minor dependencies
8
+
9
+
10
+ ## [4.7.2] - 2026-06-16
11
+
12
+ ### Features
13
+ - feat(SPEC-1088): add policy-driven minimal implementation gate
14
+
15
+ ### Chores
16
+ - chore(deps): update patch/minor dependencies
17
+
18
+
1
19
  ## [4.7.1] - 2026-06-12
2
20
 
3
21
  ### Chores
@@ -0,0 +1,110 @@
1
+ {
2
+ "version": 1,
3
+ "enabled": true,
4
+ "report": {
5
+ "maxFindings": 12,
6
+ "maxMarkdownLines": 12,
7
+ "maxEvidenceChars": 180
8
+ },
9
+ "tags": {
10
+ "delete": { "description": "Remove avoidable code or files." },
11
+ "stdlib": { "description": "Use a standard library capability before adding custom code." },
12
+ "native": { "description": "Use an existing platform or Planu-native capability." },
13
+ "installed-dependency": { "description": "Use an already-installed dependency instead of adding another." },
14
+ "yagni": { "description": "Avoid speculative code that is not required by the approved spec." },
15
+ "shrink": { "description": "Reduce an oversized abstraction or workflow." },
16
+ "dependency": { "description": "Avoid an unnecessary new package or dependency path." },
17
+ "file-scope": { "description": "Keep implementation inside the approved file scope." },
18
+ "debt-marker": { "description": "Record accepted shortcuts as structured debt evidence." }
19
+ },
20
+ "rules": [
21
+ {
22
+ "id": "minimality.delete.dead-compat",
23
+ "tag": "delete",
24
+ "severity": "warning",
25
+ "confidence": "medium",
26
+ "patterns": ["legacy compatibility", "deprecated fallback", "unused fallback"],
27
+ "replacementGuidance": "Remove the compatibility path unless the approved spec explicitly requires backward compatibility."
28
+ },
29
+ {
30
+ "id": "minimality.stdlib.custom-parser",
31
+ "tag": "stdlib",
32
+ "severity": "warning",
33
+ "confidence": "medium",
34
+ "patterns": ["custom parser", "manual parser", "hand rolled parser"],
35
+ "replacementGuidance": "Use an existing parser from the runtime or current dependency set before writing parsing code."
36
+ },
37
+ {
38
+ "id": "minimality.native.existing-planu-surface",
39
+ "tag": "native",
40
+ "severity": "warning",
41
+ "confidence": "high",
42
+ "patterns": ["new public mcp tool", "new command", "new cli command"],
43
+ "replacementGuidance": "Prefer existing Planu SDD surfaces unless the spec proves a new public surface is required."
44
+ },
45
+ {
46
+ "id": "minimality.installed-dependency.add-package",
47
+ "tag": "installed-dependency",
48
+ "severity": "warning",
49
+ "confidence": "medium",
50
+ "patterns": ["add dependency", "new dependency", "install package"],
51
+ "replacementGuidance": "Check package.json first and reuse an already-installed dependency when possible."
52
+ },
53
+ {
54
+ "id": "minimality.yagni.future-proofing",
55
+ "tag": "yagni",
56
+ "severity": "blocker",
57
+ "confidence": "high",
58
+ "patterns": ["future-proof", "just in case", "might need later", "eventually support"],
59
+ "replacementGuidance": "Remove speculative scope unless it is directly required by an acceptance criterion.",
60
+ "blockWhenRiskAtLeast": "medium"
61
+ },
62
+ {
63
+ "id": "minimality.shrink.over-architecture",
64
+ "tag": "shrink",
65
+ "severity": "warning",
66
+ "confidence": "medium",
67
+ "patterns": ["framework", "orchestration layer", "plugin architecture", "abstract factory"],
68
+ "replacementGuidance": "Shrink the design to the smallest correct module/function that satisfies the spec."
69
+ },
70
+ {
71
+ "id": "minimality.dependency.new-runtime",
72
+ "tag": "dependency",
73
+ "severity": "blocker",
74
+ "confidence": "high",
75
+ "patterns": ["npm install", "pnpm add", "yarn add"],
76
+ "replacementGuidance": "Avoid adding runtime dependencies unless the approved spec explicitly requires one.",
77
+ "blockWhenRiskAtLeast": "low"
78
+ },
79
+ {
80
+ "id": "minimality.file-scope.unapproved-area",
81
+ "tag": "file-scope",
82
+ "severity": "warning",
83
+ "confidence": "medium",
84
+ "patterns": ["touch unrelated", "broad refactor", "repo-wide refactor"],
85
+ "replacementGuidance": "Keep edits inside the approved technical references and tests."
86
+ },
87
+ {
88
+ "id": "minimality.debt-marker.comment-only",
89
+ "tag": "debt-marker",
90
+ "severity": "warning",
91
+ "confidence": "medium",
92
+ "patterns": ["todo debt", "temporary hack", "shortcut accepted"],
93
+ "replacementGuidance": "Record accepted shortcuts as structured debt evidence with ceiling and upgrade trigger."
94
+ }
95
+ ],
96
+ "safetyExclusions": [
97
+ "security",
98
+ "accessibility",
99
+ "data-loss",
100
+ "compliance",
101
+ "money",
102
+ "access-control",
103
+ "explicitly requested",
104
+ "trust boundary"
105
+ ],
106
+ "excludedPathPatterns": ["dist/**", "coverage/**", "node_modules/**", "pnpm-lock.yaml", "*.snap"],
107
+ "debtEvidence": {
108
+ "requiredFields": ["specId", "files", "ceiling", "upgradeTrigger", "reviewRationale"]
109
+ }
110
+ }
@@ -17,6 +17,22 @@
17
17
  "generic": { "maxLines": 60 }
18
18
  }
19
19
  },
20
+ "contextArtifacts": {
21
+ "enabled": true,
22
+ "ttlMs": 86400000,
23
+ "minTokens": 200
24
+ },
25
+ "contentCompaction": {
26
+ "strategies": {
27
+ "json": { "maxLines": 60, "maxSnippetChars": 240 },
28
+ "test-log": { "maxLines": 80, "keepFailures": true, "maxSnippetChars": 240 },
29
+ "runtime-log": { "maxLines": 60, "keepFailures": true, "uniqueOnly": true, "maxSnippetChars": 240 },
30
+ "search-results": { "maxLines": 80, "uniqueOnly": true, "maxSnippetChars": 180 },
31
+ "code": { "maxLines": 120, "maxSnippetChars": 240 },
32
+ "spec-or-handoff": { "maxLines": 120, "maxSnippetChars": 240 },
33
+ "generic-text": { "maxLines": 60, "maxSnippetChars": 240 }
34
+ }
35
+ },
20
36
  "tools": {
21
37
  "groups": {
22
38
  "spec": ["create_spec", "check_readiness", "challenge_spec", "update_status"],
@@ -1,10 +1,10 @@
1
1
  import type { ToolResult } from '../../types/index.js';
2
- import type { CompactDecision } from '../../types/compact/compact-mode.js';
2
+ import type { CompactDecision, CompactModeOptions } from '../../types/compact/compact-mode.js';
3
3
  /**
4
4
  * Apply compact mode formatting to a ToolResult.
5
5
  * - Truncates text content blocks to tokenBudget
6
6
  * - Preserves structuredContent (essential data: status, scores, blockers)
7
7
  * - Adds _meta.compactMode and _meta.contextUsed
8
8
  */
9
- export declare function applyCompactMode(result: ToolResult, decision: CompactDecision): ToolResult;
9
+ export declare function applyCompactMode(result: ToolResult, decision: CompactDecision, options?: CompactModeOptions): ToolResult;
10
10
  //# sourceMappingURL=compact-middleware.d.ts.map
@@ -1,6 +1,47 @@
1
1
  // engine/compact/compact-middleware.ts — SPEC-922: Apply compact mode to tool responses
2
+ import { compactContentAware } from '../token-optimizer/content-aware-compactor.js';
2
3
  /** Approximate chars per token for English text. */
3
4
  const CHARS_PER_TOKEN = 4;
5
+ function defaultCompactPolicy(tokenBudget) {
6
+ return {
7
+ contextArtifacts: {
8
+ enabled: true,
9
+ ttlMs: 24 * 60 * 60 * 1000,
10
+ minTokens: tokenBudget,
11
+ },
12
+ contentCompaction: {
13
+ strategies: {
14
+ json: { maxLines: tokenBudget },
15
+ 'test-log': { maxLines: tokenBudget },
16
+ 'runtime-log': { maxLines: tokenBudget },
17
+ 'search-results': { maxLines: tokenBudget },
18
+ code: { maxLines: tokenBudget },
19
+ 'spec-or-handoff': { maxLines: tokenBudget },
20
+ 'generic-text': { maxLines: tokenBudget },
21
+ },
22
+ },
23
+ redaction: {
24
+ maxSnippetChars: 240,
25
+ redactPatterns: ['secret', 'token', 'password', 'api_key'],
26
+ },
27
+ };
28
+ }
29
+ function withCompactionMeta(result, compaction) {
30
+ if (compaction === undefined) {
31
+ return result;
32
+ }
33
+ return {
34
+ ...result,
35
+ structuredContent: {
36
+ ...(result.structuredContent ?? {}),
37
+ compaction,
38
+ },
39
+ _meta: {
40
+ ...(typeof result._meta === 'object' && result._meta !== null ? result._meta : {}),
41
+ compaction,
42
+ },
43
+ };
44
+ }
4
45
  /** Truncate a single text block to a token budget. */
5
46
  function truncateBlock(text, tokenBudget) {
6
47
  const maxChars = tokenBudget * CHARS_PER_TOKEN;
@@ -17,7 +58,7 @@ function truncateBlock(text, tokenBudget) {
17
58
  * - Preserves structuredContent (essential data: status, scores, blockers)
18
59
  * - Adds _meta.compactMode and _meta.contextUsed
19
60
  */
20
- export function applyCompactMode(result, decision) {
61
+ export function applyCompactMode(result, decision, options = {}) {
21
62
  if (decision.mode !== 'compact') {
22
63
  // Still add _meta even in verbose mode if contextUsed was provided
23
64
  if (decision.contextUsed !== undefined) {
@@ -32,11 +73,31 @@ export function applyCompactMode(result, decision) {
32
73
  }
33
74
  return result;
34
75
  }
35
- const truncatedContent = result.content.map((block) => ({
36
- ...block,
37
- text: truncateBlock(block.text, decision.tokenBudget),
38
- }));
39
- return {
76
+ let firstCompaction;
77
+ const truncatedContent = result.content.map((block) => {
78
+ if (options.projectPath !== undefined) {
79
+ const compacted = compactContentAware({
80
+ text: block.text,
81
+ policy: defaultCompactPolicy(decision.tokenBudget),
82
+ projectPath: options.projectPath,
83
+ sourcePath: options.sourcePath,
84
+ flow: options.flow,
85
+ kind: options.flow,
86
+ });
87
+ firstCompaction ??= compacted.artifact;
88
+ return {
89
+ ...block,
90
+ text: compacted.text.length < block.text.length
91
+ ? `${compacted.text}\n\nArtifact: ${compacted.artifact?.artifactRef ?? 'not stored'}`
92
+ : truncateBlock(block.text, decision.tokenBudget),
93
+ };
94
+ }
95
+ return {
96
+ ...block,
97
+ text: truncateBlock(block.text, decision.tokenBudget),
98
+ };
99
+ });
100
+ return withCompactionMeta({
40
101
  ...result,
41
102
  content: truncatedContent,
42
103
  _meta: {
@@ -45,6 +106,6 @@ export function applyCompactMode(result, decision) {
45
106
  contextUsed: decision.contextUsed,
46
107
  tokenBudget: decision.tokenBudget,
47
108
  },
48
- };
109
+ }, firstCompaction);
49
110
  }
50
111
  //# sourceMappingURL=compact-middleware.js.map
@@ -0,0 +1,2 @@
1
+ export { getContextArtifactStats, retrieveContextArtifact, storeContextArtifact } from './store.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,2 @@
1
+ export { getContextArtifactStats, retrieveContextArtifact, storeContextArtifact } from './store.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,5 @@
1
+ import type { ContextArtifactStats, RetrieveContextArtifactResult, StoreContextArtifactInput, StoreContextArtifactResult } from '../../types/context-artifacts.js';
2
+ export declare function storeContextArtifact(input: StoreContextArtifactInput): StoreContextArtifactResult;
3
+ export declare function retrieveContextArtifact(projectPath: string, ref: string): RetrieveContextArtifactResult;
4
+ export declare function getContextArtifactStats(projectPath: string): ContextArtifactStats;
5
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1,176 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { getSensitivePathRefusal, shouldBypassLeanMode, } from '../context-intelligence/compression-guards.js';
5
+ import { hashProjectPath, projectDataDir } from '../../storage/base-store.js';
6
+ const REF_PREFIX = 'ctx_';
7
+ const REF_PATTERN = /^ctx_[a-f0-9]{32}$/;
8
+ const FORBIDDEN_METADATA_KEY = /(?:secret|token|password|credential|api[_-]?key|private[_-]?key)/i;
9
+ function artifactDir(projectPath) {
10
+ return join(projectDataDir(hashProjectPath(projectPath)), 'context-artifacts');
11
+ }
12
+ function artifactPath(projectPath, ref) {
13
+ if (!REF_PATTERN.test(ref)) {
14
+ return null;
15
+ }
16
+ return join(artifactDir(projectPath), `${ref}.json`);
17
+ }
18
+ function sha256(value) {
19
+ return createHash('sha256').update(value, 'utf8').digest('hex');
20
+ }
21
+ function safeMetadata(metadata) {
22
+ const safe = {};
23
+ for (const [key, value] of Object.entries(metadata ?? {})) {
24
+ if (FORBIDDEN_METADATA_KEY.test(key)) {
25
+ continue;
26
+ }
27
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
28
+ safe[key] =
29
+ typeof value === 'string' && FORBIDDEN_METADATA_KEY.test(value) ? '[redacted]' : value;
30
+ }
31
+ }
32
+ return safe;
33
+ }
34
+ function compactionMetadata(artifact) {
35
+ return {
36
+ artifactRef: artifact.ref,
37
+ originalTokens: artifact.originalTokens,
38
+ compactTokens: artifact.compactTokens,
39
+ tokensSaved: artifact.tokensSaved,
40
+ strategy: artifact.strategy,
41
+ contentType: artifact.contentType,
42
+ expiresAt: artifact.expiresAt,
43
+ retrievalHint: `Use artifactRef ${artifact.ref} to retrieve the full local output before ${artifact.expiresAt}.`,
44
+ };
45
+ }
46
+ function readArtifactFile(path) {
47
+ try {
48
+ return JSON.parse(readFileSync(path, 'utf-8'));
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ }
54
+ function writeArtifactFile(path, artifact) {
55
+ writeFileSync(path, JSON.stringify(artifact, null, 2), 'utf-8');
56
+ }
57
+ export function storeContextArtifact(input) {
58
+ if (input.sourcePath) {
59
+ const refusal = getSensitivePathRefusal(input.sourcePath);
60
+ if (refusal !== null) {
61
+ return { stored: false, refusedReason: refusal };
62
+ }
63
+ }
64
+ if (input.flow && shouldBypassLeanMode(input.flow)) {
65
+ return {
66
+ stored: false,
67
+ refusedReason: `Refusing recoverable artifact for sensitive flow ${input.flow}`,
68
+ };
69
+ }
70
+ const contentHash = sha256(input.originalContent);
71
+ const ref = `${REF_PREFIX}${contentHash.slice(0, 32)}`;
72
+ const path = artifactPath(input.projectPath, ref);
73
+ if (path === null) {
74
+ return { stored: false, refusedReason: 'Invalid generated artifact ref' };
75
+ }
76
+ const now = Date.now();
77
+ const createdAt = new Date(now).toISOString();
78
+ const expiresAt = new Date(now + input.ttlMs).toISOString();
79
+ const tokensSaved = Math.max(0, input.originalTokens - input.compactTokens);
80
+ const previous = existsSync(path) ? readArtifactFile(path) : null;
81
+ const artifact = {
82
+ ref,
83
+ contentHash,
84
+ createdAt: previous?.createdAt ?? createdAt,
85
+ expiresAt,
86
+ ttlMs: input.ttlMs,
87
+ projectId: hashProjectPath(input.projectPath),
88
+ contentType: input.contentType,
89
+ strategy: input.strategy,
90
+ originalTokens: input.originalTokens,
91
+ compactTokens: input.compactTokens,
92
+ tokensSaved,
93
+ retrievalCount: previous?.retrievalCount ?? 0,
94
+ metadata: safeMetadata(input.metadata),
95
+ originalContent: input.originalContent,
96
+ compactContent: input.compactContent,
97
+ };
98
+ mkdirSync(artifactDir(input.projectPath), { recursive: true });
99
+ writeArtifactFile(path, artifact);
100
+ return { stored: true, artifact, metadata: compactionMetadata(artifact) };
101
+ }
102
+ export function retrieveContextArtifact(projectPath, ref) {
103
+ const path = artifactPath(projectPath, ref);
104
+ if (path === null) {
105
+ return {
106
+ found: false,
107
+ reason: 'invalid-ref',
108
+ hint: 'Artifact refs are opaque ctx_<hash> identifiers.',
109
+ };
110
+ }
111
+ if (!existsSync(path)) {
112
+ return {
113
+ found: false,
114
+ reason: 'missing',
115
+ hint: 'The artifact is missing or belongs to a different project.',
116
+ };
117
+ }
118
+ const artifact = readArtifactFile(path);
119
+ if (artifact === null) {
120
+ return {
121
+ found: false,
122
+ reason: 'corrupt',
123
+ hint: 'The artifact payload is corrupt and cannot be recovered.',
124
+ };
125
+ }
126
+ if (artifact.projectId !== hashProjectPath(projectPath)) {
127
+ return {
128
+ found: false,
129
+ reason: 'unauthorized',
130
+ hint: 'The artifact does not belong to this project scope.',
131
+ };
132
+ }
133
+ if (Date.parse(artifact.expiresAt) <= Date.now()) {
134
+ return {
135
+ found: false,
136
+ reason: 'expired',
137
+ hint: 'The artifact expired; rerun the source operation if exact output is needed.',
138
+ };
139
+ }
140
+ const updated = { ...artifact, retrievalCount: artifact.retrievalCount + 1 };
141
+ writeArtifactFile(path, updated);
142
+ return {
143
+ found: true,
144
+ artifact: updated,
145
+ hint: 'Full artifact recovered from local Planu storage.',
146
+ };
147
+ }
148
+ export function getContextArtifactStats(projectPath) {
149
+ const dir = artifactDir(projectPath);
150
+ const stats = {
151
+ artifactCount: 0,
152
+ totalOriginalTokens: 0,
153
+ totalCompactTokens: 0,
154
+ totalTokensSaved: 0,
155
+ retrievalCount: 0,
156
+ };
157
+ if (!existsSync(dir)) {
158
+ return stats;
159
+ }
160
+ for (const entry of readdirSync(dir)) {
161
+ if (!entry.endsWith('.json')) {
162
+ continue;
163
+ }
164
+ const artifact = readArtifactFile(join(dir, entry));
165
+ if (artifact === null || Date.parse(artifact.expiresAt) <= Date.now()) {
166
+ continue;
167
+ }
168
+ stats.artifactCount += 1;
169
+ stats.totalOriginalTokens += artifact.originalTokens;
170
+ stats.totalCompactTokens += artifact.compactTokens;
171
+ stats.totalTokensSaved += artifact.tokensSaved;
172
+ stats.retrievalCount += artifact.retrievalCount;
173
+ }
174
+ return stats;
175
+ }
176
+ //# sourceMappingURL=store.js.map
@@ -96,6 +96,62 @@ export declare const ValidationReportV1Schema: z.ZodObject<{
96
96
  evidence: z.ZodArray<z.ZodString>;
97
97
  }, z.core.$strip>>;
98
98
  }, z.core.$strip>>;
99
+ minimalityReport: z.ZodOptional<z.ZodObject<{
100
+ enabled: z.ZodBoolean;
101
+ blocked: z.ZodBoolean;
102
+ findings: z.ZodArray<z.ZodObject<{
103
+ ruleId: z.ZodString;
104
+ tag: z.ZodString;
105
+ severity: z.ZodEnum<{
106
+ blocker: "blocker";
107
+ warning: "warning";
108
+ info: "info";
109
+ }>;
110
+ confidence: z.ZodEnum<{
111
+ low: "low";
112
+ medium: "medium";
113
+ high: "high";
114
+ }>;
115
+ target: z.ZodString;
116
+ line: z.ZodOptional<z.ZodNumber>;
117
+ evidence: z.ZodString;
118
+ replacementGuidance: z.ZodString;
119
+ blocksDone: z.ZodBoolean;
120
+ evidenceSource: z.ZodArray<z.ZodObject<{
121
+ source: z.ZodEnum<{
122
+ spec: "spec";
123
+ runtime: "runtime";
124
+ policy: "policy";
125
+ "project-config": "project-config";
126
+ }>;
127
+ key: z.ZodString;
128
+ value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
129
+ }, z.core.$strip>>;
130
+ }, z.core.$strip>>;
131
+ safetyExceptions: z.ZodArray<z.ZodObject<{
132
+ target: z.ZodString;
133
+ reason: z.ZodString;
134
+ evidence: z.ZodString;
135
+ }, z.core.$strip>>;
136
+ debtEvidence: z.ZodArray<z.ZodObject<{
137
+ specId: z.ZodString;
138
+ files: z.ZodArray<z.ZodString>;
139
+ ceiling: z.ZodString;
140
+ upgradeTrigger: z.ZodString;
141
+ reviewRationale: z.ZodString;
142
+ }, z.core.$strip>>;
143
+ evidence: z.ZodArray<z.ZodObject<{
144
+ source: z.ZodEnum<{
145
+ spec: "spec";
146
+ runtime: "runtime";
147
+ policy: "policy";
148
+ "project-config": "project-config";
149
+ }>;
150
+ key: z.ZodString;
151
+ value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
152
+ }, z.core.$strip>>;
153
+ markdown: z.ZodString;
154
+ }, z.core.$strip>>;
99
155
  score: z.ZodOptional<z.ZodNumber>;
100
156
  completedAt: z.ZodISODateTime;
101
157
  }, z.core.$strip>;
@@ -194,6 +250,62 @@ export declare const ARTIFACT_SCHEMAS: {
194
250
  evidence: z.ZodArray<z.ZodString>;
195
251
  }, z.core.$strip>>;
196
252
  }, z.core.$strip>>;
253
+ minimalityReport: z.ZodOptional<z.ZodObject<{
254
+ enabled: z.ZodBoolean;
255
+ blocked: z.ZodBoolean;
256
+ findings: z.ZodArray<z.ZodObject<{
257
+ ruleId: z.ZodString;
258
+ tag: z.ZodString;
259
+ severity: z.ZodEnum<{
260
+ blocker: "blocker";
261
+ warning: "warning";
262
+ info: "info";
263
+ }>;
264
+ confidence: z.ZodEnum<{
265
+ low: "low";
266
+ medium: "medium";
267
+ high: "high";
268
+ }>;
269
+ target: z.ZodString;
270
+ line: z.ZodOptional<z.ZodNumber>;
271
+ evidence: z.ZodString;
272
+ replacementGuidance: z.ZodString;
273
+ blocksDone: z.ZodBoolean;
274
+ evidenceSource: z.ZodArray<z.ZodObject<{
275
+ source: z.ZodEnum<{
276
+ spec: "spec";
277
+ runtime: "runtime";
278
+ policy: "policy";
279
+ "project-config": "project-config";
280
+ }>;
281
+ key: z.ZodString;
282
+ value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
283
+ }, z.core.$strip>>;
284
+ }, z.core.$strip>>;
285
+ safetyExceptions: z.ZodArray<z.ZodObject<{
286
+ target: z.ZodString;
287
+ reason: z.ZodString;
288
+ evidence: z.ZodString;
289
+ }, z.core.$strip>>;
290
+ debtEvidence: z.ZodArray<z.ZodObject<{
291
+ specId: z.ZodString;
292
+ files: z.ZodArray<z.ZodString>;
293
+ ceiling: z.ZodString;
294
+ upgradeTrigger: z.ZodString;
295
+ reviewRationale: z.ZodString;
296
+ }, z.core.$strip>>;
297
+ evidence: z.ZodArray<z.ZodObject<{
298
+ source: z.ZodEnum<{
299
+ spec: "spec";
300
+ runtime: "runtime";
301
+ policy: "policy";
302
+ "project-config": "project-config";
303
+ }>;
304
+ key: z.ZodString;
305
+ value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
306
+ }, z.core.$strip>>;
307
+ markdown: z.ZodString;
308
+ }, z.core.$strip>>;
197
309
  score: z.ZodOptional<z.ZodNumber>;
198
310
  completedAt: z.ZodISODateTime;
199
311
  }, z.core.$strip>;
@@ -73,6 +73,46 @@ export const ValidationReportV1Schema = z.object({
73
73
  })),
74
74
  })
75
75
  .optional(),
76
+ minimalityReport: z
77
+ .object({
78
+ enabled: z.boolean(),
79
+ blocked: z.boolean(),
80
+ findings: z.array(z.object({
81
+ ruleId: z.string(),
82
+ tag: z.string(),
83
+ severity: z.enum(['info', 'warning', 'blocker']),
84
+ confidence: z.enum(['low', 'medium', 'high']),
85
+ target: z.string(),
86
+ line: z.number().optional(),
87
+ evidence: z.string(),
88
+ replacementGuidance: z.string(),
89
+ blocksDone: z.boolean(),
90
+ evidenceSource: z.array(z.object({
91
+ source: z.enum(['policy', 'project-config', 'runtime', 'spec']),
92
+ key: z.string(),
93
+ value: z.union([z.string(), z.number(), z.boolean()]).optional(),
94
+ })),
95
+ })),
96
+ safetyExceptions: z.array(z.object({
97
+ target: z.string(),
98
+ reason: z.string(),
99
+ evidence: z.string(),
100
+ })),
101
+ debtEvidence: z.array(z.object({
102
+ specId: z.string(),
103
+ files: z.array(z.string()),
104
+ ceiling: z.string(),
105
+ upgradeTrigger: z.string(),
106
+ reviewRationale: z.string(),
107
+ })),
108
+ evidence: z.array(z.object({
109
+ source: z.enum(['policy', 'project-config', 'runtime', 'spec']),
110
+ key: z.string(),
111
+ value: z.union([z.string(), z.number(), z.boolean()]).optional(),
112
+ })),
113
+ markdown: z.string(),
114
+ })
115
+ .optional(),
76
116
  score: z.number().optional(),
77
117
  completedAt: z.iso.datetime(),
78
118
  });
@@ -0,0 +1,3 @@
1
+ import type { MinimalImplementationInput, MinimalImplementationReport } from '../../types/minimal-implementation-gate.js';
2
+ export declare function analyzeMinimalImplementation(input: MinimalImplementationInput): MinimalImplementationReport;
3
+ //# sourceMappingURL=analyzer.d.ts.map