runApplyPipeline function
Future<ApplyPipelineResult>
runApplyPipeline({
- required ChangePlan approvedPlan,
- required PlanLocationRecord location,
- required PlanStore planStore,
- required AppliedHistoryStore historyStore,
- required ProcessRunner processRunner,
- required Future<
ValidationOutcome> validate(- String projectRoot,
- List<
CommandRecord> commands
- required void releaseStaging(
- String stagingRoot
- ApplyFileSystem fileSystem = const ApplyFileSystem(),
- ApplyObservations? observations,
Implementation
Future<ApplyPipelineResult> runApplyPipeline({
required ChangePlan approvedPlan,
required PlanLocationRecord location,
required PlanStore planStore,
required AppliedHistoryStore historyStore,
required ProcessRunner processRunner,
required Future<ValidationOutcome> Function(
String projectRoot,
List<CommandRecord> commands,
)
validate,
required void Function(String stagingRoot) releaseStaging,
ApplyFileSystem fileSystem = const ApplyFileSystem(),
ApplyObservations? observations,
}) async {
try {
applyPreflight(
plan: approvedPlan,
stagingRoot: location.stagingRoot,
canonicalDestination: location.canonicalDestination,
fileSystem: fileSystem,
observations: observations,
);
} on ApplyException catch (error) {
return _preflightFailure(error.exitCode, error.message);
} on FileSystemException catch (error) {
return _preflightFailure(
ExitReporter.planStaleOrStagingIntegrityFailure,
error.toString(),
);
}
final applying = _with(approvedPlan, state: PlanState.applying);
planStore.savePlan(applying);
late ApplyPhase1Result phase1;
try {
phase1 = applyPhase1(
plan: approvedPlan,
location: location,
fileSystem: fileSystem,
observations: observations,
runPreflight: false,
);
} on Object catch (error) {
if (error is! ApplyException && error is! FileSystemException) rethrow;
final failureMessage = error is ApplyException
? error.message
: error.toString();
final failed = _with(applying, state: PlanState.failed);
planStore.savePlan(failed);
final recovery = rollbackMaterialization(
plan: approvedPlan,
canonicalDestination: location.canonicalDestination,
fileSystem: fileSystem,
);
final terminalState = PlanState.values.byName(recovery.state.name);
planStore.savePlan(_with(failed, state: terminalState));
final exitCode = recovery.state == RecoveryState.rolledBack
? ExitReporter.applyFailedRolledBack
: ExitReporter.applyFailedPartialRecoveryOrManualRequired;
final summary = switch (recovery.state) {
RecoveryState.rolledBack =>
'rolled back ${recovery.removedCreatedPaths.length} created files',
RecoveryState.partiallyRecovered =>
'partial recovery: ${recovery.removedCreatedPaths.length} removed, '
'${recovery.unresolvedPaths.length} unresolved',
RecoveryState.manualRecoveryRequired =>
'manual recovery required: ${recovery.unresolvedPaths.length} '
'unresolved',
};
final message = '$failureMessage Recovery $summary.';
return ApplyPipelineResult(
exitCode: exitCode,
ok: false,
result: {
'planId': approvedPlan.planId,
'state': recovery.state.name,
'phase1': const {'filesWritten': 0, 'invariantHolds': false},
'phase2': null,
'validation': null,
'recovery': recovery.toJson(),
},
error: _error('APPLY_FAILED', exitCode, message),
textLine: 'APPLY_FAILED: $message',
);
}
final phase2 = await applyPhase2(
plan: approvedPlan,
location: location,
processRunner: processRunner,
);
final phase2Json = {
'commands': phase2.commands,
'pathsTouched': phase2.pathsTouched,
'outsideDeclaredSet': phase2.outsideDeclaredSet,
if (phase2.failure != null) 'failure': phase2.failure!.toJson(),
};
if (!phase2.ok) {
final failed = _with(applying, state: PlanState.failed);
planStore.savePlan(failed);
planStore.savePlan(_with(failed, state: PlanState.manualRecoveryRequired));
final message = phase2.failure == null
? 'Project materialized but not build-ready: Phase 2 wrote outside '
'the declared set: ${phase2.outsideDeclaredSet.join(', ')}.'
: materializedButNotBuildReadyMessage(phase2.failure!);
return ApplyPipelineResult(
exitCode: ExitReporter.applyFailedPartialRecoveryOrManualRequired,
ok: false,
result: {
'planId': approvedPlan.planId,
'state': PlanState.manualRecoveryRequired.name,
'phase1': {
'filesWritten': phase1.filesWritten,
'invariantHolds': phase1.invariantHolds,
},
'phase2': phase2Json,
'validation': null,
'recovery': null,
},
error: {
'code': 'PHASE2_FAILED',
'exitCode': ExitReporter.applyFailedPartialRecoveryOrManualRequired,
'message': message,
'cause': phase2Json,
'missingDecisions': const [],
},
textLine: 'PHASE2_FAILED: $message',
);
}
final validating = _with(applying, state: PlanState.validating);
planStore.savePlan(validating);
final validation = await validate(
location.canonicalDestination,
approvedPlan.validationCommands,
);
final validationJson = {
...validation.report.toJson(),
'violations': validation.violations,
};
if (validation.report.result == ValidationResult.failed) {
final failed = _with(validating, state: PlanState.failed);
planStore.savePlan(failed);
planStore.savePlan(_with(failed, state: PlanState.manualRecoveryRequired));
final failingGate = _failingGate(validation);
final recovery = validationFailureRecovery(failingGate: failingGate);
final message =
'Project applied but NOT validated — $failingGate. DEFECT: the '
'staging-time generation-validity check should have caught this. '
'Staging is retained for recovery.';
return ApplyPipelineResult(
exitCode: ExitReporter.validationFailed,
ok: false,
result: {
'planId': approvedPlan.planId,
'state': PlanState.manualRecoveryRequired.name,
'phase1': {
'filesWritten': phase1.filesWritten,
'invariantHolds': phase1.invariantHolds,
},
'phase2': phase2Json,
'validation': validationJson,
'recovery': {...recovery.toJson(), 'stagingRetained': true},
},
error: {
'code': 'VALIDATION_FAILED',
'exitCode': ExitReporter.validationFailed,
'message': message,
'cause': validationJson,
'missingDecisions': const [],
},
textLine: 'VALIDATION_FAILED: $message',
);
}
finalizeSuccessfulApply(
appliedPlan: approvedPlan,
location: location,
validationResult: validation.report.result,
historyStore: historyStore,
releaseStaging: releaseStaging,
);
planStore.savePlan(_with(validating, state: PlanState.applied));
return ApplyPipelineResult(
exitCode: ExitReporter.success,
ok: true,
result: {
'planId': approvedPlan.planId,
'state': PlanState.applied.name,
'phase1': {
'filesWritten': phase1.filesWritten,
'invariantHolds': phase1.invariantHolds,
},
'phase2': phase2Json,
'validation': validationJson,
'recovery': null,
},
error: null,
textLine:
'Applied ${approvedPlan.planId}: ${phase1.filesWritten} files written; '
'reviewed-content invariant holds; Phase 2 regenerated '
'${phase2.pathsTouched.length} paths; validation passed.',
);
}