Skip to content

Storyboards: distinguish schema-invalid vs business-rule negative paths #2824

@bokelley

Description

@bokelley

Summary

The storyboard sample_request schema lint (landed in #2768) treats expect_error: true as a negative-test marker and skips schema validation on those steps. This covers two distinct negative-path categories that deserve separate treatment:

  1. Schema-invalid negative path — the payload is intentionally malformed (missing required field, wrong type, etc.) to verify the agent's validation response. Examples: universal/error-compliance.yaml#missing_fields, universal/schema-validation.yaml#reversed_dates.
  2. Business-rule negative path — the payload is schema-valid but violates a business rule or state-machine invariant. The agent rejects it, but for semantic reasons, not structural ones. Examples: invalid_transitions.yaml pause-canceled-buy, measurement_terms_rejected.yaml, governance_denied.yaml.

Current heuristic (isNegativeStep) lumps both categories together, which means shape drift on business-rule negative fixtures slips past the lint. A state-machine test that sends a schema-valid update_media_buy payload to a canceled buy would still be caught at runtime — but if someone introduces a typo that also makes the payload schema-invalid, the lint now silently accepts it because expect_error: true short-circuits validation.

Proposal

Introduce an explicit attribute to disambiguate:

steps:
  - id: pause_canceled_buy
    expect_error: true
    negative_path: business_rule    # schema-valid, agent rejects on business grounds
    # sample_request still schema-validated

  - id: reversed_dates
    expect_error: true
    negative_path: schema_invalid   # payload intentionally malformed
    # sample_request schema validation skipped

Defaults + migration:

  • Default negative_path: schema_invalid (preserves current behavior — no regression on day 1)
  • Authors of business-rule negative steps opt in by setting negative_path: business_rule
  • Lint validates the sample_request when negative_path: business_rule
  • Existing sample_request_skip_schema: true is a superset of schema_invalid — keep it as the explicit "skip validation, don't ask why" escape hatch

Why this matters

Flagged during the expert review of PR #2798 (from code-reviewer):

expect_error: true slightly over-broad for state-machine business-rule tests. Sites like state-machine.yaml 329/366/408, measurement_terms_rejected.yaml 92, governance_denied.yaml 166, and invalid_transitions.yaml 60/177/250 send schema-valid payloads and expect a business-rule error. Treating these as negative steps means shape drift on those specific steps would slip by the lint.

Today the cost is acceptable because business-rule fixtures have other lints covering them (scoping, branch-sets, context-entity). But as the suite grows, the latent coverage gap compounds.

Scope

  • Update isNegativeStep in scripts/lint-storyboard-sample-request-schema.cjs to branch on negative_path
  • Extend universal/storyboard-schema.yaml to document the new attribute
  • Audit existing expect_error: true steps: tag them schema_invalid or business_rule as appropriate
  • Lint enforces sample_request validation for business_rule steps (expect ~15-20 new assertions to fire on existing fixtures, most resolvable)
  • Tests in tests/lint-storyboard-sample-request-schema.test.cjs expand to cover both code paths

Prior work

Metadata

Metadata

Assignees

No one assigned

    Labels

    claude-triagedIssue has been triaged by the Claude Code triage routine. Remove to re-triage.enhancementNew feature or requestschemaJSON Schema source-of-truth: definitions, codegen artifacts, validation, hygiene

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions