Validation Rules
This page consolidates all validation rules from the Specification into an ordered checklist for implementers. Rules are grouped by the stage at which they should be enforced.
A refusal is reported under a code of the core error registry: the rules below name the code where one is specific to them, and a fault of the method that no sharper code names is method.schema_violation. Each refusal is one item of the verdict's errors, and a run refused because its method does not validate is the problem method.invalid, carrying the same items (Validating a bundle). The Error Codes page lists every method. code.
Before Parsing: The Method Tree
Before any file is parsed, establish the method tree the files come from:
- Find the root from what the caller handed over: the nearest directory at or above it holding
METHODS.toml, else the directory handed over, else, for a.mthdsfile named alone, a tree of that one file. - List the tree under the membership rule, with every pattern normalized to NFC, pruning excluded directories first, among them a nested repository or a nested package, meaning a directory below the root holding an entry named
.gitof any kind, whatever it holds, or its ownMETHODS.toml, and leaving out a run's inputs file, a regular file or a symbolic link namedinputs.jsonorinputs.tomlat any depth, while a directory of either name excludes nothing. Refuse a symbolic link or any other entry that is neither a regular file nor a directory, naming its path, unless the rule excludes it, and refuse in the same way a.gitignorefile the listing reads whose bytes are not UTF-8, once a leading byte order mark is skipped. A package read from a cache is not listed again: its files are its tree as written. - Normalize every name to NFC, and refuse a path that breaks the tree path rules, two names equal after normalization, two paths that collide, or one colliding with a directory of another, under the Unicode full case folding the tree path rules define, and a path past the length bound, naming each path. A name the membership rule excludes is never checked, so an invalid one is excluded silently.
- When the tree arrives as a map of file entries rather than from a directory, refuse a key that is not a tree path, such as one not already in NFC, or that the membership rule could not admit, such as one whose last segment is
inputs.jsonorinputs.toml; a key given twice in the map or inside an entry object; two keys that collide, or one colliding with a directory of another; a malformed entry, such as non-canonical Base64 or asizewritten with a fraction or an exponent; and a hash entry whose bytes the receiver does not hold.
Stage 1: TOML Parsing
Before any MTHDS-specific validation, the file must be valid TOML.
- The file MUST be valid UTF-8-encoded TOML.
- A
.mthdsfile MUST have the.mthdsextension. METHODS.tomlMUST be named exactlyMETHODS.toml.methods.lockMUST be named exactlymethods.lock.
Stage 2: Bundle Structural Validation
After parsing TOML into a dictionary, validate the bundle structure:
domainMUST be present.domainMUST be a valid domain code: one or moresnake_casesegments ([a-z][a-z0-9_]*) separated by..main_pipe, if present, MUST besnake_caseand MUST reference a pipe defined in the same bundle.- Concept codes MUST be
PascalCase([A-Z][a-zA-Z0-9]*). - Concept codes MUST NOT match any native concept code (
Dynamic,Text,Image,Document,Html,TextAndImages,Number,YesNo,Choice,Rating,Date,Time,Page,JSON,SearchResult,Anything,Composite). - Pipe codes MUST be
snake_case([a-z][a-z0-9_]*). - Input names MUST be plain
snake_caseidentifiers ([a-z][a-z0-9_]*), never dotted. Any other input name MUST be rejected, and the refusal SHOULD be reported asmethod.identifier_invalid. refinesandstructureMUST NOT both be set on the same concept.
Stage 3: Concept Field Validation
For each field in a concept's structure:
descriptionMUST be present.- If
typeis omitted,choicesMUST be non-empty. type = "dict"requires bothkey_typeandvalue_type.type = "concept"requiresconcept_refand forbidsdefault_value.type = "list"withitem_type = "concept"requiresitem_concept_ref.concept_refMUST NOT be set unlesstype = "concept".item_concept_refMUST NOT be set unlessitem_type = "concept".default_valuetype MUST match the declaredtype.- If
choicesis set anddefault_valueis present,default_valueMUST be inchoices. - Field names MUST NOT start with
_.
Stage 4: Pipe Type-Specific Validation
Each concrete pipe type has specific rules. A typeless [pipe.<code>] section is valid only when it is a contract-only signature containing description, optional inputs, required output, optional signature_for (when present, one of the concrete PipeType values), and no implementation fields.
PipeLLM:
- All prompt and system_prompt variables MUST have matching inputs.
- All inputs MUST be referenced in prompt or system_prompt.
PipeStructure:
inputsMUST contain exactly one entry.- The single input concept MUST be
Textor a concept that refinesText. outputMUST NOT beTextand MUST NOT be a concept that refinesText.outputMAY use multiplicity (Foo,Foo[],Foo[N]).
PipeFunc:
function_nameMUST be present and, once normalized to NFC, MUST be an import path holding at least one dot, each dot-separated segment a Python identifier that is not a keyword and that NFKC normalization leaves unchanged, so a segment holding a ligature such asfior a fullwidth letter is rejected.- Given the method tree, the module path, everything before the last dot, MUST name a module of the tree:
<module path>.pyor<module path>/__init__.py, each dot read as/, and not both. See PipeFunc.
PipeImgGen:
promptMUST be present.- All prompt and negative_prompt variables MUST have matching inputs.
- All inputs MUST be referenced in prompt or negative_prompt.
PipeExtract:
inputsMUST contain exactly one entry.outputMUST be"Page[]".
PipeSearch:
promptMUST be present.- All prompt variables MUST have matching inputs.
- All inputs MUST be referenced in prompt.
outputMUST beSearchResultor a concept that refinesSearchResult.
PipeJudge:
promptMUST be present and non-empty.- Exactly one of
questionorquestionsMUST be present. A pipe carrying neither is rejected, and the message SHOULD say thatpromptholds the evidence and the question is written inquestion. question, and thequestionof every entry ofquestions, MUST be non-empty.- All prompt and question variables MUST have matching inputs.
- All inputs MUST be referenced in
promptor in a question. - A question variable MUST NOT reach an
Imageor aDocument, single or a list; onlypromptpresents files. optionsandlevelsMUST NOT both be present on one question.optionsMUST hold at least two entries with non-empty keys.levelsMUST hold at least two levels, each a non-empty string or a table carrying a non-emptylabel, a non-emptydescriptionor both and no other key; either every level carries alabelor none does, and no two labels of one scale are equal.criteriaandthresholdMUST NOT be present besideoptionsorlevels.criteriaMUST carry bothyesandno, each non-empty, and no other key; a table declaring one side is rejected, and the message SHOULD suggest writing the missing side as the complement of the other.thresholdMUST be strictly between 0 and 1.- With
question,outputMUST beYesNo(nooptionsorlevels),Choice(options) orRating(levels), or a concept that refines it, and MUST NOT use multiplicity brackets. - With
questions, there MUST be at least one question, each key a valid structure field name, and each question table MUST carryquestionand onlyoptions,levels,criteriaandthresholdbeside it; those four MUST NOT be present on the pipe itself.outputMUST be a structured concept without multiplicity brackets whose fields are exactly the question names, each declaredtype = "concept"with aconcept_refnaming the verdict native of its question's kind or a concept that refines it.
PipeCompose:
- Exactly one of
templateorconstructMUST be present. outputMUST NOT use multiplicity brackets ([]or[N]).- All template/construct variables MUST have matching inputs.
- All inputs MUST be referenced in the template, or in construct mode by a
frompath or a field template, nested constructs included.
PipeSequence:
stepsMUST have at least one entry.nb_outputandmultiple_outputMUST NOT both be set on the same step.batch_overandbatch_asMUST either both be present or both be absent.batch_overandbatch_asMUST NOT be the same value.- A dotted
batch_overMUST follow the path grammar, or the step is rejected asmethod.binding_step_invalid. Its walk MUST derive a list, and one deriving a single value is reported as abatch_overnaming a value that is not a list (see Dottedbatch_over). - A step MUST carry exactly one of
pipeandfrom. A step carrying both is rejected asmethod.binding_step_invalid, and a step carrying neither, such as{ result = "x" }, is rejected by the schema asmethod.schema_violation, since no sharper code names it. - A binding step (one carrying
from) MUST carryresultand MUST NOT carrynb_output,multiple_output,batch_overorbatch_as. ItsresultMUST take the form of a plain input name (Stage 2, rule 7), and itsfromMUST follow the path grammar. Each of these faults ismethod.binding_step_invalid. - A binding step's path MUST be walkable through the declared structures, or the step is rejected as
method.field_path_unresolved. The walk starts from the concept and multiplicity of the latest value stored under the root's name before the step, in step order, and from those the sequence'sinputsdeclare only when no earlier step stored that name (see The Concept of the Result). - A pipe step's
resultandbatch_asare stored names, and each MUST take the form of a plain input name (Stage 2, rule 7), as a binding step'sresultdoes. The schema rejects a name in any other form, such asPagesora.b, and the refusal SHOULD be reported asmethod.identifier_invalid(see Stored Names). - A pipe step's plain
batch_overMUST NOT start with_bound_, a prefix reserved for the private names under which an implementation binds a dottedbatch_over. The schema rejects such a name, and the refusal SHOULD be reported asmethod.identifier_invalid.
PipeParallel:
branchesMUST have at least one entry.- Every branch MUST be a pipe step. A binding step in
branchesis rejected asmethod.binding_step_invalid. - A branch MUST NOT carry a dotted
batch_over, which is a binding followed by a batch, and one that does is rejected asmethod.binding_step_invalid. A plainbatch_overon a branch is allowed (see Dottedbatch_over). - A branch's
resultandbatch_asMUST take the form of a plain input name, and its plainbatch_overMUST NOT start with the reserved prefix_bound_. A branch that breaks either rule is rejected asmethod.identifier_invalid, as on a sequence step. outputMUST beCompositeor a structured concept.outputMUST NOT use multiplicity brackets ([]or[N]).- For structured output, required fields MUST be produced by matching branch
resultnames and branch output concepts MUST be compatible with the corresponding fields.
Optionality:
- Presence markers (
?and!) MAY appear only on pipeinputsandoutput. - Presence markers MUST NOT be combined with multiplicity brackets.
!MUST NOT appear onoutput.- A
PipeConditionthat can resolve tocontinueMUST declare an optional (?) output. - A template-rendering pipe MUST guard references to declared-optional inputs.
- A maybe-absent
PipeParallelbranch MUST NOT feed a required structured output field.
PipeCondition:
- Exactly one of
expression_templateorexpressionMUST be present. outcomesMUST have at least one entry.
PipeBatch:
input_list_nameMUST be a plain input name.input_list_nameMUST be ininputs.input_item_nameMUST NOT be empty.input_item_nameMUST NOT equalinput_list_name.input_item_nameMUST NOT equal any key ininputs.input_item_nameMUST take the form of a plain input name, and one that does not is rejected asmethod.identifier_invalid, which the schema catches (see Stored Names).
Stage 5: Reference Validation (Bundle-Level)
Within a single bundle:
- Bare concept references MUST resolve to: a native concept, a concept in the current bundle, or a concept in the same domain (same package).
- Bare pipe references MUST resolve to: a pipe in the current bundle, or a pipe in the same domain (same package).
- Domain-qualified references MUST resolve within the current package.
- Cross-package references (
->syntax) are deferred to package-level validation.
Stage 6: Manifest Validation
For METHODS.toml:
[package]section MUST be present.nameMUST be present,snake_case, and 2–25 characters.addressMUST match the pattern^[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+/[a-zA-Z0-9._/-]+$.versionMUST be valid semver.descriptionMUST NOT be empty.main_pipe, if present, MUST be a validsnake_casepipe code.- All dependency aliases MUST be unique and
snake_case. - All dependency addresses MUST match the hostname/path pattern.
- All dependency version constraints MUST be valid.
- Domain paths in
[exports]MUST be valid domain codes and MUST NOT use reserved domains (native,mthds,pipelex). - All pipe codes in
[exports]MUST be validsnake_case. exclude, if present, MUST be an array of strings, none holding a line break.
Stage 7: Package-Level Validation
After loading all bundles and resolving dependencies:
- Bundles MUST NOT declare a domain starting with a reserved segment.
- Cross-package references MUST reference known dependency aliases.
- Cross-package pipe references MUST target pipes exported by the dependency — diagnosed at validation time, never silently filtered at load time.
- Exported pipes MUST exist in the scanned bundles.
- Same-domain concept and pipe code collisions across bundles (within one package) are errors.
- Every file reference, normalized to NFC, MUST resolve, lexically and inside the tree, to a file of the tree, and a reference that does not is refused by the first step of the resolution that fails: as malformed, such as an absolute one, as not a tree path, such as an empty one, as leaving the tree, or as not in the tree, naming a file the tree does not hold, a directory of the tree or its root. A reference refused as not in the tree names the reference and the path it resolved to,
.for the root, and the rule that excluded the file when one did. A URL in a template that names a scheme, holds an authority or has no path is not a file reference, and is left as written. - Every
PipeFunc's module MUST be a module of its tree, as Stage 4 states. - A
requirements.txtat the root MUST hold only blank lines, comments and PEP 508 requirements with no URL, and no option (seerequirements.txt).
Stage 8: Lock File Validation
For methods.lock:
- Each entry's
versionMUST be valid semver. - Each entry's
digestMUST matchsha256:[0-9a-f]{64}. An entry carrying ahashfield predates tree digests: refuse the lock with a message saying thatlockregenerates it, and verify nothing. - Each entry's
fingerprintMUST be a 64-character lowercase hex digest. - Each entry's
sourceMUST start withhttps://and end with.git. - Each entry's
commitMUST be a 40-character lowercase hex digest.