Skip to content

Wardline 01 08 pattern rules

8. Pattern rules

This section defines nine rules in two categories. Six pattern rules (WL-001 through WL-006) detect syntactic proxies for semantic violations in declared semantic contexts. Each takes the form: "if the application declares context X, then pattern Y is prohibited." Three structural verification rules (WL-007, WL-008, and WL-009) enforce invariants on declared boundary functions — they verify structural properties of the boundary itself rather than detecting patterns within annotated bodies. All nine rules are language-agnostic — they describe structural patterns whose danger depends on the declared semantic context, not on syntax alone. The distinction matters for conformance (§15): pattern rules and structural verification rules have different conformance criteria and may be implemented by different tools.

Living pattern catalogue. WL-001 through WL-006 are a starting vocabulary, not a closed set. Each pattern rule detects a syntactic proxy for a semantic violation, and models that learn to avoid flagged patterns will produce semantic equivalents — helper wrappers, conditional assignments, schema-level defaults, language-specific suppression idioms — that carry the same risk but do not match the original AST pattern. Language bindings MUST maintain version-tracked lists of semantic equivalents for each pattern rule, extending detection coverage as new evasion variants are identified. The precision/recall corpus (§11) SHOULD include evasion-variant specimens alongside standard specimens, so that detection of semantic equivalents is measured under the same precision floors as the base patterns. Conformance evidence for these catalogues is provided through the declared rule subset, regime documentation, and the golden corpus specimens that exercise evasion variants (§11, §15.2). The rule set evolves as model capability evolves: patterns that current models produce clumsily will be produced cleanly by future models, and the enforcement surface MUST track that trajectory deliberately rather than assuming a fixed pattern vocabulary.

Three categories of validity underpin the rule vocabulary:

  • Structural validity — machine-verifiable properties: field presence, type conformance, structural guarantee adherence. Enforcement tools can check these without human judgement.
  • Semantic validity — properties requiring human judgement: whether a default is institutionally appropriate, whether an exception translation preserves severity, whether a field mapping captures the intended meaning.
  • Authority restoration — properties requiring institutional permission: whether a serialised representation may be restored to a tier supported by available evidence, governed through restoration boundary declarations (§6.3).

Pattern rules operate at the structural-validity layer: they detect structural signals that correlate with semantic violations. They do not verify semantic correctness directly.

8.1 The rules

