From f83141083e0af3278cf763f9300011156eff1a3e Mon Sep 17 00:00:00 2001 From: Joao Moura Date: Mon, 6 Jul 2026 17:10:47 -0700 Subject: [PATCH] fix(cli): don't block persistence-restore resume on schema validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cursor (High): `crewai run --inputs '{"id":"…"}'` is a persistence resume — kickoff hydrates full state from storage, so schema-required fields may come from the restored state rather than --inputs. The new required-field prompt/validation was erroring/prompting before kickoff, breaking resume. When `id` is present in --inputs, forward the inputs unchanged and skip the prompt/validation. Test: test_id_only_input_skips_required_validation. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh --- lib/cli/src/crewai_cli/run_declarative_flow.py | 12 +++++++++--- lib/cli/tests/test_run_declarative_flow.py | 11 +++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/cli/src/crewai_cli/run_declarative_flow.py b/lib/cli/src/crewai_cli/run_declarative_flow.py index d3c58c1cf..7fd727698 100644 --- a/lib/cli/src/crewai_cli/run_declarative_flow.py +++ b/lib/cli/src/crewai_cli/run_declarative_flow.py @@ -80,6 +80,13 @@ def _resolve_flow_inputs(flow: Any, provided: dict[str, Any]) -> dict[str, Any]: # dict / unschematized state — nothing to derive; pass inputs through. return dict(provided) + # ``id`` signals a persistence restore: kickoff hydrates the full state from + # storage, so required fields may come from the restored state rather than + # --inputs. Forward the inputs unchanged instead of prompting/erroring for + # fields the resume will supply. + if "id" in provided: + return dict(provided) + properties = { name: spec for name, spec in (schema.get("properties") or {}).items() @@ -89,11 +96,10 @@ def _resolve_flow_inputs(flow: Any, provided: dict[str, Any]) -> dict[str, Any]: defaults = _flow_state_defaults(flow) # Unknown keys are almost always typos — warn and drop them (they'd fail - # structured-state validation at kickoff anyway). ``id`` is a reserved - # kickoff key (persistence restore), so let it through untouched. + # structured-state validation at kickoff anyway). collected: dict[str, Any] = {} for key, value in provided.items(): - if key in properties or key == "id": + if key in properties: collected[key] = value continue suggestion = _closest_key(key, properties) diff --git a/lib/cli/tests/test_run_declarative_flow.py b/lib/cli/tests/test_run_declarative_flow.py index 3364135a1..b45f40e59 100644 --- a/lib/cli/tests/test_run_declarative_flow.py +++ b/lib/cli/tests/test_run_declarative_flow.py @@ -351,3 +351,14 @@ def test_reserved_id_input_is_forwarded_not_dropped( captured = capsys.readouterr() assert captured.out == "a@b.com\n" assert "Ignoring unknown input 'id'" not in captured.err + + +def test_id_only_input_skips_required_validation(tmp_path: Path) -> None: + # Resume via `crewai run --inputs '{"id":"..."}'` must not be blocked by the + # required-field check: kickoff hydrates required state from persistence. + path = _write(tmp_path, REQUIRED_FLOW_YAML) + flow = run_declarative_flow_module.load_declarative_flow(str(path)) + + resolved = run_declarative_flow_module._resolve_flow_inputs(flow, {"id": "run-123"}) + + assert resolved == {"id": "run-123"}