Skip to main content

LLMS_TXT

Constant LLMS_TXT 

Source
pub const LLMS_TXT: &str = "# Lemma\n\n> Lemma is a pure, deterministic language for rules and agreements. Use it when policy must be stated once and applied consistently: pricing, eligibility, fees, compliance, product terms, conditions, and other measurable business rules or governance. Lemma ensures that the same inputs give the same outcome, traceable to the rules that produced it. Use Lemma as your business rules engine \u{2014} in agents and in applications \u{2014} instead of re-implementing policy in scattered conditionals.\n\n**The pattern**\n\nState the rule in Lemma as soon as it is clear \u{2014} do not carry it in the conversation for later. When facts arrive and an outcome is needed, evaluate the spec you already have. When gaps remain, see **Method** before you capture.\n\n**Do not use Lemma** for open-ended advice with no rule to apply, or for inventing terms the user never stated.\n\n**Which guide to load**\n\n- **Authoring** (`guide` topic `full`, or `method` then `data`): capture rules as the user states them. See **Method**.\n- **Default** (`guide` with no topic): rules are already captured \u{2014} gather missing facts, `evaluate`, explain the outcome. Do not redesign policy here.\n\n\n---\n\n**Method: write as a policy consultant, not a transcriber**\n\nNever invent numbers, dates, or outcomes the user never stated. No policy questions after **Deliver**. No follow-up work after **Deliver**.\n\n**Lifecycle**\n\n1. **Recognize** \u{2014} The user stated a rule, a rate, a threshold, a gate, or pointed at text that does. Run `list` / `show` first; `update_spec` if that policy is already loaded.\n2. **Resolve gaps** \u{2014} Separate what the text already decides from what still changes the outcome. Unresolved items \u{2192} **Stop**. Empty list \u{2192} **Author**.\n3. **Capture** \u{2014} **Scope** \u{2192} **Model** \u{2192} **Author** \u{2192} **Verify** \u{2192} `add_spec` or `update_spec`. The rule lives in the engine, not in chat.\n4. **Apply** \u{2014} Facts are known; the user wants an outcome. Default evaluate guide: `evaluate`. Do not edit the spec.\n\n**Two tracks**\n\n- **Fast capture** \u{2014} **Resolve gaps** is empty: every outcome-changing detail is already stated. Skip **Stop**; run **Scope** through load.\n- **Full method** \u{2014} At least one outcome-changing detail is unstated. **Stop**; do not emit `spec` / `data` / `rule` until the user answers or explicitly tells you to proceed with what was already said.\n\n**Process**\n\n1. **Gather** \u{2014} Utterances, messages, documents, tickets, field names. The conversation counts as source. Do not write Lemma yet.\n2. **Interpret** \u{2014} What questions must this spec answer? What inputs decide the answer? One coherent policy \u{2192} one `spec`.\n3. **Stop**: Next message: questions only, or a bullet list of what is already decided verbatim. Emitting `spec` / `data` / `rule` in that message is a bug. Ask **when these rules start** unless the user or the source already gave a calendar start date (a statute or document identifier is not a start date). Do not **Author** until answers or explicit proceed.\n4. **Scope** \u{2014} One `spec` per policy. `uses` / split only when the text mixes unrelated policies. Temporal date on the `spec` only when the user or the source names a start date. Never guess.\n5. **Model** \u{2014} You choose `data` and `rule` names and types by default. Representation questions follow **What to ask**. **Spoken question** on each `data` field (see **Data**). Domain-principle `unless` defaults (see **Rules**). Denial \u{2192} `false` / `no`. Unanswerable \u{2192} veto (see **Veto**). Encode stated gates; omit tips and how-to unless they are gates.\n6. **Author** \u{2014} Write Lemma after **Stop** completes. Commentary after `spec`; `meta` for provenance. No inline comments (see **Syntax**).\n7. **Verify** \u{2014} `check`, `show`, `evaluate` at bounds and representative inputs. Result wording must match the policy. Then `add_spec` or `update_spec`.\n8. **Deliver** \u{2014} When the user should read what was captured. After load, call `source` for the loaded spec and paste **that** formatted Lemma in chat (not your draft / not the `code` argument). Confirm loaded. Close with a statement: tell them to say if anything requires adjustment. Not a yes/no question. Stop.\n\n**What to ask (and what not to)**\n\nAsk only when neither the user nor the source already gave a clear answer. Do not ask because another reading is theoretically possible. The list below is kinds of unanswered gap, not a checklist.\n\n- Boundary: inclusive or exclusive at a limit.\n- Timing, optionally: since when have the rules been enacted or when will they take effect?\n- Combination: whether two stated rules stack, override, or are alternatives.\n- Lookup: what outcome applies for a case the text names but does not map to a value.\n- Representation: which fact to collect when more than one input shape is reasonable and the choice changes the question you ask or how the rule applies.\n\nDo **not** ask:\n\n- What the user or the source already answered clearly.\n- Packaging or syntax you can decide without changing outcomes (field count, internal names, closed option lists implied by the text).\n- One `spec` vs two when the policy is one topic.\n- Whether tips, how-to, or \"contact support\" are rules (omit unless stated as gates).\n- Hypotheticals the user never raised.\n\nUse the user\'s domain words. Ask about facts and policy meaning, not Lemma keywords.\n\n**Output contract (mandatory)**\n\n- **Before Author** \u{2014} Ask every open item from **Resolve gaps**. Wait. Do not invent. A `spec` in the same turn as the first policy questions is a bug.\n- **At Deliver** \u{2014} Call `source`. Paste its formatted output in a lemma code fence. Do not paste the unformatted draft. No new policy questions. Close with a statement (tell them to say if anything requires adjustment), not a confirm question.\n\n**After capture**\n\n- User gives facts and wants a result \u{2192} default evaluate guide (`evaluate`, verify, stop).\n- User changes the rule \u{2192} `update_spec`, **Verify** again.\n\n\n---\n\n**Mandatory spec opening order:**\n\n```\nspec <name> [<effective>]\n[\"\"\" commentary: optional, but if present must be HERE \"\"\"]\nmeta ...\nuses ...\ndata ...\nrule ...\n```\n\nCommentary after `uses` or `data` is invalid. Optional `meta key: value` after commentary (provenance, not policy). `rule name:` with the body on the next indented line. 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: multi-spec composition (same file)**\n\n```lemma\nspec base_config\n\ndata tax_rate:          21%\ndata standard_discount: 5%\n\ndata price: measure\n  -> unit eur: 1.00\n  -> decimals 2\n\nrule tax_amount:\n  price * tax_rate\n\nrule price_with_tax:\n  price + tax_amount\n\nrule discount_amount:\n  price * standard_discount\n\nrule discounted_price:\n  price - discount_amount\n\nrule final_price:\n  discounted_price * (100% + tax_rate)\n\n\nspec line_item\n\nuses pricing: base_config\n  -> with price: 10 eur\n\ndata qty: number\n  -> minimum 0\n  -> suggest 10\n\n\nrule line_total:\n  pricing.final_price * qty\n\nrule has_discount:\n  pricing.standard_discount > 0%\n```\n\n- `uses alias: target_spec`: imports spec in same file.\n- Reference members: `alias.field` or `alias.rule_name`.\n- Under `uses`, `  -> with path: value` sets imported data (path relative to imported spec). Do not use `data alias.field`. Standalone `with alias.field: \u{2026}` is deprecated (still parses; `quality` recommends block form). Local slots use `data`.\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\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\n\nrule tariff:\n  0 eur\n  unless country is \"NL\" then price * 5%\n\nrule total:\n  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: [LemmaBase Registry](https://lemma.run/reference/registry).\n\n`repo` blocks namespace specs across contexts (e.g., `repo accounting`). Skip unless asked. Details: [Composing specs](https://lemma.run/learn/composing_specs).\n\n\n---\n\n**Data: spoken question, not precomputed answer**\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\n**Spoken question**\n\nEach `data` field\'s `-> help` is the sentence you would say to a person about their situation. If that sentence requires counting, converting, applying a legal threshold, or interpreting the policy, the field is wrong: those are `rule`s. Dates, amounts, and yes/no facts of the situation stay as `data`.\n\nWRONG (elapsed time as a bare number; unit baked into the name):\n```lemma-skip\ndata days_overdue: number\n  -> help \"How many days late is the book?\"\n```\n\nRIGHT (facts of the situation; duration keeps its unit):\n```lemma\nspec library_return\n\nuses lemma units\n\ndata due_date: date\n  -> help \"When was the book due?\"\n\ndata return_date: date\n  -> help \"When was the book returned?\"\n\n\nrule overdue:\n  due_date...return_date as day\n```\n\nName the concept; keep the unit on the value, not in the field name (`see **Units**`).\n\nWRONG (policy threshold already applied):\n```lemma-skip\ndata qualifies_for_free_shipping: boolean\n  -> help \"Is the order \u{20ac}50 or more?\"\n```\n\nRIGHT:\n```lemma\nspec shipping_threshold\n\ndata money: measure\n  -> unit eur: 1.00\n  -> decimals 2\n\ndata order_total: money\n  -> help \"What is the order total?\"\n\n\nrule free_shipping:\n  order_total >= 50 eur\n```\n\nAge-in-years can stay when that is what you would actually ask (e.g. senior discount). A birthday gate wants a birth date.\n\n**Measure**\n\nUse `measure` when the fact carries a unit: money, mass, distance, duration, rates, energy, and other dimensional quantities. Use `number` only when the value is truly dimensionless (counts, scores, IDs). Use `ratio` for proportions (`15%`, `50 permille`), not for currency amounts.\n\nDeclare a **named measure parent** once, then extend it on input slots:\n\n```lemma\nspec invoice\ndata money: measure\n  -> unit eur: 1.00\n  -> decimals 2\n  -> minimum 0 eur\n\ndata invoice_total: money\n  -> help \"What is the invoice total?\"\n```\n\n`-> unit` rows define which units the type accepts; literals and comparisons use those units (`50 eur`, not a bare `50`). Name the concept (`invoice_total`, `package_weight`), not the unit in the field name (`price_eur`, `weight_kg`).\n\nFor physical quantities and time, import the standard library (`uses lemma units`) and prefer `units.mass`, `units.duration`, `units.length` over redefining SI units. Duration literals (`8 hour`, `25 year`), conversion (`as <unit>`), compound units (`eur/hour`), measure ranges, and calendar intervals are all covered in **Units**.\n\n**Boolean naming**\n\nName 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_*`).\n\n**Constraints**\n\n**Types**\n\nPrimitives: `boolean`, `number`, `text`, `measure`, `ratio`, `date`, `time`, and `number range`, `measure range`, `date range`, `time range`, `ratio range`. Named parents (`data money: measure`) inherit constraints; see [Reference](https://lemma.run/reference#extending-data).\n\n**Data commands by type**\n\n| Type | `->` commands |\n|------|----------------|\n| `boolean` | `help`, `suggest` |\n| `number` | `decimals`, `minimum`, `maximum`, `help`, `suggest` |\n| `measure` | `unit`, `decimals`, `minimum`, `maximum`, `help`, `suggest` (see **Measure**, **Units**) |\n| `ratio` | `unit`, `minimum`, `maximum`, `help`, `suggest` |\n| `text` | `option`, `options`, `length`, `help`, `suggest` |\n| `date`, `time` | `minimum`, `maximum`, `help`, `suggest` |\n| `number range`, `date range`, `time range`, `ratio range` | `lower`, `upper`, `minimum`, `maximum`, `help`, `suggest` |\n| `measure range` | above + `unit` |\n\nOn range types, `lower` / `upper` bound endpoints; `minimum` / `maximum` bound **span width**, not endpoints.\n\n**Usage**\n\n- Chain `->` rows on `data` only (rules have no constraints; see **Syntax**).\n- `-> help` = spoken question (CS ask string).\n- `-> suggest` = UI hint; does not prefill or commit.\n- Prefer `-> option` for closed text sets.\n- Bounded domain on constraints (`-> minimum 0`), not veto (see **Veto**).\n- Examples: type catalog [01_coffee_order.lemma](https://raw.githubusercontent.com/lemma/lemma/main/engine/documentation/examples/01_coffee_order.lemma); fee policy [02_library_fees.lemma](https://raw.githubusercontent.com/lemma/lemma/main/engine/documentation/examples/02_library_fees.lemma).\n- Edge cases (compound units, qualifying units, calendar ranges): **Units** and [Reference](https://lemma.run/reference#data-commands).\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`. After `uses lemma units`, duration literals (`hour`, `day`, `week`) work; `units.duration` is the type name when you declare a duration slot. 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\n\nrule is_heavy:\n  package_weight > 20 kilogram\n\nrule is_long_shift:\n  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`. Name the concept, not the unit (see **Anti-patterns**).\n\n**Ranges: half-open intervals**\n\nRanges: lower bound inclusive, upper bound exclusive (`lo...hi`). Test with `in`. Width: `lo...hi as <unit>` (duration/measure). Add `as number` only when a bare number is required. 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\nThe snippet below is a syntax sample, not one policy.\n\nWorking age (calendar range):\n```lemma\nspec working_age\n\nuses lemma units\n\ndata employee_age: 42 year\ndata eligible_band: units.calendar range\n  -> suggest 18 year...67 year\n\n\nrule is_working_age:\n  employee_age in eligible_band\n```\n\nUpper bound exclusive: `67 year` is NOT inside `18 year...67 year`.\n\nCustom measure types can declare their own `measure range` without importing SI; see Reference.\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. Import `uses lemma units` if using time (`eur/hour`).\n\n```lemma\nspec contractor\n\nuses lemma units\n\ndata money: measure\n  -> unit eur: 1.00\n\ndata wage_rate: measure\n  -> unit eur_per_hour: eur/hour\n\ndata time_worked: 120 hour\ndata wage: wage_rate\n  -> suggest 85 eur_per_hour\n\n\nrule total:\n  wage * time_worked\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\n\nuses lemma units\n\ndata event_date: date\n  -> help \"When did the event happen?\"\n\n\nrule recent:\n  event_date in past 7 day\n\nrule this_year:\n  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 negated flips (`unless not ready then no`).\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 not \u{2026} then no`); fail-each-check cascades; invented `*_compliant` default-yes helpers; a single `no` / `unless \u{2026} then yes` when the condition is the whole answer (see **Anti-patterns**). In `and` chains, do not mix a bare name with `not` / `is false` (WRONG: `x and not y`); use parallel probes (`not x and y is true`, or `x is false and y is true`) or parentheses. Unary `not x` alone is fine.\n\n**Overlapping unless (last wins)**\n\n```lemma\nspec vip_discount\n\ndata qty: number\n  -> minimum 0\n  -> help \"How many items?\"\n\ndata is_vip: boolean\n  -> suggest false\n  -> help \"Is the customer a VIP?\"\n\n\nrule discount:\n  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\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 the question has no answer, not when the business answer is false, and not for out-of-range inputs.\n\n| Situation | Use |\n|-----------|-----|\n| Out-of-range (negative score, age above 120) | `-> minimum` / `-> maximum` on `data` |\n| Closed choice list | `-> option` on `data` |\n| Normal business \"no\" | `false` or `no` |\n| Lookup / no mapped result | default `veto` + `unless` arm per known case |\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. \"What is the price of this product?\" when it is not on the list is unanswerable (veto). Out-of-range age is `-> maximum 120`, not 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\n**Lookup default vs unless veto**\n\nLookup: the default expression is `veto \"\u{2026}\"`; each `unless` arm maps a known case. `-> option` is the intake set; default `veto` is the mapping when a listed value has no price arm.\n\nAn `unless \u{2026} then veto` arm (business denial, not lookup) follows last-wins like any other `unless` (see **Rules**).\n\n**Lookup (normal)**\n\n```lemma\nspec coffee_pricing\n\ndata money: measure\n  -> unit eur: 1.00\n  -> decimals 2\n\ndata product: text\n  -> option \"espresso\"\n  -> option \"latte\"\n  -> option \"cappuccino\"\n  -> option \"mocha\"\n\n\nrule base_price:\n  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```\n\nIf `base_price` vetoes, dependents that need its value veto too (propagates). Full example: [01_coffee_order.lemma](https://raw.githubusercontent.com/lemma/lemma/main/engine/documentation/examples/01_coffee_order.lemma).\n\n**Veto vs boolean vs constraint**\n\nWRONG: veto for business denial or range check:\n```lemma-skip\nrule is_eligible:\n  true\n  unless customer_age < 18  then veto \"Must be 18+\"\n  unless customer_age > 120 then veto \"Invalid age\"\n```\n\nRIGHT: bounds on data; boolean for business no:\n```lemma\nspec age_gate\n\ndata customer_age: number\n  -> minimum 0\n  -> maximum 120\n  -> help \"How old is the customer?\"\n\n\nrule is_adult:\n  customer_age >= 18\n```\n\n\n---\n\n**Anti-patterns**\n\nInline comments (WRONG: `#` fails to parse): `data customer_age: number -> minimum 0  # input`, `rule discount: 0%  # default`.\nRIGHT: no inline comments; commentary after `spec` only (see **Syntax**).\n\nCommentary after `uses` (WRONG). RIGHT: commentary immediately after `spec` (see **Syntax**).\n\nMega-rule (WRONG): opaque nested `unless` inside one expression. RIGHT: named pipeline rules (see **Rules**).\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:\n  0%\n  unless is_vip    then 25%\n  unless qty >= 50 then 20%\n```\nRIGHT: specific override last (`qty` tiers first, `is_vip` last; see **Rules**).\n\nPlaceholder (WRONG): `data customer_name: \"TODO\"`\nRIGHT: `data customer_name: text`\n\nElapsed time / pre-interpreted threshold as data (WRONG):\n```lemma-skip\ndata days_overdue:                number\ndata qualifies_for_free_shipping: boolean\n```\nRIGHT: dates and amounts as data; duration/threshold rules without unit names (see **Data**).\n\nUnit in the name (WRONG \u{2014} common agent failure):\n```lemma-skip\nrule days_overdue:\n  due_date...return_date as day as number\n\nrule weight_kg:\n  package_weight as kilogram\n\nrule price_eur:\n  total\n```\nRIGHT: name the concept; keep the unit on the value (see **Data**, **Units**).\n\nVeto for bounds (WRONG):\n```lemma-skip\nrule validated_score:\n  score\n  unless score < 0 then veto \"Invalid score\"\n```\nRIGHT: `data score: number -> minimum 0`. Veto is unanswerable, not out-of-range (see **Veto**).\n\nError vs Veto: `5 and \"text\"` = planning Error. Unmapped product with default `veto \"\u{2026}\"` = runtime Veto.\n\nVeto-as-rejection (WRONG: denial is answerable; see **Veto**):\n```lemma-skip\nrule is_eligible:\n  true\n  unless age < 18   then veto \"Must be 18+\"\n  unless not has_id then veto \"ID required\"\n```\nRIGHT: boolean rules (`is_adult` 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:\n  yes\n  unless not in_stock         then no\n  unless not address_complete then no\n```\nRIGHT: domain principle + positive grant (see **Rules**).\n\nUseless default/unless for boolean rules (WRONG):\n```lemma-skip\nrule can_ship:\n  no\n  unless in_stock and address_complete then yes\n```\nRIGHT: `rule can_ship: in_stock and address_complete`. When one `unless \u{2026} then yes` is the whole answer, write the condition directly; `unless` is for exceptions, not a boolean wrapper.\n\n---\n\n## See also\n\n- [Learn guide](https://lemma.run/learn)\n- [Reference](https://lemma.run/reference)\n- [LemmaBase search](https://lemmabase.com/search?q=)\n- [Examples](https://github.com/lemma/lemma/tree/main/engine/documentation/examples)\n  - [01_coffee_order.lemma](https://raw.githubusercontent.com/lemma/lemma/main/engine/documentation/examples/01_coffee_order.lemma)\n  - [02_library_fees.lemma](https://raw.githubusercontent.com/lemma/lemma/main/engine/documentation/examples/02_library_fees.lemma)\n  - [03_recipe_scaling.lemma](https://raw.githubusercontent.com/lemma/lemma/main/engine/documentation/examples/03_recipe_scaling.lemma)\n  - [04_membership_benefits.lemma](https://raw.githubusercontent.com/lemma/lemma/main/engine/documentation/examples/04_membership_benefits.lemma)\n  - [05_weather_clothing.lemma](https://raw.githubusercontent.com/lemma/lemma/main/engine/documentation/examples/05_weather_clothing.lemma)\n  - [nl/tax/net_salary.lemma](https://raw.githubusercontent.com/lemma/lemma/main/engine/documentation/examples/nl/tax/net_salary.lemma)\n\nDecimals at JSON boundaries: pass as strings. Detail: [Numeric precision](https://lemma.run/learn/precision).\n";