Rule Pattern Why It Is Dangerous
WL-001 Accessing a member (field or attribute) with a fallback default Buries a policy decision inside a data access idiom. Where member absence is meaningful (Tier 1 contexts), fabricating a default converts an integrity failure into silent data corruption. A missing field on an authoritative record or a missing attribute on an authoritative object is a structural anomaly, not an expected condition. In guarded contexts (Tier 3), the structural guarantee establishes field presence, so a fallback default is redundant — and redundant defaults mask defects when the guarantee changes. Language bindings map this single framework-level rule to language-specific patterns (e.g., .get() with default, getattr() with default, null-coalescing operators). This rule covers both field-level and attribute-level fallback defaults. Language bindings MAY split this framework rule into language-specific sub-rules where the target language has distinct access patterns for different member types (e.g., dictionary key access vs. object attribute access). When a binding splits WL-001, the sub-rules inherit WL-001's severity matrix entries as their default. Where the language-specific semantics that motivated the split create risks absent from the framework-level pattern, a sub-rule MAY establish its own matrix row that deviates from the inherited default. Such deviations MUST be documented in the binding's matrix with explicit rationale identifying the language-specific semantic risk. The §8.3 narrowing constraint applies to each sub-rule relative to its own documented matrix row, not relative to the parent framework rule's row. The binding documents the mapping between its sub-rules and this framework rule.
WL-002 Using existence-checking as a structural gate ERROR in all eight taint states, with exceptionability varying by context — UNCONDITIONAL where structural guarantees are declared, STANDARD elsewhere. In contexts where the wardline declares structural guarantees — Tier 1, Tier 2, Tier 3, and validated UNKNOWN states (UNKNOWN_GUARDED, UNKNOWN_ASSURED) — data structure is guaranteed by prior validation; existence-checking is unconditionally suspicious because it either masks a defect or is redundant noise. In EXTERNAL_RAW, UNKNOWN_RAW, and MIXED_RAW contexts — where structural guarantees have not been established — WL-002 fires as ERROR/STANDARD (governable), reflecting that existence-checking may be legitimate domain logic. The derivation principle: where the wardline declares structural guarantees, existence-checking is either redundant or masking a defect; where no guarantee is declared, the check may be legitimate and can be governed through the exception model. Architectural guidance: EXTERNAL_RAW (Tier 4) data processing should occur within validation boundaries where existence-checking is expected.
WL-003 Catching all exceptions broadly Prevents crashes but also prevents errors from being recorded. In high-assurance contexts, unrecorded errors are worse than crashes. The severity gradient in §8.3 reflects how much structural certainty the wardline has already established: broad catches are most severe where the context claims high trust, and less severe where raw external parsing legitimately encounters malformed input. See §8.4©.
WL-004 Catching exceptions silently (no action taken) Destroys evidence. The exception and its diagnostic context are lost. In Tier 1 code, every exception should be understood and handled specifically — a broad silent catch means you don't understand your own failure modes well enough to handle them individually, and that IS the integrity problem. In Tier 4 code, external input can throw anything — malformed payloads, encoding errors, network timeouts, API contract changes — and a silent catch on a specific parse attempt may be the correct defensive posture because you genuinely cannot enumerate all failure modes of external systems. The tier principle applies: T1/T2 is where you need surgical exception handling; T4 is the sandbox where defensive suppression of unpredictable external failures is expected practice. Note: a logger.warning() in a handler is noting that something happened, not resolving it — the exception is still swallowed and execution continues as if nothing went wrong. The log entry is a confession, not a remedy. See §8.4(g).
WL-005 Audit-critical writes inside broad exception handlers The audit write may fail silently, creating gaps in the legal record. The handler catches the audit failure along with everything else, and execution continues as though the record was written. This rule is narrower than WL-003: it is the audit-critical intersection of broad exception handling and Group 2 audit primacy, which is why its severity escalates to UNCONDITIONAL in ASSURED as well as INTEGRAL. See §8.4(e).
WL-006 Type-checking internal data at runtime Runtime type-checking is suspicious whenever the wardline already declares structural type guarantees for the data — authoritative tiers, validated tiers, and validated UNKNOWN states. In EXTERNAL_RAW and UNKNOWN_RAW, runtime type-checking is expected because the type guarantees have not yet been established. Where the system's own data is already supposed to be structurally sound, runtime type-checking suggests structural doubt about the artefact rather than legitimate input handling.
WL-007 Boundary with no rejection path A boundary function that accepts all input is structurally unsound. Applies to shape-validation boundaries (T4→T3), semantic-validation boundaries (T3→T2), combined-validation boundaries (T4→T2), and restoration boundaries (§6.3). The scanner enforces that at least one rejection path exists (§8.2). The expected nature of the rejection varies by boundary type — structural checks for shape validators, domain-constraint checks for semantic validators, evidence-appropriate checks for restoration boundaries — but the scanner enforces presence, not kind.
WL-008 Semantic validation without prior shape validation Data reaching a declared semantic-validation boundary (e.g., @validates_semantic in Python, @ValidatesSemantic in Java, or equivalent) whose inputs have not passed through a declared shape-validation boundary is structurally unsound. Applying domain-constraint checks to data whose field presence and type correctness have not been established may crash, produce misleading results, or silently operate on wrong types. This rule formalises invariant 3 from §6.2: shape validation MUST precede semantic validation.
WL-009 Tier 1 promotion on serialization path without restoration evidence A function declared @integral_read or @integral_construction (or binding equivalent) whose data source is a manifest-declared serialization boundary, and whose inputs do not trace through a declared restoration boundary within the two-hop analysis scope (§9.1). The function claims Tier 1 authority for deserialized data on assertion alone — the restoration model (§6.3) requires evidence-backed provenance claims, but no evidence is declared, no governance review of the restoration act is possible, and the scanner cannot verify that write-side invariants are re-established on read. This is the restoration-symmetry failure: construction paths include validation that serialization strips, and the read side re-stamps the deserialized representation as authoritative without re-verifying. WL-009 enforces that the restoration model is invoked, not that the evidence is sufficient — sufficiency remains a governance-reviewed claim (§13, residual risk 10). Language bindings MAY permit same-function composition only if that composition is explicitly legal in the binding's decorator-combination contract.
Pattern rules and structural verification rules with enforcement rationale

