From d1543829237132738abbab1a49b500f86bba2ee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Va=C5=A1ko?= Date: Tue, 28 Apr 2026 14:03:31 +0200 Subject: [PATCH 1/3] fix(rename): skip stateless objects in PathsGenerator to prevent panic When a manifest entry exists for a config that has neither local nor remote state (e.g. an orphaned scheduler config after its target orchestrator was deleted), PathsGenerator.doUpdate called LocalOrRemoteState() which panicked unconditionally. Two guards added to doUpdate: 1. Skip the object itself when both HasLocalState and HasRemoteState are false. 2. Skip the object when its parent is stateless, since a valid path cannot be resolved without a resolved parent. Reproduces the crash in PSGO-233 / SUPPORT-15941 where kbc pull panicked on "object Local or Remote state must be set" for clients with an existing local manifest containing an orphaned scheduler entry. Co-Authored-By: Claude Sonnet 4.6 --- internal/pkg/plan/rename/plan_test.go | 79 +++++++++++++++++++++++++++ internal/pkg/state/local/paths.go | 13 +++++ 2 files changed, 92 insertions(+) diff --git a/internal/pkg/plan/rename/plan_test.go b/internal/pkg/plan/rename/plan_test.go index b824abd5a7..394eec6d25 100644 --- a/internal/pkg/plan/rename/plan_test.go +++ b/internal/pkg/plan/rename/plan_test.go @@ -83,6 +83,85 @@ func TestRenameAllPlan(t *testing.T) { }, plan) } +// TestRenameAllPlan_StatelessParentSkipped verifies that NewPlan does not panic +// when a config with remote state has a parent that is stateless in the registry. +// This reproduces the production scenario from PSGO-233: a scheduler config has +// a SchedulerForRelation pointing to an orchestrator whose manifest entry is +// orphaned (nil Local and nil Remote). State.All() returns the scheduler (has +// remote state) but not the orchestrator, so doUpdate reaches the parent via +// MustGet and must skip gracefully without calling LocalOrRemoteState(). +func TestRenameAllPlan_StatelessParentSkipped(t *testing.T) { + t.Parallel() + _, testFile, _, _ := runtime.Caller(0) + testDir := filesystem.Dir(testFile) + fs := testFs(t, filesystem.Join(testDir, "..", "..", "fixtures", "local", "to-rename")) + d := dependencies.NewMocked(t, t.Context()) + + getGenericExResponder, err := httpmock.NewJsonResponder(200, map[string]any{ + "id": "ex-generic-v2", + "type": "extractor", + "name": "Generic", + }) + require.NoError(t, err) + getMySQLExResponder, err := httpmock.NewJsonResponder(200, map[string]any{ + "id": "keboola.ex-db-mysql", + "type": "extractor", + "name": "MySQL", + }) + require.NoError(t, err) + d.MockedHTTPTransport().RegisterResponder("GET", `=~/storage/components/ex-generic-v2`, getGenericExResponder.Once()) + d.MockedHTTPTransport().RegisterResponder("GET", `=~/storage/components/keboola.ex-db-mysql`, getMySQLExResponder.Once()) + + projectState, err := d.MockedProject(fs).LoadState(t.Context(), loadState.Options{LoadLocalState: true}, d) + require.NoError(t, err) + + // Stateless orchestrator: in the registry as an orphaned manifest entry, but + // both Local and Remote are nil — simulates a deleted orchestrator. + orchKey := model.ConfigKey{BranchID: 123, ComponentID: "keboola.orchestrator", ID: "orch-orphaned"} + orchState := &model.ConfigState{ + ConfigManifest: &model.ConfigManifest{ + ConfigKey: orchKey, + Paths: model.Paths{ + AbsPath: model.NewAbsPath("my-main-branch", "other/keboola.orchestrator/orch-orphaned"), + }, + }, + } + + // Scheduler with remote state whose SchedulerForRelation parent key points to + // the stateless orchestrator. State.All() will include this scheduler (it has + // remote state), so doUpdate will recurse into the stateless orchestrator parent. + schedulerKey := model.ConfigKey{BranchID: 123, ComponentID: "keboola.scheduler", ID: "sched-for-orch"} + schedulerState := &model.ConfigState{ + ConfigManifest: &model.ConfigManifest{ + ConfigKey: schedulerKey, + Paths: model.Paths{ + AbsPath: model.NewAbsPath("my-main-branch", "other/keboola.scheduler/sched-for-orch"), + }, + }, + Remote: &model.Config{ + ConfigKey: schedulerKey, + Relations: model.Relations{ + &model.SchedulerForRelation{ + ComponentID: "keboola.orchestrator", + ConfigID: "orch-orphaned", + }, + }, + }, + } + + require.NoError(t, projectState.State().Set(orchState)) + require.NoError(t, projectState.State().Set(schedulerState)) + + // NewPlan must not panic; neither orphaned object should appear in rename actions. + plan, err := NewPlan(projectState.State()) + require.NoError(t, err) + + for _, action := range plan.actions { + assert.NotEqual(t, action.Manifest.Key(), orchKey) + assert.NotEqual(t, action.Manifest.Key(), schedulerKey) + } +} + func testFs(t *testing.T, inputDir string) filesystem.Fs { t.Helper() diff --git a/internal/pkg/state/local/paths.go b/internal/pkg/state/local/paths.go index c13d2bea85..bc9e1a1421 100644 --- a/internal/pkg/state/local/paths.go +++ b/internal/pkg/state/local/paths.go @@ -67,6 +67,13 @@ func (g *PathsGenerator) doUpdate(objectState model.ObjectState, origin model.Ke return nil } + // Orphaned manifest entries have no local or remote state; skip them so + // LocalOrRemoteState() does not panic and they are excluded from rename. + if !objectState.HasLocalState() && !objectState.HasRemoteState() { + g.processed[objectState.Key().String()] = true + return nil + } + // Detect cyclic relations if origin != nil && objectState.Key().String() == origin.String() { return errors.Errorf(`a cyclic relation was found when generating path to %s`, origin.Desc()) @@ -93,6 +100,12 @@ func (g *PathsGenerator) doUpdate(objectState model.ObjectState, origin model.Ke return err } + // Skip if the parent is stateless — its path cannot be resolved. + if !parent.HasLocalState() && !parent.HasRemoteState() { + g.processed[objectState.Key().String()] = true + return nil + } + // Set new parent path manifest.SetParentPath(parent.Path()) } From 7773cea4d59cf932420ea23c7edece539ce075cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Va=C5=A1ko?= Date: Tue, 28 Apr 2026 21:36:24 +0200 Subject: [PATCH 2/3] test(e2e): add pull+rename E2E test with orphaned scheduler Regression test for PSGO-233: verifies that kbc pull --force succeeds when the manifest contains an orphaned scheduler (fake ID, no local files, not in remote) AND a config exists at a wrong path that needs to be renamed. Previously PathsGenerator.doUpdate could panic on stateless objects encountered during the rename plan. Co-Authored-By: Claude Sonnet 4.6 --- .../args | 1 + .../expected-code | 1 + .../expected-stderr | 2 + .../expected-stdout | 8 +++ .../in/.keboola/manifest.json | 63 +++++++++++++++++++ .../in/description.md | 1 + .../in/main/description.md | 1 + .../ex-generic-v2/wrong-name/config.json | 1 + .../ex-generic-v2/wrong-name/description.md | 1 + .../ex-generic-v2/wrong-name/meta.json | 3 + .../in/main/meta.json | 4 ++ .../initial-state.json | 12 ++++ .../out/.keboola/manifest.json | 49 +++++++++++++++ .../out/.keboola/project.json | 9 +++ .../out/description.md | 1 + .../out/main/description.md | 1 + .../extractor/ex-generic-v2/empty/config.json | 1 + .../ex-generic-v2/empty/description.md | 1 + .../extractor/ex-generic-v2/empty/meta.json | 4 ++ .../out/main/meta.json | 4 ++ 20 files changed, 168 insertions(+) create mode 100644 test/cli/pull/pull-force-orphaned-scheduler-with-rename/args create mode 100644 test/cli/pull/pull-force-orphaned-scheduler-with-rename/expected-code create mode 100644 test/cli/pull/pull-force-orphaned-scheduler-with-rename/expected-stderr create mode 100644 test/cli/pull/pull-force-orphaned-scheduler-with-rename/expected-stdout create mode 100644 test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/.keboola/manifest.json create mode 100644 test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/description.md create mode 100644 test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/main/description.md create mode 100644 test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/main/extractor/ex-generic-v2/wrong-name/config.json create mode 100644 test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/main/extractor/ex-generic-v2/wrong-name/description.md create mode 100644 test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/main/extractor/ex-generic-v2/wrong-name/meta.json create mode 100644 test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/main/meta.json create mode 100644 test/cli/pull/pull-force-orphaned-scheduler-with-rename/initial-state.json create mode 100644 test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/.keboola/manifest.json create mode 100644 test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/.keboola/project.json create mode 100644 test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/description.md create mode 100644 test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/main/description.md create mode 100644 test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/main/extractor/ex-generic-v2/empty/config.json create mode 100644 test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/main/extractor/ex-generic-v2/empty/description.md create mode 100644 test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/main/extractor/ex-generic-v2/empty/meta.json create mode 100644 test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/main/meta.json diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/args b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/args new file mode 100644 index 0000000000..b70ceb1738 --- /dev/null +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/args @@ -0,0 +1 @@ +pull --force --storage-api-token %%TEST_KBC_STORAGE_API_TOKEN%% diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/expected-code b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/expected-code new file mode 100644 index 0000000000..573541ac97 --- /dev/null +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/expected-code @@ -0,0 +1 @@ +0 diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/expected-stderr b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/expected-stderr new file mode 100644 index 0000000000..8457e9c872 --- /dev/null +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/expected-stderr @@ -0,0 +1,2 @@ +Manifest loaded with warnings (some records were skipped): + - %s diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/expected-stdout b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/expected-stdout new file mode 100644 index 0000000000..0ee7cbfabc --- /dev/null +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/expected-stdout @@ -0,0 +1,8 @@ +Ignoring invalid local state. +Plan for "pull" operation: + * B main | changed: description + * C main/extractor/ex-generic-v2/wrong-name | changed: description, name +Plan for "rename" operation: + - main/extractor/ex-generic-v2/{wrong-name -> empty} +Rename done. +Pull done. diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/.keboola/manifest.json b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/.keboola/manifest.json new file mode 100644 index 0000000000..5c00d696fc --- /dev/null +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/.keboola/manifest.json @@ -0,0 +1,63 @@ +{ + "version": 2, + "project": { + "id": %%TEST_KBC_PROJECT_ID%%, + "apiHost": "%%TEST_KBC_STORAGE_API_HOST%%" + }, + "allowTargetEnv": false, + "sortBy": "path", + "naming": { + "branch": "{branch_name}", + "config": "{component_type}/{component_id}/{config_name}", + "configRow": "rows/{config_row_name}", + "schedulerConfig": "schedules/{config_name}", + "sharedCodeConfig": "_shared/{target_component_id}", + "sharedCodeConfigRow": "codes/{config_row_name}", + "variablesConfig": "variables", + "variablesValuesRow": "values/{config_row_name}", + "dataAppConfig": "app/{component_id}/{config_name}" + }, + "allowedBranches": [ + "__all__" + ], + "ignoredComponents": [], + "templates": { + "repositories": [ + { + "type": "git", + "name": "keboola", + "url": "https://github.com/keboola/keboola-as-code-templates.git", + "ref": "main" + } + ] + }, + "branches": [ + { + "id": %%TEST_BRANCH_MAIN_ID%%, + "path": "main" + } + ], + "configurations": [ + { + "branchId": %%TEST_BRANCH_MAIN_ID%%, + "componentId": "keboola.scheduler", + "id": "456", + "path": "schedules/schedule1", + "relations": [ + { + "componentId": "ex-generic-v2", + "configId": "123", + "type": "schedulerFor" + } + ], + "rows": [] + }, + { + "branchId": %%TEST_BRANCH_MAIN_ID%%, + "componentId": "ex-generic-v2", + "id": "%%TEST_BRANCH_ALL_CONFIG_EMPTY_ID%%", + "path": "extractor/ex-generic-v2/wrong-name", + "rows": [] + } + ] +} diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/description.md b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/description.md new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/description.md @@ -0,0 +1 @@ + diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/main/description.md b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/main/description.md new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/main/description.md @@ -0,0 +1 @@ + diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/main/extractor/ex-generic-v2/wrong-name/config.json b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/main/extractor/ex-generic-v2/wrong-name/config.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/main/extractor/ex-generic-v2/wrong-name/config.json @@ -0,0 +1 @@ +{} diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/main/extractor/ex-generic-v2/wrong-name/description.md b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/main/extractor/ex-generic-v2/wrong-name/description.md new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/main/extractor/ex-generic-v2/wrong-name/description.md @@ -0,0 +1 @@ + diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/main/extractor/ex-generic-v2/wrong-name/meta.json b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/main/extractor/ex-generic-v2/wrong-name/meta.json new file mode 100644 index 0000000000..6152d92c2d --- /dev/null +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/main/extractor/ex-generic-v2/wrong-name/meta.json @@ -0,0 +1,3 @@ +{ + "name": "wrong-name" +} diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/main/meta.json b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/main/meta.json new file mode 100644 index 0000000000..154a3ce050 --- /dev/null +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/in/main/meta.json @@ -0,0 +1,4 @@ +{ + "name": "Main", + "isDefault": true +} diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/initial-state.json b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/initial-state.json new file mode 100644 index 0000000000..85cfd77233 --- /dev/null +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/initial-state.json @@ -0,0 +1,12 @@ +{ + "allBranchesConfigs": ["empty"], + "branches": [ + { + "branch": { + "name": "Main", + "description": "my description", + "isDefault": true + } + } + ] +} diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/.keboola/manifest.json b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/.keboola/manifest.json new file mode 100644 index 0000000000..c5f2088b34 --- /dev/null +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/.keboola/manifest.json @@ -0,0 +1,49 @@ +{ + "version": 2, + "project": { + "id": %%TEST_KBC_PROJECT_ID%%, + "apiHost": "%%TEST_KBC_STORAGE_API_HOST%%" + }, + "allowTargetEnv": false, + "sortBy": "path", + "naming": { + "branch": "{branch_name}", + "config": "{component_type}/{component_id}/{config_name}", + "configRow": "rows/{config_row_name}", + "schedulerConfig": "schedules/{config_name}", + "sharedCodeConfig": "_shared/{target_component_id}", + "sharedCodeConfigRow": "codes/{config_row_name}", + "variablesConfig": "variables", + "variablesValuesRow": "values/{config_row_name}", + "dataAppConfig": "app/{component_id}/{config_name}" + }, + "allowedBranches": [ + "__all__" + ], + "ignoredComponents": [], + "templates": { + "repositories": [ + { + "type": "git", + "name": "keboola", + "url": "https://github.com/keboola/keboola-as-code-templates.git", + "ref": "main" + } + ] + }, + "branches": [ + { + "id": %%TEST_BRANCH_MAIN_ID%%, + "path": "main" + } + ], + "configurations": [ + { + "branchId": %%TEST_BRANCH_MAIN_ID%%, + "componentId": "ex-generic-v2", + "id": "%%TEST_BRANCH_ALL_CONFIG_EMPTY_ID%%", + "path": "extractor/ex-generic-v2/empty", + "rows": [] + } + ] +} diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/.keboola/project.json b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/.keboola/project.json new file mode 100644 index 0000000000..2f8539cf19 --- /dev/null +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/.keboola/project.json @@ -0,0 +1,9 @@ +{ + "backends": [ + %A + ], + "features": [ + %A + ], + "defaultBranchId": %A +} diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/description.md b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/description.md new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/description.md @@ -0,0 +1 @@ + diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/main/description.md b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/main/description.md new file mode 100644 index 0000000000..d5a110bef8 --- /dev/null +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/main/description.md @@ -0,0 +1 @@ +my description \ No newline at end of file diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/main/extractor/ex-generic-v2/empty/config.json b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/main/extractor/ex-generic-v2/empty/config.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/main/extractor/ex-generic-v2/empty/config.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/main/extractor/ex-generic-v2/empty/description.md b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/main/extractor/ex-generic-v2/empty/description.md new file mode 100644 index 0000000000..28f6091c29 --- /dev/null +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/main/extractor/ex-generic-v2/empty/description.md @@ -0,0 +1 @@ +test fixture diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/main/extractor/ex-generic-v2/empty/meta.json b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/main/extractor/ex-generic-v2/empty/meta.json new file mode 100644 index 0000000000..eb2c70d865 --- /dev/null +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/main/extractor/ex-generic-v2/empty/meta.json @@ -0,0 +1,4 @@ +{ + "name": "empty", + "isDisabled": false +} diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/main/meta.json b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/main/meta.json new file mode 100644 index 0000000000..154a3ce050 --- /dev/null +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/out/main/meta.json @@ -0,0 +1,4 @@ +{ + "name": "Main", + "isDefault": true +} From c6695dba434d0b3f1060edd403058f72f375d609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Va=C5=A1ko?= Date: Wed, 29 Apr 2026 06:34:07 +0200 Subject: [PATCH 3/3] test(e2e): fix expected stdout for pull+rename orphaned scheduler test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove "Ignoring invalid local state." — the orphaned scheduler "456" is removed by SetRecords at manifest-load time (stderr warning only), so no localErr is produced and the stdout message is not printed. Co-Authored-By: Claude Sonnet 4.6 --- internal/pkg/plan/rename/plan_test.go | 5 +++++ .../expected-stdout | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/pkg/plan/rename/plan_test.go b/internal/pkg/plan/rename/plan_test.go index 394eec6d25..0638d6a649 100644 --- a/internal/pkg/plan/rename/plan_test.go +++ b/internal/pkg/plan/rename/plan_test.go @@ -156,6 +156,11 @@ func TestRenameAllPlan_StatelessParentSkipped(t *testing.T) { plan, err := NewPlan(projectState.State()) require.NoError(t, err) + // The fixture produces 3 legitimate rename actions (branch, mysql config, mysql row). + // Asserting the count ensures the plan is not vacuously empty — an empty plan would + // make the loop below a no-op and the NotEqual assertions would never fire. + require.Len(t, plan.actions, 3, "plan must contain the fixture's legitimate rename actions") + for _, action := range plan.actions { assert.NotEqual(t, action.Manifest.Key(), orchKey) assert.NotEqual(t, action.Manifest.Key(), schedulerKey) diff --git a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/expected-stdout b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/expected-stdout index 0ee7cbfabc..bb9ceec8c0 100644 --- a/test/cli/pull/pull-force-orphaned-scheduler-with-rename/expected-stdout +++ b/test/cli/pull/pull-force-orphaned-scheduler-with-rename/expected-stdout @@ -1,4 +1,3 @@ -Ignoring invalid local state. Plan for "pull" operation: * B main | changed: description * C main/extractor/ex-generic-v2/wrong-name | changed: description, name