fix(cli): don't block persistence-restore resume on schema validation

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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
This commit is contained in:
Joao Moura
2026-07-06 17:10:47 -07:00
parent 327d991860
commit f83141083e
2 changed files with 20 additions and 3 deletions

View File

@@ -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)

View File

@@ -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"}