8.2 Structural verification

In addition to the pattern rules, structural verification requirements apply to validation boundary functions:

WL-007: Boundary functions MUST contain rejection paths. A function declared as a validation or restoration boundary that contains no conditional branching, no exception raising, and no early return is structurally unsound. A boundary that accepts all input is not a boundary. Under the four-tier model, shape-validation boundaries, semantic-validation boundaries, combined-validation boundaries, and restoration boundaries (§6.3) are all subject to WL-007. The scanner enforces presence of at least one rejection path, not the kind of check the rejection path performs. The expected nature of the rejection depends on the boundary type — shape validators are expected to reject on structural grounds (schema conformance, type correctness, field presence), semantic validators on domain-constraint grounds (value ranges, safety predicates, cross-field consistency), and restoration boundaries on grounds appropriate to their declared evidence categories — but this distinction is a review and governance concern, not a scanner enforcement target. Language bindings MAY define heuristic categories for structural vs domain-constraint checks as an advisory quality target, but such heuristics are not REQUIRED for WL-007 conformance.

WL-008: Semantic validation MUST be preceded by shape validation. This is an ordering constraint, not a body-content check. The scanner verifies that a declared semantic-validation boundary's inputs trace back to a shape-validation boundary's outputs. A combined validation boundary (e.g., @validates_external / @ValidatesExternal, T4→T2) satisfies this requirement internally because it performs both phases. Two separate functions MUST be ordered: shape validation MUST precede semantic validation on every data-flow path. In effective-state terms, WL-008 fires when any input to a declared semantic-validation boundary is in EXTERNAL_RAW, UNKNOWN_RAW, or MIXED_RAW. Inputs in states that already imply prior shape validation — GUARDED, ASSURED, INTEGRAL, UNKNOWN_GUARDED, and UNKNOWN_ASSURED — do not trigger WL-008.

WL-009: Tier 1 promotion paths through serialization boundaries MUST declare restoration evidence. This is a topology constraint, not a body-content check. The scanner cross-references @integral_read and @integral_construction annotations against manifest-declared serialization boundaries and @restoration_boundary annotations. WL-009 fires when all three conditions hold: (1) a function is declared @integral_read or @integral_construction (Group 1); (2) the function's data source is a manifest-declared serialization boundary (identified via BoundaryEntry objects in the overlay's boundaries array with serialization_boundary: true); (3) the function's inputs do not trace through a declared restoration boundary with sufficient evidence within the two-hop analysis scope (§9.1). Functions whose Group 1 decorator data source is not manifest-declared as a serialization boundary (e.g., in-memory Tier 1 reads) are not subject to WL-009. The manifest dependency is intentional: serialization boundaries are part of the trust topology and are declared in the manifest, not inferred by the scanner. Language bindings that permit same-function @integral_* + @restoration_boundary composition MUST document that as part of their binding contract; bindings that classify the combination as contradictory satisfy WL-009 only through an upstream traced restoration boundary.

A rejection path is formally defined as a control-flow path within the function body that terminates without producing the function's normal return value. The following constructs constitute rejection paths:

  • An exception-raising statement (raise in Python, throw in Java, or equivalent)
  • An early return preceded by a conditional guard (the guard establishes that some inputs are rejected)
  • A call to a function that unconditionally raises, if the called function is resolvable via two-hop call-graph analysis (§9.1). For WL-007, "resolvable" means the call target is statically identifiable by the binding as a named function or method, including a local alias to a statically identifiable target where the binding supports alias resolution. Targets that require dynamic dispatch, reflection, interface/protocol dispatch, higher-order callback resolution, or framework-generated indirection are outside the required minimum scope. Real validation commonly delegates through two layers (validator → schema library → actual check), and one-hop analysis generates false positives on structurally sound validators that use thin wrappers

