Skip to main content

LLMS_TXT

Constant LLMS_TXT 

Source
pub const LLMS_TXT: &str = "# Lemma\n\n> Lemma: declarative business rule language. Translate natural-language policy to readable, deterministic `.lemma` specs. Inputs derived from rules: declare `data` with constraints. No runtime value invention.\n\nGoal: produce Lemma source for human reading and system evaluation. Prefer named pipeline rules. No clever one-liners. Split unrelated policies to separate specs.\n\nAuthoring method, discovery loop, and output contract: see **Method**. Explanations opt-in (`explain: true` / `lemma run -x`).\n\n**NO INLINE COMMENTS.** Lemma has no `#`, `//`, or `--` comment syntax. See **Syntax** for opening order and commentary placement.\n\n\n---\n\n**Method: write as a policy consultant, not a transcriber**\n\nSyntax alone can encode the wrong policy. Never invent numbers, dates, or outcomes the source does not decide. Ask those gaps before writing. No retroactive questions after authoring. No follow-up ideas after deliver.\n\n**Process**\n\n1. **Gather**: Collect sources (prose, statute, ticket, answers, field names). Inventory. Do not write Lemma yet.\n2. **Interpret**: Restate as questions the system must answer. Name actors, triggers, windows. One coherent policy \u{2192} one `spec` (you decide).\n3. **Interrogate**: Ask only when the **source leaves a policy outcome undecided**. Phrase questions in plain language a non-Lemma author understands. Do not invent. Do not Author until answers or explicit acceptance.\n4. **Scope**: One `spec` per coherent policy; split / `uses` only when the source clearly mixes unrelated domains; `@\u{2026}` catalogs; temporal date only when the author confirms a start date. Never guess.\n5. **Model**: Public `data` API and named `rule` pipeline (you choose field shape; do not ask the author). Domain-principle `unless` defaults (see **Rules**). Denial = `false`/`no`; unanswerable = veto (see **Veto**). Prefer raw inputs over precomputed numbers. Encode rule gates the source states; omit advisory how-to that is not a gate. Name booleans for the fact you mean; prefer the natural predicate with `-> suggest` for the usual case (`data item_damaged: boolean -> suggest false`). Do not invent awkward opposites (`item_undamaged`) or negated names (`not_*`, `no_*`, `has_no_*`).\n6. **Author**: After questions resolved. Commentary after `spec`, `meta` for provenance; no inline comments (see **Syntax**). Spec is the only documentation.\n7. **Verify**: `check`, `show`, `evaluate` at boundaries (half-open ranges). Result text must match conditions. Then `add_spec` / `update_spec` to load.\n8. **Deliver**: Always paste the full Lemma source in chat so the user can verify what was saved or loaded (use `source` if you need the loaded text). Confirm loaded. Close with a statement, not a question: e.g. tell them to say if anything requires adjustment. No pitch for more work. Stop.\n\n**What to ask (and what not to)**\n\nAsk when the source does not decide a **policy outcome**, e.g. a threshold boundary (\"above \u{20ac}50\" vs \"\u{20ac}50 or more\"), or whether rules start on a specific date.\n\nDo **not** ask:\n\n- Modeling shape (`data` field count, option vs boolean, one input vs two)\n- Spec packaging (one vs split) when the source is one coherent policy\n- Whether advisory prose (tips, how-to steps, \"contact support\") is \"in scope\": encode gates; omit advice\n- Edge cases the source never mentions\n\nPlain language: \"Is this policy effective from a specific date, or have these rules always been in effect?\", not \"Effective date: pin a date on the spec, or leave undated?\"\n\n**Output contract (mandatory)**\n\n- **Before Author**: Ask the few real gaps (if any). Wait. Do not invent.\n- **At Deliver**: Full Lemma source in chat for user verify + loaded confirmation + assumptions the author confirmed. No new policy questions. No pitch for more work. Close with a statement (tell them to say if anything requires adjustment), not a confirm question.\n\n**Principles**\n\n- Spec is the only documentation: `-> help`, commentary, `meta` (see **Syntax**).\n- `data` names are a public API. Prefer `-> option` for closed text sets.\n- Decomposition is output quality: named intermediates, not mega-rules.\n\n**Worked example**\n\nSource: *\"Standard shipping is \u{20ac}4.95. Free shipping when the order is \u{20ac}50 or more.\"*\n\nAsk before writing (real gaps only):\n\n1. At exactly \u{20ac}50: free shipping or \u{20ac}4.95?\n2. Is this shipping policy effective from a specific date, or have these rules always been in effect?\n\nWRONG to ask: one `shipping` spec or split; how many `data` fields for destination; express vs economy when the source never mentions them; whether packing tips are hard rules.\n\nAfter answers: Model / Author / Verify / `add_spec`. Fee rule uses domain-principle default (usual fee, free when threshold met). If author says free at \u{20ac}50 or more \u{2192} `order_total >= 50 eur`. Paste source, deliver and stop.\n\n\n---\n\n**Mandatory spec opening order:**\n\n```\nspec <name> [<effective>]\n[\"\"\" commentary: optional, but if present must be HERE \"\"\"]\nuses ...\ndata ...\nrule ...\n```\n\nCommentary after `uses` or `data` is invalid. No `#`, `//`, `--` comments. Use descriptive names. Put user explanations outside code fences, never inside ` ```lemma ` blocks.\n\n**Gotchas (parse errors)**\n\n- No `or` operator. Disjunction via `unless` chains or separate boolean rules.\n- Constraints (`-> help`, `-> option`, `-> minimum`, etc.) apply to `data` only. Rules have no constraints.\n\n\n---\n\n**Organization: spec \u{2192} rule**\n\nDefault: one file, one implicit repo. No `repo` blocks unless multi-namespace workspace requested. Structure: **spec \u{2192} rule**.\n\n**Spec** = namespace for `data` and `rules`. **Rule** = named computed value. Reference rules by name; engine resolves if name is data or rule. One file can have multiple specs.\nHierarchical names: `spec employee/contract`. Effective date for temporal changes: `spec pricing 2026-01-01`.\nCommentary placement: see **Syntax**.\n\n**Example A: minimal single-spec file**\n\n```lemma\nspec pricing 2026-01-01\n\"\"\"\nPricing rules for bulk and member discounts.\n\"\"\"\n\ndata qty:        number\ndata base_price: 100\ndata is_member:  false\n\nrule vat_amount: base_price * 21%\nrule price_with_vat: base_price + vat_amount\n\nrule bulk_discount:\n  qty >= 100 and price_with_vat > 500\n\nrule discount: 0%\n  unless qty >= 10     then 10%\n  unless bulk_discount then 15%\n  unless is_member     then 20%\n\nrule discount_amount: base_price * discount\nrule price_with_discount: base_price - discount_amount\n```\n\n**Example B: multi-spec composition (same file)**\n\n```lemma\nspec base_config\n\ndata standard_discount: 5%\ndata tax_rate:          21%\ndata base_price:        number -> minimum 0 -> suggest 100\n\nrule tax_amount:       base_price * tax_rate\nrule price_with_tax:   base_price + tax_amount\nrule discount_amount:  base_price * standard_discount\nrule discounted_price: base_price - discount_amount\nrule final_price:      discounted_price * (100% + tax_rate)\n\n\nspec line_item\n\ndata qty: number -> minimum 0 -> suggest 10\n\nuses pricing: base_config\n\nrule line_total:   pricing.final_price * qty\nrule has_discount: pricing.standard_discount > 0%\n\n\nspec simple_order\n\nuses line: line_item\nwith line.qty: 100\n\nrule order_total:          line.line_total\nrule effective_unit_price: order_total / line.qty\n```\n\n- `uses alias: target_spec`: imports spec in same file.\n- Reference members: `alias.field` or `alias.rule_name`.\n- `with alias.field: value`: sets imported data. Do not use `data alias.field`. Local `with name: \u{2026}` invalid: use `data name: \u{2026}`.\n\n**LemmaBase: shared specs from the registry**\n\nSpecs on [LemmaBase.com](https://lemmabase.com) imported with `@` repo qualifiers. Search: [lemmabase.com/search?q=](https://lemmabase.com/search?q=) (e.g. `?q=finance`).\n\n```lemma\nspec invoicing\n\"\"\"\nInvoice lines using ISO country codes from LemmaBase.\n\"\"\"\n\nuses lemma units\n\nuses iso: @iso/countries alpha2 2026-01-01\n\ndata price: measure\n  -> unit eur 1\n\ndata country: iso.code\n\nrule tariff: 0 eur\n  unless country is \"NL\" then price * 5%\n\nrule total: price + tariff\n```\n\nForms:\n- `uses @user/repo spec_name`: import registry spec (alias = spec name)\n- `uses alias: @user/repo spec_name`: import with alias (`iso.field`)\n- `uses @user/repo spec_name 2026-01-01`: pin effective date\n\nReference imported members: `iso.code`. Detail: [Registry](https://github.com/lemma/lemma/blob/main/cli/documentation/reference/registry.md).\n\n`repo` blocks namespace specs across contexts (e.g., `repo accounting`). Skip unless asked. Details: [Composing specs](https://github.com/lemma/lemma/blob/main/cli/documentation/learn/composing_specs.md).\n\n\n---\n\n**Natural language \u{2192} Lemma**\n\nRequest: *\"Library charges \u{20ac}0.25/day for regular books, \u{20ac}0.50 for reference, \u{20ac}1 for new releases. First offense gets 50% off. Grace period 3 day except new releases. Block checkout if fee exceeds \u{20ac}10.\"*\n\nMap logic:\n- Book types \u{2192} `data book_type` with `-> option` constraints\n- Due and return dates \u{2192} `data` input slots; overdue days derived by a rule\n- First offense \u{2192} `data` input slot\n- Per-day rates \u{2192} `rule daily_fee` with unless branches\n- Grace period \u{2192} `rule is_in_grace_period`\n- Fee pipeline \u{2192} `rule total_fee`, `rule final_fee`\n- Checkout block \u{2192} `rule can_checkout: yes` with `unless final_fee > 10 eur then no` (boolean, not veto)\n\n**Example C: library fees (full spec)**\n\n```lemma\nspec library_fees\nuses lemma units\n\ndata money: measure\n  -> decimals 2\n  -> unit eur 1.00\n  -> minimum 0 eur\n\ndata book_kind: text\n  -> option \"regular\"\n  -> option \"reference\"\n  -> option \"new_release\"\n\ndata book_type:        book_kind\ndata is_first_offense: boolean\ndata due_date:         date\ndata return_date:      date\n\nrule days_overdue: (due_date...return_date) as day as number\n\nrule daily_fee: 0 eur\n  unless book_type is \"regular\"     then 0.25 eur\n  unless book_type is \"reference\"   then 0.5 eur\n  unless book_type is \"new_release\" then 1 eur\n\nrule is_in_grace_period: days_overdue <= 3\n  unless book_type is \"new_release\" then no\n\nrule total_fee: days_overdue * daily_fee\n\nrule final_fee: total_fee\n  unless is_first_offense   then total_fee * 50%\n  unless is_in_grace_period then 0 eur\n\nrule can_checkout: yes\n  unless final_fee > 10 eur then no\n```\n\n\n---\n\n**Data: constraint definitions, not placeholders**\n\n`data` declares variables. Constraints define validity. Type-only `data` (no value) is an input slot. Use real domain values. Never `\"TODO\"` or dummy placeholders.\n\nFor which inputs a rule still needs at runtime, call MCP `guide` with no topic (evaluate guide): `list` \u{2192} `show` once \u{2192} `evaluate` \u{2192} ask one `missing_data` field \u{2192} repeat. Load `guide` topic `full` only when authoring new specs. `show` is the static catalog, not a required-input checklist. `-> help` is the literal CS ask string; do not replace the question with a different one.\n\n**Example D: typed data (coffee order)**\n\n```lemma\nspec coffee_order\n\ndata money: measure\n  -> decimals 2\n  -> unit eur 1.00\n  -> unit gbp 1.17\n  -> unit usd 0.84\n  -> minimum 0 eur\n\ndata product: text\n  -> option \"espresso\"\n  -> option \"latte\"\n  -> option \"cappuccino\"\n  -> option \"mocha\"\n\ndata size: text\n  -> option \"small\"\n  -> option \"medium\"\n  -> option \"large\"\n\ndata age: number\n  -> maximum 100\n  -> minimum 0\n\ndata number_of_cups: number\n  -> maximum 10\n\ndata has_loyalty_card: boolean\n```\n\n- `age`, `number_of_cups`: input slots (type-only + bounds)\n- `money`: custom measure type with units, decimals, minimum\n- `product`, `size`: text enumeration via `-> option` (prefer over veto for static sets)\n\n**Example E: data patterns**\n\nInput slot:\n```lemma\nspec intake\n\ndata customer_age: number -> minimum 0 -> maximum 120\n```\n\nFixed policy constant:\n```lemma\nspec fiscal_policy\n\ndata tax_rate: 21%\n```\n\nText enumeration:\n```lemma\nspec membership\n\ndata status: text\n  -> option \"active\"\n  -> option \"inactive\"\n```\n\nTyped alias:\n```lemma\nspec accounts\n\ndata money: measure -> unit eur 1.00\n\ndata wallet: money -> minimum 0 eur\n```\n\nWith help text (literal CS ask string):\n```lemma\nspec payroll\n\ndata pay_period: text\n  -> option \"month\"\n  -> option \"week\"\n  -> help \"How often you are paid.\"\n```\n\nBoolean with usual-case suggest:\n```lemma\nspec returns\n\ndata item_damaged: boolean\n  -> suggest false\n  -> help \"Item damaged?\"\n```\n\nConstraints chain: `-> minimum`, `-> maximum`, `-> option`, `-> unit`, `-> decimals`, `-> suggest`, `-> help`, etc. Details in Reference.\n\n\n---\n\n**Standard library: `uses lemma units`**\n\nLemma embeds SI bases, derived compounds (force, pressure, energy, power, frequency, electrical), imperial, area/volume, and information (`bit`/`byte`) in `repo lemma` / `spec units`. Import: `uses lemma units`. Reference types: `units.mass`, `units.duration`, `units.length`, `units.force`. Unit names: **singular only** (`8 hour`). Length uses American `meter`. Durations (`hour`, `day`, `week`) require `units.duration`. No Celsius/Fahrenheit (kelvin only).\n\n```lemma\nspec logistics\n\"\"\"\nPhysical shipment constraints using SI units from the standard library.\n\"\"\"\n\nuses lemma units\n\ndata package_weight: 12 kilogram\ndata shift_length:   8 hour\ndata route_distance: 45 kilometer\n\nrule weight_grams:  package_weight as gram\nrule shift_hours:   shift_length as hour\nrule distance_km:   route_distance as kilometer\n\nrule is_heavy:      package_weight > 20 kilogram\nrule is_long_shift: shift_length >= 8 hour\n```\n\nPrefer `units.mass`, `units.duration`, `units.length` over redefining units. Convert in family: `as <unit>`. Strip unit: `amount as eur as number`. Cross-family relabel: `5 eur as kg` -> `5 kg`.\n\n**Ranges: half-open intervals**\n\nRanges: lower bound inclusive, upper bound exclusive (`lo...hi`). Test with `in`. Width: `(lo...hi) as <unit> as number` (or `(lo...hi) as <unit>`). Bare `as number` on date/measure ranges fails. Typedefs: `number range`, `date range`, `measure range`, `ratio range`. Month/year intervals: `uses lemma units` and inline literals (`18 year...67 year`) or `units.calendar range`.\n\n```lemma\nspec eligibility\nuses lemma units\n\ndata employee_age:       42 year\ndata performance_score:  75\ndata package_weight:     45 kilogram\ndata hire_date:          2024-01-15\ndata review_date:        2024-06-30\ndata discount_rate:      15%\n\ndata eligible_band: units.calendar range\n  -> suggest 18 year...67 year\n\ndata score_band: number range\n  -> suggest 0...100\n\nrule is_working_age: employee_age in eligible_band\n\nrule is_top_score: performance_score in 90...100\n\nrule is_heavy: package_weight in 30 kilogram...80 kilogram\n\nrule in_discount_band: discount_rate in 0%...50%\n\nrule in_q2: hire_date in 2024-04-01...2024-07-01\n\nrule review_days: (hire_date...review_date) as day\n\nrule span_years: (1990-05-20...2024-06-15) as year\n```\n\nUpper bound exclusive: `67 year` is NOT inside `18 year...67 year` (returns false). Declare range slots on `data` for reuse, inline `value in lo...hi` for one-off.\n\nRange over custom measure type (no SI import needed):\n\n```lemma\nspec freight\n\ndata weight: measure\n  -> unit gram 1\n  -> unit kilogram 1000\n\ndata load_band: weight range\n  -> suggest 30 kilogram...80 kilogram\n\nrule inside_band: 45 kilogram in load_band\n\nrule band_width: (30 kilogram...80 kilogram) as kilogram\n```\n\n**Derived measures: compound units**\n\nBuild compound units with `/`, `*`, `^`. Name derived unit, then give compound expression. Prior measure types must declare referenced base units (`eur`, `hour`, `employee`, `results`). Import `uses lemma units` if using time (`eur/hour`).\n\n```lemma\nspec contractor\nuses lemma units\n\ndata money: measure\n  -> unit eur 1.00\n\ndata headcount: measure\n  -> unit employee 1\n\ndata outcome: measure\n  -> unit results 1\n\ndata wage_rate: measure\n  -> unit eur_per_second eur/second\n  -> unit eur_per_hour eur/hour\n\ndata productivity: measure\n  -> unit result_per_employee results/eur/hour/employee\n\ndata premium_per_head: measure\n  -> unit eur_hour_per_employee eur_per_hour/employee\n\ndata time_worked: 120 hour\ndata wage: wage_rate -> suggest 85 eur_per_hour\ndata yield_rate: productivity -> suggest 3 result_per_employee\n\nrule total: wage * time_worked\n\nrule is_high_yield: yield_rate >= 2 result_per_employee\n```\n\nLayer compound units: `eur_per_hour` builds on `eur` and `hour`. Dimensional checks run at plan time.\n\n**Date predicates relative to `now`**\n\n`now` is evaluation/effective instant. Import `uses lemma units` for duration windows.\n\n| Form | Meaning |\n|------|---------|\n| `date in past` / `in future` | Before / after `now` |\n| `date in past N day` / `in future N day` | In last / next N duration units |\n| `past N day` / `future N day` | Relative date-range window |\n| `date in calendar year\\|month\\|week` | Current calendar period |\n| `date in past\\|future calendar year\\|month\\|week` | Adjacent calendar period |\n| `date not in calendar year\\|month\\|week` | Not current calendar period |\n\n```lemma\nspec recency\nuses lemma units\ndata event_date: date\nrule recent: event_date in past 7 day\nrule this_year: event_date in calendar year\n```\n\n\n---\n\n**Rules and unless: last matching clause wins**\n\nDefault expression, then `unless <condition> then <result>`. Source order; **bottommost match wins**. General first, specific last. Snake_case names; boolean predicates (`is_eligible`, `can_ship`). Named pipeline rules, no opaque mega-expressions.\n\n**Domain-principle default**\n\nDefault is not \"prefer yes/no.\" It is the answer **in principle for this rule\'s domain**. Experts must read top-to-bottom as: \"In principle X; unless Y, then Z.\"\n\n1. Name the question (`can_ship` \u{2192} \"Can we ship this order?\").\n2. Before special cases, what is true in principle? That is the default: from *this* rule\'s domain, not optimism or \"start true and subtract failures.\"\n3. What positive facts change the answer? Those are `unless` conditions, not `\u{2026} is false then flip`.\n4. Write `rule name: <principle> unless <positive conditions> then <exception>`.\n\nExamples by domain: shipping often earned (`no` unless grant); discount often `0%`; fees use the policy\'s usual fee, not \"free unless expensive.\"\n\nForbidden: double denial (`yes` / `unless \u{2026} is false then no`); fail-each-check cascades; invented `*_compliant` default-yes helpers.\n\n```lemma-skip\nrule can_ship: no\n  unless in_stock\n    and address_complete\n    then yes\n```\n\n**Example F: overlapping unless (last wins)**\n\n```lemma\nspec vip_discount\n\ndata qty: number\ndata is_vip:   boolean\n\nrule discount: 0%\n  unless qty >= 10  then 10%\n  unless qty >= 50  then 20%\n  unless is_vip          then 25%\n```\n\nVIP ordering 75 items gets **25%** (not 20%): both `qty >= 50` and `is_vip` match; bottommost wins.\n\n**Example G: progressive unless chain**\n\n```lemma\nspec rules_and_unless\n\ndata is_premium: yes\ndata base_price: number -> minimum 0\ndata qty:        number -> minimum 0\n\nrule total_before_discount: base_price * qty\n\nrule discount_percentage: 0%\n  unless qty >= 10 then 10%\n  unless qty >= 20 then 15%\n  unless is_premium then 20%\n\nrule discount_amount:      total_before_discount * discount_percentage\nrule total_after_discount: total_before_discount - discount_amount\n\nrule shipping_cost: 15\n  unless total_after_discount >= 100 then 10\n  unless total_after_discount >= 200 then 0\n\nrule final_total: total_after_discount + shipping_cost\n```\n\n**Example H: short pipeline sketch**\n\n```lemma\nspec shipping_fees\nuses lemma units\n\ndata item_weight: units.mass\ndata order_total: number -> minimum 0\n\nrule base_rate: 22\nrule weight_surcharge: 0\n  unless item_weight > 5 kilogram then 7.5\n\nrule final_shipping: base_rate + weight_surcharge\n  unless order_total >= 100 then 0\n```\n\n\n---\n\n**Veto: impossible to answer, not `false`**\n\nVeto is like Rust\'s `Err`: rule has **no value** and propagates to dependents. Use veto when answer is impossible, not when business answer is false.\n\n| Situation | Use |\n|-----------|-----|\n| Invalid/out-of-domain input | `unless ... then veto \"reason\"` |\n| Unmapped choice / no rule applies | default `veto` + unless arm per choice |\n| Normal business \"no\" | `false` or `no` |\n| Test veto without propagating | `x is veto` (returns boolean) |\n\n**Litmus test:** Can the question be answered? If yes, even when the answer is negative, use `true`/`false`. If the question itself is unanswerable for this input, use veto. \"Is the customer eligible?\" is always answerable (`true` or `false`). \"What is the price of this coffee?\" when the product is not on the menu is unanswerable (veto).\n\nA vetoed rule is not `false`. `x is false` does not match a vetoed `x`. To test whether a rule vetoed, use `x is veto`.\n\nPlace veto unless clauses **last** to override other branches.\n\n**Example I: enumeration with default veto**\n\n```lemma\nspec choice_mapping\n\ndata choice_field: number\n\nrule selected: veto\n  unless choice_field is 1 then true\n  unless choice_field is 2 then false\n  unless choice_field is 3 then true\n```\n\nDefault `veto` if unlisted. Each `unless` maps known choice. `false` is valid answer for choice 2.\n\n**Example J: veto lookup + propagation**\n\n```lemma\nspec coffee_pricing\n\ndata money: measure\n  -> unit eur 1.00\n  -> decimals 2\n\ndata product: text\ndata size:    text\n\nrule base_price: veto \"Unknown type of coffee\"\n  unless product is \"espresso\"   then 2.5 eur\n  unless product is \"latte\"      then 3.5 eur\n  unless product is \"cappuccino\" then 3.5 eur\n  unless product is \"mocha\"      then 4 eur\n\nrule size_multiplier: veto \"Unknown size of coffee\"\n  unless size is \"small\"  then 80%\n  unless size is \"medium\" then 100%\n  unless size is \"large\"  then 120%\n\nrule price_per_cup: base_price * size_multiplier\n```\n\nIf `base_price` vetoes, `price_per_cup` vetoes automatically (propagates).\n\nPartial eval still walks later `and` / arithmetic siblings after `MissingData` or a definitive veto so nested control can record. `missing_data` lists unbound keys only when some completion can still yield a **value** (`missing_flag and (base > 0)` with unbound `missing_flag` awaits `missing_flag`; product with a definitive factor settles). `is veto` remains a boolean probe.\n\n**Example K: veto vs boolean**\n\nWRONG: veto for business decision:\n```lemma-skip\nrule can_checkout: veto\n  unless fee <= 10 eur then true\n```\n\nRIGHT: veto for invalid input, boolean for business logic:\n```lemma\nspec checkout_policy\n\ndata money: measure\n  -> unit eur 1.00\n  -> decimals 2\n\ndata customer_age: number\ndata fee:          money\n\nrule age_validation:\n  true\n  unless customer_age < 18  then veto \"Customer must be 18 or older\"\n  unless customer_age > 120 then veto \"Invalid age\"\n\nrule can_checkout: yes\n  unless customer_age < 18 then no\n  unless fee > 10 eur then no\n```\n\n**Example L: veto propagation with unless fallback**\n\n```lemma\nspec scoring\n\ndata score:       number\ndata use_default: boolean\n\nrule validated_score: score\n  unless score < 0 then veto \"Invalid score\"\n\nrule result: validated_score\n  unless use_default then 50\n```\n\nIf `validated_score` vetoes but `use_default` is true, `result` is 50. Unless branch avoids needing vetoed value.\n\n**Workflow checklist**\n\n1. **Scope**: one spec per coherent policy; compose with `uses`; skip `repo` unless needed.\n2. **Inputs**: every user-supplied fact is `data` with constraints.\n3. **Outputs**: every answerable question is a `rule`.\n4. **Factor**: intermediate calculations as named rules.\n5. **Unless**: default first, general to specific, vetoes last.\n6. **Validate**: `check`, then `show`, then `evaluate` (MCP `guide` with no topic for `missing_data` intake; topic `full` only when authoring).\n7. **Advanced**: `uses lemma units`, ranges, compound units when needed.\n\n\n---\n\n**Anti-patterns**\n\nInline comments (WRONG: `#` fails to parse):\n```lemma-skip\ndata customer_age: number -> minimum 0  # input\nrule discount: 0%  # default\n```\n\nCommentary after `uses` (WRONG). RIGHT: commentary immediately after `spec` (see **Syntax**).\n\nMega-rule (WRONG):\n```lemma-skip\nrule final_total:\n  base_price * qty\n  - base_price * qty * (0% unless qty >= 10 then 10% unless is_premium then 20%)\n  + (15 unless base_price * qty >= 100 then 10 unless base_price * qty >= 200 then 0)\n```\n\nDecomposed pipeline (RIGHT):\n```lemma\nspec order_pricing\n\ndata base_price: number -> minimum 0\ndata qty:        number -> minimum 0\n\nrule subtotal:             base_price * qty\nrule discount_percentage:  0% unless qty >= 10 then 10%\nrule discount_amount:      subtotal * discount_percentage\nrule total_after_discount: subtotal - discount_amount\nrule shipping_cost:        15 unless total_after_discount >= 100 then 10\nrule final_total:          total_after_discount + shipping_cost\n```\n\nHardcoded input (WRONG): `rule discount: 10 * 0.1`\nRIGHT: `data qty: number` then `rule discount: qty * 0.1`\n\nWrong unless order (WRONG: VIP gets 20% not 25%):\n```lemma-skip\nrule discount: 0%\n  unless is_vip then 25%\n  unless qty >= 50 then 20%\n```\nRIGHT: specific override last (`qty` tiers first, `is_vip` last).\n\nPlaceholder (WRONG): `data customer_name: \"TODO\"`\nRIGHT: `data customer_name: text`\n\nError vs Veto: `5 and \"text\"` = planning Error. `unless age > 120 then veto \"\u{2026}\"` = runtime Veto.\n\nVeto-as-rejection (WRONG: denial is answerable; see **Veto**):\n```lemma-skip\nrule is_eligible: true\n  unless age < 18 then veto \"Must be 18+\"\n  unless has_id is false then veto \"ID required\"\n```\nRIGHT: boolean rules composed with `and` (`is_old_enough and has_valid_id`).\n\nUnnecessary `repo` (WRONG). RIGHT: single-file `spec` without `repo`.\n\nNo `or` (WRONG): `rule is_eligible: is_adult or has_guardian`. See **Syntax**; use `unless` or separate booleans.\n\nConstraints on rules (WRONG): `rule discount: 10% -> help \"\u{2026}\"`. `->` is data-only (see **Syntax**).\n\nDomain-blind polarity / double denial (WRONG):\n```lemma-skip\nrule can_ship: yes\n  unless in_stock is false then no\n  unless address_complete is false then no\n```\nRIGHT: domain principle + positive grant (see **Rules**):\n```lemma-skip\nrule can_ship: no\n  unless in_stock and address_complete then yes\n```\n\n\n---\n\n## See also\n\n- [Learn guide](https://github.com/lemma/lemma/blob/main/cli/documentation/learn/readme.md)\n- [Reference](https://github.com/lemma/lemma/blob/main/cli/documentation/reference/readme.md)\n- [LemmaBase search](https://lemmabase.com/search?q=)\n- [Examples](https://github.com/lemma/lemma/tree/main/cli/documentation/examples)\n\nDecimals at JSON boundaries: pass as strings. Detail: [Numeric precision](https://github.com/lemma/lemma/blob/main/cli/documentation/learn/precision.md).\n";