Skip to content

fix(models): lenient GlobalArgsSchema validation for direct execution#1399

Merged
stack72 merged 2 commits into
mainfrom
fix/lenient-globalargs-direct-execution
May 18, 2026
Merged

fix(models): lenient GlobalArgsSchema validation for direct execution#1399
stack72 merged 2 commits into
mainfrom
fix/lenient-globalargs-direct-execution

Conversation

@stack72
Copy link
Copy Markdown
Contributor

@stack72 stack72 commented May 18, 2026

Summary

  • Use .partial() on GlobalArgsSchema at three validation points in the direct execution / workflow execution pipeline so missing globalArgs fields are allowed while provided fields are still type-checked
  • swamp model create retains strict validation (its own safeParse in create.ts is untouched)
  • Reframe direct execution as the default approach in swamp-model and swamp-workflow skills

Context

Closes swamp-club#366. Models like @swamp/aws/s3/bucket-policy have required fields (Bucket, PolicyDocument) in their GlobalArgsSchema because the upstream CloudFormation schema declares them required. This blocked workflow YAML direct execution of get — the method doesn't need those fields, but the validation rejected the empty globalArgs before the method could run.

Direct execution already had no useful GlobalArgsSchema validation — models with all-optional fields (like @swamp/aws/s3/bucket) passed {} without checking anything. The validation was binary: all-optional = pass everything, any required = block everything.

Changes

File Change
direct_execution.ts .partial() on GlobalArgsSchema when auto-creating definitions
validation_service.ts .partial() in pre-execution model validation
method_execution_service.ts .partial() at method execution time
direct_execution_test.ts 2 new tests: empty required globalArgs passes, wrong types still rejected
validation_service_test.ts 2 new tests: same pattern
method_execution_service_test.ts 2 new tests: same pattern
swamp-model/SKILL.md Reframe direct execution as default, model create as exception
swamp-model/references/direct-execution.md Same
swamp-workflow/SKILL.md Same
swamp-workflow/references/direct-execution.md Same

Test plan

  • 6 new unit tests (2 per validation point) — empty required globalArgs succeeds, invalid types on provided fields still rejected
  • All 134 existing tests pass across the three test files
  • UAT CLI suite: 501 passed, 62 failed (baseline with released binary: 450 passed, 113 failed — no regressions)
  • Manual reproduction: workflow direct-exec of bucket-policy get now passes all three validation gates

🤖 Generated with Claude Code

stack72 and others added 2 commits May 18, 2026 23:31
…tion (swamp-club#366)

Direct execution creates ephemeral model instances where not all
globalArgs are needed — get/sync/delete don't require creation-time
fields. Previously, strict GlobalArgsSchema validation blocked these
calls with "expected string, received undefined" for models like
bucket-policy that have required fields in their upstream schema.

Apply .partial() at three validation points so provided fields are
still type-checked but missing fields are allowed. swamp model create
retains strict validation via its own safeParse in create.ts.

Also reframes direct execution as the default approach in the
swamp-model and swamp-workflow skills, since model create is only
needed for persistent definitions with CEL expressions or shared config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CLI UX Review

Blocking

None.

Suggestions

None.

Verdict

PASS — this PR has no user-facing UX changes. The three modified source files (direct_execution.ts, validation_service.ts, method_execution_service.ts) change internal validation logic only; no CLI flags, help text, output formats, or error message templates are added or altered. The behavioral effect is strictly positive from a UX standpoint: users running get on models with required GlobalArgsSchema fields (e.g. @swamp/aws/s3/bucket-policy) no longer receive spurious validation errors. Invalid types on provided fields still produce the same clear Invalid global arguments for type '...' error message as before. The skill documentation changes are internal Claude guidance, not user-visible CLI output.

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Clean, well-tested fix. The .partial() approach correctly relaxes required-field enforcement for direct execution while preserving type validation on provided fields. The three validation points are consistent, model create retains strict validation in create.ts:183, and the 6 new tests cover both the happy path (empty required args pass) and error path (wrong types still rejected).

Suggestions

  1. Duplicated .partial() guard logic: The pattern "partial" in schema && typeof schema.partial === "function" ? schema.partial() : schema appears identically in direct_execution.ts, validation_service.ts, and method_execution_service.ts. A small shared helper (e.g. toLenientSchema(schema: z.ZodTypeAny): z.ZodTypeAny) would reduce the duplication and make the intent clearer at each call site.

  2. swamp model validate also becomes lenient: The validation_service.ts change affects all callers of validateModel, including the swamp model validate command for persistent definitions. This means manually editing a YAML to remove a required global arg would no longer be caught by model validate. This may be intentional (the PR description argues the old validation was "binary: all-optional = pass everything, any required = block everything"), but worth noting explicitly if not already considered.

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adversarial Review

Critical / High

None.

Medium

  1. validation_service.ts:511-514.partial() weakens swamp model validate for persisted definitions.
    The validateGlobalArguments method is called from validateModel, which serves both direct/workflow execution (execution_service.ts:469) and the swamp model validate CLI command (validate.ts:219, 286). The .partial() change is correct for direct execution, but it also means swamp model validate will no longer catch missing required globalArgs in persisted definitions — e.g., if a user manually edits a YAML file and deletes the Bucket field from a bucket-policy definition, swamp model validate will now report it as passing.

    Breaking example: Create a definition with model create providing all required fields. Manually edit the YAML to remove Bucket. Run swamp model validate. Before this PR: fails. After: passes.

    Impact: Low in practice since model create enforces strict validation on creation, and manual YAML editing is an edge case. But swamp model validate exists specifically to catch this kind of drift.

    Suggested fix: Thread a lenient: boolean option through validateGlobalArguments (or through validateModel), defaulting to true for workflow/direct-execution callers and false for the validate command. Alternatively, accept the trade-off and document it.

Low

  1. All three files — Models with ZodEffects globalArgs won't benefit from lenient validation.
    The duck-typing guard "partial" in schema && typeof schema.partial === "function" correctly falls back to strict validation for ZodEffects (from .refine()/.transform() wrappers). This means if a model wraps its globalArguments schema with a cross-field refinement, direct execution will still be blocked by required field validation. This is a safe fallback — not a bug — but models using refinements won't get the fix. Documenting this limitation would help future debugging.

Verdict

PASS — The core logic is correct: .partial() validates provided field types while allowing omissions, model create retains strict validation via its own safeParse, and all three validation points are updated consistently. The 6 new tests cover the key cases (empty required globalArgs passes, wrong types still rejected). The medium finding about swamp model validate is a valid trade-off that should be considered but doesn't block merge.

@stack72 stack72 merged commit 694acbd into main May 18, 2026
11 checks passed
@stack72 stack72 deleted the fix/lenient-globalargs-direct-execution branch May 18, 2026 23:02
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.

1 participant