The following do NOT constitute rejection paths:

  • An assertion statement (assert in Python, Java assert) — assertions may be disabled at runtime (-O in Python, -da in Java) and do not provide a reliable rejection mechanism in production. Language bindings MAY define additional constructs that are excluded from rejection-path analysis for analogous reasons
  • A null/None return without a preceding conditional — this is an unconditional return, not a rejection
  • A rejection path that is statically unreachable (e.g., guarded by if False: or equivalent constant-False expressions) — the scanner SHOULD detect trivially unreachable rejection paths (constant-False guards, if 0:, if "":) and treat them as absent. Full reachability analysis is not REQUIRED, but constant-expression guards are detectable via AST evaluation of literal nodes
  • A function body that unconditionally raises — this satisfies the literal "contains a rejection path" requirement but never produces a valid output. This is a degenerate case: the scanner SHOULD emit an advisory finding (distinct from WL-007) when a validation boundary function contains no success path

8.2.1 Structural-guarantee defaults and WL-001

WL-001 is SUPPRESS in EXTERNAL_RAW context. Tier 4 is the developer-freedom zone — boundary code legitimately uses .get(), getattr(), and similar patterns to handle optional fields, missing config keys, and malformed input. The enforcement activates when data crosses upward through a validation boundary, not at the T4 access site. However, external data processing must still distinguish between required fields (where absence is an error) and optional fields (where a default is institutionally approved). The declared-domain-default mechanism provides this governance within validation boundaries. It classifies fields on external data sources into three states:

Three-state field classification for external data (the Python worked example at Part II-A §A.8, Step 2 demonstrates this mechanism with schema_default() and overlay declarations):

  • Required — field absence is an error. WL-001 fires as ERROR/STANDARD (no change from current behaviour). A .get() with a fallback default on a required field remains a finding regardless of any declaration, because field presence is established by the structural guarantee, and its absence signals a data integrity problem.
  • Optional with approved default — field absence is expected by contract, and the default value is institutionally approved. WL-001 is SUPPRESS when three conditions are met simultaneously: (1) the field is declared as optional-by-contract in the wardline manifest or overlay boundary declaration, (2) the actual default in the code matches the declared approved default exactly, and (3) the access occurs within a declared validation or normalisation boundary — specifically, a shape-validation boundary (@validates_shape) or a combined validation boundary (@validates_external), since optional-field handling is a structural concern addressed during shape validation. Semantic-validation boundaries are excluded because optional-field handling is resolved during the T4→T3 or T4→T2 transition, not during the T3→T2 transition. Outside a declared boundary, the same access with the same default is still a WL-001 finding — the declaration is not a roaming licence to invent values wherever convenient.
  • Optional, no default — field may be absent, but the correct handling is explicit representation of absence (None/null/sentinel), not value substitution. WL-001 fires as ERROR/STANDARD if you default it. This covers the case analysed in GCBD §2.3 (the allergy-field example): a missing allergy field is expected to be represented as "unknown," not defaulted to an empty list.

This is a classification decision, not an exception. An exception says "this violation is acceptable"; an optional-field declaration says "this isn't a violation because the field's absence is expected and the default is institutionally approved." The distinction matters for governance: exceptions accumulate in the exception register and require periodic re-review; structural-guarantee declarations are part of the trust topology and are reviewed as part of the manifest.

Where declarations live. Optional-field declarations reside in the overlay's boundary declarations (§14.1.2), associated with a specific data source and validation boundary — they are properties of the data source's structural guarantee, not of individual functions. A partner feed's structural guarantee declares which fields are required, which are optional, and what the approved defaults are. Multiple functions may access the same optional field; the declaration is on the data source, not repeated at each access site. The enforcement tool checks that the code default matches the declared default — a .get() with a default that differs from the declared approved default is a stronger finding than a .get() with no declaration at all, because the developer has explicitly chosen a value the institution didn't approve. For a given data source and field, approved defaults SHOULD be unique across boundaries — a feed that carries different approved defaults depending on which parser touched it indicates an undeclared variance. If boundary-specific semantic variants are genuinely required, the manifest MUST explicitly declare the variance with documented rationale; implicit divergence (two boundaries declaring different defaults for the same field on the same source) is a manifest validation error.

Mismatch severity. When a .get() uses a default value that differs from the declared approved default for that field, the finding severity is ERROR/UNCONDITIONAL. This is more severe than the undeclared case (ERROR/STANDARD) because the mismatch represents a direct contradiction between the code and the institutional policy — someone has either made an error or is circumventing the declared structural guarantee.

