Claude-skill-registry law-machine-readable-interpreter
Interprets legal text in regelrecht YAML files and generates machine-readable execution logic with parameters, operations, outputs, and cross-law references. Use when user wants to make a law executable, add machine_readable sections, or interpret legal articles for computational execution.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/law-machine-readable-interpreter" ~/.claude/skills/majiayu000-claude-skill-registry-law-machine-readable-interpreter && rm -rf "$T"
skills/data/law-machine-readable-interpreter/SKILL.mdLaw Machine-Readable Interpreter
Analyzes legal text in YAML files and generates complete machine_readable execution logic.
What This Skill Does
- Reads YAML law files from
regulation/nl/ - Analyzes each article's legal text
- Identifies computational elements:
- Input parameters (BSN, dates, amounts)
- Constants and definitions
- Conditions and logic
- Cross-references to other laws/articles
- Output values
- Generates complete
sections with:machine_readable
- who has binding authoritycompetent_authority
- dependencies on other laws/regulationsrequires
- constants and fixed valuesdefinitions
section containing:execution
- legal character and decision typeproduces
- caller-provided inputsparameters
- data from other sourcesinput
- what this article producesoutput
- operations and logicactions
- Converts monetary amounts to eurocent (€ 795,47 → 79547)
- Creates TODO comments for missing external law references
- Uses aggressive AI interpretation (full automation)
Important Principles
- Aggressive interpretation: Generate complete logic even if uncertain
- Eurocent conversion: Convert all monetary amounts (€ X,XX → eurocent)
- Cross-references: Detect references to other laws/articles
- TODOs for missing refs: Add TODO comments when external laws don't exist in repo
- legal_basis: Add traceability to specific law text where applicable
Schema Structure Overview
The schema has NO
public or endpoint fields. The machine_readable section structure is:
machine_readable: competent_authority: # Who has binding authority name: "Belastingdienst" type: "INSTANCE" # or "CATEGORY" (default: INSTANCE) requires: # Dependencies (optional) - law: "Zorgverzekeringswet" values: ["is_verzekerd"] definitions: # Constants (optional) VERMOGENSGRENS: value: 15485900 execution: produces: # Legal character (optional) legal_character: "BESCHIKKING" # or TOETS, WAARDEBEPALING, BESLUIT_VAN_ALGEMENE_STREKKING decision_type: "TOEKENNING" # or AFWIJZING, GOEDKEURING, AANSLAG, etc. parameters: # Caller-provided inputs - name: "bsn" type: "string" required: true input: # Data from other sources - name: "toetsingsinkomen" type: "amount" source: regulation: "awir" # Name of the law/regulation output: "toetsingsinkomen" # Output field to retrieve parameters: bsn: "$bsn" output: # What this produces - name: "heeft_recht" type: "boolean" actions: # Computation logic - output: "heeft_recht" operation: "GREATER_THAN_OR_EQUAL" subject: "$leeftijd" value: 18
Step-by-Step Instructions
Step 1: Identify Target Law File
When user asks to "interpret" or "make executable" a law:
- Search
for the law fileregulation/nl/ - If multiple versions exist, ask which date to use
- Read the entire YAML file
Step 2: Analyze Each Article
For each article in the
articles array:
-
Read the legal text in the
fieldtext -
Identify if it's executable:
- Does it define a calculation, condition, or decision?
- Does it provide a concrete output value?
- If YES → Add
sectionmachine_readable - If NO (just definitions) → Skip or add minimal section
-
Extract key elements:
- Parameters: What inputs are needed? (BSN, dates, amounts, etc.)
- Constants: Fixed values defined in the text
- Conditions: If/when/unless statements
- Calculations: Mathematical operations
- References: Mentions of other articles/laws
- Outputs: What the article calculates/determines
Step 3: Identify Competent Authority
Determine who has binding authority for the decision:
competent_authority: name: "Belastingdienst/Toeslagen" type: "INSTANCE" # Specific organization
Or for categories (must be resolved per context):
competent_authority: name: "gemeente" type: "CATEGORY" # Abstract category, resolved at runtime
Step 4: Identify and Define Parameters
Look for inputs that must be provided by the caller:
Common parameters:
(string) - Citizen service numberbsn
(date) - Reference datepeildatum
(number) - Yearjaar
(amount) - Amountbedrag
Example from text:
"Een persoon heeft recht op zorgtoeslag indien hij de leeftijd van 18 jaar heeft bereikt"
→ Needs
bsn to look up person's age
YAML output:
parameters: - name: "bsn" type: "string" required: true description: "Burgerservicenummer van de persoon"
Step 5: Extract Constants and Definitions
Look for fixed values mentioned in the text:
Example from text:
"De grens bedraagt € 154.859 voor een alleenstaande"
YAML output:
definitions: VERMOGENSGRENS_ALLEENSTAANDE: value: 15485900 # Converted to eurocent! description: "Vermogensgrens voor alleenstaande personen"
Monetary Conversion Rules:
- € 154.859 → 15485900 (eurocent)
- € 2.112 → 211200 (eurocent)
- € 795,47 → 79547 (eurocent)
- Always use integer eurocent values
Step 6: Identify Cross-Law References (Input Sources)
Look for references to other laws or articles:
Patterns to detect:
- "ingevolge de [Law Name]"
- "bedoeld in artikel X"
- "genoemd in [regulation]"
- Markdown links:
[text](https://wetten.overheid.nl/BWBR...)
Source Structure:
input: - name: "is_verzekerd" type: "boolean" source: regulation: "zorgverzekeringswet" # Law identifier output: "is_verzekerd" # Output field name parameters: bsn: "$bsn" description: "Verzekerd ingevolge de Zorgverzekeringswet"
For external data (not from another law):
input: - name: "geboortedatum" type: "date" source: output: "geboortedatum" # No regulation = external data source description: "Geboortedatum uit BRP"
If external law not found in repo:
input: - name: "is_verzekerd" type: "boolean" source: # TODO: Implement zorgverzekeringswet regulation: "zorgverzekeringswet" output: "is_verzekerd" parameters: bsn: "$bsn"
Step 7: Define Outputs
Identify what the article produces:
Data types available:
- Text valuesstring
- Numeric valuesnumber
- True/falseboolean
- Monetary values (in eurocent)amount
- Date valuesdate
- Complex structuresobject
- Listsarray
With type specifications:
output: - name: "zorgtoeslag_bedrag" type: "amount" type_spec: unit: "eurocent" description: "Het bedrag van de zorgtoeslag"
With temporal metadata:
output: - name: "toetsingsinkomen" type: "amount" temporal: type: "period" period_type: "year"
Step 8: Interpret Conditions and Logic (Actions)
Convert legal conditions to operations:
Available Operations:
| Category | Operations |
|---|---|
| Arithmetic | , , , , , |
| Comparison | , , , , , |
| Logical | , , |
| Membership | , |
| Null check | |
| Conditional | |
| Iteration | |
| Date | |
| String | |
Common Legal Patterns → Operations:
| Legal Text | Operation |
|---|---|
| "heeft bereikt de leeftijd van 18 jaar" | , subject: $leeftijd, value: 18 |
| "niet meer bedraagt dan X" | |
| "ten minste X" | |
| "indien ... en ..." | with values array |
| "indien ... of ..." | with values array |
| "niet ..." | |
| "gelijk aan" | |
Simple comparison:
actions: - output: "is_volwassen" operation: "GREATER_THAN_OR_EQUAL" subject: "$leeftijd" value: 18
Multiple conditions (AND/OR):
actions: - output: "voldoet_aan_voorwaarden" operation: "AND" values: - operation: "EQUALS" subject: "$is_verzekerd" value: true - operation: "GREATER_THAN_OR_EQUAL" subject: "$leeftijd" value: 18
Conditional with if-then-else (using conditions array):
actions: - output: "toeslag_percentage" conditions: - test: operation: "EQUALS" subject: "$huishouden_type" value: "alleenstaand" then: 100 else: 50
Conditional with IF operation:
actions: - output: "resultaat" operation: "IF" test: operation: "GREATER_THAN" subject: "$inkomen" value: 50000 then: 0 else: "$berekend_bedrag"
Calculation chain:
actions: - output: "premie_basis" operation: "MULTIPLY" values: - "$standaardpremie" - "$percentage" - output: "premie_na_korting" operation: "SUBTRACT" values: - "$premie_basis" - "$korting"
Date calculation:
actions: - output: "leeftijd" operation: "SUBTRACT_DATE" values: - "$peildatum" - "$geboortedatum"
Resolve from ministeriele regeling:
actions: - output: "standaardpremie" resolve: type: "ministeriele_regeling" output: "standaardpremie" match: output: "jaar" value: "$jaar"
Step 9: Add Legal Basis (Traceability)
For important computations, add legal_basis to trace back to the law:
actions: - output: "heeft_recht" operation: "AND" values: - "$is_verzekerd" - "$is_volwassen" legal_basis: law: "Wet op de zorgtoeslag" bwb_id: "BWBR0018451" article: "2" paragraph: "1" url: "https://wetten.overheid.nl/BWBR0018451#Artikel2" explanation: "Lid 1 bepaalt de voorwaarden voor recht op zorgtoeslag"
Step 10: Set Produces (Legal Character)
If the article produces a formal decision:
execution: produces: legal_character: "BESCHIKKING" # Individual decision decision_type: "TOEKENNING" # Grant/approval
Legal character options:
- Individual administrative decisionBESCHIKKING
- Check/verificationTOETS
- Value determinationWAARDEBEPALING
- General binding decisionBESLUIT_VAN_ALGEMENE_STREKKING
Decision type options:
- GrantTOEKENNING
- RejectionAFWIJZING
- ApprovalGOEDKEURING
- Tax assessmentAANSLAG
- General binding regulationALGEMEEN_VERBINDEND_VOORSCHRIFT
- Policy ruleBELEIDSREGEL
- Preparatory decisionVOORBEREIDINGSBESLUIT
- Other actionANDERE_HANDELING
Step 11: Complete Example
Legal text:
Artikel 2 1. Een persoon heeft recht op zorgtoeslag indien hij: a. de leeftijd van 18 jaar heeft bereikt; b. verzekerd is ingevolge de Zorgverzekeringswet.
Complete machine_readable section:
machine_readable: competent_authority: name: "Belastingdienst/Toeslagen" requires: - law: "Zorgverzekeringswet" values: ["is_verzekerd"] execution: produces: legal_character: "BESCHIKKING" decision_type: "TOEKENNING" parameters: - name: "bsn" type: "string" required: true description: "Burgerservicenummer" - name: "peildatum" type: "date" required: true description: "Datum waarop het recht wordt getoetst" input: - name: "geboortedatum" type: "date" source: output: "geboortedatum" description: "Geboortedatum uit BRP" - name: "is_verzekerd" type: "boolean" source: # TODO: Implement zorgverzekeringswet regulation: "zorgverzekeringswet" output: "is_verzekerd" parameters: bsn: "$bsn" output: - name: "leeftijd" type: "number" type_spec: unit: "years" - name: "heeft_recht" type: "boolean" description: "Geeft aan of de persoon recht heeft op zorgtoeslag" actions: - output: "leeftijd" operation: "SUBTRACT_DATE" values: - "$peildatum" - "$geboortedatum" legal_basis: article: "2" paragraph: "1" explanation: "Leeftijd bepaald op peildatum" - output: "heeft_recht" operation: "AND" values: - operation: "GREATER_THAN_OR_EQUAL" subject: "$leeftijd" value: 18 - operation: "EQUALS" subject: "$is_verzekerd" value: true legal_basis: article: "2" paragraph: "1" explanation: "Voorwaarden a en b van lid 1"
Step 12: Apply Changes to YAML
For each article that needs a
machine_readable section:
- Use the Edit tool to add the section after the
fieldurl - Maintain proper YAML indentation (2 spaces per level)
- Add comments for TODOs and clarifications
- Convert all monetary amounts to eurocent
Step 13: Validate Against Schema and Lint
Before reporting, validate the updated YAML:
Step 13a: Run YAML linting
uv run yamllint {LAW_FILE_PATH}
This checks for:
- Line length (max 125 chars - wrap long text!)
- Proper indentation
- Quote usage
- YAML formatting
Step 13b: Run schema validation
uv run python script/validate.py {LAW_FILE_PATH}
This validates against the JSON schema.
If validation fails:
- Review schema errors carefully
- Common issues with machine_readable sections:
- Missing required
field in sourceoutput - Wrong operation types (check enum values)
- Missing required fields in parameters/input/output
- Incorrect nesting or indentation
- Missing required
- Fix errors and re-validate
- Continue until both lint and validation pass
Step 14: Reverse Validation (Hallucination Check)
After schema validation passes, verify that every element in the
machine_readable section can be traced back to the original legal text.
For each element, check:
-
Definitions/Constants:
- Is this value explicitly mentioned in the article text?
- If NOT → Remove it from the YAML
- If needed for logic but not in text → Add to "Assumptions" in report
-
Input fields:
- Is this data source referenced in the article text?
- Look for phrases like "ingevolge", "bedoeld in", "genoemd in"
- If NOT traceable → Remove or mark as assumption
-
Output fields:
- Does the article actually produce this output?
- Is it stated or clearly implied in the legal text?
- If NOT → Remove it
-
Actions/Operations:
- Does the legal text contain the logic for this operation?
- Can you point to specific sentences that justify this action?
- If NOT → Remove or simplify
-
Conditions:
- Are these conditions explicitly stated in the article?
- Watch for invented edge cases not in the law
- If NOT → Remove
Decision matrix:
| Traceable in text? | Needed for logic? | Action |
|---|---|---|
| YES | YES | Keep |
| YES | NO | Keep (may be informational) |
| NO | YES | Report as assumption |
| NO | NO | Remove |
Example check:
# Article text: "Een persoon heeft recht indien hij 18 jaar is" # GOOD - traceable: - output: "heeft_recht" # ✓ "heeft recht" in text operation: "GREATER_THAN_OR_EQUAL" subject: "$leeftijd" value: 18 # ✓ "18 jaar" in text # BAD - not traceable (hallucinated): - output: "woont_in_nederland" # ✗ Not mentioned in article operation: "EQUALS" subject: "$woonland" value: "NL" # → REMOVE THIS
After reverse validation:
- Remove all non-traceable elements that aren't needed
- Add "Assumptions" section to report for elements that are:
- Not explicitly in text
- But required to make the logic complete
- These need user review
Step 15: Report Results
After successful validation:
-
Count processed articles:
- How many articles total?
- How many now have machine_readable sections?
-
List TODOs:
- Which external laws need to be downloaded?
- Any ambiguous interpretations?
-
List Assumptions (from reverse validation):
- Elements not explicitly in text but needed for logic
- These require user verification
-
Report to user:
Interpreted {LAW_NAME} Articles processed: {TOTAL} Made executable: {EXECUTABLE_COUNT} Schema validation: PASSED Reverse validation: PASSED Assumptions (need review): - Article 2: Added "peildatum" parameter (implied but not stated) - Article 3: Assumed "inkomen" refers to toetsingsinkomen TODOs remaining: - Download and interpret: {external_law_1} - Clarify calculation in article {X} The law is now executable via the engine!
Common Patterns
Pattern 1: Age Check
input: - name: "geboortedatum" type: "date" source: output: "geboortedatum" description: "Geboortedatum uit BRP" actions: - output: "leeftijd" operation: "SUBTRACT_DATE" values: - "$peildatum" - "$geboortedatum" - output: "is_volwassen" operation: "GREATER_THAN_OR_EQUAL" subject: "$leeftijd" value: 18
Pattern 2: Income Threshold
definitions: INKOMENSGRENS: value: 7954700 # € 79.547 in eurocent input: - name: "toetsingsinkomen" type: "amount" source: regulation: "awir" output: "toetsingsinkomen" parameters: bsn: "$bsn" jaar: "$jaar" actions: - output: "onder_inkomensgrens" operation: "LESS_THAN_OR_EQUAL" subject: "$toetsingsinkomen" value: "$INKOMENSGRENS"
Pattern 3: Multiple Conditions (AND)
actions: - output: "voldoet_aan_voorwaarden" operation: "AND" values: - operation: "EQUALS" subject: "$is_verzekerd" value: true - operation: "GREATER_THAN_OR_EQUAL" subject: "$leeftijd" value: 18 - operation: "EQUALS" subject: "$woont_in_nederland" value: true
Pattern 4: Calculation Chain
actions: - output: "premie_basis" operation: "MULTIPLY" values: - "$standaardpremie" - "$percentage" - output: "premie_na_korting" operation: "SUBTRACT" values: - "$premie_basis" - "$korting" - output: "premie_finaal" operation: "MAX" values: - 0 - "$premie_na_korting"
Pattern 5: Conditional Value
actions: - output: "vermogensgrens" conditions: - test: operation: "EQUALS" subject: "$heeft_partner" value: true then: "$VERMOGENSGRENS_PARTNERS" else: "$VERMOGENSGRENS_ALLEENSTAANDE"
Pattern 6: Lookup from Ministeriele Regeling
actions: - output: "standaardpremie" resolve: type: "ministeriele_regeling" output: "standaardpremie" match: output: "jaar" value: "$jaar"
Tips for Success
- Be aggressive: Generate complete logic even if uncertain
- Use descriptive names:
nottoetsingsinkomenincome - Always eurocent: Never use decimal euro amounts
- Check for existing laws: Use Glob to search
regulation/nl/ - Break down complex logic: Multiple simple actions > one complex action
- Add descriptions: Help future readers understand the logic
- Mark TODOs clearly: Use
comments for missing refs# TODO: - Validate types: Ensure type consistency (boolean, number, string, date, amount)
- Document assumptions: Add comments when interpretation is unclear
- Add legal_basis: Trace important computations back to the law text
Error Handling
If legal text is ambiguous:
- Make best guess with TODO comment
- Explain uncertainty to user
- Suggest manual review
If external law not found:
- Create TODO placeholder in source
- Add to list of missing dependencies
- Continue with other articles
If operation unclear:
- Use simpler operations
- Break into multiple steps
- Add explanatory comments