Safety & Best-Practice Rules

Every rule in CKL encodes a real cooking principle. These aren't arbitrary constraints—they are things that professional chefs know implicitly, written down so that everyone benefits.

Rules fall into four categories:

  • Safety rules prevent mistakes that can make someone sick. A recipe that violates a safety rule will not compile. These are non-negotiable.
  • Declaration rules keep the ingredient and equipment lists complete and consistent with the procedure. These are errors.
  • Structural rules reject procedures that cannot be executed as written. These are errors.
  • Best-practice rules flag technique issues that will produce poor results. A recipe will still compile, but you'll see a warning explaining what could go wrong.

Every diagnostic carries a code; the Rule Index lists all of them with their severity.

---

Safety Rules

These rules enforce food-safety science. They cannot be bypassed in normal mode.

Poultry must reach safe temperature (E101)

The principle: Poultry carries salmonella and campylobacter bacteria that are only destroyed at 74°C (165°F) or above. Cooking chicken to a lower temperature risks food-borne illness. CKL enforces this automatically. If your recipe specifies a poultry target temperature below 74°C, it is rejected:
# This recipe is unsafe — CKL will stop you
ingredient "chicken breast" 500g
until internal_temp >= 60C        # ✗ too low for poultry

Write it correctly:

ingredient "chicken breast" 500g
until internal_temp >= 74C        # ✓ safe for poultry

> Why 74°C? This is the instantaneous-kill temperature recommended by food safety authorities worldwide (USDA, FSA, EFSA). Lower temperatures can be safe with extended hold times (sous vide), but CKL enforces the conservative standard because a notation read by anyone must default to the safest guidance.

Poultry temperature is never verified (W101)

The principle: A recipe can be unsafe by omission. If poultry is declared but the recipe never checks internal temperature, nothing in the procedure establishes that the meat is cooked through — a timing-only instruction depends on thickness, starting temperature, and pan, none of which the recipe controls.

This is a warning, not an error: the recipe may genuinely verify doneness elsewhere (a sub-recipe, a stock that boils for hours). But a poultry recipe intended for a cook to follow should state the check explicitly.

ingredient "chicken thigh" 500g
pan "skillet" 30cm
heat pan to 180C
apply "chicken thigh" to pan
wait 12min                        # ⚠ W101 — time alone doesn't prove doneness

Add the verification:

wait 12min
until internal_temp >= 74C        # ✓ doneness is established

---

Declaration Rules

These rules keep the "what you need" section complete and truthful. They are errors: a recipe whose ingredient or equipment list doesn't match its procedure cannot be followed.

Every ingredient must be declared (E102)

The principle: A recipe should account for everything that goes into a dish. Using an ingredient that was never listed in your "what you need" section means your ingredient list is incomplete—someone following the recipe won't have it ready.

This mirrors mise en place: before you start cooking, every ingredient is measured, prepared, and visible.

# Incomplete — "chicken" appears from nowhere
apply "chicken" to pan            # ✗ not declared

Declare it first:

ingredient "chicken" 500g         # ✓ declared
apply "chicken" to pan

Every piece of equipment must be declared (E103)

The principle: Just as you wouldn't start cooking without knowing which pans and tools you need, CKL requires that all equipment be declared before you reference it. This ensures the equipment list is complete and accurate.
# What pan? What size? A reader wouldn't know
heat pan to 180C                  # ✗ pan not declared

Declare your equipment:

pan "skillet" 30cm                # ✓ declared with size
heat pan to 180C

Equipment must exist in the database (E104)

The principle: Equipment names are not free text — each one resolves to an entry in equipment/, which carries the purpose, materials, heat sources, and buying guidance shown on the generated site. An unrecognized name means the recipe links to nothing, so the name is rejected rather than silently dropped.
pan "nonstick frypan thing" 28cm  # ✗ E104 — no such entry

Use a known name or one of its aliases:

pan "non-stick pan" 28cm          # ✓ resolves to equipment/non-stick-pan.json

To add genuinely new equipment, drop a JSON file into equipment/ — no compiler change is needed. The same check applies to the equipment named in a stage declaration.

No duplicate ingredient declarations (E105)

The principle: An ingredient appears once in the "what you need" list, with its full quantity. Declaring it twice means the shopping list is wrong and the true total is ambiguous — a reader cannot tell whether 30g and 20g means 50g or a correction.
ingredient "butter" 30g
ingredient "butter" 20g           # ✗ E105 — declare 50g once instead

If the ingredient is genuinely used at two points, declare the total once and split it across steps (see I601).

Every declared ingredient must be used (E106)

The principle: An ingredient listed but never used is the mirror image of E102 — the cook buys and measures something the procedure never calls for. In practice this is almost always a step that was deleted or never written.
ingredient "thyme" 3g             # ✗ E106 — never applied to anything

Either use it in the procedure or remove the declaration.

Combine must reference declared stages (E107)

The principle: combine merges the results of named preparation stages. Referencing a stage that was never declared means there is nothing to merge — usually a typo in the stage name or a stage that was removed.
stage zabaione bowl "mixing bowl" 3l
stage cream_mix bowl "mixing bowl" 5l
combine zabaione whipped_cream in cream_mix   # ✗ E107 — "whipped_cream" was never declared

Declare every stage before combining it:

stage zabaione bowl "mixing bowl" 3l
stage whipped_cream bowl "mixing bowl" 2l
stage cream_mix bowl "mixing bowl" 5l
combine zabaione whipped_cream in cream_mix   # ✓ both sources exist

---

Best-Practice Rules

These rules encode technique knowledge that experienced cooks take for granted. They produce warnings, not errors—your recipe will still compile, but the warning explains why the result may suffer.