Third-party library models. When an application imports a Pydantic model, dataclass, or equivalent type definition from a third-party library and uses it in a tier-classified flow, the library's schema defaults become governance-relevant under this section. The library maintainer chose those defaults for general-purpose API ergonomics — not for the application's institutional context. The correct response is not to modify the library or to write individual optional_fields overlay entries for every field. Instead, the application should either (a) wrap the library model in an application-owned validation boundary that constructs the tier-appropriate representation, treating the library model as a data transfer format rather than a governance artefact, or (b) review the library model's defaults as a batch using the schema_defaults_reviewed field in the dependency_taint declaration (§14.1.2), which suppresses per-field findings for models whose defaults have been assessed and accepted at the governance level. Option (a) is architecturally preferred — it places the validation boundary within the application's governance perimeter. Option (b) is a governance expedient for cases where wrapping every library model is disproportionate to the risk.

Governance visibility. Approved structural-guarantee default declarations are tracked in the fingerprint baseline (§10.2) as a distinct change category alongside annotation changes. A new optional-field declaration on a security-relevant field — declaring security_classification as optional with default "OFFICIAL" — is a high-risk classification decision that warrants the same governance scrutiny as a tier-escalation declaration. The fingerprint baseline makes it visible; the governance model ensures it's reviewed.

8.3 Severity matrix

How to read this matrix. Each cell encodes severity / exceptionability as a two-letter code (see key below the next paragraph). Suppress severity always pairs with Transparent exceptionability (Su/T). Read across a row to see how a single rule varies by context; read down a column to see how a single context treats all rules. The exceptionability classes are defined in §10.1.

WL-007, WL-008, and WL-009 are structural verification rules (not pattern rules) and apply only to declared boundary functions, but are shown in the matrix for completeness. Their severity is UNCONDITIONAL across all contexts because they are framework invariants rather than context-dependent judgements.

Matrix key. Severity: E = Error (wrong in this context), W = Warning (suspicious), Su = Suppress (expected). Exceptionability: U = Unconditional, St = Standard, R = Relaxed, T = Transparent.

Rule Pattern Integral Assured Guarded Ext. Raw Unk. Raw Unk. Guarded Unk. Assured Mixed Raw
WL-001 Member access with fallback default2 E/U E/St W/R Su/T Su/T W/R E/St Su/T
WL-002 Existence-checking as structural gate E/U E/U E/St Su/T Su/T E/St E/St Su/T
WL-003 Catching all exceptions broadly E/U E/St W/St W/R E/St W/St W/St E/St
WL-004 Catching exceptions silently E/U E/St W/St W/R E/St W/St W/St E/St
WL-005 Audit-critical writes in broad handlers E/U E/U E/St E/St E/St E/St E/St E/St
WL-006 Runtime type-checking internal data E/St W/R W/R Su/T Su/T W/R W/R W/St
WL-007 Validation with no rejection path E/U E/U E/U E/U E/U E/U E/U E/U
WL-008 Semantic validation without shape validation E/U E/U E/U E/U E/U E/U E/U E/U
WL-009 Integral-read without restoration evidence E/U E/U E/U E/U E/U E/U E/U E/U
Severity matrix showing rule severity and exceptionability by taint state

Binding-level matrix deviations. Language bindings MAY modify individual cells where the target language's type system structurally prevents a violation class. Binding-level deviations MUST narrow severity or exceptionability relative to the framework matrix, not widen them. A binding MUST NOT assign higher severity or more restrictive exceptionability than the framework matrix specifies for the same cell. For example, the Java binding (Part II-B §B.4.4) changes WL-002 in GUARDED from E/U to Su/T for records, because Java records guarantee complete construction. Such deviations MUST be documented in the binding's matrix with explicit rationale. See §12 (language evaluation criteria) for the general principle governing binding-level deviations. When a binding splits a framework rule into sub-rules under §8.1, each sub-rule's documented matrix row is the baseline for §8.3 conformance — the narrowing constraint applies relative to the sub-rule's own matrix, not relative to the parent framework rule.

8.4 Worked examples

Subsections 8.4 and 8.5 are non-normative. They explain the reasoning behind the severity matrix but do not impose additional requirements on implementations.

Six pressure-point cells illustrate the matrix's reasoning:

