Closed-World Semantics: Silence Must Mean Checked
The False Negative Trap
Consider a standard compliance check for financial data. You have a rule: “All transactions over $10,000 must have a manager approval ID.” You write a verifier that scans the JSON payload. It finds the transaction amount. It checks for the presence of the `approval_id` field. If the field exists, it passes. If the field is missing, it flags an error. This seems correct. It is not. The failure mode occurs when the schema allows optional fields. If the `approval_id` field is defined as optional in the schema, and the input omits it entirely, a naive verifier might skip the check. It sees no value, so it has nothing to validate. It returns silence. Under open-world assumptions, silence means “unknown.” Perhaps the approval was granted via a separate channel. Perhaps the field was omitted by mistake. The verifier does not know. It did not fail. It did not pass. It did nothing. In a closed-world system, this silence is a violation. The rule states that transactions over $10,000 must have an ID. The absence of the ID is not unknown; it is false. The condition is not met. The verifier must explicitly record this absence as a failure. It must say: “Expected `approval_id` for transaction ID 123. Field absent. Violation.” This requires a structural change in how you write checks. You cannot just check values. You must check the existence of the checks themselves. Every rule in your pipeline must have a corresponding assertion that fires if the target data is missing. If the data is missing, the rule did not run. If the rule did not run, the verification is incomplete. An incomplete verification is a failed verification. This approach eliminates the class of errors where a missing field is treated as compliant because it was not explicitly invalid. It forces the verifier to be exhaustive. It also exposes gaps in your rule set. If you have a rule that never fires because the data structure never provides the expected field, you have a dead rule. Dead rules indicate a mismatch between your policy and your data model. You must fix one or the other.
Defining the Closed World
Not everything can be closed. The world is not closed. Your verifier is. You must draw a hard boundary around what your verifier claims to know. This boundary is defined by your schema and your rule set. Anything outside this boundary is irrelevant. Anything inside must be accounted for. The rule for deciding what can be closed is simple: if you can enumerate all possible states, you can close the world. If you cannot enumerate them, you must remain open, or you will have false positives. For example, consider a field `user_role`. If the allowed roles are `admin`, `editor`, and `viewer`, you can close the world. Any value not in this list is invalid. Any absence of this field is invalid. You have a finite set. You can check exhaustively. Now consider a field `user_comment`. This is free text. You cannot enumerate all possible comments. You cannot close the world on the content. You can only close the world on the structure. You can verify that the field is a string. You can verify its length. You can verify it does not contain SQL injection patterns. But you cannot verify its meaning. Meaning requires an LLM. Structure does not. Your verifier must be strictly structural. It checks types, ranges, formats, and references. It does not check intent. It does not check tone. It checks whether the data fits the container. If the container is defined, the world is closed. If the container is undefined, the verifier has no business checking it. This distinction is critical. Many teams try to verify semantics without models. They write regex patterns for natural language. These patterns fail. They have high false positive rates. They break on edge cases. They are not deterministic. Stick to structure. Structure is deterministic. Structure is closed.
Implementing Exhaustive Coverage
To implement closed-world semantics, you need a way to track which rules have run. Your pipeline must maintain a state of checked fields. For every field in the schema that has a rule, the verifier must record that the rule was applied. If a field is in the schema but has no rule, it is unverified. If a field is unverified, the overall verification fails. This sounds expensive. It is. It requires a mapping between schema fields and rule IDs. It requires a pass at the end of the verification loop to check that all expected rules have fired. This is the cost of certainty. You pay it in complexity. You gain it in reliability. Consider the implementation in the pack. You have a list of rules. Each rule specifies a path in the JSON. The verifier walks the JSON. For each path, it looks for a matching rule. If it finds one, it runs it. If it does not find one, it records a violation: “No rule for path X.” This ensures that new fields added to the schema do not silently bypass verification. They will fail until a rule is written for them. This creates a feedback loop. When you add a new field to your data model, your tests will fail. This is good. It forces you to write a rule. It prevents technical debt in your verification layer. It ensures that your verifier evolves with your data. There is a limitation here. This approach assumes your schema is accurate. If your schema says a field is required, but your data producer omits it, the verifier will fail. This is correct. The data producer is wrong. But if your schema is wrong, the verifier will enforce the wrong rules. Garbage in, garbage out. The schema is the single source of truth. It must be maintained. It must be versioned. It must be reviewed. The table below compares the outcomes of open and closed world assumptions for a missing field.
| Assumption | Field Present | Field Missing | Verifier State |
|---|---|---|---|
| Open World | Checked | Unknown | Silent / Pass |
| Closed World | Checked | False | Violation / Fail |
The closed world approach is stricter. It rejects more inputs. It requires more rules. It is harder to maintain. But it is correct. It does not lie. It tells you exactly what it checked. If it did not check something, it tells you. This is the foundation of trust. Without it, you have nothing. The next lesson, “The Pack Contract: rules.yaml, vocabulary.yaml, Fixtures, README”, details how to structure these rules and schemas into a shareable package.
