Cookpit v3.2 — Canonical Patterns Reference
Patterns that the systematic walk confirmed work in v3.2 but were NOT explicitly published in
rules.md. This document publishes them as canonical reference so the chef-detective doesn't have to derive them per recipe.Cross-references rules.md J (processes), D (lane model), N4 (leadTime), and the prompt's deductive working order.
1. Concurrency patterns (rules J supplement)
The walk confirmed six concurrency patterns work in v3.2. All are schema-conformant; this section names them so the chef-detective and downstream consumers share vocabulary.
1.1 Parallel workstreams on different lanes
Two cooking activities run concurrently on different lane streams of the same course. The primary lane (M1, S1, D1) carries the dominant strand; secondary (M2 etc.) carries the parallel strand.
Examples: carbonara (pasta on M1, pancetta on M2), pork-fillet-braised-cheeks-and-pork-belly (cheek braise on M1, confit on M2 — fully concurrent for 8 hours).
Process model: two processes[] entries, each with its own
startTask and endTask on the appropriate lane.
1.2 Intra-minute lane usage for tight clusters
Three sub-actions of a single source moment fire on M1 / M2 / M3 five seconds apart, within the same minute. The lanes here are not "parallel workstreams" — they are intra-minute publication slots that the lane model's seconds invariant happens to provide.
Example: carbonara final cluster at minute 10 (transfer on M1, egg-cheese in on M2, loosen on M3).
Process model: rules.md D4 explicitly blesses this reading ("Lanes are intra-minute publication slots…either parallel workstreams or tight intra-minute action sequences"). Either reading is conformant.
1.3 Cross-lane processes
A single process spans two lanes — startTask on M1, endTask on M2 (or any combination). The duration interval is computed at minute precision ignoring lane seconds.
Example: no current active-corpus example. The pattern is schema-conformant and reserved for future corpus entries that need it. A canonical illustration would place startTask at 00:00:30.M1 and endTask at 00:03:35.M2 — duration PT3M, the 3-minute clock-time gap between minute 0 and minute 3, regardless of lanes.
Process model: v3.2 places no per-lane restriction on a process's startTask and endTask; both are taskId references regardless of lane.
1.4 Shared-boundary chains
A chain of processes where consecutive processes share a boundary task — process A's endTask IS process B's startTask. The boundary task is the moment that simultaneously ends A and starts B.
Example: boeuf's pearl-fry → mushroom-fry pair (sharing endpoint).
Process model: process.startTask and process.endTask may
appear in multiple processes[] entries; the schema places no
uniqueness restriction.
1.5 Serial-vessel chains
A specialisation of 1.4 where the shared-boundary chain happens on a single cooking vessel. The vessel is reused; the boundary tasks are hand-offs from one cooking style to the next.
Example: no current active-corpus example. The pattern is schema-conformant and reserved for future corpus entries that reuse a single cooking vessel through consecutive cooking styles.
Process model: identical to 1.4; the "single vessel" semantics
are entirely encoded by the same equipmentRefs appearing on each
linked task.
1.6 Fully concurrent passive cookery on different lanes
A specialisation of 1.1 where two long passive processes run for extended (multi-hour) durations entirely concurrently. The lane model handles the parallel cookery; the live timer covers the longer of the two.
Example: pork-fillet-braised-cheeks-and-pork-belly (8-hour cheeks on M1, 8-hour confit on M2, fully overlapping minute 8 → minute 488).
Process model: identical to 1.1; the "fully concurrent passive" semantics are encoded by both processes' duration spanning the same clock-time window.
2. Lane-model dual reading (rules D supplement)
Rule D4 explicitly blesses two readings of the lane model:
- (a) parallel workstreams — primary lane carries the main workstream of a course; secondary and tertiary lanes carry simultaneous parallel workstreams.
- (b) tight intra-minute sequences — when the source crowds a moment with multiple sub-actions in a single minute, the secondary and tertiary lanes carry those sub-actions in their source order five seconds apart.
Both readings are corpus-confirmed working. The chef-detective picks the reading that fits the source: when two genuinely parallel strands run on different vessels, use (a); when the source's tight moment of "off-heat → eggs in → toss" wants firing within seconds of one another, use (b).
2.1 When to use which reading
| Situation | Lane reading |
|---|---|
| Source has 'meanwhile' / 'while X cooks' phrasing | (a) parallel |
| Two strands on two distinct vessels for ≥1 minute | (a) parallel |
| Source crowds 3+ sub-actions in one minute | (b) cluster |
| Source has a fast hand-off (off-heat / pour / toss) | (b) cluster |
| Sequential prep on a single vessel | M1 only — no secondary lane needed |
2.2 The single-course primary-lane principle
A recipe with a single course (courses: ["main"]) MAY use M1, M2
and M3 freely under either reading. M2 and M3 do NOT require a
parallel-workstream justification when they're used for tight
intra-minute clustering.
A recipe with multiple courses (e.g. ["starter", "main", "dessert"]) keeps each course's lanes scoped to that course's
sub-stream — D2 tasks must be course: "dessert", etc. The
lane-scope rule (D2 / D3) catches scope violations.
3. leadTime scale pattern (rules N4 supplement)
The walk established a clean three-tier leadTime pattern:
| Source phrasing | leadTime value |
|---|---|
| "Make the day before" | P1D |
| "Sit-on-the-counter for 8 hours" / "Press 8h" | PT8H |
| "Preheat the oven to X" (oven-bring-up window) | PT15M |
| "Bring stock to room temperature" (15-30 min) | PT15M |
ISO 8601 duration syntax accepts hour, minute, second, day, week and
month components; the vocabulary is open at the upper end (P3D,
P1W for cured meats, P1M for fermented vegetables).
3.1 Make-ahead vs leadTime
leadTime records the window the source's text implies before the
live timer can start. It is not the duration the prereq item
takes (the press doesn't actively press for 8 hours; the meat
sits). For a prereq that takes active work (chopping, weighing,
grating), no leadTime is needed; the prep is implicitly fast.
3.2 Make-ahead exceeding 24 hours
For prereqs with leadTime > 24 hours (cured meats P3D, fermented
breads P5D, slow pickles P1W), the Chef app's pre-cook reminders
should surface the leadTime at the appropriate point in the user's
calendar. This is a runtime concern; the file-level encoding is
just the ISO duration.
4. Process-label phase qualifiers (lexicon §3.6 supplement)
Lexicon §3.6 limits process labels to "three to five words at most" in present-continuous form. The walk used phase qualifiers in two shapes:
- (a) verbal qualifiers —
Making the roux,Building the bechamel,Finishing the bechamel. Different verbs for different cooking phases of the same broad activity. - (b) positional qualifiers —
Cooking the casserole, first oven,Cooking the casserole, second oven. Same verb, different position in the cook sequence.
Both are within the 3-5 word limit and conformant to §3.6. The chef-detective picks (a) when the verbs genuinely differ (the roux phase is materially different from the milk-incorporation phase); (b) when the source's structure is sequential ("first roast" vs "second roast").
5. Detective-inserted check tasks (prompt.md supplement)
The detective frame in prompt.md endorses "deduce what the source implies". The pork-fillet-braised-cheeks-and-pork-belly file pushed this further: the detective ADDED two 4-hour mid-process check tasks (cheek-braise check on M1, confit check on M2) that the source omits, on the grounds that competent professional practice for any 6+ hour passive cook includes mid-cook level checks.
This is a recognised pattern, distinct from pure source transcription. The recommended treatment:
- timingBasis.basis = canonicalProcessEstimate.
- timingBasis.source = the explicit professional-practice
rationale, e.g.
"Source states no check moment; chef-detective adds a 4-hour check task to catch evaporation and prevent the 8-hour cook drying out. Competent professional practice for any 6+ hour braise." - The check task carries a sensory completion cue describing what the check IS ("liquor still gently steaming, cartouche sitting on top, no fast bubbles breaking the surface").
The Chef app surfaces these as scheduled prompts; the user acknowledges the check at the right moment, the cook continues.
5.1 When to insert a check task
| Process duration | Insert mid-cook check? |
|---|---|
| ≤ 30 minutes | No — too short to drift |
| 30-90 minutes | Discretionary — only for delicate cooks |
| 90-360 minutes | Yes — at the half-way mark |
| ≥ 360 minutes | Yes — every 4 hours |
In the active corpus, only the pork-fillet-braised-cheeks-and-pork- belly file needed mid-cook check tasks because only that recipe has cooks ≥ 6 hours.
6. Filename slug separator and lifecycle flag (rules.md O amendments)
The published examples use both separators —
spaghetti_carbonara.v3.2.cpt.A.jsonld and
perfect_boeuf_bourguignon.v3.2.cpt.A.jsonld use underscores;
pork-fillet-braised-cheeks-and-pork-belly.v3.2.cpt.A.jsonld and
roast-chicken-with-cider-and-sage.v3.2.cpt.A.jsonld use hyphens.
Rule O2 mandates hyphen-slug. The bundle resolves the convention
conflict by accepting either:
O2 (amended): the slug derivation may use either hyphen (-) or
underscore (_) as the word separator. Both are filesystem-safe and
filesystem-portable; both are reasonable readability defaults.
Implementations SHOULD pick one and apply it consistently within a
project. Slugs MUST NOT mix separators within a single filename.
Existing corpus files retain their separator; new projects MAY elect either convention.
6.1 Lifecycle flag in the filename (rules.md O1, O7)
The full filename pattern in v3.2 is:
<slug>.v3.2.cpt.<status>.jsonld
where <status> is the single-character lifecycle flag:
| Flag | Meaning | Stage produced |
|---|---|---|
U | Unauthenticated. AI Chef output, or any file not stamped by the canonical validator. | 1 (generation) |
A | Authenticated. Canonical validator has stamped the file at stage 3, embedded an authenticated cookpit.attestation block, and renamed the file. | 3 (attestation) |
Examples:
spaghetti_carbonara.v3.2.cpt.U.jsonld ← AI Chef output
spaghetti_carbonara.v3.2.cpt.A.jsonld ← post-attestation
The flag MUST agree with cookpit.attestation.status inside the file;
disagreement is V-ATTESTATION-CONSISTENCY (hard). The flag is decorative
— consumer trust is established by the cryptographic binding inside the
attestation block, not by the filename (rules.md A0.7, R10).
6.2 Backward compatibility for legacy corpus filenames
Files predating the <status> segment (the in-repo corpus's
spaghetti_carbonara.v3.2.cpt.jsonld and similar) are treated as U
for stage-4 consumer purposes (rules.md O8). New tooling SHOULD rename
such files to add the explicit U flag at the next opportunity.
Authenticated files MUST always carry the explicit A flag.
7. Six concurrency patterns table (cross-reference)
| Pattern | Active-corpus example |
|---|---|
| 1.1 Parallel workstreams on different lanes | carbonara (pasta/pancetta), pork-fillet (cheeks/confit) |
| 1.2 Intra-minute lane usage for tight clusters | carbonara final cluster |
| 1.3 Cross-lane processes | none — pattern reserved for future entries |
| 1.4 Shared-boundary chains | boeuf (pearl/mushroom) |
| 1.5 Serial-vessel chains | none — pattern reserved for future entries |
| 1.6 Fully concurrent passive cookery on different lanes | pork-fillet (8-hour cheeks/confit) |
All six patterns are schema-conformant in v3.2. Patterns without a current active-corpus example remain canonical and are recognised by the validator; the first new corpus entry that exercises one will provide the worked illustration.
8. Phase composition patterns (rules.md Q supplement)
The three-phase model (prepCook → preCook → liveCook) is v3.2's
mechanism for honestly representing recipes that cannot be flattened
into a single live timer. Four phase compositions are canonical; the
chef-detective picks the composition by reading the source.
8.1 liveCook only — the canonical default
Most recipes have no source evidence for prepCook or preCook.
Static prep lives in cookpit.prerequisites (chopping, weighing,
grating). The single liveCook phase covers everything from start
to plate.
| Source signature | Phase composition |
|---|---|
| Single-arc recipe with mise + cook + plate (≤ 90 min cook) | liveCook only |
Examples: carbonara, goulash, boeuf bourguignon, roast chicken with cider and sage.
8.2 preCook + liveCook — cooked-component recipes
The recipe builds a cooked mainstay component ahead of final assembly: a meringue base, a poached salmon for a pâté, a slow braise whose product is plated.
| Source signature | Phase composition |
|---|---|
| Source explicitly cooks-then-cools a component before final assembly | preCook + liveCook |
| Source has two distinct cook windows separated by a hold/cool | preCook + liveCook |
Examples: none in the active corpus. Canonical illustrations include a bake-and-cool meringue base before whip-and-plate, or a slow-cooked mainstay (ham hock, confit duck, shredded poached salmon) prepared ahead of a fast assembly cook.
8.3 prepCook + liveCook — active-prep recipes
The recipe needs an active timed prep window before cooking. Salt cures, presses, marinades that need active monitoring.
| Source signature | Phase composition |
|---|---|
| Source has a timed prep window with stated duration ≥ 30 min | prepCook + liveCook |
Examples: none in the active corpus. Canonical illustrations include a salt-cure for gravadlax, or a marinade that needs active monitoring at scheduled checkpoints.
8.4 prepCook + preCook + liveCook — full three-phase recipes
The recipe combines an active timed prep window AND a cooked mainstay component before final assembly. The pork-fillet-braised-cheeks-and-pork-belly file is the canonical example.
| Source signature | Phase composition |
|---|---|
| Source has timed prep + slow component cook + final assembly, all distinct windows | three-phase |
Example: pork-fillet-braised-cheeks-and-pork-belly (prepCook = 8 h press of the belly; preCook = 8 h cheek braise + 8 h confit, fully concurrent; liveCook = the final 1 h 45 min assembly cook on a single plate).
8.5 Phase-selection decision tree
Source has a timed-active-prep window with stated duration?
├── yes → declare prepCook
└── no → put the prep in prerequisites
Source cooks a mainstay component ahead of final assembly?
├── yes → declare preCook
└── no → no preCook
Always declare liveCook (it's the final-assembly cook ending in serving).
8.6 Phase handoff
prepCook and preCook are independent timed windows. When both
are declared, the Chef app starts BOTH A0 timers the moment the
file-level prerequisites are confirmed; the chef chooses at
runtime whether to run them concurrently (typical when the press
sits overnight while the slow braise begins on the morning of the
cook day) or sequentially (typical when the same chef must
actively monitor each in turn). Either upstream phase may fire its
time-up alarm first; that alone does NOT unblock liveCook.
liveCook is strictly downstream of both upstream phases and
becomes available only when the LAST of {prepCook, preCook} fires
its A0 time-up alarm. liveCook never overlaps prepCook or preCook
(rules.md Q4).
Cross-phase references are forbidden — process A in preCook cannot reference a task in liveCook (Q5). Continuity is encoded structurally by the join semantics alone: the union of the two upstream phases' completion cues is the precondition for liveCook.
8.7 What lives where
| Concern | Where it lives |
|---|---|
| Static prep (chop, weigh, grate) | cookpit.prerequisites.ingredients[] |
| Make-ahead with calendar offset | cookpit.prerequisites.<group>[].leadTime |
| Active timed prep window BEFORE the cook day | cookpit.prepCook.tasks[] / processes[] |
| Slow cook of a mainstay component | cookpit.preCook.processes[] + completion cue |
| Final assembly cook ending in serving | cookpit.liveCook.tasks[] / processes[] |
| Live prep inside the cook window (F2) | cookpit.liveCook.tasks[] — never relocated to prepCook (rules.md F5) |
| Resources used by any phase | File-level cookpit.ingredients[] etc. (closed-world) |
| Hotspots (file-level safety/quality moments) | cookpit.prerequisites.hotspots[] (taskRefs may target any phase) |
The closed-world rule (rules.md A2 / H1-H4) covers the entire file across all phases — there is no per-phase ingredient list.
The "live prep stays live" row deserves emphasis. The presence of
a cookpit.prepCook block in the file is NOT a licence to drag
deglazing, finishing herbs, mounting butter, slicing meat off the
bone, tempering chocolate, "while X cooks" actions or any other
F2-qualifying live prep out of liveCook. F2 is the sole authority
on whether prep is live or pre-Start. The three-phase model adds
two more pre-Start primitives (prepCook for active timed windows;
preCook for cooked-component windows); it does NOT weaken F2.