Repository: kurtosis-tech/kurtosis
Pull request #954 (2023-07-20 15:35:51)

feat: Idempotent run V1 - services can now be live-updated inside an enclave
## Description:
Kurtosis is now able to re-run the same Starlark script with changes to the `ServiceConfig` objects used in the `add_service` and `add_services` instructions. Kurtosis will now re-run those specific instructions to update the underlying service. Dependent instructions referencing this service (like `exec`, `wait`) will also be re-run when the service is updated.

## Is this change user facing?
YES
<!-- If yes, please add the "user facing" label to the PR -->
<!-- If yes, don't forget to include docs changes where relevant -->

## References (if applicable):
<!-- Add relevant Github Issues, Discord threads, or other helpful information. -->


gbouv: Yup good catch @leoporoli , that was a mistake from me. I fixed it, thanks for flagging

Patch:
diff --git a/core/server/api_container/server/service_network/default_service_network.go b/core/server/api_container/server/service_network/default_service_network.go
index aae89f1c5..3b20ac070 100644
--- a/core/server/api_container/server/service_network/default_service_network.go
+++ b/core/server/api_container/server/service_network/default_service_network.go
@@ -348,6 +348,10 @@ func (network *DefaultServiceNetwork) AddServices(
 	startedServices := map[service.ServiceName]*service.Service{}
 	failedServices := map[service.ServiceName]error{}
 
+	if len(serviceConfigs) == 0 {
+		return startedServices, failedServices, nil
+	}
+
 	// Save the services currently running in enclave for later
 	currentlyRunningServicesInEnclave := map[service.ServiceName]bool{}
 	for serviceName := range network.registeredServiceInfo {
@@ -460,10 +464,9 @@ func (network *DefaultServiceNetwork) AddServices(
 	return startedServices, map[service.ServiceName]error{}, nil
 }
 
-// UpdateService updates a service currently running inside an enclave. See UpdateServices for more details
-func (network *DefaultServiceNetwork) UpdateService(ctx context.Context, serviceName service.ServiceName, serviceConfig *service.ServiceConfig) (*service.Service, error) {
+func (network *DefaultServiceNetwork) UpdateService(ctx context.Context, serviceName service.ServiceName, updateServiceConfig *service.ServiceConfig) (*service.Service, error) {
 	serviceConfigMap := map[service.ServiceName]*service.ServiceConfig{
-		serviceName: serviceConfig,
+		serviceName: updateServiceConfig,
 	}
 
 	startedServices, serviceFailed, err := network.UpdateServices(ctx, serviceConfigMap, singleServiceStartupBatch)
@@ -489,6 +492,10 @@ func (network *DefaultServiceNetwork) UpdateServices(ctx context.Context, update
 	failedServicesPool := map[service.ServiceName]error{}
 	successfullyUpdatedService := map[service.ServiceName]*service.Service{}
 
+	if len(updateServiceConfigs) == 0 {
+		return successfullyUpdatedService, failedServicesPool, nil
+	}
+
 	// First, remove the service
 	serviceUuidToNameMap := map[service.ServiceUUID]service.ServiceName{}
 	serviceUuidsToRemove := map[service.ServiceUUID]bool{}
@@ -511,7 +518,7 @@ func (network *DefaultServiceNetwork) UpdateServices(ctx context.Context, update
 		if serviceName, found := serviceUuidToNameMap[serviceUuid]; found {
 			failedServicesPool[serviceName] = serviceErr
 		} else {
-			return nil, nil, stacktrace.NewError("Error mapping service UUID to service name. This is a bug in Kurtosis")
+			return nil, nil, stacktrace.NewError("Error mapping service UUID to service name. This is a bug in Kurtosis.\nserviceUuidsToRemove=%v\nfailedRemovedServices=%v\nsuccessfullyRemovedServices=%v\nserviceUuidToNameMap=%v", serviceUuidsToRemove, failedRemovedServices, successfullyRemovedServices, serviceUuidToNameMap)
 		}
 	}
 
diff --git a/core/server/api_container/server/service_network/mock_service_network.go b/core/server/api_container/server/service_network/mock_service_network.go
index de096899b..5ce08a3c8 100644
--- a/core/server/api_container/server/service_network/mock_service_network.go
+++ b/core/server/api_container/server/service_network/mock_service_network.go
@@ -1174,17 +1174,17 @@ func (_c *MockServiceNetwork_UnsetConnection_Call) RunAndReturn(run func(context
 	return _c
 }
 
-// UpdateService provides a mock function with given fields: ctx, serviceName, serviceConfig
-func (_m *MockServiceNetwork) UpdateService(ctx context.Context, serviceName service.ServiceName, serviceConfig *service.ServiceConfig) (*service.Service, error) {
-	ret := _m.Called(ctx, serviceName, serviceConfig)
+// UpdateService provides a mock function with given fields: ctx, serviceName, updateServiceConfig
+func (_m *MockServiceNetwork) UpdateService(ctx context.Context, serviceName service.ServiceName, updateServiceConfig *service.ServiceConfig) (*service.Service, error) {
+	ret := _m.Called(ctx, serviceName, updateServiceConfig)
 
 	var r0 *service.Service
 	var r1 error
 	if rf, ok := ret.Get(0).(func(context.Context, service.ServiceName, *service.ServiceConfig) (*service.Service, error)); ok {
-		return rf(ctx, serviceName, serviceConfig)
+		return rf(ctx, serviceName, updateServiceConfig)
 	}
 	if rf, ok := ret.Get(0).(func(context.Context, service.ServiceName, *service.ServiceConfig) *service.Service); ok {
-		r0 = rf(ctx, serviceName, serviceConfig)
+		r0 = rf(ctx, serviceName, updateServiceConfig)
 	} else {
 		if ret.Get(0) != nil {
 			r0 = ret.Get(0).(*service.Service)
@@ -1192,7 +1192,7 @@ func (_m *MockServiceNetwork) UpdateService(ctx context.Context, serviceName ser
 	}
 
 	if rf, ok := ret.Get(1).(func(context.Context, service.ServiceName, *service.ServiceConfig) error); ok {
-		r1 = rf(ctx, serviceName, serviceConfig)
+		r1 = rf(ctx, serviceName, updateServiceConfig)
 	} else {
 		r1 = ret.Error(1)
 	}
@@ -1208,12 +1208,12 @@ type MockServiceNetwork_UpdateService_Call struct {
 // UpdateService is a helper method to define mock.On call
 //   - ctx context.Context
 //   - serviceName service.ServiceName
-//   - serviceConfig *service.ServiceConfig
-func (_e *MockServiceNetwork_Expecter) UpdateService(ctx interface{}, serviceName interface{}, serviceConfig interface{}) *MockServiceNetwork_UpdateService_Call {
-	return &MockServiceNetwork_UpdateService_Call{Call: _e.mock.On("UpdateService", ctx, serviceName, serviceConfig)}
+//   - updateServiceConfig *service.ServiceConfig
+func (_e *MockServiceNetwork_Expecter) UpdateService(ctx interface{}, serviceName interface{}, updateServiceConfig interface{}) *MockServiceNetwork_UpdateService_Call {
+	return &MockServiceNetwork_UpdateService_Call{Call: _e.mock.On("UpdateService", ctx, serviceName, updateServiceConfig)}
 }
 
-func (_c *MockServiceNetwork_UpdateService_Call) Run(run func(ctx context.Context, serviceName service.ServiceName, serviceConfig *service.ServiceConfig)) *MockServiceNetwork_UpdateService_Call {
+func (_c *MockServiceNetwork_UpdateService_Call) Run(run func(ctx context.Context, serviceName service.ServiceName, updateServiceConfig *service.ServiceConfig)) *MockServiceNetwork_UpdateService_Call {
 	_c.Call.Run(func(args mock.Arguments) {
 		run(args[0].(context.Context), args[1].(service.ServiceName), args[2].(*service.ServiceConfig))
 	})
diff --git a/core/server/api_container/server/service_network/service_network.go b/core/server/api_container/server/service_network/service_network.go
index 4b076a228..5a2f6cf4a 100644
--- a/core/server/api_container/server/service_network/service_network.go
+++ b/core/server/api_container/server/service_network/service_network.go
@@ -53,7 +53,7 @@ type ServiceNetwork interface {
 	UpdateService(
 		ctx context.Context,
 		serviceName service.ServiceName,
-		serviceConfig *service.ServiceConfig,
+		updateServiceConfig *service.ServiceConfig,
 	) (
 		*service.Service,
 		error,
diff --git a/core/server/api_container/server/startosis_engine/enclave_structure/enclave_component_status.go b/core/server/api_container/server/startosis_engine/enclave_structure/enclave_component_status.go
new file mode 100644
index 000000000..718a1073c
--- /dev/null
+++ b/core/server/api_container/server/startosis_engine/enclave_structure/enclave_component_status.go
@@ -0,0 +1,17 @@
+package enclave_structure
+
+//go:generate go run github.com/dmarkham/enumer -trimprefix "EnclaveComponentStatus" -type=EnclaveComponentStatus
+type EnclaveComponentStatus uint8
+
+const (
+	// ComponentIsNew means the component was first created during this run
+	ComponentIsNew EnclaveComponentStatus = iota
+
+	// ComponentWasLeftIntact means the component was present prior to the beginning of this run and has been left
+	// intact
+	ComponentWasLeftIntact
+
+	// ComponentIsUpdated means the component was present prior to the beginning of this run but has been updated
+	// during this run
+	ComponentIsUpdated
+)
diff --git a/core/server/api_container/server/startosis_engine/enclave_structure/enclave_components.go b/core/server/api_container/server/startosis_engine/enclave_structure/enclave_components.go
new file mode 100644
index 000000000..005b29840
--- /dev/null
+++ b/core/server/api_container/server/startosis_engine/enclave_structure/enclave_components.go
@@ -0,0 +1,26 @@
+package enclave_structure
+
+import (
+	"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service"
+)
+
+type EnclaveComponents struct {
+	enclaveServices map[service.ServiceName]EnclaveComponentStatus
+}
+
+func NewEnclaveComponents() *EnclaveComponents {
+	return &EnclaveComponents{
+		enclaveServices: map[service.ServiceName]EnclaveComponentStatus{},
+	}
+}
+
+func (components *EnclaveComponents) AddService(serviceName service.ServiceName, componentStatus EnclaveComponentStatus) {
+	components.enclaveServices[serviceName] = componentStatus
+}
+
+func (components *EnclaveComponents) HasServiceBeenUpdated(serviceName service.ServiceName) bool {
+	if serviceStatus, found := components.enclaveServices[serviceName]; found {
+		return serviceStatus == ComponentIsUpdated
+	}
+	return false
+}
diff --git a/core/server/api_container/server/startosis_engine/enclave_structure/enclavecomponentstatus_enumer.go b/core/server/api_container/server/startosis_engine/enclave_structure/enclavecomponentstatus_enumer.go
new file mode 100644
index 000000000..41098f5e2
--- /dev/null
+++ b/core/server/api_container/server/startosis_engine/enclave_structure/enclavecomponentstatus_enumer.go
@@ -0,0 +1,82 @@
+// Code generated by "enumer -trimprefix EnclaveComponentStatus -type=EnclaveComponentStatus"; DO NOT EDIT.
+
+package enclave_structure
+
+import (
+	"fmt"
+	"strings"
+)
+
+const _EnclaveComponentStatusName = "ComponentIsNewComponentWasLeftIntactComponentIsUpdated"
+
+var _EnclaveComponentStatusIndex = [...]uint8{0, 14, 36, 54}
+
+const _EnclaveComponentStatusLowerName = "componentisnewcomponentwasleftintactcomponentisupdated"
+
+func (i EnclaveComponentStatus) String() string {
+	if i >= EnclaveComponentStatus(len(_EnclaveComponentStatusIndex)-1) {
+		return fmt.Sprintf("EnclaveComponentStatus(%d)", i)
+	}
+	return _EnclaveComponentStatusName[_EnclaveComponentStatusIndex[i]:_EnclaveComponentStatusIndex[i+1]]
+}
+
+// An "invalid array index" compiler error signifies that the constant values have changed.
+// Re-run the stringer command to generate them again.
+func _EnclaveComponentStatusNoOp() {
+	var x [1]struct{}
+	_ = x[ComponentIsNew-(0)]
+	_ = x[ComponentWasLeftIntact-(1)]
+	_ = x[ComponentIsUpdated-(2)]
+}
+
+var _EnclaveComponentStatusValues = []EnclaveComponentStatus{ComponentIsNew, ComponentWasLeftIntact, ComponentIsUpdated}
+
+var _EnclaveComponentStatusNameToValueMap = map[string]EnclaveComponentStatus{
+	_EnclaveComponentStatusName[0:14]:       ComponentIsNew,
+	_EnclaveComponentStatusLowerName[0:14]:  ComponentIsNew,
+	_EnclaveComponentStatusName[14:36]:      ComponentWasLeftIntact,
+	_EnclaveComponentStatusLowerName[14:36]: ComponentWasLeftIntact,
+	_EnclaveComponentStatusName[36:54]:      ComponentIsUpdated,
+	_EnclaveComponentStatusLowerName[36:54]: ComponentIsUpdated,
+}
+
+var _EnclaveComponentStatusNames = []string{
+	_EnclaveComponentStatusName[0:14],
+	_EnclaveComponentStatusName[14:36],
+	_EnclaveComponentStatusName[36:54],
+}
+
+// EnclaveComponentStatusString retrieves an enum value from the enum constants string name.
+// Throws an error if the param is not part of the enum.
+func EnclaveComponentStatusString(s string) (EnclaveComponentStatus, error) {
+	if val, ok := _EnclaveComponentStatusNameToValueMap[s]; ok {
+		return val, nil
+	}
+
+	if val, ok := _EnclaveComponentStatusNameToValueMap[strings.ToLower(s)]; ok {
+		return val, nil
+	}
+	return 0, fmt.Errorf("%s does not belong to EnclaveComponentStatus values", s)
+}
+
+// EnclaveComponentStatusValues returns all values of the enum
+func EnclaveComponentStatusValues() []EnclaveComponentStatus {
+	return _EnclaveComponentStatusValues
+}
+
+// EnclaveComponentStatusStrings returns a slice of all String values of the enum
+func EnclaveComponentStatusStrings() []string {
+	strs := make([]string, len(_EnclaveComponentStatusNames))
+	copy(strs, _EnclaveComponentStatusNames)
+	return strs
+}
+
+// IsAEnclaveComponentStatus returns "true" if the value is listed in the enum definition. "false" otherwise
+func (i EnclaveComponentStatus) IsAEnclaveComponentStatus() bool {
+	for _, v := range _EnclaveComponentStatusValues {
+		if i == v {
+			return true
+		}
+	}
+	return false
+}
diff --git a/core/server/api_container/server/startosis_engine/enclave_structure/instruction_resolution_status.go b/core/server/api_container/server/startosis_engine/enclave_structure/instruction_resolution_status.go
new file mode 100644
index 000000000..b6c448908
--- /dev/null
+++ b/core/server/api_container/server/startosis_engine/enclave_structure/instruction_resolution_status.go
@@ -0,0 +1,21 @@
+package enclave_structure
+
+//go:generate go run github.com/dmarkham/enumer -trimprefix "InstructionResolutionStatus" -type=InstructionResolutionStatus
+type InstructionResolutionStatus uint8
+
+const (
+	// InstructionIsEqual means this instruction is strictly equal to the one it's being compared to.
+	InstructionIsEqual InstructionResolutionStatus = iota
+
+	// InstructionIsUpdate means this instruction is the same as the one it's being compared to, but with some
+	// changes to its parameters. It should be re-run so that the enclave can be updated.
+	InstructionIsUpdate
+
+	// InstructionIsUnknown means the two instructions are completely different.
+	InstructionIsUnknown
+
+	// InstructionIsNotResolvableAbort means one of the compared instructions is fundamentally incompatible with the
+	// concept of idempotency. Kurtosis should stop trying to run it in an idempotent way and fall back to the default
+	// behaviour
+	InstructionIsNotResolvableAbort
+)
diff --git a/core/server/api_container/server/startosis_engine/enclave_structure/instructionresolutionstatus_enumer.go b/core/server/api_container/server/startosis_engine/enclave_structure/instructionresolutionstatus_enumer.go
new file mode 100644
index 000000000..851a78b09
--- /dev/null
+++ b/core/server/api_container/server/startosis_engine/enclave_structure/instructionresolutionstatus_enumer.go
@@ -0,0 +1,86 @@
+// Code generated by "enumer -trimprefix InstructionResolutionStatus -type=InstructionResolutionStatus"; DO NOT EDIT.
+
+package enclave_structure
+
+import (
+	"fmt"
+	"strings"
+)
+
+const _InstructionResolutionStatusName = "InstructionIsEqualInstructionIsUpdateInstructionIsUnknownInstructionIsNotResolvableAbort"
+
+var _InstructionResolutionStatusIndex = [...]uint8{0, 18, 37, 57, 88}
+
+const _InstructionResolutionStatusLowerName = "instructionisequalinstructionisupdateinstructionisunknowninstructionisnotresolvableabort"
+
+func (i InstructionResolutionStatus) String() string {
+	if i >= InstructionResolutionStatus(len(_InstructionResolutionStatusIndex)-1) {
+		return fmt.Sprintf("InstructionResolutionStatus(%d)", i)
+	}
+	return _InstructionResolutionStatusName[_InstructionResolutionStatusIndex[i]:_InstructionResolutionStatusIndex[i+1]]
+}
+
+// An "invalid array index" compiler error signifies that the constant values have changed.
+// Re-run the stringer command to generate them again.
+func _InstructionResolutionStatusNoOp() {
+	var x [1]struct{}
+	_ = x[InstructionIsEqual-(0)]
+	_ = x[InstructionIsUpdate-(1)]
+	_ = x[InstructionIsUnknown-(2)]
+	_ = x[InstructionIsNotResolvableAbort-(3)]
+}
+
+var _InstructionResolutionStatusValues = []InstructionResolutionStatus{InstructionIsEqual, InstructionIsUpdate, InstructionIsUnknown, InstructionIsNotResolvableAbort}
+
+var _InstructionResolutionStatusNameToValueMap = map[string]InstructionResolutionStatus{
+	_InstructionResolutionStatusName[0:18]:       InstructionIsEqual,
+	_InstructionResolutionStatusLowerName[0:18]:  InstructionIsEqual,
+	_InstructionResolutionStatusName[18:37]:      InstructionIsUpdate,
+	_InstructionResolutionStatusLowerName[18:37]: InstructionIsUpdate,
+	_InstructionResolutionStatusName[37:57]:      InstructionIsUnknown,
+	_InstructionResolutionStatusLowerName[37:57]: InstructionIsUnknown,
+	_InstructionResolutionStatusName[57:88]:      InstructionIsNotResolvableAbort,
+	_InstructionResolutionStatusLowerName[57:88]: InstructionIsNotResolvableAbort,
+}
+
+var _InstructionResolutionStatusNames = []string{
+	_InstructionResolutionStatusName[0:18],
+	_InstructionResolutionStatusName[18:37],
+	_InstructionResolutionStatusName[37:57],
+	_InstructionResolutionStatusName[57:88],
+}
+
+// InstructionResolutionStatusString retrieves an enum value from the enum constants string name.
+// Throws an error if the param is not part of the enum.
+func InstructionResolutionStatusString(s string) (InstructionResolutionStatus, error) {
+	if val, ok := _InstructionResolutionStatusNameToValueMap[s]; ok {
+		return val, nil
+	}
+
+	if val, ok := _InstructionResolutionStatusNameToValueMap[strings.ToLower(s)]; ok {
+		return val, nil
+	}
+	return 0, fmt.Errorf("%s does not belong to InstructionResolutionStatus values", s)
+}
+
+// InstructionResolutionStatusValues returns all values of the enum
+func InstructionResolutionStatusValues() []InstructionResolutionStatus {
+	return _InstructionResolutionStatusValues
+}
+
+// InstructionResolutionStatusStrings returns a slice of all String values of the enum
+func InstructionResolutionStatusStrings() []string {
+	strs := make([]string, len(_InstructionResolutionStatusNames))
+	copy(strs, _InstructionResolutionStatusNames)
+	return strs
+}
+
+// IsAInstructionResolutionStatus returns "true" if the value is listed in the enum definition. "false" otherwise
+func (i InstructionResolutionStatus) IsAInstructionResolutionStatus() bool {
+	for _, v := range _InstructionResolutionStatusValues {
+		if i == v {
+			return true
+		}
+	}
+	return false
+}
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service.go
index 5493695cf..248787909 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction"
@@ -103,7 +104,7 @@ func (builtin *AddServiceCapabilities) Interpret(_ string, arguments *builtin_ar
 	builtin.serviceName = service.ServiceName(serviceName.GoString())
 	builtin.serviceConfig = apiServiceConfig
 	builtin.readyCondition = readyCondition
-	builtin.resultUuid, err = builtin.runtimeValueStore.CreateValue()
+	builtin.resultUuid, err = builtin.runtimeValueStore.GetOrCreateValueAssociatedWithService(builtin.serviceName)
 	if err != nil {
 		return nil, startosis_errors.WrapWithInterpretationError(err, "Unable to create runtime value to hold '%v' command return values", AddServiceBuiltinName)
 	}
@@ -113,7 +114,6 @@ func (builtin *AddServiceCapabilities) Interpret(_ string, arguments *builtin_ar
 		return nil, interpretationErr
 	}
 	return returnValue, nil
-
 }
 
 func (builtin *AddServiceCapabilities) Validate(_ *builtin_argument.ArgumentValuesSet, validatorEnvironment *startosis_validator.ValidatorEnvironment) *startosis_errors.ValidationError {
@@ -128,7 +128,12 @@ func (builtin *AddServiceCapabilities) Execute(ctx context.Context, _ *builtin_a
 	if err != nil {
 		return "", stacktrace.Propagate(err, "An error occurred replace a magic string in '%s' instruction arguments for service '%s'. Execution cannot proceed", AddServiceBuiltinName, builtin.serviceName)
 	}
-	startedService, err := builtin.serviceNetwork.AddService(ctx, replacedServiceName, replacedServiceConfig)
+	var startedService *service.Service
+	if _, found := builtin.serviceNetwork.GetServiceRegistration(builtin.serviceName); found {
+		startedService, err = builtin.serviceNetwork.UpdateService(ctx, replacedServiceName, replacedServiceConfig)
+	} else {
+		startedService, err = builtin.serviceNetwork.AddService(ctx, replacedServiceName, replacedServiceConfig)
+	}
 	if err != nil {
 		return "", stacktrace.Propagate(err, "Unexpected error occurred starting service '%s'", replacedServiceName)
 	}
@@ -148,6 +153,28 @@ func (builtin *AddServiceCapabilities) Execute(ctx context.Context, _ *builtin_a
 	return instructionResult, nil
 }
 
+func (builtin *AddServiceCapabilities) TryResolveWith(instructionsAreEqual bool, other kurtosis_plan_instruction.KurtosisPlanInstructionCapabilities, enclaveComponents *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
+	if instructionsAreEqual {
+		enclaveComponents.AddService(builtin.serviceName, enclave_structure.ComponentWasLeftIntact)
+		return enclave_structure.InstructionIsEqual
+	}
+	if other == nil {
+		enclaveComponents.AddService(builtin.serviceName, enclave_structure.ComponentIsNew)
+		return enclave_structure.InstructionIsUnknown
+	}
+	otherAddServiceCapabilities, ok := other.(*AddServiceCapabilities)
+	if !ok {
+		enclaveComponents.AddService(builtin.serviceName, enclave_structure.ComponentIsNew)
+		return enclave_structure.InstructionIsUnknown
+	}
+	if otherAddServiceCapabilities.serviceName == builtin.serviceName {
+		enclaveComponents.AddService(builtin.serviceName, enclave_structure.ComponentIsUpdated)
+		return enclave_structure.InstructionIsUpdate
+	}
+	enclaveComponents.AddService(builtin.serviceName, enclave_structure.ComponentIsNew)
+	return enclave_structure.InstructionIsUnknown
+}
+
 func validateAndConvertConfigAndReadyCondition(
 	serviceNetwork service_network.ServiceNetwork,
 	rawConfig starlark.Value,
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go
index 178415650..df5a32b21 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go
@@ -71,8 +71,8 @@ func validateSingleService(validatorEnvironment *startosis_validator.ValidatorEn
 		return startosis_errors.NewValidationError(invalidServiceNameErrorText(serviceName))
 	}
 
-	if validatorEnvironment.DoesServiceNameExist(serviceName) {
-		return startosis_errors.NewValidationError("There was an error validating '%s' as service '%s' already exists", AddServiceBuiltinName, serviceName)
+	if validatorEnvironment.DoesServiceNameExist(serviceName) == startosis_validator.ServiceCreatedOrUpdatedDuringPackageRun {
+		return startosis_errors.NewValidationError("There was an error validating '%s' as service '%s' was created inside this package. Adding the same service twice in the same package is not allowed", AddServiceBuiltinName, serviceName)
 	}
 	if serviceConfig.GetFilesArtifactsExpansion() != nil {
 		for _, artifactName := range serviceConfig.GetFilesArtifactsExpansion().ServiceDirpathsToArtifactIdentifiers {
@@ -83,9 +83,11 @@ func validateSingleService(validatorEnvironment *startosis_validator.ValidatorEn
 	}
 	validatorEnvironment.AddServiceName(serviceName)
 	validatorEnvironment.AppendRequiredContainerImage(serviceConfig.GetContainerImageName())
+	var portIds []string
 	for portId := range serviceConfig.GetPrivatePorts() {
-		validatorEnvironment.AddPrivatePortIDForService(portId, serviceName)
+		portIds = append(portIds, portId)
 	}
+	validatorEnvironment.AddPrivatePortIDForService(portIds, serviceName)
 	return nil
 }
 
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_services.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_services.go
index 6295a7283..792021bb7 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_services.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_services.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction"
@@ -123,25 +124,56 @@ func (builtin *AddServicesCapabilities) Execute(ctx context.Context, _ *builtin_
 		renderedServiceConfigs[renderedServiceName] = renderedServiceConfig
 	}
 
-	startedServices, failedServices, err := builtin.serviceNetwork.AddServices(ctx, renderedServiceConfigs, parallelism)
+	serviceToUpdate := map[service.ServiceName]*service.ServiceConfig{}
+	serviceToCreate := map[service.ServiceName]*service.ServiceConfig{}
+	for serviceName, serviceConfig := range renderedServiceConfigs {
+		if _, found := builtin.serviceNetwork.GetServiceRegistration(serviceName); found {
+			serviceToUpdate[serviceName] = serviceConfig
+		} else {
+			serviceToCreate[serviceName] = serviceConfig
+		}
+	}
+
+	updatedServices, failedToBeUpdatedServices, err := builtin.serviceNetwork.UpdateServices(ctx, serviceToUpdate, parallelism)
 	if err != nil {
-		return "", stacktrace.Propagate(err, "Unexpected error occurred starting a batch of services")
+		var allServiceNames []string
+		for serviceName := range serviceToUpdate {
+			allServiceNames = append(allServiceNames, string(serviceName))
+		}
+		return "", stacktrace.Propagate(err, "Unexpected error occurred updating the following batch of services: %s", strings.Join(allServiceNames, ", "))
 	}
-	if len(failedServices) > 0 {
-		failedServiceNames := make([]service.ServiceName, len(failedServices))
-		idx := 0
-		for failedServiceName := range failedServices {
-			failedServiceNames[idx] = failedServiceName
-			idx++
+
+	startedServices, failedToBeStartedServices, err := builtin.serviceNetwork.AddServices(ctx, serviceToCreate, parallelism)
+	if err != nil {
+		var allServiceNames []string
+		for serviceName := range serviceToCreate {
+			allServiceNames = append(allServiceNames, string(serviceName))
 		}
-		return "", stacktrace.NewError("Some errors occurred starting the following services: '%v'. The entire batch was rolled back an no service was started. Errors were: \n%v", failedServiceNames, failedServices)
+		return "", stacktrace.Propagate(err, "Unexpected error occurred starting the following batch of services: %s", strings.Join(allServiceNames, ", "))
+	}
+	if len(failedToBeStartedServices) > 0 || len(failedToBeUpdatedServices) > 0 {
+		var failedServiceNames []service.ServiceName
+		for failedServiceName := range failedToBeStartedServices {
+			failedServiceNames = append(failedServiceNames, failedServiceName)
+		}
+		for failedServiceName := range failedToBeUpdatedServices {
+			failedServiceNames = append(failedServiceNames, failedServiceName)
+		}
+		return "", stacktrace.NewError("Some errors occurred starting or updating the following services: '%v'. The entire batch was rolled back an no service was started. Errors were:\nService creations: %v\nService Updates: %v", failedServiceNames, failedToBeStartedServices, failedToBeUpdatedServices)
+	}
+	startedAndUpdatedService := map[service.ServiceName]*service.Service{}
+	for startedServiceName, startedService := range startedServices {
+		startedAndUpdatedService[startedServiceName] = startedService
+	}
+	for updatedServiceName, updatedService := range updatedServices {
+		startedAndUpdatedService[updatedServiceName] = updatedService
 	}
 	shouldDeleteAllStartedServices := true
 
 	//TODO we should move the readiness check functionality to the default service network to improve performance
 	///TODO because we won't have to wait for all services to start for checking readiness, but first we have to
 	//TODO propagate the Recipes to this layer too and probably move the wait instruction also
-	if failedServicesChecks := builtin.allServicesReadinessCheck(ctx, startedServices, parallelism); len(failedServicesChecks) > 0 {
+	if failedServicesChecks := builtin.allServicesReadinessCheck(ctx, startedAndUpdatedService, parallelism); len(failedServicesChecks) > 0 {
 		var allServiceChecksErrMsg string
 		for serviceName, serviceErr := range failedServicesChecks {
 			serviceMsg := fmt.Sprintf("Service '%v' error:\n%v\n", serviceName, serviceErr)
@@ -151,21 +183,65 @@ func (builtin *AddServicesCapabilities) Execute(ctx context.Context, _ *builtin_
 	}
 	defer func() {
 		if shouldDeleteAllStartedServices {
-			builtin.removeAllStartedServices(ctx, startedServices)
+			builtin.removeAllStartedServices(ctx, startedAndUpdatedService)
 		}
 	}()
 
 	instructionResult := strings.Builder{}
 	instructionResult.WriteString(fmt.Sprintf("Successfully added the following '%d' services:", len(startedServices)))
-	for serviceName, serviceObj := range startedServices {
+	for serviceName, serviceObj := range startedAndUpdatedService {
 		fillAddServiceReturnValueWithRuntimeValues(serviceObj, builtin.resultUuids[serviceName], builtin.runtimeValueStore)
 		instructionResult.WriteString(fmt.Sprintf("\n  Service '%s' added with UUID '%s'", serviceName, serviceObj.GetRegistration().GetUUID()))
-
 	}
 	shouldDeleteAllStartedServices = false
 	return instructionResult.String(), nil
 }
 
+func (builtin *AddServicesCapabilities) TryResolveWith(instructionsAreEqual bool, other kurtosis_plan_instruction.KurtosisPlanInstructionCapabilities, enclaveComponents *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
+	if instructionsAreEqual {
+		for serviceName := range builtin.serviceConfigs {
+			enclaveComponents.AddService(serviceName, enclave_structure.ComponentWasLeftIntact)
+		}
+		return enclave_structure.InstructionIsEqual
+	}
+	otherAddServicesCapabilities, ok := other.(*AddServicesCapabilities)
+	if !ok {
+		for serviceName := range builtin.serviceConfigs {
+			enclaveComponents.AddService(serviceName, enclave_structure.ComponentIsNew)
+		}
+		return enclave_structure.InstructionIsUnknown
+	}
+
+	// The instruction can be re-run only if the set of added services is a subset of what was added by the instruction it's being compared to
+	previouslyAddedService := map[service.ServiceName]bool{}
+	for serviceName := range otherAddServicesCapabilities.serviceConfigs {
+		previouslyAddedService[serviceName] = false
+	}
+	for serviceName := range builtin.serviceConfigs {
+		if _, found := previouslyAddedService[serviceName]; found {
+			previouslyAddedService[serviceName] = true // toggle the boolean to true
+		}
+	}
+	for _, servicePresentInCurrentInstruction := range previouslyAddedService {
+		if !servicePresentInCurrentInstruction {
+			// if one service is not present in the current instruction, instruction cannot be re-run
+			for serviceName := range builtin.serviceConfigs {
+				enclaveComponents.AddService(serviceName, enclave_structure.ComponentIsNew)
+			}
+			return enclave_structure.InstructionIsUnknown
+		}
+	}
+
+	for serviceName := range builtin.serviceConfigs {
+		if _, found := previouslyAddedService[serviceName]; found {
+			enclaveComponents.AddService(serviceName, enclave_structure.ComponentIsUpdated)
+		} else {
+			enclaveComponents.AddService(serviceName, enclave_structure.ComponentIsNew)
+		}
+	}
+	return enclave_structure.InstructionIsUpdate
+}
+
 func (builtin *AddServicesCapabilities) removeAllStartedServices(
 	ctx context.Context,
 	startedServices map[service.ServiceName]*service.Service,
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/assert/assert.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/assert/assert.go
index 7c992f61a..da0c954b0 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/assert/assert.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/assert/assert.go
@@ -3,6 +3,7 @@ package assert
 import (
 	"context"
 	"fmt"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction/shared_helpers/magic_string_helper"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
@@ -142,6 +143,13 @@ func (builtin *AssertCapabilities) Execute(_ context.Context, _ *builtin_argumen
 	return instructionResult, nil
 }
 
+func (builtin *AssertCapabilities) TryResolveWith(instructionsAreEqual bool, _ kurtosis_plan_instruction.KurtosisPlanInstructionCapabilities, _ *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
+	if instructionsAreEqual {
+		return enclave_structure.InstructionIsEqual
+	}
+	return enclave_structure.InstructionIsUnknown
+}
+
 // Assert verifies whether the currentValue matches the targetValue w.r.t. the assertion operator
 // TODO: This and ValidateAssertionToken below are used by both assert and wait. Refactor it to a better place
 func Assert(currentValue starlark.Comparable, assertion string, targetValue starlark.Comparable) error {
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/exec/exec.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/exec/exec.go
index faad8d2c1..c1fdd2f39 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/exec/exec.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/exec/exec.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction"
@@ -172,6 +173,15 @@ func (builtin *ExecCapabilities) Execute(ctx context.Context, _ *builtin_argumen
 	return instructionResult, err
 }
 
+func (builtin *ExecCapabilities) TryResolveWith(instructionsAreEqual bool, _ kurtosis_plan_instruction.KurtosisPlanInstructionCapabilities, enclaveComponents *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
+	if instructionsAreEqual && enclaveComponents.HasServiceBeenUpdated(builtin.serviceName) {
+		return enclave_structure.InstructionIsUpdate
+	} else if instructionsAreEqual {
+		return enclave_structure.InstructionIsEqual
+	}
+	return enclave_structure.InstructionIsUnknown
+}
+
 func (builtin *ExecCapabilities) isAcceptableCode(recipeResult map[string]starlark.Comparable) bool {
 	isAcceptableCode := false
 	for _, acceptableCode := range builtin.acceptableCodes {
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/kurtosis_instruction.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/kurtosis_instruction.go
index 1137dd360..fc71494d8 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/kurtosis_instruction.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/kurtosis_instruction.go
@@ -3,6 +3,7 @@ package kurtosis_instruction
 import (
 	"context"
 	"github.com/kurtosis-tech/kurtosis/api/golang/core/kurtosis_core_rpc_api_bindings"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_validator"
 	"go.starlark.net/starlark"
@@ -31,4 +32,7 @@ type KurtosisInstruction interface {
 	// ValidateAndUpdateEnvironment validates if the instruction can be applied to an environment, and mutates that
 	// environment to reflect how Kurtosis would look like after this instruction is successfully executed.
 	ValidateAndUpdateEnvironment(environment *startosis_validator.ValidatorEnvironment) error
+
+	// TryResolveWith assesses whether the instruction can be resolved with the one passed as an argument.
+	TryResolveWith(other KurtosisInstruction, enclaveComponents *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus
 }
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/kurtosis_print/kurtosis_print.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/kurtosis_print/kurtosis_print.go
index 4e0c0f7df..dd3938341 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/kurtosis_print/kurtosis_print.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/kurtosis_print/kurtosis_print.go
@@ -3,6 +3,7 @@ package kurtosis_print
 import (
 	"context"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction/shared_helpers/magic_string_helper"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
@@ -86,3 +87,10 @@ func (builtin *PrintCapabilities) Execute(_ context.Context, _ *builtin_argument
 	}
 	return maybeSerializedArgsWithRuntimeValue, nil
 }
+
+func (builtin *PrintCapabilities) TryResolveWith(instructionsAreEqual bool, _ kurtosis_plan_instruction.KurtosisPlanInstructionCapabilities, _ *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
+	if instructionsAreEqual {
+		return enclave_structure.InstructionIsEqual
+	}
+	return enclave_structure.InstructionIsUnknown
+}
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/mock_instruction/mock_kurtosis_instruction.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/mock_instruction/mock_kurtosis_instruction.go
index e60a3e02c..68e9ff119 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/mock_instruction/mock_kurtosis_instruction.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/mock_instruction/mock_kurtosis_instruction.go
@@ -4,8 +4,11 @@ package mock_instruction
 
 import (
 	context "context"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction"
 
 	kurtosis_core_rpc_api_bindings "github.com/kurtosis-tech/kurtosis/api/golang/core/kurtosis_core_rpc_api_bindings"
+	enclave_structure "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
+
 	kurtosis_starlark_framework "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 
 	mock "github.com/stretchr/testify/mock"
@@ -208,6 +211,49 @@ func (_c *MockKurtosisInstruction_String_Call) RunAndReturn(run func() string) *
 	return _c
 }
 
+// TryResolveWith provides a mock function with given fields: other, enclaveComponents
+func (_m *MockKurtosisInstruction) TryResolveWith(other kurtosis_instruction.KurtosisInstruction, enclaveComponents *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
+	ret := _m.Called(other, enclaveComponents)
+
+	var r0 enclave_structure.InstructionResolutionStatus
+	if rf, ok := ret.Get(0).(func(kurtosis_instruction.KurtosisInstruction, *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus); ok {
+		r0 = rf(other, enclaveComponents)
+	} else {
+		r0 = ret.Get(0).(enclave_structure.InstructionResolutionStatus)
+	}
+
+	return r0
+}
+
+// MockKurtosisInstruction_TryResolveWith_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TryResolveWith'
+type MockKurtosisInstruction_TryResolveWith_Call struct {
+	*mock.Call
+}
+
+// TryResolveWith is a helper method to define mock.On call
+//   - other KurtosisInstruction
+//   - enclaveComponents *enclave_structure.EnclaveComponents
+func (_e *MockKurtosisInstruction_Expecter) TryResolveWith(other interface{}, enclaveComponents interface{}) *MockKurtosisInstruction_TryResolveWith_Call {
+	return &MockKurtosisInstruction_TryResolveWith_Call{Call: _e.mock.On("TryResolveWith", other, enclaveComponents)}
+}
+
+func (_c *MockKurtosisInstruction_TryResolveWith_Call) Run(run func(other kurtosis_instruction.KurtosisInstruction, enclaveComponents *enclave_structure.EnclaveComponents)) *MockKurtosisInstruction_TryResolveWith_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(kurtosis_instruction.KurtosisInstruction), args[1].(*enclave_structure.EnclaveComponents))
+	})
+	return _c
+}
+
+func (_c *MockKurtosisInstruction_TryResolveWith_Call) Return(_a0 enclave_structure.InstructionResolutionStatus) *MockKurtosisInstruction_TryResolveWith_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *MockKurtosisInstruction_TryResolveWith_Call) RunAndReturn(run func(kurtosis_instruction.KurtosisInstruction, *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus) *MockKurtosisInstruction_TryResolveWith_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
 // ValidateAndUpdateEnvironment provides a mock function with given fields: environment
 func (_m *MockKurtosisInstruction) ValidateAndUpdateEnvironment(environment *startosis_validator.ValidatorEnvironment) error {
 	ret := _m.Called(environment)
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/plan_module/plan_module.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/plan_module/plan_module.go
index 007a1d551..3090b9bd5 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/plan_module/plan_module.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/plan_module/plan_module.go
@@ -1,6 +1,7 @@
 package plan_module
 
 import (
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/instructions_plan"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/instructions_plan/resolver"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction"
@@ -14,12 +15,13 @@ const (
 
 func PlanModule(
 	instructionsPlan *instructions_plan.InstructionsPlan,
+	enclaveComponents *enclave_structure.EnclaveComponents,
 	instructionsPlanMask *resolver.InstructionsPlanMask,
 	kurtosisPlanInstructions []*kurtosis_plan_instruction.KurtosisPlanInstruction,
 ) *starlarkstruct.Module {
 	moduleBuiltins := starlark.StringDict{}
 	for _, planInstruction := range kurtosisPlanInstructions {
-		wrappedPlanInstruction := kurtosis_plan_instruction.NewKurtosisPlanInstructionWrapper(planInstruction, instructionsPlanMask, instructionsPlan)
+		wrappedPlanInstruction := kurtosis_plan_instruction.NewKurtosisPlanInstructionWrapper(planInstruction, enclaveComponents, instructionsPlanMask, instructionsPlan)
 		moduleBuiltins[planInstruction.GetName()] = starlark.NewBuiltin(planInstruction.GetName(), wrappedPlanInstruction.CreateBuiltin())
 	}
 
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/remove_connection/remove_connection.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/remove_connection/remove_connection.go
index b34d188d3..dfd1d0afc 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/remove_connection/remove_connection.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/remove_connection/remove_connection.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network/service_network_types"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction/shared_helpers"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
@@ -88,6 +89,10 @@ func (builtin *RemoveConnectionCapabilities) Execute(ctx context.Context, _ *bui
 	return instructionResult, nil
 }
 
+func (builtin *RemoveConnectionCapabilities) TryResolveWith(_ bool, _ kurtosis_plan_instruction.KurtosisPlanInstructionCapabilities, _ *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
+	return enclave_structure.InstructionIsNotResolvableAbort
+}
+
 func validateSubnetworks(value starlark.Value) *startosis_errors.InterpretationError {
 	subnetworks, ok := value.(starlark.Tuple)
 	if !ok {
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/remove_service/remove_service.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/remove_service/remove_service.go
index 46c8bf94d..2f04e89b4 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/remove_service/remove_service.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/remove_service/remove_service.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction"
@@ -69,7 +70,7 @@ func (builtin *RemoveServiceCapabilities) Interpret(_ string, arguments *builtin
 }
 
 func (builtin *RemoveServiceCapabilities) Validate(_ *builtin_argument.ArgumentValuesSet, validatorEnvironment *startosis_validator.ValidatorEnvironment) *startosis_errors.ValidationError {
-	if !validatorEnvironment.DoesServiceNameExist(builtin.serviceName) {
+	if validatorEnvironment.DoesServiceNameExist(builtin.serviceName) == startosis_validator.ServiceNotFound {
 		return startosis_errors.NewValidationError("There was an error validating '%v' as service name '%v' doesn't exist", RemoveServiceBuiltinName, builtin.serviceName)
 	}
 	validatorEnvironment.RemoveServiceName(builtin.serviceName)
@@ -85,3 +86,7 @@ func (builtin *RemoveServiceCapabilities) Execute(ctx context.Context, _ *builti
 	instructionResult := fmt.Sprintf("Service '%s' with service UUID '%s' removed", builtin.serviceName, serviceUUID)
 	return instructionResult, nil
 }
+
+func (builtin *RemoveServiceCapabilities) TryResolveWith(_ bool, _ kurtosis_plan_instruction.KurtosisPlanInstructionCapabilities, _ *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
+	return enclave_structure.InstructionIsNotResolvableAbort
+}
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/render_templates/render_templates.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/render_templates/render_templates.go
index 2fccb01cd..a2c1e946d 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/render_templates/render_templates.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/render_templates/render_templates.go
@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network/render_templates"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction"
@@ -129,6 +130,13 @@ func (builtin *RenderTemplatesCapabilities) Execute(_ context.Context, _ *builti
 	return instructionResult, nil
 }
 
+func (builtin *RenderTemplatesCapabilities) TryResolveWith(instructionsAreEqual bool, _ kurtosis_plan_instruction.KurtosisPlanInstructionCapabilities, _ *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
+	if instructionsAreEqual {
+		return enclave_structure.InstructionIsEqual
+	}
+	return enclave_structure.InstructionIsUnknown
+}
+
 func parseTemplatesAndData(templatesAndData *starlark.Dict) (map[string]*render_templates.TemplateData, *startosis_errors.InterpretationError) {
 	templateAndDataByDestRelFilepath := make(map[string]*render_templates.TemplateData)
 	for _, relPathInFilesArtifactKey := range templatesAndData.Keys() {
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/request/request.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/request/request.go
index f620e3267..999a40639 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/request/request.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/request/request.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction"
@@ -161,7 +162,7 @@ func (builtin *RequestCapabilities) Interpret(_ string, arguments *builtin_argum
 }
 
 func (builtin *RequestCapabilities) Validate(_ *builtin_argument.ArgumentValuesSet, validatorEnvironment *startosis_validator.ValidatorEnvironment) *startosis_errors.ValidationError {
-	if serviceExists := validatorEnvironment.DoesServiceNameExist(builtin.serviceName); !serviceExists {
+	if validatorEnvironment.DoesServiceNameExist(builtin.serviceName) == startosis_validator.ServiceNotFound {
 		return startosis_errors.NewValidationError("Tried creating a request for service '%s' which doesn't exist", builtin.serviceName)
 	}
 	if validationErr := recipe.ValidateHttpRequestRecipe(builtin.httpRequestRecipe, builtin.serviceName, validatorEnvironment); validationErr != nil {
@@ -183,6 +184,15 @@ func (builtin *RequestCapabilities) Execute(ctx context.Context, _ *builtin_argu
 	return instructionResult, err
 }
 
+func (builtin *RequestCapabilities) TryResolveWith(instructionsAreEqual bool, _ kurtosis_plan_instruction.KurtosisPlanInstructionCapabilities, enclaveComponents *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
+	if instructionsAreEqual && enclaveComponents.HasServiceBeenUpdated(builtin.serviceName) {
+		return enclave_structure.InstructionIsUpdate
+	} else if instructionsAreEqual {
+		return enclave_structure.InstructionIsEqual
+	}
+	return enclave_structure.InstructionIsUnknown
+}
+
 func (builtin *RequestCapabilities) isAcceptableCode(recipeResult map[string]starlark.Comparable) bool {
 	isAcceptableCode := false
 	for _, acceptableCode := range builtin.acceptableCodes {
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/set_connection/set_connection.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/set_connection/set_connection.go
index e6daa1790..ef2280338 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/set_connection/set_connection.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/set_connection/set_connection.go
@@ -6,6 +6,7 @@ import (
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network/partition_topology"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network/service_network_types"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction/shared_helpers"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
@@ -134,6 +135,10 @@ func (builtin *SetConnectionCapabilities) Execute(ctx context.Context, _ *builti
 	return instructionResult, nil
 }
 
+func (builtin *SetConnectionCapabilities) TryResolveWith(_ bool, _ kurtosis_plan_instruction.KurtosisPlanInstructionCapabilities, _ *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
+	return enclave_structure.InstructionIsNotResolvableAbort
+}
+
 func validateSubnetworks(value starlark.Value) *startosis_errors.InterpretationError {
 	subnetworks, ok := value.(starlark.Tuple)
 	if !ok {
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/start_service/start_service.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/start_service/start_service.go
index 9ea248555..e385e9cc2 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/start_service/start_service.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/start_service/start_service.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction"
@@ -69,7 +70,7 @@ func (builtin *StartServiceCapabilities) Interpret(_ string, arguments *builtin_
 }
 
 func (builtin *StartServiceCapabilities) Validate(_ *builtin_argument.ArgumentValuesSet, validatorEnvironment *startosis_validator.ValidatorEnvironment) *startosis_errors.ValidationError {
-	if !validatorEnvironment.DoesServiceNameExist(builtin.serviceName) {
+	if validatorEnvironment.DoesServiceNameExist(builtin.serviceName) == startosis_validator.ServiceNotFound {
 		return startosis_errors.NewValidationError("There was an error validating '%v' as service name '%v' doesn't exist", StartServiceBuiltinName, builtin.serviceName)
 	}
 	return nil
@@ -83,3 +84,7 @@ func (builtin *StartServiceCapabilities) Execute(ctx context.Context, _ *builtin
 	instructionResult := fmt.Sprintf("Service '%s' started", builtin.serviceName)
 	return instructionResult, nil
 }
+
+func (builtin *StartServiceCapabilities) TryResolveWith(_ bool, _ kurtosis_plan_instruction.KurtosisPlanInstructionCapabilities, _ *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
+	return enclave_structure.InstructionIsNotResolvableAbort
+}
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/stop_service/stop_service.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/stop_service/stop_service.go
index bb69d8f98..fda2b3db7 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/stop_service/stop_service.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/stop_service/stop_service.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction"
@@ -69,7 +70,7 @@ func (builtin *StopServiceCapabilities) Interpret(_ string, arguments *builtin_a
 }
 
 func (builtin *StopServiceCapabilities) Validate(_ *builtin_argument.ArgumentValuesSet, validatorEnvironment *startosis_validator.ValidatorEnvironment) *startosis_errors.ValidationError {
-	if !validatorEnvironment.DoesServiceNameExist(builtin.serviceName) {
+	if validatorEnvironment.DoesServiceNameExist(builtin.serviceName) == startosis_validator.ServiceNotFound {
 		return startosis_errors.NewValidationError("There was an error validating '%v' as service name '%v' doesn't exist", StopServiceBuiltinName, builtin.serviceName)
 	}
 	return nil
@@ -83,3 +84,7 @@ func (builtin *StopServiceCapabilities) Execute(ctx context.Context, _ *builtin_
 	instructionResult := fmt.Sprintf("Service '%s' stopped", builtin.serviceName)
 	return instructionResult, nil
 }
+
+func (builtin *StopServiceCapabilities) TryResolveWith(_ bool, _ kurtosis_plan_instruction.KurtosisPlanInstructionCapabilities, _ *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
+	return enclave_structure.InstructionIsNotResolvableAbort
+}
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/store_service_files/store_service_files.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/store_service_files/store_service_files.go
index 260aee8bc..46864acec 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/store_service_files/store_service_files.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/store_service_files/store_service_files.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 	kurtosis_backend_service "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction"
@@ -106,7 +107,7 @@ func (builtin *StoreServiceFilesCapabilities) Interpret(_ string, arguments *bui
 }
 
 func (builtin *StoreServiceFilesCapabilities) Validate(_ *builtin_argument.ArgumentValuesSet, validatorEnvironment *startosis_validator.ValidatorEnvironment) *startosis_errors.ValidationError {
-	if !validatorEnvironment.DoesServiceNameExist(builtin.serviceName) {
+	if validatorEnvironment.DoesServiceNameExist(builtin.serviceName) == startosis_validator.ServiceNotFound {
 		return startosis_errors.NewValidationError("There was an error validating '%v' with service name '%v' that does not exist", StoreServiceFilesBuiltinName, builtin.serviceName)
 	}
 	if validatorEnvironment.DoesArtifactNameExist(builtin.artifactName) {
@@ -124,3 +125,10 @@ func (builtin *StoreServiceFilesCapabilities) Execute(ctx context.Context, _ *bu
 	instructionResult := fmt.Sprintf("Files with artifact name '%s' uploaded with artifact UUID '%s'", builtin.artifactName, artifactUuid)
 	return instructionResult, nil
 }
+
+func (builtin *StoreServiceFilesCapabilities) TryResolveWith(instructionsAreEqual bool, _ kurtosis_plan_instruction.KurtosisPlanInstructionCapabilities, _ *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
+	if instructionsAreEqual {
+		return enclave_structure.InstructionIsEqual
+	}
+	return enclave_structure.InstructionIsUnknown
+}
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go
index 8efcd3dde..0e84fbb34 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go
@@ -8,6 +8,7 @@ import (
 	"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/files_artifacts_expansion"
 	"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction/shared_helpers/magic_string_helper"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
@@ -325,6 +326,13 @@ func (builtin *RunPythonCapabilities) Execute(ctx context.Context, _ *builtin_ar
 	return instructionResult, err
 }
 
+func (builtin *RunPythonCapabilities) TryResolveWith(instructionsAreEqual bool, _ kurtosis_plan_instruction.KurtosisPlanInstructionCapabilities, _ *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
+	if instructionsAreEqual {
+		return enclave_structure.InstructionIsEqual
+	}
+	return enclave_structure.InstructionIsUnknown
+}
+
 func setupRequiredPackages(ctx context.Context, builtin *RunPythonCapabilities) (*exec_result.ExecResult, error) {
 	var maybePackagesWithRuntimeValuesReplaced []string
 	for _, pythonPackage := range builtin.packages {
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_sh.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_sh.go
index d9d1e492e..9113caf08 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_sh.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_sh.go
@@ -6,6 +6,7 @@ import (
 	"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/files_artifacts_expansion"
 	"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction/shared_helpers/magic_string_helper"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
@@ -231,6 +232,13 @@ func (builtin *RunShCapabilities) Execute(ctx context.Context, _ *builtin_argume
 	return instructionResult, err
 }
 
+func (builtin *RunShCapabilities) TryResolveWith(instructionsAreEqual bool, _ kurtosis_plan_instruction.KurtosisPlanInstructionCapabilities, _ *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
+	if instructionsAreEqual {
+		return enclave_structure.InstructionIsEqual
+	}
+	return enclave_structure.InstructionIsUnknown
+}
+
 func getCommandToRun(builtin *RunShCapabilities) (string, error) {
 	// replace future references to actual strings
 	maybeSubCommandWithRuntimeValues, err := magic_string_helper.ReplaceRuntimeValueInString(builtin.run, builtin.runtimeValueStore)
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/update_service/update_service.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/update_service/update_service.go
index 9f8ade15f..10f3ac5e9 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/update_service/update_service.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/update_service/update_service.go
@@ -7,6 +7,7 @@ import (
 	"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network/partition_topology"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction"
@@ -104,7 +105,7 @@ func (builtin *UpdateServiceCapabilities) Validate(_ *builtin_argument.ArgumentV
 			return startosis_errors.NewValidationError("Service was about to be moved to subnetwork '%s' but the Kurtosis enclave was started with subnetwork capabilities disabled. Make sure to run the Starlark script with subnetwork enabled.", *builtin.updateServiceConfig.Subnetwork)
 		}
 	}
-	if !validatorEnvironment.DoesServiceNameExist(builtin.serviceName) {
+	if validatorEnvironment.DoesServiceNameExist(builtin.serviceName) == startosis_validator.ServiceNotFound {
 		return startosis_errors.NewValidationError("There was an error validating '%v' as service name '%v' does not exist", UpdateServiceBuiltinName, builtin.serviceName)
 	}
 	return nil
@@ -135,6 +136,10 @@ func (builtin *UpdateServiceCapabilities) Execute(ctx context.Context, _ *builti
 	return instructionResult, nil
 }
 
+func (builtin *UpdateServiceCapabilities) TryResolveWith(_ bool, _ kurtosis_plan_instruction.KurtosisPlanInstructionCapabilities, _ *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
+	return enclave_structure.InstructionIsNotResolvableAbort
+}
+
 func validateAndConvertConfig(rawConfig starlark.Value) (*kurtosis_core_rpc_api_bindings.UpdateServiceConfig, *startosis_errors.InterpretationError) {
 	config, ok := rawConfig.(*update_service_config.UpdateServiceConfig)
 	if !ok {
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/upload_files/upload_files.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/upload_files/upload_files.go
index 0d7a523ec..c981f257a 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/upload_files/upload_files.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/upload_files/upload_files.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"github.com/kurtosis-tech/kurtosis/api/golang/core/lib/shared_utils"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction"
@@ -128,3 +129,10 @@ func (builtin *UploadFilesCapabilities) Execute(_ context.Context, _ *builtin_ar
 	instructionResult := fmt.Sprintf("Files with artifact name '%s' uploaded with artifact UUID '%s'", builtin.artifactName, filesArtifactUuid)
 	return instructionResult, nil
 }
+
+func (builtin *UploadFilesCapabilities) TryResolveWith(instructionsAreEqual bool, _ kurtosis_plan_instruction.KurtosisPlanInstructionCapabilities, _ *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
+	if instructionsAreEqual {
+		return enclave_structure.InstructionIsEqual
+	}
+	return enclave_structure.InstructionIsUnknown
+}
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/wait/wait.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/wait/wait.go
index 7495c7715..9b8624273 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/wait/wait.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/wait/wait.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction/assert"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction/shared_helpers"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
@@ -221,7 +222,7 @@ func (builtin *WaitCapabilities) Interpret(_ string, arguments *builtin_argument
 }
 
 func (builtin *WaitCapabilities) Validate(_ *builtin_argument.ArgumentValuesSet, validatorEnvironment *startosis_validator.ValidatorEnvironment) *startosis_errors.ValidationError {
-	if serviceExists := validatorEnvironment.DoesServiceNameExist(builtin.serviceName); !serviceExists {
+	if validatorEnvironment.DoesServiceNameExist(builtin.serviceName) == startosis_validator.ServiceNotFound {
 		return startosis_errors.NewValidationError("Tried creating a wait for service '%s' which doesn't exist", builtin.serviceName)
 	}
 
@@ -271,3 +272,12 @@ func (builtin *WaitCapabilities) Execute(ctx context.Context, _ *builtin_argumen
 
 	return instructionResult, nil
 }
+
+func (builtin *WaitCapabilities) TryResolveWith(instructionsAreEqual bool, _ kurtosis_plan_instruction.KurtosisPlanInstructionCapabilities, enclaveComponents *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
+	if instructionsAreEqual && enclaveComponents.HasServiceBeenUpdated(builtin.serviceName) {
+		return enclave_structure.InstructionIsUpdate
+	} else if instructionsAreEqual {
+		return enclave_structure.InstructionIsEqual
+	}
+	return enclave_structure.InstructionIsUnknown
+}
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction/kurtosis_plan_instruction.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction/kurtosis_plan_instruction.go
index 4c14bc7c9..6f8bcd931 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction/kurtosis_plan_instruction.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction/kurtosis_plan_instruction.go
@@ -1,10 +1,12 @@
 package kurtosis_plan_instruction
 
 import (
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/instructions_plan"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/instructions_plan/resolver"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_errors"
+	"github.com/kurtosis-tech/stacktrace"
 	"github.com/sirupsen/logrus"
 	"go.starlark.net/starlark"
 )
@@ -22,15 +24,17 @@ type KurtosisPlanInstruction struct {
 type KurtosisPlanInstructionWrapper struct {
 	*KurtosisPlanInstruction
 
+	enclaveComponents   *enclave_structure.EnclaveComponents
 	instructionPlanMask *resolver.InstructionsPlanMask
 
 	// TODO: This can be changed to KurtosisPlanInstructionInternal when we get rid of KurtosisInstruction
 	instructionsPlan *instructions_plan.InstructionsPlan
 }
 
-func NewKurtosisPlanInstructionWrapper(instruction *KurtosisPlanInstruction, instructionPlanMask *resolver.InstructionsPlanMask, instructionsPlan *instructions_plan.InstructionsPlan) *KurtosisPlanInstructionWrapper {
+func NewKurtosisPlanInstructionWrapper(instruction *KurtosisPlanInstruction, enclaveComponents *enclave_structure.EnclaveComponents, instructionPlanMask *resolver.InstructionsPlanMask, instructionsPlan *instructions_plan.InstructionsPlan) *KurtosisPlanInstructionWrapper {
 	return &KurtosisPlanInstructionWrapper{
 		KurtosisPlanInstruction: instruction,
+		enclaveComponents:       enclaveComponents,
 		instructionPlanMask:     instructionPlanMask,
 		instructionsPlan:        instructionsPlan,
 	}
@@ -50,41 +54,58 @@ func (builtin *KurtosisPlanInstructionWrapper) CreateBuiltin() func(thread *star
 			return nil, interpretationErr
 		}
 
-		var instructionPulledFromMaskIdx int
-		var instructionPulledFromMaskMaybe *instructions_plan.ScheduledInstruction
+		var scheduledInstructionPulledFromMaskMaybe *instructions_plan.ScheduledInstruction
+		var instructionResolutionStatus enclave_structure.InstructionResolutionStatus
 		if builtin.instructionPlanMask.HasNext() {
-			instructionPulledFromMaskIdx, instructionPulledFromMaskMaybe = builtin.instructionPlanMask.Next()
-			if instructionPulledFromMaskMaybe != nil && instructionPulledFromMaskMaybe.GetInstruction().String() != instructionWrapper.String() {
-				// if the instructions differs, then the mask is invalid
-				builtin.instructionPlanMask.MarkAsInvalid()
-				logrus.Debugf("The instruction number %d in the plan mask did not match the newly interpreter "+
-					"instruction and therefore the plan mask was marked as invalid:\nInstruction from mask - '%s'"+
-					"\nInstruction from interpretation: '%s'",
-					instructionPulledFromMaskIdx,
-					instructionPulledFromMaskMaybe.GetInstruction().String(),
-					instructionWrapper.String())
-				// TODO: we could interrupt the interpretation here, because with an invalid mask the list of
-				//  instruction generated will be invalid anyway. Though we currently don't have a nive way to
-				//  interrupt an interpretation in progress (other than by throwing an error, which would be
-				//  misleading here)
-				//  To properly solve that, I think we should switch to an interactive interpretation where we
-				//  interpret each instruction one after the other, and evaluating the state after each step
+			_, scheduledInstructionPulledFromMaskMaybe = builtin.instructionPlanMask.Next()
+			if scheduledInstructionPulledFromMaskMaybe != nil {
+				instructionResolutionStatus = instructionWrapper.TryResolveWith(scheduledInstructionPulledFromMaskMaybe.GetInstruction(), builtin.enclaveComponents)
+			} else {
+				instructionResolutionStatus = instructionWrapper.TryResolveWith(nil, builtin.enclaveComponents)
 			}
+		} else {
+			instructionResolutionStatus = instructionWrapper.TryResolveWith(nil, builtin.enclaveComponents)
 		}
 
-		if instructionPulledFromMaskMaybe != nil {
-			// If there's a mask for this instruction, add the mask the plan and returned the mask's returned value
-			builtin.instructionsPlan.AddScheduledInstruction(instructionPulledFromMaskMaybe).Executed(true).ImportedFromCurrentEnclavePlan(false)
-			return instructionPulledFromMaskMaybe.GetReturnedValue(), nil
-		} else {
+		switch instructionResolutionStatus {
+		case enclave_structure.InstructionIsEqual:
+			// add instruction from the mask and mark it as executed but not imported from the current enclave plan
+			builtin.instructionsPlan.AddScheduledInstruction(scheduledInstructionPulledFromMaskMaybe).Executed(true).ImportedFromCurrentEnclavePlan(false)
+			return scheduledInstructionPulledFromMaskMaybe.GetReturnedValue(), nil
+		case enclave_structure.InstructionIsUpdate:
 			// otherwise add the instruction as a new one to the plan and return its own returned value
+			if err := builtin.instructionsPlan.AddInstruction(instructionWrapper, returnedFutureValue); err != nil {
+				return nil, startosis_errors.WrapWithInterpretationError(err, "Unable to add Kurtosis instruction '%s' at position '%s' to the plan currently being assembled. This is a Kurtosis internal bug",
+					instructionWrapper.String(),
+					instructionWrapper.GetPositionInOriginalScript().String())
+			}
+			return returnedFutureValue, nil
+		case enclave_structure.InstructionIsUnknown:
+			if err := builtin.instructionsPlan.AddInstruction(instructionWrapper, returnedFutureValue); err != nil {
+				return nil, startosis_errors.WrapWithInterpretationError(err,
+					"Unable to add Kurtosis instruction '%s' at position '%s' to the plan currently being assembled. This is a Kurtosis internal bug",
+					instructionWrapper.String(),
+					instructionWrapper.GetPositionInOriginalScript().String())
+			}
+			if scheduledInstructionPulledFromMaskMaybe != nil {
+				builtin.instructionPlanMask.MarkAsInvalid()
+				logrus.Debugf("Marking the plan as invalid as instruction '%s' differs from '%s'",
+					instructionWrapper.String(), scheduledInstructionPulledFromMaskMaybe.GetInstruction().String())
+			}
+			return returnedFutureValue, nil
+		case enclave_structure.InstructionIsNotResolvableAbort:
+			// if the instructions differs, then the mask is invalid
+			builtin.instructionPlanMask.MarkAsInvalid()
+			logrus.Debugf("Marking the plan as invalid as instruction '%s' had the following resolution status: '%s'",
+				instructionWrapper.String(), instructionResolutionStatus)
 			if err := builtin.instructionsPlan.AddInstruction(instructionWrapper, returnedFutureValue); err != nil {
 				return nil, startosis_errors.WrapWithInterpretationError(err,
-					"Unable to add Kurtosis instruction '%s' at position '%s' to the current plan being assembled. This is a Kurtosis internal bug",
+					"Unable to add Kurtosis instruction '%s' at position '%s' to the plan currently being assembled. This is a Kurtosis internal bug",
 					instructionWrapper.String(),
 					instructionWrapper.GetPositionInOriginalScript().String())
 			}
 			return returnedFutureValue, nil
 		}
+		return nil, stacktrace.NewError("Unexpected error, resolution status of instruction '%s' did not match any of the covered case.", instructionResolutionStatus)
 	}
 }
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction/kurtosis_plan_instruction_capabilities.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction/kurtosis_plan_instruction_capabilities.go
index 039fb6635..5ffda026b 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction/kurtosis_plan_instruction_capabilities.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction/kurtosis_plan_instruction_capabilities.go
@@ -2,6 +2,7 @@ package kurtosis_plan_instruction
 
 import (
 	"context"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_errors"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_validator"
@@ -14,4 +15,6 @@ type KurtosisPlanInstructionCapabilities interface {
 	Validate(arguments *builtin_argument.ArgumentValuesSet, validatorEnvironment *startosis_validator.ValidatorEnvironment) *startosis_errors.ValidationError
 
 	Execute(ctx context.Context, arguments *builtin_argument.ArgumentValuesSet) (string, error)
+
+	TryResolveWith(instructionsAreEqual bool, other KurtosisPlanInstructionCapabilities, enclaveComponents *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus
 }
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction/kurtosis_plan_instruction_internal.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction/kurtosis_plan_instruction_internal.go
index f1092e5e2..1f7fa008f 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction/kurtosis_plan_instruction_internal.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction/kurtosis_plan_instruction_internal.go
@@ -4,6 +4,8 @@ import (
 	"context"
 	"github.com/kurtosis-tech/kurtosis/api/golang/core/kurtosis_core_rpc_api_bindings"
 	"github.com/kurtosis-tech/kurtosis/api/golang/core/lib/binding_constructors"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_errors"
@@ -75,6 +77,25 @@ func (builtin *kurtosisPlanInstructionInternal) Execute(ctx context.Context) (*s
 	return &result, nil
 }
 
+func (builtin *kurtosisPlanInstructionInternal) TryResolveWith(other kurtosis_instruction.KurtosisInstruction, enclaveComponents *enclave_structure.EnclaveComponents) enclave_structure.InstructionResolutionStatus {
+	isAnAbortAllInstruction := builtin.capabilities.TryResolveWith(false, nil, enclaveComponents) == enclave_structure.InstructionIsNotResolvableAbort
+	if isAnAbortAllInstruction {
+		return enclave_structure.InstructionIsNotResolvableAbort
+	}
+
+	if other == nil {
+		return enclave_structure.InstructionIsUnknown
+	}
+
+	otherPlanInstruction, ok := other.(*kurtosisPlanInstructionInternal)
+	if !ok {
+		return enclave_structure.InstructionIsUnknown
+	}
+
+	instructionsAreEqual := builtin.String() == other.String()
+	return builtin.capabilities.TryResolveWith(instructionsAreEqual, otherPlanInstruction.capabilities, enclaveComponents)
+}
+
 func (builtin *kurtosisPlanInstructionInternal) interpret(locatorOfModuleInWhichThisBuiltInIsBeingCalled string) (starlark.Value, *startosis_errors.InterpretationError) {
 	result, interpretationErr := builtin.capabilities.Interpret(locatorOfModuleInWhichThisBuiltInIsBeingCalled, builtin.GetArguments())
 	if interpretationErr != nil {
diff --git a/core/server/api_container/server/startosis_engine/runtime_value_store/runtime_value_store.go b/core/server/api_container/server/startosis_engine/runtime_value_store/runtime_value_store.go
index 73e5ff629..9bd214351 100644
--- a/core/server/api_container/server/startosis_engine/runtime_value_store/runtime_value_store.go
+++ b/core/server/api_container/server/startosis_engine/runtime_value_store/runtime_value_store.go
@@ -1,18 +1,21 @@
 package runtime_value_store
 
 import (
+	"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service"
 	"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/uuid_generator"
 	"github.com/kurtosis-tech/stacktrace"
 	"go.starlark.net/starlark"
 )
 
 type RuntimeValueStore struct {
-	recipeResultMap map[string]map[string]starlark.Comparable
+	recipeResultMap         map[string]map[string]starlark.Comparable
+	serviceAssociatedValues map[service.ServiceName]string
 }
 
 func NewRuntimeValueStore() *RuntimeValueStore {
 	return &RuntimeValueStore{
-		recipeResultMap: make(map[string]map[string]starlark.Comparable),
+		recipeResultMap:         make(map[string]map[string]starlark.Comparable),
+		serviceAssociatedValues: make(map[service.ServiceName]string),
 	}
 }
 
@@ -25,6 +28,19 @@ func (re *RuntimeValueStore) CreateValue() (string, error) {
 	return uuid, nil
 }
 
+func (re *RuntimeValueStore) GetOrCreateValueAssociatedWithService(serviceName service.ServiceName) (string, error) {
+	if uuid, found := re.serviceAssociatedValues[serviceName]; found {
+		delete(re.recipeResultMap, uuid) // deleting old values so that they do not interfere until that are set again
+		return uuid, nil
+	}
+	uuid, err := re.CreateValue()
+	if err != nil {
+		return "", stacktrace.Propagate(err, "An error occurred creating a simple runtime value")
+	}
+	re.serviceAssociatedValues[serviceName] = uuid
+	return uuid, nil
+}
+
 func (re *RuntimeValueStore) SetValue(uuid string, value map[string]starlark.Comparable) {
 	re.recipeResultMap[uuid] = value
 }
diff --git a/core/server/api_container/server/startosis_engine/startosis_interpreter.go b/core/server/api_container/server/startosis_engine/startosis_interpreter.go
index 182be6fb3..28b132116 100644
--- a/core/server/api_container/server/startosis_engine/startosis_interpreter.go
+++ b/core/server/api_container/server/startosis_engine/startosis_interpreter.go
@@ -7,6 +7,7 @@ import (
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/builtins"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/builtins/print_builtin"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/instructions_plan"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/instructions_plan/resolver"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction/plan_module"
@@ -91,8 +92,9 @@ func (interpreter *StartosisInterpreter) InterpretAndOptimizePlan(
 ) (string, *instructions_plan.InstructionsPlan, *kurtosis_core_rpc_api_bindings.StarlarkInterpretationError) {
 
 	// run interpretation with no mask at all to generate the list of instructions as if the enclave was empty
+	enclaveComponents := enclave_structure.NewEnclaveComponents()
 	emptyPlanInstructionsMask := resolver.NewInstructionsPlanMask(0)
-	naiveInstructionsPlanSerializedScriptOutput, naiveInstructionsPlan, interpretationErrorApi := interpreter.Interpret(ctx, packageId, mainFunctionName, relativePathtoMainFile, serializedStarlark, serializedJsonParams, emptyPlanInstructionsMask)
+	naiveInstructionsPlanSerializedScriptOutput, naiveInstructionsPlan, interpretationErrorApi := interpreter.Interpret(ctx, packageId, mainFunctionName, relativePathtoMainFile, serializedStarlark, serializedJsonParams, enclaveComponents, emptyPlanInstructionsMask)
 	if interpretationErrorApi != nil {
 		return startosis_constants.NoOutputObject, nil, interpretationErrorApi
 	}
@@ -162,7 +164,7 @@ func (interpreter *StartosisInterpreter) InterpretAndOptimizePlan(
 		}
 
 		// Now that we have a potential plan mask, try running interpretation again using this plan mask
-		attemptSerializedScriptOutput, attemptInstructionsPlan, interpretationErrorApi := interpreter.Interpret(ctx, packageId, mainFunctionName, relativePathtoMainFile, serializedStarlark, serializedJsonParams, potentialMask)
+		attemptSerializedScriptOutput, attemptInstructionsPlan, interpretationErrorApi := interpreter.Interpret(ctx, packageId, mainFunctionName, relativePathtoMainFile, serializedStarlark, serializedJsonParams, enclaveComponents, potentialMask)
 		if interpretationErrorApi != nil {
 			// Note: there's no real reason why this interpretation would fail with an error, given that the package
 			// has been interpreted once already (right above). But to be on the safe side, check the error
@@ -231,6 +233,7 @@ func (interpreter *StartosisInterpreter) Interpret(
 	relativePathtoMainFile string,
 	serializedStarlark string,
 	serializedJsonParams string,
+	enclaveComponents *enclave_structure.EnclaveComponents,
 	instructionsPlanMask *resolver.InstructionsPlanMask,
 ) (string, *instructions_plan.InstructionsPlan, *kurtosis_core_rpc_api_bindings.StarlarkInterpretationError) {
 	interpreter.mutex.Lock()
@@ -278,7 +281,7 @@ func (interpreter *StartosisInterpreter) Interpret(
 		firstParamName, _ := mainFunction.Param(planParamIndex)
 		if firstParamName == planParamName {
 			kurtosisPlanInstructions := KurtosisPlanInstructions(interpreter.serviceNetwork, interpreter.recipeExecutor, interpreter.moduleContentProvider)
-			planModule := plan_module.PlanModule(newInstructionsPlan, instructionsPlanMask, kurtosisPlanInstructions)
+			planModule := plan_module.PlanModule(newInstructionsPlan, enclaveComponents, instructionsPlanMask, kurtosisPlanInstructions)
 			argsTuple = append(argsTuple, planModule)
 		}
 
@@ -387,7 +390,10 @@ func findFirstEqualInstructionPastIndex(currentEnclaveInstructionsList []*instru
 		return -1 // no result as the naiveInstructionsList is empty
 	}
 	for i := minIndex; i < len(currentEnclaveInstructionsList); i++ {
-		if currentEnclaveInstructionsList[i].GetInstruction().String() == naiveInstructionsList[0].GetInstruction().String() {
+		// We just need to compare instructions to see if they match, without needing any enclave specific context here
+		fakeEnclaveComponent := enclave_structure.NewEnclaveComponents()
+		instructionResolutionResult := naiveInstructionsList[0].GetInstruction().TryResolveWith(currentEnclaveInstructionsList[i].GetInstruction(), fakeEnclaveComponent)
+		if instructionResolutionResult == enclave_structure.InstructionIsEqual || instructionResolutionResult == enclave_structure.InstructionIsUpdate {
 			return i
 		}
 	}
diff --git a/core/server/api_container/server/startosis_engine/startosis_runner.go b/core/server/api_container/server/startosis_engine/startosis_runner.go
index b24cb38a8..73b960db9 100644
--- a/core/server/api_container/server/startosis_engine/startosis_runner.go
+++ b/core/server/api_container/server/startosis_engine/startosis_runner.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"github.com/kurtosis-tech/kurtosis/api/golang/core/kurtosis_core_rpc_api_bindings"
 	"github.com/kurtosis-tech/kurtosis/api/golang/core/lib/binding_constructors"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/instructions_plan"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/instructions_plan/resolver"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/starlark_warning"
@@ -103,6 +104,7 @@ func (runner *StartosisRunner) Run(
 				relativePathToMainFile,
 				serializedStartosis,
 				serializedParams,
+				enclave_structure.NewEnclaveComponents(),
 				resolver.NewInstructionsPlanMask(0),
 			)
 		} else {
diff --git a/core/server/api_container/server/startosis_engine/startosis_validator/service_existence.go b/core/server/api_container/server/startosis_engine/startosis_validator/service_existence.go
new file mode 100644
index 000000000..ab256a715
--- /dev/null
+++ b/core/server/api_container/server/startosis_engine/startosis_validator/service_existence.go
@@ -0,0 +1,15 @@
+package startosis_validator
+
+//go:generate go run github.com/dmarkham/enumer -trimprefix "ServiceExistence" -type=ServiceExistence
+type ServiceExistence uint8
+
+const (
+	// ServiceNotFound - Service does not exist in the enclave
+	ServiceNotFound ServiceExistence = iota
+
+	// ServiceExistedBeforePackageRun - The service existed in the enclave at the beginning of the package run.
+	ServiceExistedBeforePackageRun
+
+	// ServiceCreatedOrUpdatedDUringPackageRun - The service was created or updated during this run.
+	ServiceCreatedOrUpdatedDuringPackageRun
+)
diff --git a/core/server/api_container/server/startosis_engine/startosis_validator/serviceexistence_enumer.go b/core/server/api_container/server/startosis_engine/startosis_validator/serviceexistence_enumer.go
new file mode 100644
index 000000000..4677261f3
--- /dev/null
+++ b/core/server/api_container/server/startosis_engine/startosis_validator/serviceexistence_enumer.go
@@ -0,0 +1,82 @@
+// Code generated by "enumer -trimprefix ServiceExistence -type=ServiceExistence"; DO NOT EDIT.
+
+package startosis_validator
+
+import (
+	"fmt"
+	"strings"
+)
+
+const _ServiceExistenceName = "ServiceNotFoundServiceExistedBeforePackageRunServiceCreatedOrUpdatedDuringPackageRun"
+
+var _ServiceExistenceIndex = [...]uint8{0, 15, 45, 84}
+
+const _ServiceExistenceLowerName = "servicenotfoundserviceexistedbeforepackagerunservicecreatedorupdatedduringpackagerun"
+
+func (i ServiceExistence) String() string {
+	if i >= ServiceExistence(len(_ServiceExistenceIndex)-1) {
+		return fmt.Sprintf("ServiceExistence(%d)", i)
+	}
+	return _ServiceExistenceName[_ServiceExistenceIndex[i]:_ServiceExistenceIndex[i+1]]
+}
+
+// An "invalid array index" compiler error signifies that the constant values have changed.
+// Re-run the stringer command to generate them again.
+func _ServiceExistenceNoOp() {
+	var x [1]struct{}
+	_ = x[ServiceNotFound-(0)]
+	_ = x[ServiceExistedBeforePackageRun-(1)]
+	_ = x[ServiceCreatedOrUpdatedDuringPackageRun-(2)]
+}
+
+var _ServiceExistenceValues = []ServiceExistence{ServiceNotFound, ServiceExistedBeforePackageRun, ServiceCreatedOrUpdatedDuringPackageRun}
+
+var _ServiceExistenceNameToValueMap = map[string]ServiceExistence{
+	_ServiceExistenceName[0:15]:       ServiceNotFound,
+	_ServiceExistenceLowerName[0:15]:  ServiceNotFound,
+	_ServiceExistenceName[15:45]:      ServiceExistedBeforePackageRun,
+	_ServiceExistenceLowerName[15:45]: ServiceExistedBeforePackageRun,
+	_ServiceExistenceName[45:84]:      ServiceCreatedOrUpdatedDuringPackageRun,
+	_ServiceExistenceLowerName[45:84]: ServiceCreatedOrUpdatedDuringPackageRun,
+}
+
+var _ServiceExistenceNames = []string{
+	_ServiceExistenceName[0:15],
+	_ServiceExistenceName[15:45],
+	_ServiceExistenceName[45:84],
+}
+
+// ServiceExistenceString retrieves an enum value from the enum constants string name.
+// Throws an error if the param is not part of the enum.
+func ServiceExistenceString(s string) (ServiceExistence, error) {
+	if val, ok := _ServiceExistenceNameToValueMap[s]; ok {
+		return val, nil
+	}
+
+	if val, ok := _ServiceExistenceNameToValueMap[strings.ToLower(s)]; ok {
+		return val, nil
+	}
+	return 0, fmt.Errorf("%s does not belong to ServiceExistence values", s)
+}
+
+// ServiceExistenceValues returns all values of the enum
+func ServiceExistenceValues() []ServiceExistence {
+	return _ServiceExistenceValues
+}
+
+// ServiceExistenceStrings returns a slice of all String values of the enum
+func ServiceExistenceStrings() []string {
+	strs := make([]string, len(_ServiceExistenceNames))
+	copy(strs, _ServiceExistenceNames)
+	return strs
+}
+
+// IsAServiceExistence returns "true" if the value is listed in the enum definition. "false" otherwise
+func (i ServiceExistence) IsAServiceExistence() bool {
+	for _, v := range _ServiceExistenceValues {
+		if i == v {
+			return true
+		}
+	}
+	return false
+}
diff --git a/core/server/api_container/server/startosis_engine/startosis_validator/validator_environment.go b/core/server/api_container/server/startosis_engine/startosis_validator/validator_environment.go
index 86fb24805..6e489b7fe 100644
--- a/core/server/api_container/server/startosis_engine/startosis_validator/validator_environment.go
+++ b/core/server/api_container/server/startosis_engine/startosis_validator/validator_environment.go
@@ -8,16 +8,20 @@ import (
 type ValidatorEnvironment struct {
 	isNetworkPartitioningEnabled bool
 	requiredDockerImages         map[string]bool
-	serviceNames                 map[service.ServiceName]bool
+	serviceNames                 map[service.ServiceName]ServiceExistence
 	artifactNames                map[string]bool
 	serviceNameToPrivatePortIDs  map[service.ServiceName][]string
 }
 
 func NewValidatorEnvironment(isNetworkPartitioningEnabled bool, serviceNames map[service.ServiceName]bool, artifactNames map[string]bool, serviceNameToPrivatePortIds map[service.ServiceName][]string) *ValidatorEnvironment {
+	serviceNamesWithServiceExistence := map[service.ServiceName]ServiceExistence{}
+	for serviceName := range serviceNames {
+		serviceNamesWithServiceExistence[serviceName] = ServiceExistedBeforePackageRun
+	}
 	return &ValidatorEnvironment{
 		isNetworkPartitioningEnabled: isNetworkPartitioningEnabled,
 		requiredDockerImages:         map[string]bool{},
-		serviceNames:                 serviceNames,
+		serviceNames:                 serviceNamesWithServiceExistence,
 		artifactNames:                artifactNames,
 		serviceNameToPrivatePortIDs:  serviceNameToPrivatePortIds,
 	}
@@ -32,20 +36,23 @@ func (environment *ValidatorEnvironment) GetNumberOfContainerImages() uint32 {
 }
 
 func (environment *ValidatorEnvironment) AddServiceName(serviceName service.ServiceName) {
-	environment.serviceNames[serviceName] = true
+	environment.serviceNames[serviceName] = ServiceCreatedOrUpdatedDuringPackageRun
 }
 
 func (environment *ValidatorEnvironment) RemoveServiceName(serviceName service.ServiceName) {
 	delete(environment.serviceNames, serviceName)
 }
 
-func (environment *ValidatorEnvironment) DoesServiceNameExist(serviceName service.ServiceName) bool {
-	_, ok := environment.serviceNames[serviceName]
-	return ok
+func (environment *ValidatorEnvironment) DoesServiceNameExist(serviceName service.ServiceName) ServiceExistence {
+	serviceExistence, found := environment.serviceNames[serviceName]
+	if !found {
+		return ServiceNotFound
+	}
+	return serviceExistence
 }
 
-func (environment *ValidatorEnvironment) AddPrivatePortIDForService(portID string, serviceName service.ServiceName) {
-	environment.serviceNameToPrivatePortIDs[serviceName] = append(environment.serviceNameToPrivatePortIDs[serviceName], portID)
+func (environment *ValidatorEnvironment) AddPrivatePortIDForService(portIDs []string, serviceName service.ServiceName) {
+	environment.serviceNameToPrivatePortIDs[serviceName] = portIDs
 }
 
 func (environment *ValidatorEnvironment) DoesPrivatePortIDExistForService(portID string, serviceName service.ServiceName) bool {


Test patch:
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_service_framework_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_service_framework_test.go
index 18e4b939c..6c8731b2e 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_service_framework_test.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_service_framework_test.go
@@ -36,6 +36,7 @@ func (t *addServiceTestCase) GetInstruction() *kurtosis_plan_instruction.Kurtosi
 	serviceNetwork := service_network.NewMockServiceNetwork(t)
 	runtimeValueStore := runtime_value_store.NewRuntimeValueStore()
 
+	serviceNetwork.EXPECT().GetServiceRegistration(TestServiceName).Times(1).Return(nil, false)
 	serviceNetwork.EXPECT().AddService(
 		mock.Anything,
 		TestServiceName,
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_services_framework_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_services_framework_test.go
index 9f8dbe9e1..4dceb24bb 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_services_framework_test.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_services_framework_test.go
@@ -39,6 +39,17 @@ func (t *addServicesTestCase) GetInstruction() *kurtosis_plan_instruction.Kurtos
 	serviceNetwork := service_network.NewMockServiceNetwork(t)
 	runtimeValueStore := runtime_value_store.NewRuntimeValueStore()
 
+	serviceNetwork.EXPECT().GetServiceRegistration(TestServiceName).Times(1).Return(nil, false)
+	serviceNetwork.EXPECT().GetServiceRegistration(TestServiceName2).Times(1).Return(nil, false)
+	serviceNetwork.EXPECT().UpdateServices(
+		mock.Anything,
+		map[service.ServiceName]*service.ServiceConfig{},
+		mock.Anything,
+	).Times(1).Return(
+		map[service.ServiceName]*service.Service{},
+		map[service.ServiceName]error{},
+		nil,
+	)
 	serviceNetwork.EXPECT().AddServices(
 		mock.Anything,
 		mock.MatchedBy(func(configs map[service.ServiceName]*service.ServiceConfig) bool {
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/starlark_framework_engine_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/starlark_framework_engine_test.go
index 5c0e67663..6c5ba16c1 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/starlark_framework_engine_test.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/starlark_framework_engine_test.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/builtins"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/instructions_plan"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/instructions_plan/resolver"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
@@ -81,8 +82,9 @@ func testKurtosisPlanInstruction(t *testing.T, builtin KurtosisPlanInstructionBa
 	predeclared := getBasePredeclaredDict(t)
 	// Add the KurtosisPlanInstruction that is being tested
 	instructionFromBuiltin := builtin.GetInstruction()
+	emptyEnclaveComponents := enclave_structure.NewEnclaveComponents()
 	emptyInstructionsPlanMask := resolver.NewInstructionsPlanMask(0)
-	instructionWrapper := kurtosis_plan_instruction.NewKurtosisPlanInstructionWrapper(instructionFromBuiltin, emptyInstructionsPlanMask, instructionsPlan)
+	instructionWrapper := kurtosis_plan_instruction.NewKurtosisPlanInstructionWrapper(instructionFromBuiltin, emptyEnclaveComponents, emptyInstructionsPlanMask, instructionsPlan)
 	predeclared[instructionWrapper.GetName()] = starlark.NewBuiltin(instructionWrapper.GetName(), instructionWrapper.CreateBuiltin())
 
 	starlarkCode := builtin.GetStarlarkCode()
diff --git a/core/server/api_container/server/startosis_engine/startosis_interpreter_idempotent_test.go b/core/server/api_container/server/startosis_engine/startosis_interpreter_idempotent_test.go
index 83323daf2..4ca250166 100644
--- a/core/server/api_container/server/startosis_engine/startosis_interpreter_idempotent_test.go
+++ b/core/server/api_container/server/startosis_engine/startosis_interpreter_idempotent_test.go
@@ -3,21 +3,18 @@ package startosis_engine
 import (
 	"context"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
-	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/instructions_plan"
-	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction"
-	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction/mock_instruction"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/instructions_plan/resolver"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/runtime_value_store"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_constants"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_packages/mock_package_content_provider"
 	"github.com/stretchr/testify/require"
 	"github.com/stretchr/testify/suite"
-	"go.starlark.net/starlark"
 	"testing"
 )
 
 const (
 	noInputParams = "{}"
-	noReturnValue = starlark.None
 )
 
 type StartosisInterpreterIdempotentTestSuite struct {
@@ -46,31 +43,26 @@ func (suite *StartosisInterpreterIdempotentTestSuite) TearDownTest() {
 // Package to run ->   [`print("instruction1")`  `print("instruction2")`  `print("instruction3")`]
 // Check that this results in the entire set of instruction being skipped
 func (suite *StartosisInterpreterIdempotentTestSuite) TestInterpretAndOptimize_IdenticalPackage() {
-	packageContentProvider := mock_package_content_provider.NewMockPackageContentProvider()
-	defer packageContentProvider.RemoveAll()
-	runtimeValueStore := runtime_value_store.NewRuntimeValueStore()
-	testServiceNetwork := service_network.NewMockServiceNetwork(suite.T())
-	interpreter := NewStartosisInterpreter(testServiceNetwork, packageContentProvider, runtimeValueStore)
-	script := `
-def run(plan, args):
+	script := `def run(plan, args):
 	plan.print("instruction1")
 	plan.print("instruction2")
 	plan.print("instruction3")
 `
+	// Interpretation of the initial script to generate the current enclave plan
+	_, currentEnclavePlan, interpretationApiErr := suite.interpreter.Interpret(
+		context.Background(),
+		startosis_constants.PackageIdPlaceholderForStandaloneScript,
+		useDefaultMainFunctionName,
+		startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript,
+		script,
+		noInputParams,
+		enclave_structure.NewEnclaveComponents(),
+		resolver.NewInstructionsPlanMask(0))
+	require.Nil(suite.T(), interpretationApiErr)
+	require.Equal(suite.T(), 3, currentEnclavePlan.Size())
 
-	instruction1Str := `print(msg="instruction1")`
-	instruction1 := suite.newMockInstruction(instruction1Str)
-	instruction2Str := `print(msg="instruction2")`
-	instruction2 := suite.newMockInstruction(instruction2Str)
-	instruction3Str := `print(msg="instruction3")`
-	instruction3 := suite.newMockInstruction(instruction3Str)
-
-	currentEnclavePlan := instructions_plan.NewInstructionsPlan()
-	require.Nil(suite.T(), currentEnclavePlan.AddInstruction(instruction1, noReturnValue))
-	require.Nil(suite.T(), currentEnclavePlan.AddInstruction(instruction2, noReturnValue))
-	require.Nil(suite.T(), currentEnclavePlan.AddInstruction(instruction3, noReturnValue))
-
-	_, instructionsPlan, interpretationError := interpreter.InterpretAndOptimizePlan(
+	// Interpret the updated script against the current enclave plan
+	_, instructionsPlan, interpretationError := suite.interpreter.InterpretAndOptimizePlan(
 		context.Background(),
 		startosis_constants.PackageIdPlaceholderForStandaloneScript,
 		useDefaultMainFunctionName,
@@ -86,17 +78,17 @@ def run(plan, args):
 	require.Equal(suite.T(), 3, len(instructionSequence))
 
 	scheduledInstruction1 := instructionSequence[0]
-	require.Equal(suite.T(), instruction1Str, scheduledInstruction1.GetInstruction().String())
+	require.Equal(suite.T(), `print(msg="instruction1")`, scheduledInstruction1.GetInstruction().String())
 	require.False(suite.T(), scheduledInstruction1.IsImportedFromCurrentEnclavePlan())
 	require.True(suite.T(), scheduledInstruction1.IsExecuted())
 
 	scheduledInstruction2 := instructionSequence[1]
-	require.Equal(suite.T(), instruction2Str, scheduledInstruction2.GetInstruction().String())
+	require.Equal(suite.T(), `print(msg="instruction2")`, scheduledInstruction2.GetInstruction().String())
 	require.False(suite.T(), scheduledInstruction2.IsImportedFromCurrentEnclavePlan())
 	require.True(suite.T(), scheduledInstruction2.IsExecuted())
 
 	scheduledInstruction3 := instructionSequence[2]
-	require.Equal(suite.T(), instruction3Str, scheduledInstruction3.GetInstruction().String())
+	require.Equal(suite.T(), `print(msg="instruction3")`, scheduledInstruction3.GetInstruction().String())
 	require.False(suite.T(), scheduledInstruction3.IsImportedFromCurrentEnclavePlan())
 	require.True(suite.T(), scheduledInstruction3.IsExecuted())
 }
@@ -107,28 +99,35 @@ def run(plan, args):
 // Check that the first two instructions are already executed, and the last one is in the new plan marked as not
 // executed
 func (suite *StartosisInterpreterIdempotentTestSuite) TestInterpretAndOptimize_AppendNewInstruction() {
-	script := `
-def run(plan, args):
+	initialScript := `def run(plan, args):
 	plan.print("instruction1")
 	plan.print("instruction2")
-	plan.print("instruction3")
 `
+	// Interpretation of the initial script to generate the current enclave plan
+	_, currentEnclavePlan, interpretationApiErr := suite.interpreter.Interpret(
+		context.Background(),
+		startosis_constants.PackageIdPlaceholderForStandaloneScript,
+		useDefaultMainFunctionName,
+		startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript,
+		initialScript,
+		noInputParams,
+		enclave_structure.NewEnclaveComponents(),
+		resolver.NewInstructionsPlanMask(0))
+	require.Nil(suite.T(), interpretationApiErr)
+	require.Equal(suite.T(), 2, currentEnclavePlan.Size())
 
-	instruction1Str := `print(msg="instruction1")`
-	instruction1 := suite.newMockInstruction(instruction1Str)
-	instruction2Str := `print(msg="instruction2")`
-	instruction2 := suite.newMockInstruction(instruction2Str)
-
-	currentEnclavePlan := instructions_plan.NewInstructionsPlan()
-	require.Nil(suite.T(), currentEnclavePlan.AddInstruction(instruction1, noReturnValue))
-	require.Nil(suite.T(), currentEnclavePlan.AddInstruction(instruction2, noReturnValue))
-
+	updatedScript := `def run(plan, args):
+	plan.print("instruction1")
+	plan.print("instruction2")
+	plan.print("instruction3")
+`
+	// Interpret the updated script against the current enclave plan
 	_, instructionsPlan, interpretationError := suite.interpreter.InterpretAndOptimizePlan(
 		context.Background(),
 		startosis_constants.PackageIdPlaceholderForStandaloneScript,
 		useDefaultMainFunctionName,
 		startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript,
-		script,
+		updatedScript,
 		noInputParams,
 		currentEnclavePlan,
 	)
@@ -139,12 +138,12 @@ def run(plan, args):
 	require.Equal(suite.T(), 3, len(instructionSequence))
 
 	scheduledInstruction1 := instructionSequence[0]
-	require.Equal(suite.T(), instruction1Str, scheduledInstruction1.GetInstruction().String())
+	require.Equal(suite.T(), `print(msg="instruction1")`, scheduledInstruction1.GetInstruction().String())
 	require.False(suite.T(), scheduledInstruction1.IsImportedFromCurrentEnclavePlan())
 	require.True(suite.T(), scheduledInstruction1.IsExecuted())
 
 	scheduledInstruction2 := instructionSequence[1]
-	require.Equal(suite.T(), instruction2Str, scheduledInstruction2.GetInstruction().String())
+	require.Equal(suite.T(), `print(msg="instruction2")`, scheduledInstruction2.GetInstruction().String())
 	require.False(suite.T(), scheduledInstruction2.IsImportedFromCurrentEnclavePlan())
 	require.True(suite.T(), scheduledInstruction2.IsExecuted())
 
@@ -160,26 +159,33 @@ def run(plan, args):
 // Check that the first two instructions are marked as imported from a previous plan, already executed, and the last
 // one is in the new plan marked as not executed
 func (suite *StartosisInterpreterIdempotentTestSuite) TestInterpretAndOptimize_DisjointInstructionSet() {
-	script := `
-def run(plan, args):
-	plan.print("instruction3")
+	initialScript := `def run(plan, args):
+	plan.print("instruction1")
+	plan.print("instruction2")
 `
+	// Interpretation of the initial script to generate the current enclave plan
+	_, currentEnclavePlan, interpretationApiErr := suite.interpreter.Interpret(
+		context.Background(),
+		startosis_constants.PackageIdPlaceholderForStandaloneScript,
+		useDefaultMainFunctionName,
+		startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript,
+		initialScript,
+		noInputParams,
+		enclave_structure.NewEnclaveComponents(),
+		resolver.NewInstructionsPlanMask(0))
+	require.Nil(suite.T(), interpretationApiErr)
+	require.Equal(suite.T(), 2, currentEnclavePlan.Size())
 
-	instruction1Str := `print(msg="instruction1")`
-	instruction1 := suite.newMockInstruction(instruction1Str)
-	instruction2Str := `print(msg="instruction2")`
-	instruction2 := suite.newMockInstruction(instruction2Str)
-
-	currentEnclavePlan := instructions_plan.NewInstructionsPlan()
-	require.Nil(suite.T(), currentEnclavePlan.AddInstruction(instruction1, noReturnValue))
-	require.Nil(suite.T(), currentEnclavePlan.AddInstruction(instruction2, noReturnValue))
-
+	updatedScript := `def run(plan, args):
+	plan.print("instruction3")
+`
+	// Interpret the updated script against the current enclave plan
 	_, instructionsPlan, interpretationError := suite.interpreter.InterpretAndOptimizePlan(
 		context.Background(),
 		startosis_constants.PackageIdPlaceholderForStandaloneScript,
 		useDefaultMainFunctionName,
 		startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript,
-		script,
+		updatedScript,
 		noInputParams,
 		currentEnclavePlan,
 	)
@@ -190,12 +196,12 @@ def run(plan, args):
 	require.Equal(suite.T(), 3, len(instructionSequence))
 
 	scheduledInstruction1 := instructionSequence[0]
-	require.Equal(suite.T(), instruction1Str, scheduledInstruction1.GetInstruction().String())
+	require.Equal(suite.T(), `print(msg="instruction1")`, scheduledInstruction1.GetInstruction().String())
 	require.True(suite.T(), scheduledInstruction1.IsImportedFromCurrentEnclavePlan())
 	require.True(suite.T(), scheduledInstruction1.IsExecuted())
 
 	scheduledInstruction2 := instructionSequence[1]
-	require.Equal(suite.T(), instruction2Str, scheduledInstruction2.GetInstruction().String())
+	require.Equal(suite.T(), `print(msg="instruction2")`, scheduledInstruction2.GetInstruction().String())
 	require.True(suite.T(), scheduledInstruction2.IsImportedFromCurrentEnclavePlan())
 	require.True(suite.T(), scheduledInstruction2.IsExecuted())
 
@@ -211,30 +217,35 @@ def run(plan, args):
 // Check that instruction 1 and 2 are part of the new plan, already executed, and instruction3 is ALSO part of the
 // plan, but marked as imported from a previous plan
 func (suite *StartosisInterpreterIdempotentTestSuite) TestInterpretAndOptimize_ReplacePartOfInstructionWithIdenticalInstruction() {
-	script := `
-def run(plan, args):
+	initialScript := `def run(plan, args):
 	plan.print(msg="instruction1")
 	plan.print(msg="instruction2")
+	plan.print(msg="instruction3")
 `
+	// Interpretation of the initial script to generate the current enclave plan
+	_, currentEnclavePlan, interpretationApiErr := suite.interpreter.Interpret(
+		context.Background(),
+		startosis_constants.PackageIdPlaceholderForStandaloneScript,
+		useDefaultMainFunctionName,
+		startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript,
+		initialScript,
+		noInputParams,
+		enclave_structure.NewEnclaveComponents(),
+		resolver.NewInstructionsPlanMask(0))
+	require.Nil(suite.T(), interpretationApiErr)
+	require.Equal(suite.T(), 3, currentEnclavePlan.Size())
 
-	instruction1Str := `print(msg="instruction1")`
-	instruction1 := suite.newMockInstruction(instruction1Str)
-	instruction2Str := `print(msg="instruction2")`
-	instruction2 := suite.newMockInstruction(instruction2Str)
-	instruction3Str := `print(msg="instruction3")`
-	instruction3 := suite.newMockInstruction(instruction3Str)
-
-	currentEnclavePlan := instructions_plan.NewInstructionsPlan()
-	require.Nil(suite.T(), currentEnclavePlan.AddInstruction(instruction1, noReturnValue))
-	require.Nil(suite.T(), currentEnclavePlan.AddInstruction(instruction2, noReturnValue))
-	require.Nil(suite.T(), currentEnclavePlan.AddInstruction(instruction3, noReturnValue))
-
+	updatedScript := `def run(plan, args):
+	plan.print(msg="instruction1")
+	plan.print(msg="instruction2")
+`
+	// Interpret the updated script against the current enclave plan
 	_, instructionsPlan, interpretationError := suite.interpreter.InterpretAndOptimizePlan(
 		context.Background(),
 		startosis_constants.PackageIdPlaceholderForStandaloneScript,
 		useDefaultMainFunctionName,
 		startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript,
-		script,
+		updatedScript,
 		noInputParams,
 		currentEnclavePlan,
 	)
@@ -245,12 +256,12 @@ def run(plan, args):
 	require.Equal(suite.T(), 3, len(instructionSequence))
 
 	scheduledInstruction1 := instructionSequence[0]
-	require.Equal(suite.T(), instruction1Str, scheduledInstruction1.GetInstruction().String())
+	require.Equal(suite.T(), `print(msg="instruction1")`, scheduledInstruction1.GetInstruction().String())
 	require.False(suite.T(), scheduledInstruction1.IsImportedFromCurrentEnclavePlan())
 	require.True(suite.T(), scheduledInstruction1.IsExecuted())
 
 	scheduledInstruction2 := instructionSequence[1]
-	require.Equal(suite.T(), instruction2Str, scheduledInstruction2.GetInstruction().String())
+	require.Equal(suite.T(), `print(msg="instruction2")`, scheduledInstruction2.GetInstruction().String())
 	require.False(suite.T(), scheduledInstruction2.IsImportedFromCurrentEnclavePlan())
 	require.True(suite.T(), scheduledInstruction2.IsExecuted())
 
@@ -267,31 +278,36 @@ def run(plan, args):
 // [`print("instruction1")`  `print("instruction2")`  `print("instruction3")`  `print("instruction1")`  `print("instruction2_NEW")`  `print("instruction3")`]
 // The first three are imported from a previous plan, already executed, while the last three are from all new
 func (suite *StartosisInterpreterIdempotentTestSuite) TestInterpretAndOptimize_InvalidNewVersionOfThePackage() {
-	script := `
-def run(plan, args):
+	initialScript := `def run(plan, args):
 	plan.print(msg="instruction1")
-	plan.print(msg="instruction2_NEW")
+	plan.print(msg="instruction2")
 	plan.print(msg="instruction3")
 `
+	// Interpretation of the initial script to generate the current enclave plan
+	_, currentEnclavePlan, interpretationApiErr := suite.interpreter.Interpret(
+		context.Background(),
+		startosis_constants.PackageIdPlaceholderForStandaloneScript,
+		useDefaultMainFunctionName,
+		startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript,
+		initialScript,
+		noInputParams,
+		enclave_structure.NewEnclaveComponents(),
+		resolver.NewInstructionsPlanMask(0))
+	require.Nil(suite.T(), interpretationApiErr)
+	require.Equal(suite.T(), 3, currentEnclavePlan.Size())
 
-	instruction1Str := `print(msg="instruction1")`
-	instruction1 := suite.newMockInstruction(instruction1Str)
-	instruction2Str := `print(msg="instruction2")`
-	instruction2 := suite.newMockInstruction(instruction2Str)
-	instruction3Str := `print(msg="instruction3")`
-	instruction3 := suite.newMockInstruction(instruction3Str)
-
-	currentEnclavePlan := instructions_plan.NewInstructionsPlan()
-	require.Nil(suite.T(), currentEnclavePlan.AddInstruction(instruction1, noReturnValue))
-	require.Nil(suite.T(), currentEnclavePlan.AddInstruction(instruction2, noReturnValue))
-	require.Nil(suite.T(), currentEnclavePlan.AddInstruction(instruction3, noReturnValue))
-
+	updatedScript := `def run(plan, args):
+	plan.print(msg="instruction1")
+	plan.print(msg="instruction2_NEW")
+	plan.print(msg="instruction3")
+`
+	// Interpret the updated script against the current enclave plan
 	_, instructionsPlan, interpretationError := suite.interpreter.InterpretAndOptimizePlan(
 		context.Background(),
 		startosis_constants.PackageIdPlaceholderForStandaloneScript,
 		useDefaultMainFunctionName,
 		startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript,
-		script,
+		updatedScript,
 		noInputParams,
 		currentEnclavePlan,
 	)
@@ -302,22 +318,22 @@ def run(plan, args):
 	require.Equal(suite.T(), 6, len(instructionSequence))
 
 	scheduledInstruction1 := instructionSequence[0]
-	require.Equal(suite.T(), instruction1Str, scheduledInstruction1.GetInstruction().String())
+	require.Equal(suite.T(), `print(msg="instruction1")`, scheduledInstruction1.GetInstruction().String())
 	require.True(suite.T(), scheduledInstruction1.IsImportedFromCurrentEnclavePlan())
 	require.True(suite.T(), scheduledInstruction1.IsExecuted())
 
 	scheduledInstruction2 := instructionSequence[1]
-	require.Equal(suite.T(), instruction2Str, scheduledInstruction2.GetInstruction().String())
+	require.Equal(suite.T(), `print(msg="instruction2")`, scheduledInstruction2.GetInstruction().String())
 	require.True(suite.T(), scheduledInstruction2.IsImportedFromCurrentEnclavePlan())
 	require.True(suite.T(), scheduledInstruction2.IsExecuted())
 
 	scheduledInstruction3 := instructionSequence[2]
-	require.Equal(suite.T(), instruction3Str, scheduledInstruction3.GetInstruction().String())
+	require.Equal(suite.T(), `print(msg="instruction3")`, scheduledInstruction3.GetInstruction().String())
 	require.True(suite.T(), scheduledInstruction3.IsImportedFromCurrentEnclavePlan())
 	require.True(suite.T(), scheduledInstruction3.IsExecuted())
 
 	scheduledInstruction4 := instructionSequence[3]
-	require.Equal(suite.T(), instruction1Str, scheduledInstruction4.GetInstruction().String())
+	require.Equal(suite.T(), `print(msg="instruction1")`, scheduledInstruction4.GetInstruction().String())
 	require.False(suite.T(), scheduledInstruction4.IsImportedFromCurrentEnclavePlan())
 	require.False(suite.T(), scheduledInstruction4.IsExecuted())
 
@@ -327,13 +343,73 @@ def run(plan, args):
 	require.False(suite.T(), scheduledInstruction5.IsExecuted())
 
 	scheduledInstruction6 := instructionSequence[5]
-	require.Equal(suite.T(), instruction3Str, scheduledInstruction6.GetInstruction().String())
+	require.Equal(suite.T(), `print(msg="instruction3")`, scheduledInstruction6.GetInstruction().String())
 	require.False(suite.T(), scheduledInstruction6.IsImportedFromCurrentEnclavePlan())
 	require.False(suite.T(), scheduledInstruction6.IsExecuted())
 }
 
-func (suite *StartosisInterpreterIdempotentTestSuite) newMockInstruction(instructionName string) kurtosis_instruction.KurtosisInstruction {
-	mockInstruction := mock_instruction.NewMockKurtosisInstruction(suite.T())
-	mockInstruction.EXPECT().String().Maybe().Return(instructionName)
-	return mockInstruction
+// Submit a package with an update to an add_service instruction. add_service instructions is the only one right now
+// support being run twice in an enclave, updating "live" the service underneath. Check that the add_service and its
+// direct dependencies are scheduled for a re-run but other instructions remains "SKIPPED"
+func (suite *StartosisInterpreterIdempotentTestSuite) TestInterpretAndOptimize_AddServiceIdempotency() {
+	initialScript := `def run(plan):
+	service_1 = plan.add_service(name="service_1", config=ServiceConfig(image="kurtosistech/image:1.2.3"))
+	plan.print("Service 1 - IP: {} - Hostname: {}".format(service_1.ip_address, service_1.hostname))
+	plan.exec(service_name="service_1", recipe=ExecRecipe(command=["echo", "Hello World!"]))
+	plan.assert(value=service_1.ip_address, assertion="==", target_value="fake_ip")
+`
+	// Interpretation of the initial script to generate the current enclave plan
+	_, currentEnclavePlan, interpretationApiErr := suite.interpreter.Interpret(
+		context.Background(),
+		startosis_constants.PackageIdPlaceholderForStandaloneScript,
+		useDefaultMainFunctionName,
+		startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript,
+		initialScript,
+		noInputParams,
+		enclave_structure.NewEnclaveComponents(),
+		resolver.NewInstructionsPlanMask(0))
+	require.Nil(suite.T(), interpretationApiErr)
+	require.Equal(suite.T(), 4, currentEnclavePlan.Size())
+
+	updatedScript := `def run(plan):
+	service_1 = plan.add_service(name="service_1", config=ServiceConfig(image="kurtosistech/image:1.5.0")) # <-- version updated
+	plan.print("Service 1 - IP: {} - Hostname: {}".format(service_1.ip_address, service_1.hostname)) # <-- identical
+	plan.exec(service_name="service_1", recipe=ExecRecipe(command=["echo", "Hello World!"])) # <-- identical but should be rerun b/c service_1 updated
+	plan.assert(value=service_1.ip_address, assertion="==", target_value="fake_ip") # <-- identical b/c we don't track runtime value provenance yet
+`
+	// Interpret the updated script against the current enclave plan
+	_, instructionsPlan, interpretationError := suite.interpreter.InterpretAndOptimizePlan(
+		context.Background(),
+		startosis_constants.PackageIdPlaceholderForStandaloneScript,
+		useDefaultMainFunctionName,
+		startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript,
+		updatedScript,
+		noInputParams,
+		currentEnclavePlan,
+	)
+	require.Nil(suite.T(), interpretationError)
+
+	instructionSequence, err := instructionsPlan.GeneratePlan()
+	require.Nil(suite.T(), err)
+	require.Equal(suite.T(), 4, len(instructionSequence))
+
+	scheduledInstruction1 := instructionSequence[0]
+	require.Equal(suite.T(), `add_service(name="service_1", config=ServiceConfig(image="kurtosistech/image:1.5.0"))`, scheduledInstruction1.GetInstruction().String())
+	require.False(suite.T(), scheduledInstruction1.IsImportedFromCurrentEnclavePlan())
+	require.False(suite.T(), scheduledInstruction1.IsExecuted())
+
+	scheduledInstruction2 := instructionSequence[1]
+	require.Regexp(suite.T(), `print\(msg="Service 1 - IP: {{kurtosis:[a-z0-9]{32}:ip_address\.runtime_value}} - Hostname: {{kurtosis:[a-z0-9]{32}:hostname\.runtime_value}}"\)`, scheduledInstruction2.GetInstruction().String())
+	require.False(suite.T(), scheduledInstruction2.IsImportedFromCurrentEnclavePlan())
+	require.True(suite.T(), scheduledInstruction2.IsExecuted())
+
+	scheduledInstruction3 := instructionSequence[2]
+	require.Equal(suite.T(), `exec(service_name="service_1", recipe=ExecRecipe(command=["echo", "Hello World!"]))`, scheduledInstruction3.GetInstruction().String())
+	require.False(suite.T(), scheduledInstruction3.IsImportedFromCurrentEnclavePlan())
+	require.False(suite.T(), scheduledInstruction3.IsExecuted())
+
+	scheduledInstruction4 := instructionSequence[3]
+	require.Regexp(suite.T(), `assert\(value="{{kurtosis:[a-z0-9]{32}:ip_address\.runtime_value}}", assertion="==", target_value="fake_ip"\)`, scheduledInstruction4.GetInstruction().String())
+	require.False(suite.T(), scheduledInstruction4.IsImportedFromCurrentEnclavePlan())
+	require.True(suite.T(), scheduledInstruction4.IsExecuted())
 }
diff --git a/core/server/api_container/server/startosis_engine/startosis_interpreter_test.go b/core/server/api_container/server/startosis_engine/startosis_interpreter_test.go
index 56904d53a..ea8cabaf3 100644
--- a/core/server/api_container/server/startosis_engine/startosis_interpreter_test.go
+++ b/core/server/api_container/server/startosis_engine/startosis_interpreter_test.go
@@ -8,6 +8,7 @@ import (
 	"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/builtins/print_builtin"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/instructions_plan"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/instructions_plan/resolver"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service"
@@ -31,6 +32,7 @@ var (
 )
 
 var (
+	emptyEnclaveComponents    = enclave_structure.NewEnclaveComponents()
 	emptyInstructionsPlanMask = resolver.NewInstructionsPlanMask(0)
 )
 
@@ -88,7 +90,7 @@ def run(plan):
 	plan.print("` + testString + `")
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 1, instructionsPlan.Size()) // Only the print statement
 
@@ -109,7 +111,7 @@ def deploy_contract(plan,service_name,contract_name,init_message,args):
 	mainFunctionName := "deploy_contract"
 	inputArgs := `{"service_name": "my-service", "contract_name": "my-contract", "init_message": "Init message", "args": {"arg1": "arg1-value", "arg2": "arg2-value"}}`
 
-	result, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, mainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, inputArgs, emptyInstructionsPlanMask)
+	result, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, mainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, inputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 3, instructionsPlan.Size()) // The three print functions
 	require.NotNil(suite.T(), result)
@@ -130,7 +132,7 @@ def my_func(my_arg1, my_arg2, args):
 	mainFunctionName := "my_func"
 	inputArgs := `{"my_arg1": "foo", "my_arg2": "bar", "args": {"arg1": "arg1-value", "arg2": "arg2-value"}}`
 
-	result, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, mainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, inputArgs, emptyInstructionsPlanMask)
+	result, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, mainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, inputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 0, instructionsPlan.Size()) // There are no instructions to execute
 	require.NotNil(suite.T(), result)
@@ -147,7 +149,7 @@ def run(plan):
 	plan.print(my_dict)
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 2, instructionsPlan.Size())
 
@@ -165,7 +167,7 @@ def run(plan):
 unknownInstruction()
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 
 	expectedError := startosis_errors.NewInterpretationErrorWithCustomMsg(
 		[]startosis_errors.CallFrame{
@@ -186,7 +188,7 @@ unknownVariable
 unknownInstruction2()
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 
 	expectedError := startosis_errors.NewInterpretationErrorWithCustomMsg(
 		[]startosis_errors.CallFrame{
@@ -208,7 +210,7 @@ def run():
 load("otherScript.start") # fails b/c load takes in at least 2 args
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 
 	expectedError := startosis_errors.NewInterpretationErrorFromStacktrace(
 		[]startosis_errors.CallFrame{
@@ -240,7 +242,7 @@ def run(plan):
 	plan.print("The grpc transport protocol is " + datastore_service.ports["grpc"].transport_protocol)
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, fmt.Sprintf(script, testServiceName), startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, fmt.Sprintf(script, testServiceName), startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 5, instructionsPlan.Size())
 
@@ -275,7 +277,7 @@ def run(plan):
 	plan.print("The transport protocol is " + datastore_service.ports["grpc"].transport_protocol)
 	plan.print("The application protocol is " + datastore_service.ports["grpc"].application_protocol)
 `
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, fmt.Sprintf(script, testServiceName), startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, fmt.Sprintf(script, testServiceName), startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 6, instructionsPlan.Size())
 
@@ -305,7 +307,7 @@ def run(plan):
 	plan.add_service(name = service_name, config = config)
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 
 	expectedError := startosis_errors.NewInterpretationErrorWithCauseAndCustomMsg(
 		errors.New("ServiceConfig: missing argument for image"),
@@ -336,7 +338,7 @@ def run(plan):
 	plan.add_service(name = service_name, config = config)
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	expectedError := startosis_errors.NewInterpretationErrorWithCauseAndCustomMsg(
 		startosis_errors.NewInterpretationError(`The following argument(s) could not be parsed or did not pass validation: {"transport_protocol":"Invalid argument value for 'transport_protocol': 'TCPK'. Valid values are TCP, SCTP, UDP"}`),
 		[]startosis_errors.CallFrame{
@@ -366,7 +368,7 @@ def run(plan):
 	plan.add_service(name = service_name, config = config)
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	expectedError := startosis_errors.NewInterpretationErrorWithCauseAndCustomMsg(
 		startosis_errors.NewInterpretationError(`The following argument(s) could not be parsed or did not pass validation: {"number":"Value for 'number' was expected to be an integer between 1 and 65535, but it was 'starlark.String'"}`),
 		[]startosis_errors.CallFrame{
@@ -406,7 +408,7 @@ def run(plan):
 	plan.print("Done!")
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 8, instructionsPlan.Size())
 
@@ -434,7 +436,7 @@ load("` + barModulePath + `", "a")
 def run(plan):
 	plan.print("Hello " + a)
 `
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	expectedError := startosis_errors.NewInterpretationErrorWithCustomMsg(
 		[]startosis_errors.CallFrame{
 			*startosis_errors.NewCallFrame("<toplevel>", startosis_errors.NewScriptPosition(startosis_constants.PackageIdPlaceholderForStandaloneScript, 2, 1)),
@@ -457,7 +459,7 @@ my_module = import_module("` + barModulePath + `")
 def run(plan):
 	plan.print("Hello " + my_module.a)
 `
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 1, instructionsPlan.Size()) // Only the print statement
 
@@ -481,7 +483,7 @@ def run(plan):
 	plan.print(module_doo.b)
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 1, instructionsPlan.Size())
 
@@ -505,7 +507,7 @@ def run(plan):
 	plan.print(module_doo.b)
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	expectedError := startosis_errors.NewInterpretationErrorWithCustomMsg(
 		[]startosis_errors.CallFrame{
 			*startosis_errors.NewCallFrame("<toplevel>", startosis_errors.NewScriptPosition(moduleBarLoadsModuleDoo, 1, 27)),
@@ -526,7 +528,7 @@ def run(plan):
 	plan.print(my_module.b)
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 
 	errorMsg := `Evaluation error: An error occurred while loading the module '` + nonExistentModule + `'
 	Caused by: Package '` + nonExistentModule + `' not found`
@@ -549,7 +551,7 @@ def run(plan):
 `
 
 	// assert that first load fails
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.NotNil(suite.T(), interpretationError)
 	require.Nil(suite.T(), instructionsPlan)
 
@@ -558,7 +560,7 @@ def run(plan):
 	expectedOutput := `Hello World!
 `
 	// assert that second load succeeds
-	_, instructionsPlan, interpretationError = suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError = suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 1, instructionsPlan.Size())
 	validateScriptOutputFromPrintInstructions(suite.T(), instructionsPlan, expectedOutput)
@@ -585,7 +587,7 @@ def run(plan):
 	plan.add_service(name = module_bar.service_name, config = module_bar.config)
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 3, instructionsPlan.Size())
 
@@ -629,7 +631,7 @@ def run(plan):
 	plan.print("Done!")
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 8, instructionsPlan.Size())
 
@@ -658,7 +660,7 @@ def run(plan):
 	plan.print("World!")
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 1, instructionsPlan.Size())
 
@@ -696,7 +698,7 @@ Adding service example-datastore-server
 Starting Startosis script!
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, scriptA, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, scriptA, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 4, instructionsPlan.Size())
 	assertInstructionTypeAndPosition(suite.T(), instructionsPlan, 2, add_service.AddServiceBuiltinName, moduleBar, 12, 18)
@@ -721,7 +723,7 @@ def run(plan):
 Adding service example-datastore-server
 `
 
-	_, instructionsPlan, interpretationError = suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, scriptB, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError = suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, scriptB, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 3, instructionsPlan.Size())
 	assertInstructionTypeAndPosition(suite.T(), instructionsPlan, 2, add_service.AddServiceBuiltinName, startosis_constants.PackageIdPlaceholderForStandaloneScript, 14, 18)
@@ -741,7 +743,7 @@ def run(plan):
 	plan.print(file_contents)
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 2, instructionsPlan.Size())
 
@@ -775,7 +777,7 @@ def run(plan):
 	plan.print(artifact_name)
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 3, instructionsPlan.Size())
 
@@ -814,7 +816,7 @@ def run(plan):
 	plan.print(uuid)
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 4, instructionsPlan.Size())
 
@@ -836,7 +838,7 @@ def run(plan):
 	plan.print("The service example-datastore-server has been removed")
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 3, instructionsPlan.Size())
 
@@ -854,7 +856,7 @@ func (suite *StartosisInterpreterTestSuite) TestStartosisInterpreter_NoPanicIfUp
 def run(plan):
 	plan.upload_files("` + filePath + `")
 `
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.NotNil(suite.T(), interpretationError)
 	require.Nil(suite.T(), instructionsPlan)
 }
@@ -865,7 +867,7 @@ def run(plan):
 	plan.print("Hello World!")
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 1, instructionsPlan.Size())
 
@@ -880,7 +882,7 @@ def run(plan):
 	plan.print("Hello World!")
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, `{"number": 4}`, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, `{"number": 4}`, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.NotNil(suite.T(), interpretationError)
 	require.Nil(suite.T(), instructionsPlan)
 }
@@ -891,7 +893,7 @@ def run(plan, args):
 	plan.print("My favorite number is {0}".format(args["number"]))
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, `{"number": 4}`, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, `{"number": 4}`, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 1, instructionsPlan.Size())
 
@@ -909,7 +911,7 @@ def run(plan, args):
 		plan.print("Sorry no args!")
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 1, instructionsPlan.Size())
 
@@ -924,7 +926,7 @@ def run(plan, args, invalid_arg):
 	plan.print("this wouldn't interpret so the text here doesnt matter")
 `
 
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.NotNil(suite.T(), interpretationError)
 	expectedError := "Evaluation error: function run missing 2 arguments (args, invalid_arg)"
 	require.Contains(suite.T(), interpretationError.GetErrorMessage(), expectedError)
@@ -938,7 +940,7 @@ def run(plan, a, b):
 `
 	missingArgumentCount := 1
 	missingArgument := "b"
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, `{"a": "x"}`, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, `{"a": "x"}`, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.NotNil(suite.T(), interpretationError)
 
 	expectedError := fmt.Sprintf("Evaluation error: function run missing %d argument (%v)", missingArgumentCount, missingArgument)
@@ -951,7 +953,7 @@ func (suite *StartosisInterpreterTestSuite) TestStartosisInterpreter_RunWithUnpa
 def run(plan, a, b=1):
 	plan.print("My favorite number is {0}, but my favorite letter is {1}".format(b, a))
 `
-	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, `{"a": "x"}`, emptyInstructionsPlanMask)
+	_, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, `{"a": "x"}`, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.Nil(suite.T(), interpretationError)
 	require.Equal(suite.T(), 1, instructionsPlan.Size())
 	expectedOutput := "My favorite number is 1, but my favorite letter is x\n"
@@ -964,7 +966,7 @@ def run(plan):
 	print("this doesnt matter")
 `
 
-	_, _, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyInstructionsPlanMask)
+	_, _, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask)
 	require.NotNil(suite.T(), interpretationError)
 	require.Equal(suite.T(), fmt.Sprintf("Evaluation error: %v\n\tat [3:7]: run\n\tat [0:0]: print", print_builtin.UsePlanFromKurtosisInstructionError), interpretationError.GetErrorMessage())
 }
diff --git a/core/server/api_container/server/startosis_engine/startosis_validator/validator_environment_test.go b/core/server/api_container/server/startosis_engine/startosis_validator/validator_environment_test.go
index e598c96f7..b0763d592 100644
--- a/core/server/api_container/server/startosis_engine/startosis_validator/validator_environment_test.go
+++ b/core/server/api_container/server/startosis_engine/startosis_validator/validator_environment_test.go
@@ -16,8 +16,11 @@ const (
 func TestMultiplePortIdsForValidation(t *testing.T) {
 	emptyInitialMapping := map[service.ServiceName][]string{}
 	validatorEnvironment := NewValidatorEnvironment(false, nil, nil, emptyInitialMapping)
-	validatorEnvironment.AddPrivatePortIDForService(fooPortId, testBarService)
-	validatorEnvironment.AddPrivatePortIDForService(fizzPortId, testBarService)
+	portIds := []string{
+		fooPortId,
+		fizzPortId,
+	}
+	validatorEnvironment.AddPrivatePortIDForService(portIds, testBarService)
 	require.True(t, validatorEnvironment.DoesPrivatePortIDExistForService(fooPortId, testBarService))
 	require.True(t, validatorEnvironment.DoesPrivatePortIDExistForService(fizzPortId, testBarService))
 	require.False(t, validatorEnvironment.DoesPrivatePortIDExistForService(invalidPortId, testBarService))