(a) WL-001 is SUPPRESS in EXTERNAL_RAW. Tier 4 is the sandbox, not the vault. Code at the external boundary legitimately uses fallback defaults — .get("timeout", 30) in a CLI parser, config.get("region", "us-east-1") in a config loader — because that IS the boundary processing. The language permits these patterns for valid reasons. The tier system carves out the code paths (T1 and T2) where those patterns are not permitted.

The enforcement activates when data crosses a boundary upward, not at the T4 access site. A fabricated default at T4 that survives the T4→T3 shape-validation boundary and the T3→T2 semantic-validation boundary is caught by the boundary transition rules (WL-007, WL-008) at the crossing point — the validation boundary is where field-presence requirements are enforced. The declared-domain-default mechanism (§8.2.1, schema_default()) provides a governed path for optional fields with approved defaults within those boundaries.

Where domain policy requires enforcement of specific default values on external data — e.g., preventing agents from spraying .get("security_classification", "OFFICIAL") — the governed mechanism is the optional-field declaration in §8.2.1, not the WL-001 severity at T4. The declaration says "this field's default is institutionally approved"; a .get() with a different default is a §8.2.1 mismatch finding (ERROR/UNCONDITIONAL), which is a stronger signal than a blanket WL-001 ERROR on every .get() in every loader.

(b) WL-002 is ERROR/STANDARD in GUARDED but SUPPRESS in EXTERNAL_RAW. Guarded data has passed structural validation — field presence and type correctness are guaranteed by the T4→T3 transition. An if "field" in record: check on guarded data is suspicious: the structural guarantee establishes that the field exists, so the check is either redundant (masking dead code) or indicates that the structural guarantee is not trusted. ERROR/STANDARD is appropriate — wrong by default, but overridable through the governance model for cases where the existence check serves a purpose beyond structural gating (e.g., duck-type dispatch in AST analysis code). In EXTERNAL_RAW (Tier 4), existence-checking IS the validation mechanism — "key" in raw_data before accessing it is exactly how boundary code validates structure. SUPPRESS reflects that this pattern is expected, not suspicious, at T4.

© WL-003 is WARNING/STANDARD in GUARDED but WARNING/RELAXED in EXTERNAL_RAW. Guarded data has known structure, so broad exception catching is more suspicious — structural uncertainty has been resolved, and the remaining exceptions are more likely to signal semantic or logic errors that should surface. But the data's values are still unchecked, and semantic-validation logic may legitimately use broad catches as a defensive measure while checking domain constraints. Hence WARNING rather than ERROR — suspicious, not categorically wrong. In EXTERNAL_RAW, broad exception catching during parsing is tolerable (external data is expected to be malformed), so RELAXED governance suffices.

(d) WL-006 is WARNING/RELAXED in GUARDED but SUPPRESS in EXTERNAL_RAW. The structural guarantee has confirmed types at Tier 3, so runtime type-checking is partially redundant — similar to ASSURED. But the data hasn't been semantically vetted, so the suspicion is lower than in authoritative contexts. In EXTERNAL_RAW, type-checking is expected and appropriate (we don't know the types yet), so SUPPRESS.

(e) WL-005 is UNCONDITIONAL in ASSURED. Audit-critical writes inside broad exception handlers in assured (Tier 2) contexts destroy diagnostic context for data transformation errors. The audit write records the transformation; the broad handler catches the audit failure alongside processing errors. Because assured data feeds downstream consumers, lost audit context for transformation errors is an integrity failure regardless of justification.

(f) UNKNOWN_RAW vs EXTERNAL_RAW across all rules. UNKNOWN_RAW is consistently more severe than EXTERNAL_RAW across the exception-handling rules (WL-003, WL-004) and the access-pattern rules (WL-001, WL-002). Two reasons: First, UNKNOWN_RAW masks dangerous ambiguity — the code does not know whether the data is malformed external input or corrupted internal state, and a broad catch or silent default prevents the distinction from surfacing. Second, UNKNOWN_RAW data can reach Tier 1 code via internal paths without crossing a declared T4→T3 validation boundary. EXTERNAL_RAW data must traverse the boundary chain; UNKNOWN_RAW may already be inside the perimeter. The stricter posture at UNKNOWN_RAW compensates for this shorter path to authoritative code.

