Skip to content

Validation rules V1–V23

The two-tier composition model adds 23 named validation rules to the gateway. V1–V16 and V19–V23 fire at config load (mcp-flowgate check surfaces them all); V17–V18 fire at runtime as audit events.

A CI parity scanner (scripts/check-validation-parity.sh) enforces that every rule has at least one accepts test AND one rejects test, by naming convention. The full catalog:

RuleCheckTierOutcomeDetection
V1verb: is one of the 24 cap-verb tokenscaperrorload
V2definitionId matches cap.<verb>.<name> and verb segment agrees with declared verb: fieldcaperrorload
V3snippet: block presentcaperrorload
V4snippet: block has BOTH inputs: AND outputs: keys (may be {})caperrorload
V5Each input/output entry is JSON-schema-shapedcaperrorload
V6Primary-executor verb-shape: cognitive→mcp/noop, deterministic→script/mcp, gate→human actor, coordinate→mcpcaperrorload
V10Capability MAY NOT invoke another workflow via kind: workflowcaperrorload
V17Output value matches declared snippet.outputs schemacapruntime audit event cap.output.schema_violation; executor returns SchemaViolationruntime
V18Capability abnormal termination → cap.terminated audit event with error_kind + parent_correlation_id, no partial output projectioncapruntime audit eventruntime
RuleCheckTierOutcomeDetection
V7definitionId matches flow.<name>flowerrorload
V8snippet: block ABSENT (orchestrators are entry points, not composable snippets)flowerrorload
V9verb: field ABSENTflowerrorload
V11Orchestrator MAY NOT invoke another orchestrator via kind: workflowflowerrorload
RuleCheckOutcomeDetection
V12kind: workflow executor targeting cap.* without use: blockerrorload
V13use:.inputs paths resolve to slot table (Check A — reachability)errorload
V14use:.outputs writes to a slot already typed differently (Check B — type consistency)errorload
V15expects_contract_hash matches the loaded capability’s actual hasherrorload
V16use: block omits expects_contract_hash for a stable-lifecycle targeterrorload
RuleCheckOutcomeDetection
V19Repo manifest declares schema: flowgate.repo/v1errorload
V20Two repos declare the same namespace:errorload
V21A single repo defines the same definitionId in two fileserrorload
V22A kind: workflow reference is unprefixed and the resolved (namespace-prefixed) id doesn’t exist in any loaded repoerrorload
V23Host config shadows a repo-provided id without listing it in top-level overrides:errorload
Terminal window
mcp-flowgate check --config gateway.yaml

Errors abort startup. Warnings print but allow startup. Each error names the rule code that fired, the workflow / state / transition that triggered it, and the expected shape — paste-fixable without spelunking.

For the rule-by-rule semantics with worked examples, see:

Adding a new validation rule? The parity scanner forbids one-sided coverage:

Terminal window
bash scripts/check-validation-parity.sh

For every rule V1–V23, the scanner asserts there’s at least one fn vN_accepts_* test AND at least one fn vN_rejects_* test in either crates/mcp-flowgate-core/tests/ or crates/mcp-flowgate-executors/tests/. CI runs this before cargo test --workspace, so missing coverage fails the build with a named gap list.

If you’re adding V24, you add the rule, the validator, an accepts test (fn v24_accepts_<topic>), and a rejects test (fn v24_rejects_<topic>) — and the scanner stops complaining.