Skip to content

fix(schema): forbid extra fields on AgentDef/ParallelGroup/ForEachDef/WorkflowConfig#159

Merged
jrob5756 merged 2 commits intomainfrom
fix/140-forbid-extra-fields-in-agent-schema
May 6, 2026
Merged

fix(schema): forbid extra fields on AgentDef/ParallelGroup/ForEachDef/WorkflowConfig#159
jrob5756 merged 2 commits intomainfrom
fix/140-forbid-extra-fields-in-agent-schema

Conversation

@jrob5756
Copy link
Copy Markdown
Collaborator

@jrob5756 jrob5756 commented May 6, 2026

Closes #140

Problem

A user nesting parallel: or for_each: inside an agents: item used to have the misnested field silently dropped by Pydantic's default extra="ignore". The wrapper became a regular type: agent with no model and no prompt, fell through to the Copilot provider's hard-coded gpt-4o default, and failed at runtime with:

ProviderError: ... Model "gpt-4o" is not available.

The error pointed at the provider model — three layers downstream of the actual schema mistake.

Fix

(a) extra="forbid" on key workflow models

Add model_config = ConfigDict(extra="forbid") to:

  • AgentDef (primary culprit)
  • ParallelGroup
  • ForEachDef
  • WorkflowConfig

The misnested YAML now fails at parse time with a clear Pydantic error pointing at the offending location:

2 validation errors for WorkflowConfig
agents.0.parallel
  Extra inputs are not permitted [type=extra_forbidden, ...]
agents.0.failure_mode
  Extra inputs are not permitted

This also catches field typos (e.g. prmpt: instead of prompt:, agnts: at top level instead of agents:).

(c) Show parallel/for-each counts in conductor validate summary

Adds "Parallel Groups" and "For-each Groups" rows to the validate summary table when present. Their absence is now a visible signal that something the user thought they declared isn't being parsed as such.

Bonus: fix examples/for-each-simple.yaml

This example was itself relying on the silent-drop behavior — it had a top-level input: block (every other example correctly nests it under workflow:). With extra="forbid" this would have failed, so this PR also moves input: under workflow: where it belongs. The example was previously not actually receiving the documented items workflow input.

Skipped from issue

  • (b) Validator rejecting agents with neither prompt nor model — (a) already catches the reported failure shape.
  • (d) Provider hard-coded gpt-4o default — separate concern.

Risk

Adding extra="forbid" is a breaking change for any user YAML that has typos that "happen to work." In-repo examples were audited (make validate-examples) and only for-each-simple.yaml was affected (and that case was a real bug, fixed in this PR).

Tests

  • New TestExtraFieldsForbidden test class with 5 regression tests covering misnested parallel:/for_each: on AgentDef, typo'd field on AgentDef, extra field on ParallelGroup, and extra top-level field on WorkflowConfig.
  • Full test suite: 2376 passed, 1 unrelated integration timeout (test_copilot_large_write — real Copilot SDK session timeout).
  • make lint
  • make validate-examples

Verification

The original repro from #140:

agents:
  - name: review_group
    parallel:
      - technical_reviewer
      - readability_reviewer

Before: Validation Successful then a runtime gpt-4o model error.
After: clear schema error pointing at agents.0.parallel.

…/WorkflowConfig

Closes #140

A user nesting parallel: or for_each: inside an agents: item used to
have the misnested field silently dropped, leaving a no-prompt/no-model
wrapper agent that failed obscurely at the provider with
"Model 'gpt-4o' is not available".

Add model_config = ConfigDict(extra="forbid") to AgentDef,
ParallelGroup, ForEachDef, and WorkflowConfig so misnesting and field
typos are rejected at parse time with a Pydantic extra_forbidden error
pointing at the offending location (e.g. agents.0.parallel).

Also:
- Show Parallel Groups and For-each Groups counts in the
  conductor validate summary so the absence of an expected group is
  visible (issue #140 suggestion c).
- Fix examples/for-each-simple.yaml: its top-level input: block was
  silently being dropped (it should be nested under workflow:). With
  extra=forbid this now hard-fails, so move it under workflow: where
  every other example has it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extends the extra="forbid" coverage from PR review feedback. These
models are equally user-authored in YAML and exhibit the same
silent-typo footgun that #140 reports.

- RouteDef: a typo like `whn:` instead of `when:` was silently
  dropped, leaving `route.when = None` and turning a conditional
  route into an unconditional one — a workflow-semantics bug that
  is nearly impossible to debug.
- WorkflowDef: typos like `entery_point:` or `limts:` in the
  `workflow:` block were silently ignored, falling back to
  defaults instead of the user's intent.

Adds two regression tests covering both cases. `make validate-examples`
still passes — no in-repo example was relying on these silent drops.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jrob5756 jrob5756 merged commit 8db16a7 into main May 6, 2026
7 checks passed
@jrob5756 jrob5756 deleted the fix/140-forbid-extra-fields-in-agent-schema branch May 6, 2026 16:25
@jrob5756 jrob5756 mentioned this pull request May 6, 2026
jrob5756 added a commit that referenced this pull request May 6, 2026
- conductor resume flag parity with run (#158)
- reasoning effort displayed in dashboard (#160)
- iteration_limit_reached/resolved events for dashboard (#162)
- registry latest now means default branch HEAD, not newest tag (#157)
- forbid extra fields on Agent/Parallel/ForEach/Workflow schemas (#159)
- pretty-print tool args/results in dashboard events (#161)
- capture uv stdout+stderr on Windows install failure (#156)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Schema silently accepts misnested parallel:/for_each: blocks under agents: items, causing default-model fallback failures

1 participant