@urielsh/prodify 0.1.1 → 0.1.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.
- package/.prodify/contracts/architecture.contract.json +9 -1
- package/.prodify/contracts/diagnose.contract.json +9 -1
- package/.prodify/contracts/plan.contract.json +9 -1
- package/.prodify/contracts/refactor.contract.json +13 -2
- package/.prodify/contracts/understand.contract.json +9 -1
- package/.prodify/contracts/validate.contract.json +11 -2
- package/.prodify/contracts-src/refactor.contract.md +7 -0
- package/.prodify/contracts-src/validate.contract.md +2 -0
- package/README.md +4 -2
- package/assets/presets/default/canonical/contracts-src/refactor.contract.md +7 -0
- package/assets/presets/default/canonical/contracts-src/validate.contract.md +2 -0
- package/dist/commands/setup-agent.js +3 -0
- package/dist/contracts/compiled-schema.js +10 -1
- package/dist/contracts/source-schema.js +42 -1
- package/dist/core/agent-setup.js +116 -1
- package/dist/core/diff-validator.js +183 -0
- package/dist/core/plan-units.js +64 -0
- package/dist/core/repo-root.js +17 -8
- package/dist/core/state.js +6 -0
- package/dist/core/status.js +14 -0
- package/dist/core/validation.js +87 -9
- package/dist/scoring/model.js +94 -213
- package/dist/scoring/scoring-engine.js +158 -0
- package/docs/diff-validator-design.md +44 -0
- package/docs/impact-scoring-design.md +38 -0
- package/package.json +1 -1
- package/src/commands/setup-agent.ts +3 -0
- package/src/contracts/compiled-schema.ts +10 -1
- package/src/contracts/source-schema.ts +51 -1
- package/src/core/agent-setup.ts +126 -2
- package/src/core/diff-validator.ts +230 -0
- package/src/core/plan-units.ts +82 -0
- package/src/core/repo-root.ts +21 -8
- package/src/core/state.ts +6 -0
- package/src/core/status.ts +17 -0
- package/src/core/validation.ts +136 -15
- package/src/scoring/model.ts +101 -250
- package/src/scoring/scoring-engine.ts +194 -0
- package/src/types.ts +55 -0
- package/tests/integration/cli-flows.test.js +19 -0
- package/tests/unit/agent-setup.test.js +9 -3
- package/tests/unit/diff-validator.test.js +28 -0
- package/tests/unit/scoring.test.js +42 -1
- package/tests/unit/validation.test.js +79 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import { resolveCanonicalPath } from './paths.js';
|
|
3
|
+
function extractSection(markdown, heading) {
|
|
4
|
+
const normalized = markdown.replace(/\r\n/g, '\n');
|
|
5
|
+
const match = new RegExp(`^##\\s+${heading}\\s*$([\\s\\S]*?)(?=^##\\s+|\\Z)`, 'm').exec(normalized);
|
|
6
|
+
return match?.[1]?.trim() ?? '';
|
|
7
|
+
}
|
|
8
|
+
function normalizePlanUnits(section) {
|
|
9
|
+
const lines = section.split('\n');
|
|
10
|
+
const units = [];
|
|
11
|
+
let currentId = null;
|
|
12
|
+
let currentDescription = '';
|
|
13
|
+
for (const rawLine of lines) {
|
|
14
|
+
const line = rawLine.trim();
|
|
15
|
+
const idMatch = /^-\s+Step ID:\s+(.+)$/.exec(line);
|
|
16
|
+
if (idMatch) {
|
|
17
|
+
if (currentId) {
|
|
18
|
+
units.push({
|
|
19
|
+
id: currentId,
|
|
20
|
+
description: currentDescription
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
currentId = idMatch[1].trim();
|
|
24
|
+
currentDescription = '';
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (!currentId) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const descriptionMatch = /^-\s+Description:\s+(.+)$/.exec(line);
|
|
31
|
+
if (descriptionMatch) {
|
|
32
|
+
currentDescription = descriptionMatch[1].trim();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (currentId) {
|
|
36
|
+
units.push({
|
|
37
|
+
id: currentId,
|
|
38
|
+
description: currentDescription
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return units;
|
|
42
|
+
}
|
|
43
|
+
export async function readPlanUnits(repoRoot) {
|
|
44
|
+
const planPath = resolveCanonicalPath(repoRoot, '.prodify/artifacts/04-plan.md');
|
|
45
|
+
const markdown = await fs.readFile(planPath, 'utf8');
|
|
46
|
+
return normalizePlanUnits(extractSection(markdown, 'Step Breakdown'));
|
|
47
|
+
}
|
|
48
|
+
export async function readSelectedRefactorStep(repoRoot) {
|
|
49
|
+
const refactorPath = resolveCanonicalPath(repoRoot, '.prodify/artifacts/05-refactor.md');
|
|
50
|
+
const markdown = await fs.readFile(refactorPath, 'utf8');
|
|
51
|
+
const section = extractSection(markdown, 'Selected Step');
|
|
52
|
+
if (!section) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const id = /-\s+Step ID:\s+(.+)/.exec(section)?.[1]?.trim() ?? null;
|
|
56
|
+
const description = /-\s+Description:\s+(.+)/.exec(section)?.[1]?.trim() ?? '';
|
|
57
|
+
if (!id) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
id,
|
|
62
|
+
description
|
|
63
|
+
};
|
|
64
|
+
}
|
package/dist/core/repo-root.js
CHANGED
|
@@ -39,15 +39,24 @@ export async function resolveRepoRoot(options = {}) {
|
|
|
39
39
|
}
|
|
40
40
|
return explicitRepo;
|
|
41
41
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
let current = cwd;
|
|
43
|
+
while (true) {
|
|
44
|
+
const hasProdify = await directoryHas(current, '.prodify');
|
|
45
|
+
if (hasProdify) {
|
|
46
|
+
return current;
|
|
47
|
+
}
|
|
48
|
+
const hasGit = await directoryHas(current, '.git');
|
|
49
|
+
if (hasGit) {
|
|
50
|
+
if (allowBootstrap) {
|
|
51
|
+
return current;
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
50
54
|
}
|
|
55
|
+
const parent = path.dirname(current);
|
|
56
|
+
if (parent === current) {
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
current = parent;
|
|
51
60
|
}
|
|
52
61
|
throw new ProdifyError('Could not resolve repository root from the current working directory.', {
|
|
53
62
|
code: 'REPO_ROOT_NOT_FOUND'
|
package/dist/core/state.js
CHANGED
|
@@ -2,6 +2,8 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import { ProdifyError } from './errors.js';
|
|
3
3
|
import { pathExists, writeFileEnsuringDir } from './fs.js';
|
|
4
4
|
import { resolveCanonicalPath } from './paths.js';
|
|
5
|
+
import { writeRefactorBaselineSnapshot } from './diff-validator.js';
|
|
6
|
+
import { syncScoreArtifactsForRuntimeState } from '../scoring/model.js';
|
|
5
7
|
export const RUNTIME_STATE_SCHEMA_VERSION = '2';
|
|
6
8
|
export const RUNTIME_STATUS = {
|
|
7
9
|
NOT_BOOTSTRAPPED: 'not_bootstrapped',
|
|
@@ -217,4 +219,8 @@ export async function readRuntimeState(repoRoot, { allowMissing = false, presetM
|
|
|
217
219
|
export async function writeRuntimeState(repoRoot, state) {
|
|
218
220
|
const statePath = resolveCanonicalPath(repoRoot, '.prodify/state.json');
|
|
219
221
|
await writeFileEnsuringDir(statePath, serializeRuntimeState(state));
|
|
222
|
+
if (state.runtime.current_state === 'refactor_pending') {
|
|
223
|
+
await writeRefactorBaselineSnapshot(repoRoot);
|
|
224
|
+
}
|
|
225
|
+
await syncScoreArtifactsForRuntimeState(repoRoot, state);
|
|
220
226
|
}
|
package/dist/core/status.js
CHANGED
|
@@ -11,6 +11,7 @@ import { readRuntimeState, RUNTIME_STATUS } from './state.js';
|
|
|
11
11
|
import { inspectVersionStatus } from './version-checks.js';
|
|
12
12
|
import { buildBootstrapPrompt, hasManualBootstrapGuidance } from './prompt-builder.js';
|
|
13
13
|
import { getRuntimeProfile } from './targets.js';
|
|
14
|
+
import { readScoreDelta } from '../scoring/model.js';
|
|
14
15
|
function describeCanonicalHealth(missingPaths) {
|
|
15
16
|
if (missingPaths.length === 0) {
|
|
16
17
|
return 'healthy';
|
|
@@ -126,6 +127,14 @@ function describeStageValidation(report) {
|
|
|
126
127
|
? `last pass at ${runtime.last_validation.stage} (contract ${runtime.last_validation.contract_version})`
|
|
127
128
|
: `failed at ${runtime.last_validation.stage}`;
|
|
128
129
|
}
|
|
130
|
+
function describeImpactScore(scoreDelta) {
|
|
131
|
+
if (!scoreDelta) {
|
|
132
|
+
return 'not available';
|
|
133
|
+
}
|
|
134
|
+
const threshold = scoreDelta.min_impact_score !== undefined ? `, threshold=${scoreDelta.min_impact_score}` : '';
|
|
135
|
+
const verdict = scoreDelta.passed === undefined ? '' : `, passed=${scoreDelta.passed}`;
|
|
136
|
+
return `${scoreDelta.baseline_score} -> ${scoreDelta.final_score} (delta ${scoreDelta.delta}${threshold}${verdict})`;
|
|
137
|
+
}
|
|
129
138
|
async function checkManualBootstrapGuidance(repoRoot) {
|
|
130
139
|
const agentsPath = resolveCanonicalPath(repoRoot, '.prodify/AGENTS.md');
|
|
131
140
|
if (!(await pathExists(agentsPath))) {
|
|
@@ -192,6 +201,7 @@ export async function inspectRepositoryStatus(repoRoot, options = {}) {
|
|
|
192
201
|
bootstrapProfile,
|
|
193
202
|
bootstrapPrompt,
|
|
194
203
|
stageSkillResolution: null,
|
|
204
|
+
scoreDelta: null,
|
|
195
205
|
recommendedNextAction: 'prodify init',
|
|
196
206
|
presetMetadata: preset.metadata
|
|
197
207
|
};
|
|
@@ -207,6 +217,7 @@ export async function inspectRepositoryStatus(repoRoot, options = {}) {
|
|
|
207
217
|
let runtimeState = null;
|
|
208
218
|
let runtimeStateError = null;
|
|
209
219
|
let stageSkillResolution = null;
|
|
220
|
+
let scoreDelta = null;
|
|
210
221
|
try {
|
|
211
222
|
runtimeState = await readRuntimeState(repoRoot, {
|
|
212
223
|
presetMetadata: preset.metadata
|
|
@@ -216,6 +227,7 @@ export async function inspectRepositoryStatus(repoRoot, options = {}) {
|
|
|
216
227
|
runtimeStateError = error instanceof Error ? error : new Error(String(error));
|
|
217
228
|
}
|
|
218
229
|
const manualBootstrapReady = await checkManualBootstrapGuidance(repoRoot);
|
|
230
|
+
scoreDelta = await readScoreDelta(repoRoot);
|
|
219
231
|
const canonicalOk = missingPaths.length === 0;
|
|
220
232
|
if (canonicalOk && contractInventory.ok) {
|
|
221
233
|
const skillStage = runtimeState?.runtime.current_stage
|
|
@@ -252,6 +264,7 @@ export async function inspectRepositoryStatus(repoRoot, options = {}) {
|
|
|
252
264
|
bootstrapProfile,
|
|
253
265
|
bootstrapPrompt,
|
|
254
266
|
stageSkillResolution,
|
|
267
|
+
scoreDelta,
|
|
255
268
|
recommendedNextAction: deriveNextAction({
|
|
256
269
|
initialized,
|
|
257
270
|
canonicalOk,
|
|
@@ -280,6 +293,7 @@ export function renderStatusReport(report) {
|
|
|
280
293
|
`Skills active: ${describeActiveSkills(report)}`,
|
|
281
294
|
`Execution state: ${describeRuntime(report.runtimeState?.runtime ?? null)}`,
|
|
282
295
|
`Stage validation: ${describeStageValidation(report)}`,
|
|
296
|
+
`Impact score: ${describeImpactScore(report.scoreDelta)}`,
|
|
283
297
|
`Manual bootstrap: ${report.manualBootstrapReady ? 'ready' : 'repair .prodify/AGENTS.md guidance'}`,
|
|
284
298
|
`Bootstrap profile: ${report.bootstrapProfile}`,
|
|
285
299
|
`Bootstrap prompt: ${report.bootstrapPrompt}`,
|
package/dist/core/validation.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import { loadCompiledContract } from '../contracts/compiler.js';
|
|
3
|
+
import { diffAgainstRefactorBaseline } from './diff-validator.js';
|
|
3
4
|
import { normalizeRepoRelativePath, resolveRepoPath } from './paths.js';
|
|
5
|
+
import { readPlanUnits, readSelectedRefactorStep } from './plan-units.js';
|
|
6
|
+
import { calculateCurrentImpactDelta } from '../scoring/model.js';
|
|
4
7
|
function pathIsWithin(pathToCheck, root) {
|
|
5
8
|
const normalizedPath = normalizeRepoRelativePath(pathToCheck);
|
|
6
9
|
const normalizedRoot = normalizeRepoRelativePath(root);
|
|
@@ -35,6 +38,12 @@ function containsAllStatements(sectionContent, statements) {
|
|
|
35
38
|
function buildRule(rule, message, path) {
|
|
36
39
|
return { rule, message, path };
|
|
37
40
|
}
|
|
41
|
+
function hasRequiredStructuralChanges(diffResult, requiredChanges) {
|
|
42
|
+
if (requiredChanges.length === 0) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
return requiredChanges.every((requiredChange) => diffResult.structuralChanges.structural_change_flags.includes(requiredChange));
|
|
46
|
+
}
|
|
38
47
|
async function validateArtifact(repoRoot, contract, artifact) {
|
|
39
48
|
const artifactPath = resolveRepoPath(repoRoot, artifact.path);
|
|
40
49
|
const issues = [];
|
|
@@ -43,9 +52,6 @@ async function validateArtifact(repoRoot, contract, artifact) {
|
|
|
43
52
|
try {
|
|
44
53
|
const content = await fs.readFile(artifactPath, 'utf8');
|
|
45
54
|
diagnostics.push(`validated artifact ${artifact.path}`);
|
|
46
|
-
if (!contract.allowed_write_roots.some((root) => pathIsWithin(artifact.path, root))) {
|
|
47
|
-
issues.push(buildRule('artifact/outside-allowed-roots', `Required artifact ${artifact.path} is outside allowed write roots.`, artifact.path));
|
|
48
|
-
}
|
|
49
55
|
if (artifact.format === 'markdown') {
|
|
50
56
|
const sections = collectMarkdownSections(content);
|
|
51
57
|
for (const section of artifact.required_sections) {
|
|
@@ -81,11 +87,15 @@ async function validateArtifact(repoRoot, contract, artifact) {
|
|
|
81
87
|
diagnostics
|
|
82
88
|
};
|
|
83
89
|
}
|
|
84
|
-
export async function validateStageOutputs(repoRoot, { contract, runtimeState, touchedPaths = [] }) {
|
|
90
|
+
export async function validateStageOutputs(repoRoot, { contract, runtimeState, touchedPaths = [], diffResult = null }) {
|
|
85
91
|
const violatedRules = [];
|
|
86
92
|
const missingArtifacts = [];
|
|
87
93
|
const diagnostics = [];
|
|
88
94
|
const warnings = [];
|
|
95
|
+
const effectiveDiffResult = diffResult ?? await diffAgainstRefactorBaseline(repoRoot);
|
|
96
|
+
const impactDelta = contract.min_impact_score > 0
|
|
97
|
+
? await calculateCurrentImpactDelta(repoRoot)
|
|
98
|
+
: null;
|
|
89
99
|
if (runtimeState.runtime.current_stage !== contract.stage) {
|
|
90
100
|
violatedRules.push(buildRule('runtime/stage-mismatch', `Runtime stage ${runtimeState.runtime.current_stage ?? 'none'} does not match contract stage ${contract.stage}.`));
|
|
91
101
|
}
|
|
@@ -106,6 +116,70 @@ export async function validateStageOutputs(repoRoot, { contract, runtimeState, t
|
|
|
106
116
|
if (touchedPaths.length === 0) {
|
|
107
117
|
warnings.push('No touched paths were provided; forbidden-write checks are limited to required artifacts.');
|
|
108
118
|
}
|
|
119
|
+
if (contract.enforce_plan_units) {
|
|
120
|
+
try {
|
|
121
|
+
const [planUnits, selectedStep] = await Promise.all([
|
|
122
|
+
readPlanUnits(repoRoot),
|
|
123
|
+
readSelectedRefactorStep(repoRoot)
|
|
124
|
+
]);
|
|
125
|
+
if (!selectedStep) {
|
|
126
|
+
violatedRules.push(buildRule('plan/selected-step-missing', 'Refactor artifact does not declare the selected plan unit.'));
|
|
127
|
+
}
|
|
128
|
+
else if (!planUnits.some((unit) => unit.id === selectedStep.id)) {
|
|
129
|
+
violatedRules.push(buildRule('plan/selected-step-invalid', `Selected refactor step ${selectedStep.id} does not exist in 04-plan.md.`));
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
diagnostics.push(`validated selected plan unit ${selectedStep.id}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
violatedRules.push(buildRule('plan/unreadable', 'Plan-unit validation could not read 04-plan.md or 05-refactor.md.'));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (effectiveDiffResult && (contract.diff_validation_rules.minimum_files_modified > 0
|
|
140
|
+
|| contract.diff_validation_rules.minimum_lines_changed > 0
|
|
141
|
+
|| contract.diff_validation_rules.must_create_files
|
|
142
|
+
|| contract.diff_validation_rules.required_structural_changes.length > 0)) {
|
|
143
|
+
const changedFiles = effectiveDiffResult.filesModified + effectiveDiffResult.filesAdded + effectiveDiffResult.filesDeleted;
|
|
144
|
+
const changedLines = effectiveDiffResult.linesAdded + effectiveDiffResult.linesRemoved;
|
|
145
|
+
const formattingOnly = effectiveDiffResult.filesModified > 0
|
|
146
|
+
&& effectiveDiffResult.filesModified === effectiveDiffResult.formattingOnlyPaths.length
|
|
147
|
+
&& effectiveDiffResult.filesAdded === 0
|
|
148
|
+
&& effectiveDiffResult.filesDeleted === 0;
|
|
149
|
+
if (changedFiles < contract.diff_validation_rules.minimum_files_modified) {
|
|
150
|
+
violatedRules.push(buildRule('diff/minimum-files-modified', `Refactor changed ${changedFiles} files but requires at least ${contract.diff_validation_rules.minimum_files_modified}.`));
|
|
151
|
+
}
|
|
152
|
+
if (changedLines < contract.diff_validation_rules.minimum_lines_changed) {
|
|
153
|
+
violatedRules.push(buildRule('diff/minimum-lines-changed', `Refactor changed ${changedLines} lines but requires at least ${contract.diff_validation_rules.minimum_lines_changed}.`));
|
|
154
|
+
}
|
|
155
|
+
if (contract.diff_validation_rules.must_create_files && effectiveDiffResult.filesAdded === 0) {
|
|
156
|
+
violatedRules.push(buildRule('diff/must-create-files', 'Refactor must create at least one new file.'));
|
|
157
|
+
}
|
|
158
|
+
if (formattingOnly) {
|
|
159
|
+
violatedRules.push(buildRule('diff/formatting-only', 'Refactor changes are formatting-only and do not count as meaningful code change.'));
|
|
160
|
+
}
|
|
161
|
+
if (!hasRequiredStructuralChanges(effectiveDiffResult, contract.diff_validation_rules.required_structural_changes)) {
|
|
162
|
+
violatedRules.push(buildRule('diff/required-structural-changes', `Refactor is missing required structural changes: ${contract.diff_validation_rules.required_structural_changes.join(', ')}.`));
|
|
163
|
+
}
|
|
164
|
+
diagnostics.push(`validated diff: files=${changedFiles}, lines=${changedLines}, structural=${effectiveDiffResult.structuralChanges.structural_change_flags.join(',') || 'none'}`);
|
|
165
|
+
}
|
|
166
|
+
else if (contract.diff_validation_rules.minimum_files_modified > 0
|
|
167
|
+
|| contract.diff_validation_rules.minimum_lines_changed > 0
|
|
168
|
+
|| contract.diff_validation_rules.must_create_files
|
|
169
|
+
|| contract.diff_validation_rules.required_structural_changes.length > 0) {
|
|
170
|
+
violatedRules.push(buildRule('diff/baseline-missing', 'Diff validation rules are configured but no refactor baseline snapshot was available.'));
|
|
171
|
+
}
|
|
172
|
+
if (contract.min_impact_score > 0) {
|
|
173
|
+
if (!impactDelta) {
|
|
174
|
+
violatedRules.push(buildRule('impact-score/missing-baseline', 'Impact score threshold is configured but no baseline score is available.'));
|
|
175
|
+
}
|
|
176
|
+
else if (impactDelta.delta < contract.min_impact_score) {
|
|
177
|
+
violatedRules.push(buildRule('impact-score/minimum-threshold', `Impact score delta ${impactDelta.delta} is below the required threshold ${contract.min_impact_score}.`));
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
diagnostics.push(`validated impact score delta ${impactDelta.delta}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
109
183
|
return {
|
|
110
184
|
stage: contract.stage,
|
|
111
185
|
contract_version: contract.contract_version,
|
|
@@ -113,10 +187,12 @@ export async function validateStageOutputs(repoRoot, { contract, runtimeState, t
|
|
|
113
187
|
violated_rules: violatedRules,
|
|
114
188
|
missing_artifacts: [...new Set(missingArtifacts)].sort((left, right) => left.localeCompare(right)),
|
|
115
189
|
warnings,
|
|
116
|
-
diagnostics
|
|
190
|
+
diagnostics,
|
|
191
|
+
...(effectiveDiffResult ? { diff_result: effectiveDiffResult } : {}),
|
|
192
|
+
...(impactDelta ? { impact_score_delta: impactDelta.delta } : {})
|
|
117
193
|
};
|
|
118
194
|
}
|
|
119
|
-
export async function validateStageOutputsForCurrentState(repoRoot, { runtimeState, touchedPaths = [] }) {
|
|
195
|
+
export async function validateStageOutputsForCurrentState(repoRoot, { runtimeState, touchedPaths = [], diffResult = null }) {
|
|
120
196
|
const stage = runtimeState.runtime.current_stage;
|
|
121
197
|
if (!stage) {
|
|
122
198
|
throw new Error('Cannot validate stage outputs when no stage is active.');
|
|
@@ -125,14 +201,16 @@ export async function validateStageOutputsForCurrentState(repoRoot, { runtimeSta
|
|
|
125
201
|
return validateStageOutputs(repoRoot, {
|
|
126
202
|
contract,
|
|
127
203
|
runtimeState,
|
|
128
|
-
touchedPaths
|
|
204
|
+
touchedPaths,
|
|
205
|
+
diffResult
|
|
129
206
|
});
|
|
130
207
|
}
|
|
131
|
-
export async function validateStageOutputsForStage(repoRoot, { stage, runtimeState, touchedPaths = [] }) {
|
|
208
|
+
export async function validateStageOutputsForStage(repoRoot, { stage, runtimeState, touchedPaths = [], diffResult = null }) {
|
|
132
209
|
const contract = await loadCompiledContract(repoRoot, stage);
|
|
133
210
|
return validateStageOutputs(repoRoot, {
|
|
134
211
|
contract,
|
|
135
212
|
runtimeState,
|
|
136
|
-
touchedPaths
|
|
213
|
+
touchedPaths,
|
|
214
|
+
diffResult
|
|
137
215
|
});
|
|
138
216
|
}
|