Searing needs a hot pan (W001)

The principle: The Maillard reaction—the browning that creates deep flavor on the surface of meat—requires temperatures above ~150°C. Placing protein in a cold or insufficiently heated pan causes it to steam and stick rather than sear. The result is grey, tough meat without a crust.
# Warning: searing without heat
pan "skillet" 30cm
apply "steak" to pan              # ⚠ pan isn't hot

Heat the pan first:

pan "skillet" 30cm
heat pan to 200C                  # high heat for searing
apply "steak" to pan              # ✓ Maillard reaction can occur

Deglazing needs a hot pan (W002)

The principle: Deglazing is the technique of adding liquid to a hot pan to dissolve the fond (the browned bits stuck to the surface). If the pan is cold, the liquid won't sizzle, the fond won't release, and you lose the concentrated flavor that makes a pan sauce worth making.
# Warning: deglazing a cold pan
pan "skillet" 30cm
deglaze pan with "wine"           # ⚠ pan isn't hot

Ensure the pan is hot:

pan "skillet" 30cm
heat pan to 180C
deglaze pan with "wine"           # ✓ fond will release

Shaping needs mixing first (W201)

The principle: Shaping a mixture — patties, dumplings, croquettes — relies on the structure that mixing develops. Forming something that was never mixed produces a mass that falls apart in the pan, because nothing binds the components together.
bowl "mixing bowl" 3l
apply "chickpeas" to bowl
apply "flour" to bowl
shape bowl into balls             # ⚠ W201 — nothing has bound the mixture

Mix before shaping:

mix bowl until combined
shape bowl into balls             # ✓ mixture holds together

---

Structural Rules

Unlike the best-practice warnings above, these are errors — each marks a procedure that cannot be executed as written.

Consecutive wait instructions (E301)

The principle: Two consecutive wait or rest instructions are not allowed. Having them back-to-back is either a mistake — the durations should be added together — or a cooking action is missing between them. This is an error and will prevent compilation.
# Warning: consecutive waits
wait 10 min
wait 5 min                        # ⚠ consecutive wait — combine into one

Combine them:

wait 15 min                       # ✓ single wait

Or insert an action between them if they serve different purposes:

wait 10 min
flip "chicken breast"
rest 5 min                        # ✓ action separates the waits

Cut requires knife (E401)

The principle: Any recipe that uses cut statements must declare a knife equipment. Cutting without a knife is physically impossible and would leave the recipe's equipment list incomplete. This is an error and will prevent compilation.
# Error: no knife declared
cut "onion" style dice            # ✗ E401 — where's your knife?

Fix by declaring a knife:

knife "chef's knife"
cut "onion" style dice            # ✓ knife is declared

---

Invalid reduction target (E501)

The principle: Every reduce statement must specify a valid culinary target describing the desired end state of the liquid. Vague or unknown targets are rejected because they make the recipe ambiguous — a cook reading "reduce 90min" has no idea whether the goal is a nearly-dry pan or a gently melded braise.
# Error: invalid target
reduce 5min until done            # ✗ E501 — "done" is not a valid reduction target

Valid targets are:

  • au_sec — reduced nearly dry (wine reductions, post-deglaze)
  • nappe — coats the back of a spoon (sauces)
  • thickened — noticeably thicker (tomato sauces)
  • concentrated — intense, reduced volume
  • melded — flavors fully integrated (long braises)
  • glossy — shiny, emulsified (butter sauces)
reduce 3min until au_sec          # ✓ wine reduction
reduce 90min until melded         # ✓ long braise for flavor integration

---

Informational Rules

Informational diagnostics never fail a build. They point at places where a recipe is correct but could be more precise.

Split amounts across steps (I601)

The principle: An ingredient is declared with a single total, but used in several instructions. The recipe is valid — yet a cook reading step 4 has no way to know how much of the 600g goes in there.
ingredient "chicken breast" 600g  # ℹ I601 — used in 4 instructions

Where it matters, specify per-step amounts so they sum to the declared total. Where it genuinely doesn't — an ingredient added in one go across a couple of adjacent steps — the note can be left as is.

---

Rule Index

| Code | Severity | Rule |

| ---- | -------- | ----------------------------------------------- |

| E101 | error | Poultry below 74°C minimum |

| E102 | error | Ingredient used but never declared |

| E103 | error | Equipment used but never declared |

| E104 | error | Equipment name not found in equipment/ |

| E105 | error | Duplicate ingredient declaration |

| E106 | error | Declared ingredient never used |

| E107 | error | combine references an undeclared stage |

| E301 | error | Consecutive wait / rest instructions |

| E401 | error | cut used without a declared knife |

| E501 | error | Invalid reduce target state |

| W001 | warning | Applying to unheated equipment (searing) |

| W002 | warning | Deglazing equipment that was never heated |

| W101 | warning | Poultry used without any temperature check |

| W201 | warning | Shaping a mixture that was never mixed |

| I601 | info | Ingredient total spread across several steps |

---

Strict Mode

By default, best-practice warnings let your recipe compile so you can iterate. When you want to enforce the highest standard—for example, before publishing a recipe—run in strict mode:

ckl lint recipes/ --strict

In strict mode, warnings become errors. Every sear must have sufficient heat. Every deglaze must follow heating. This is the standard used for recipes in the official knowledge base.

Extending the Rules

CKL's rule system is designed to grow as the knowledge base grows. New safety rules (safe temperatures for other proteins, allergen declarations) and new best-practice rules (resting times, emulsion stability) can be added by implementing the LintRule interface. See ARCHITECTURE.md for the technical details.