(g) WL-003 and WL-004 tier gradient for exception handling. In Tier 1 code, every exception should be understood and handled specifically. A broad except Exception: (WL-003) or a silent except: pass (WL-004) in authoritative code means the developer does not understand the failure modes well enough to handle them individually — and that lack of understanding IS the integrity problem the rule is designed to surface. The astronaut whose engine fires them into deep space won't feel any better knowing that the programmer was "inside a trusted boundary so they could do whatever they wanted." In Tier 4 code, external systems throw unpredictable exceptions — malformed JSON, encoding errors, network timeouts, API contract changes that produce novel exception types. A broad catch at T4 is often the correct defensive posture because you genuinely cannot enumerate all failure modes of systems you don't control. The tier principle applies: enforcement is strictest where the consequences are highest (T1) and most relaxed where the input is most unpredictable (T4). A handler that logs and continues (logger.warning(...) inside except Exception:) is still swallowing the exception — the log entry records that something happened, but execution continues as if nothing went wrong. Logging is not handling. The distinction matters because WL-003 exempts handlers that immediately re-raise (except Exception: raise) — those are genuinely not swallowing the exception. But except Exception: logger.warning(...); return fallback continues execution with fabricated state, which is the exact pattern WL-003 and WL-004 exist to flag.

8.5 Derivation principles

Four principles govern the matrix:

The tier boundary principle. These boundaries exist because the language permits these patterns for valid reasons. The tier system carves out code paths where those patterns are not permitted. Rules protect the integrity of high-tier code paths, not the hygiene of low-tier code. Tier 4 (EXTERNAL_RAW) is the developer-freedom zone — any language idiom is correct there. Enforcement activates when data crosses a boundary upward. A .get("timeout", 30) in CLI parsing is fine; that value reaching a T1 decision path without passing through a validation boundary is the violation. This creates an economic incentive: promoting data to a higher tier (via validation boundaries) removes findings; leaving raw data in hot code paths is expensive.

Severity answers "is this pattern correct in this context?" ERROR means the pattern is wrong regardless of intent — the wardline has declared that this context prohibits it. WARNING means the pattern is suspicious and should be reviewed. SUPPRESS means the pattern is expected or harmless in this context (e.g., type-checking external data at runtime is expected, not suspicious). Severity scales with the consequence of the pattern at that tier, not with the mere presence of the pattern.

Exceptionability answers "can a human override this finding?" UNCONDITIONAL means the finding cannot be overridden — it is a project invariant, hardcoded in the wardline. STANDARD means the finding is wrong by default but overridable through the governance model (§10) with documented rationale, reviewer identity, and expiry. RELAXED means lighter governance burden — warning-level findings that can be acknowledged with less ceremony.

Distribution. Of the 72 cells, 31 (43%) are UNCONDITIONAL — project invariants that are not configurable.1 These represent the non-negotiable core of the wardline: patterns that are always wrong in their declared context regardless of justification. The remaining cells are governable, with the governance burden proportional to severity.

WL-002 (existence-checking as a structural gate) is UNCONDITIONAL in INTEGRAL and ASSURED — contexts where the wardline declares full structural and semantic guarantees. In GUARDED, UNKNOWN_GUARDED, and UNKNOWN_ASSURED, it is ERROR/STANDARD: existence-checking is suspicious (structural validation should have established field presence), but overridable through the governance model for cases like AST duck-type dispatch. In EXTERNAL_RAW, UNKNOWN_RAW, and MIXED_RAW, it is SUPPRESS: existence-checking IS the validation mechanism at T4 — "key" in data before accessing it is exactly how boundary code validates structure. The derivation principle: the severity of existence-checking scales with how much structural certainty the tier provides; where the tier guarantees structure, the check is redundant; where it doesn't, the check is the correct practice.

WL-006 (runtime type-checking of internal data) ranges from ERROR/STANDARD in INTEGRAL contexts to SUPPRESS/TRANSPARENT in EXTERNAL_RAW and UNKNOWN_RAW — type-checking external data at runtime is expected and appropriate, while type-checking the system's own authoritative data suggests structural doubt. GUARDED and the UNKNOWN validated states occupy the middle ground at WARNING/RELAXED — type correctness has been structurally confirmed, making runtime type-checking suspicious but not as alarming as in authoritative contexts.

WL-007 (validation boundary structural verification), WL-008 (semantic validation ordering), and WL-009 (restoration symmetry) are all UNCONDITIONAL across all eight states. A validation function that contains no rejection path is structurally unsound regardless of context. Semantic validation applied to structurally unverified data is a category error regardless of context. A Tier 1 promotion function (@integral_read or @integral_construction) on a serialization path without declared restoration evidence is a topology deficiency regardless of context. These are framework invariants, not context-dependent judgements.

8.6 Taint analysis scope

Taint analysis for tier-flow enforcement is scoped to explicit flows (data dependencies) only. An explicit flow occurs when data from one tier is directly assigned, passed, or returned to a sink expecting a different tier. Implicit flows (control dependencies — where the mere fact that a branch was taken leaks information about a tier-classified value) are noted as a tool quality target for future enforcement but are not required by this framework. The rationale: explicit-flow taint analysis is tractable for static analysis at acceptable scaling cost; implicit-flow analysis requires substantially more sophisticated tooling and may not be achievable within the precision floors defined in §11.

The taint-state model has eight effective states (§6.1) and 36 unique state-pairs in the join table. The trust ordering has a maximum chain height of five (MIXED_RAW/raw-context bottom through the validated chain to INTEGRAL); the join-induced order is shallower because cross-classification merges collapse directly to MIXED_RAW. For fixed-point convergence in dataflow analysis, the relevant height is that of the join-semilattice — each variable can change state at most a small constant number of times before reaching a fixed point. The scaling impact is linear in the number of effective states and does not change the fundamental tractability of explicit-flow analysis.3

8.6.1 Implicit-flow evasion heuristic

Full implicit-flow analysis is not required by this framework (see above), but the dominant implicit-flow evasion pattern is detectable with a simple heuristic. Enforcement tools SHOULD flag functions that contain both (a) a conditional branch whose predicate depends on a tier-classified value and (b) an assignment in both branches to the same target variable. This pattern — branching on a tier-classified value and writing content to a new, untainted variable in each branch — launders the tier-classified information into an untracked variable without any explicit data flow from the classified source. The heuristic catches the 80% case (the conditional-assignment pattern that models produce naturally when optimising for plausibility) without requiring the full implicit-flow analysis that would compromise the precision floors defined in §11. Findings from this heuristic SHOULD be classified as WARNING/STANDARD — suspicious and worth reviewing, but not ERROR, because legitimate conditional logic on tier-classified values is common (e.g., routing decisions based on data tier).

Field sensitivity as a binding capability. The join_fuse / join_product distinction (§6.1) enables bindings to implement field-level taint tracking for named product types — dataclasses, records, POJOs, and equivalent structures where field membership is statically resolvable. When a binding implements the MIXED_TRACKED extension state, taint analysis operates at field granularity within product-type composites: each field retains its individual taint state rather than collapsing to MIXED_RAW at the composite level. This reduces false-positive volume on container types without weakening the conservative join for genuinely fused artefacts (string concatenation, dict merge, format-string interpolation). Bindings that do not implement field sensitivity apply join_fuse semantics uniformly — the conservative fallback that the framework mandates as the default. The framework does not prescribe a field-sensitivity algorithm; bindings declare which product types they track and demonstrate precision through their golden corpus.


  1. Counted from the 9×8 framework matrix above: WL-001 (1) + WL-002 (2) + WL-003 (1) + WL-004 (1) + WL-005 (2) + WL-006 (0) + WL-007 (8) + WL-008 (8) + WL-009 (8) = 31. Language bindings that split framework rules into multiple binding rules will have a larger matrix with a different count. The Python binding (Part II-A §A.3) splits WL-001 into PY-WL-001 and PY-WL-002, producing an 80-cell matrix with 32 UNCONDITIONAL cells — the additional cell is PY-WL-002's INTEGRAL entry, inherited from WL-001. 

  2. WL-001's matrix cells describe the default framework severity. Optional-field suppression for approved defaults within shape-validation and combined-validation boundaries is specified in §8.2.1. 

  3. An earlier three-tier version of this framework (without the Tier 2 / Tier 3 distinction) had six effective states and 21 join pairs. The four-tier model adds two states and extends the trust ordering by one level — modest scaling cost for substantially finer enforcement granularity.