Status: settled design — shipped. rev 13. rev 13 adds §CR17 — three
group frames (Supply & inventory / Roasting & sales / Forecast metrics,
localised EN / KO / JA via §TLO12) and shifts the five Registers +80 px right so
the column clears the widest dessert_prep render and sits inside its frame
(also retiring a pre-existing ~21 px Parameter↔Register overlap). Visual coords
only — no engine / value / wiring / schema change; the graph is ~80 px wider so
the initial fit zoom drops from ≈ 0.49 to ≈ 0.465 at 1280 (still above the L1
LOD floor). rev 12 narrowed the §CR7
Timeline default from 8 series to 4 (a legend-density pass — one-row legend in
EN / KO / JA; the rest behind +N more, no engine / schema / wire change).
rev 1–3 fixed the
model, the comprehension check, and shipping as the 4th Templates entry; rev 4
moved the Korean labels to a shared fresh-open label overlay
(docs/template-label-overlay.md); rev 5 aligned
the build order; rev 6 found the frozen-engine contract conflict (a
parameter node changed no simulated number) and decided Direction 1 — a
minimal, general parameter → simulation input capability in its own prior PR —
plus the §CR2.0 repositioning as a simplified operating-flow simulation
example (recorded anonymised in
docs/product-direction.md §PD11); rev 7 folded
loop-model/2 in and locked the five @param references.
rev 8 — the roasting node is a deterministic Gate, not a Converter.
Building impl PR (2) surfaced that a Converter cannot carry
"daily roast amount (kg)": under frozen Engine A a Converter's output is
producedₖ = f·outRateₖ with a single f ∈ [0, 1] and ≤ 1 activation/step
(SEMANTICS.md I2), so a Converter whose input edge is
@daily_roast_kg produces at most its constant outRate — the lever stops
mattering the moment green beans are not the binding constraint, and moves the
output backwards when they are (raising the lever lowers f). A deterministic
Gate expresses the quantity exactly and is the correct model, not a
work-around: the one input edge green_stock → roasting carries
@daily_roast_kg (still exactly one @param reference, §CR6.1), the Gate pulls
T = min(@daily_roast_kg, green available), and two fixed weights split T into
82 % roasted-bean stock and 18 % roasting weight loss — mass-conserving
(0.82·T + 0.18·T = T). When green stock is short, T falls, so the roasted
output and the weight-loss path fall together. rev 8 updates §CR6.1, §CR9.1
and §CR3.5 (the roasted-supply-margin proxy now reads the live roasted-stock
level) and adds CR-D14.
rev 9 — two blockers found in Hanrim's screen review of the impl PR.
- Lever 1 (
daily_customersfootfall) drove no product flow. It pushed a@daily_customersamount into a stand-alonecafe_demandtally Pool and was read by the Register formulas, but the roasted-stock / dessert trajectories barely moved — a "the Register numbers move only" pseudo-link, exactly what §CR16.2 rules out. Fix: replace it withcafe_retail_demand_kg(kg of roasted beans the cafe + retail counter sell per day), wired straight onto theroasted_stock → cafe_retailedge as aDrainpull. The disconnectedcafe_footfallSource andcafe_demandPool are removed — 25 → 23 nodes, and now every one of the five levers moves a real Pool trajectory. Total revenue/Total cost/Operating profitread like realised figures. They are computed from the ordered / planned levers assuming all demand is met, so revenue keeps rising even when roasted stock cannot fill the orders. Fix: rename them toProjected daily revenue/Planned daily cost/Projected daily operating marginand state plainly (here, §CR3.5, §CR8, and every scenario) that they are planning proxies, not realised revenue / cost / accounting operating profit. Never shown asactual/realised/실제 매출/이익in a title, label, blurb, Timeline series, or §CR9 scenario.
rev 9 updates §CR3.5, §CR6, §CR6.1, §CR7, §CR8, §CR9 / §CR9.1, §CR11.1, §CR12.1 and adds CR-D15. No engine / schema / wire change.
rev 10 — the three money proxies get a display unit. They read as bare
numbers today, which looks like an abstract score. Fix: set the advisory
unit hint to the locale-common string kKRW/day on projected_revenue /
planned_cost / projected_operating_margin (the P price constants are in
thousands of KRW; the defaults read 464 / 346.8 / 117.2 kKRW/day). unit
renders as the Register's sub-line — no label duplication, no value / default
/ trajectory change, planning-proxy framing unchanged (§CR3.5 / §CR8). The two
signed stock proxies stay unitless. Pinned in coffee-roastery.test.ts and the
committed examples/coffee-roastery.json. No engine / schema / wire change.
rev 11 — the external comprehension check ran (2026-09-03): partial pass.
§CR11 build-order step 9 completed. Flow, naming (within the Template's stated
simplified scope), and lever discovery passed; a completed before/after
explanation of each lever's result direction was not demonstrated;
real-operations suitability is not claimed (one reviewer expects limited
real-work pull across small / mid / large operators, and sees an
education / explanation / scenario-comparison potential — a positive
observation, not verified market fit). rev 11 adds §CR11.5 (the result) and
CR-D16, updates §CR13 steps 9–11, and mirrors two anonymised observations
into docs/product-direction.md §PD11.1. Docs only — no
Coffee JSON / engine / UI / i18n / test change.
rev 7 — the §CR16 feature has shipped, so the "blocked" notes come out.
loop-model/2 (SEMANTICS-M2.md, Frozen; PR #103, merge
c194629) lets a v2 document's resource-edge flow be a single
@parameter-id reference the engine resolves once per step. rev 7:
- §CR2.1a (new) — this Template is the first bundled
loop-model/2/ schemaloop-studio/graph/2entry. - §CR6 — the five levers are locked to concrete
@paramreferences (which node, which edge — §CR6.1); eachflowis a single bare@parameter-idand nothing else. - §CR3.5 / §CR8 / §CR9 — the "blocked / pending PR (1.5)" notes are removed;
§CR9 now names, per scenario, which Parameter changes and which result
moves (§CR9.1). The two
supply − demandread-outs are renamed to roasted supply margin / dessert prep margin — signed proxies with an explicit+/−meaning, never called "missed sales" or "waste" (they are operating cues, not measured losses or accounting figures — §CR3.5). - §CR13 — steps 1–2 (this doc PR) and the §CR16 feature PR are done; the next step is the Coffee Template impl PR.
- CR-D12 / §CR16 are marked resolved.
The size budget (§CR5), the domain scope (§CR3–§CR4), the language mechanism
(§CR12), the §CR2.0 positioning, the limitation marker, and the §CR10 / §CR11
success bar are unchanged. Still no Coffee-specific engine logic and no new
expression grammar — the Template only consumes loop-model/2. This is a
non-frozen design doc — no loop-*/N id, no Frozen marker — and merges as
settled design, implementation pending, like
docs/large-graph-readability.md and
docs/example-mmo-progression.md.
rev 7 is docs-only — this doc. No feature code, no README change (the
roadmap line and the general positioning are checked last, §CR13 step 10).
Implementation was three PRs: (1) the shared label overlay — merged
(PR #100, b938aed); (1.5) the general parameter → simulation input
feature = loop-model/2 — merged (PR #103, c194629); (2) this Template —
examples/coffee-roastery.json (schema loop-studio/graph/2), a Korean label
block, a fixture test, src/model/templates.ts, src/components/templateKeys.ts,
two name/blurb keys in en.ts + ko.ts. Nothing else in src/ for
impl PR (2): no engine, schema, wire / loop-revision/N beyond what
loop-model/2 landed.
This is the first external product-direction validation
(docs/product-direction.md §PD2 / §PD6), not a
re-run of the Early MMO progression exercise (which proved the expressive
ceiling of a large model). It asks:
Can someone with no game framing and no Loop Studio vocabulary grasp a realistic business flow in 1–2 minutes and, by changing a few values, read the results in a way they can predict?
The Productization track's premise is that blank-canvas authoring is impractical, so the default path is adjust a verified template (§PD2). Before building the Slice-2 filter UI, it is worth one concrete Template in a real, non-game domain to test that premise against a person.
The trigger. An external reader, shown Loop Studio with a game framing, said the game vocabulary meant nothing to them — but, from the screen alone, guessed it might be for real production / work flow, budgeting, organisation / HR management, or asset & logistics flow. That is a strong signal that Loop Studio reads as a real-business tool to a non-gamer, and also that the current positioning + samples do not say so. A coffee-roastery Template answers that with a screen, not a paragraph: green beans arrive, some are sold on, some are roasted and sold through cafe / online / retail, and stock and profit move as you experiment.
A coffee roastery is a good fit: a short left-to-right flow (buy green beans → roast → sell), intuitive levers (how much to roast, how many desserts to prep), and familiar tensions (running short of roasted stock; over- or under-preparing dessert).
In
- the contract for one Graph JSON registered as the 4th Templates entry: the model shape (§CR3), the size budget (§CR5), the five user-facing Parameters (§CR6), the recommended Timeline (§CR7), the Summary read-outs (§CR8), the validation scenarios (§CR9), and the completion criteria (§CR10);
- the comprehension check (§CR11), how this Template uses the shared label overlay (§CR12), the product role and boundary (§CR2), and the build order (§CR13);
- the 4th Templates entry — name / blurb / role / default state (§CR2) — and
the minimal registration code impl PR (2) adds (
templates.ts,templateKeys.ts, the KO dictionary entries, twoen.ts+ko.tsmenu keys).
Out
- Asking the reviewer to edit or "fix" the graph. The first check is read → adjust values → interpret results only (§CR11). Structural-editing viability is a later question.
- The Example / Template system itself. This is a single JSON file + a menu entry, not the packaging / surfaced-inputs work of §PD8-B.
- Any engine / schema / wire /
loop-revision/Nchange, and anysrc/change beyond the registration files above. - The modelling complexity in §CR4 — deliberately excluded.
- The label-overlay mechanism itself — designed and built separately
(
docs/template-label-overlay.md); this doc only uses it (§CR12). - LGR Slice 2+ — on hold until the external comprehension check (§CR11) is done.
An external, domain-informed reader commented twice before the example was even
sent to them. The substance (recorded anonymised in
docs/product-direction.md §PD11):
- A real roastery operations system would need lot- and varietal-level tracking, import / warehousing / cleaning-sorting / delivery, vendor / trade-partner management, real-time stock, WIP and lead-time monitoring, per-stage progress, and a financial layer — reorder point, safety stock, BEP, cost / cost-estimate / cost-accounting figures, and inventory-asset values that link to the financial statements — auto-produced, not hand-keyed. Manual keyboard-and-monitor entry would make such a tool too cumbersome to adopt.
- Showing only a few disconnected stages of the real distribution chain has limited operational usefulness: "expressing only some particular aspects — that may be valued as an artwork, but not as a practical tool."
- The stronger recommendation: decide the concrete use purpose first — who, in what setting, for what decision — and only then map the current work onto that field's vocabulary (production & operations management, process control, managerial / cost accounting; the reader also saw a path toward PERT / CPM project-network scheduling for large events). Keep extensibility open, but fix the purpose before the target industry.
That critique is right about an operations-management / ERP system, and this Template is not one, and is not a step toward claiming to be one. It is an operational-flow simulation example:
- not a system for entering real daily intake / stock;
- not lot / varietal / vendor management or product CRUD;
- not connected to a POS, inventory sensors, or an order system;
- not a WIP / lead-time / reorder-point / BEP / cost-accounting engine;
- not a replacement for any part of running a roastery;
- is a small, assumption-based model for changing a few operating assumptions and watching how green / roasted stock, sales, the roasted supply margin, the dessert prep margin and profit relate (§CR3.5 — signed proxies, not measured losses).
Positioning (adopted for the product, not just this example — §PD11): Loop Studio is not an ERP that manages live business data; it composes connected operating flows visually and compares outcomes as you change a few conditions — an operating-flow simulation. The coffee entry is a simplified one-day operating model, not a roastery management system.
Settled strings (KO verbatim):
| surface | KO | EN |
|---|---|---|
| menu name | 커피 로스터리 운영 흐름 |
Coffee roastery operations flow |
| menu blurb | 로스팅·판매·재고의 관계를 단순화해 살펴보는 운영 흐름 시뮬레이션 |
an operating-flow simulation for looking at how roasting, sales and stock relate, simplified |
limitation marker (Template surface / examples/README.md, §CR13) |
단순화한 시뮬레이션 예제이며 ERP나 실시간 모니터링 시스템이 아닙니다. |
a simplified simulation example — not an ERP or real-time monitoring system |
Never use "operations management" / "운영 관리" for this entry.
examples/coffee-roastery.json is the first bundled Template that declares
model-semantics version 2: its top-level schema is loop-studio/graph/2
(SEMANTICS-M2.md §M2-1), and its five surfaced levers are
resource-edge flow parameter references (@<parameter-id>, §CR6.1)
resolved by the engine once per step.
- The overlay is unaffected — the shared fresh-open label overlay
(
docs/template-label-overlay.md) still touches nodedata.labelonly;flowstrings,@ids,schema, and every serialized byte are English / stable across locales. openTemplate/loadGraphset the model version from the file — opening this Template from the menu loads it as v2 (the store'smodelVersioncomes from the file'sschema, exactly as an Import does; it is not the "explicit user promotion" path — a bundled v2 Template is v2 as authored).- Engine-affecting digest — a v2 document carries the
loop-model/2discriminator in theloop-revision/loop-workspaceengine digest (§M2-8); a v2 graph and a byte-identical v1 graph hash differently. - No new engine code — the Template consumes
loop-model/2; the impl PR touches onlyexamples/coffee-roastery.json, its fixture test, the four registration files, andexamples/README.md(§CR15).
-
File:
examples/coffee-roastery.json— a real Graph JSON with English canonical node labels, wired as the 4th Templates entry (templates.tsimports it), alongsiderisky-factory.json/mmo-progression.json. Korean (and later locales') node labels are supplied by the shared fresh-open label overlay (docs/template-label-overlay.md), not a second JSON (§CR12). -
Role: a Template (
docs/product-direction.md§PD3) — the user changes the five surfaced values (§CR6) and re-runs. Not an Example (Early MMO is the locked Example); not a Building block. -
Opens editable — no
canvasLocked(unlike the MMO Example). The reviewer is meant to change values. -
The three shipped entries are untouched and keep their distinct jobs:
equilibrium— "Flowing equilibrium": the engine's basic steady flow.deadlock— "Bottleneck deadlock": a stall / back-pressure failure.mmo-progression— "Early MMO progression": large-model expressiveness, locked, run-and-observe.
-
The 4th entry:
field value id coffee-roasterytemplates.coffeeRoastery.nameKO: 커피 로스터리 운영 흐름 · EN: Coffee roastery operations flow templates.coffeeRoastery.blurbKO: 로스팅·판매·재고의 관계를 단순화해 살펴보는 운영 흐름 시뮬레이션 · EN: an operating-flow simulation for looking at how roasting, sales and stock relate, simplified limitation marker (on-canvas note / examples/README.md)KO: 단순화한 시뮬레이션 예제이며 ERP나 실시간 모니터링 시스템이 아닙니다. · EN: a simplified simulation example — not an ERP or real-time monitoring system graph node labels English canonical, localized on fresh-open by the overlay (§CR12) — KO in a KO UI, EN in an EN UI role a small Template for changing a few operating assumptions and reading the result default state editable (no canvasLocked)adjustable the five operational levers (§CR6) — each a resource-edgeflow: "@<parameter-id>"(§CR6.1), delivered byloop-model/2recommendedRunConfigtimelineSeries= the 4 default series in §CR7 (the rest reachable through the series selector); a modeststeps/baseSeedIt is an experimental Template at the
previewstage.
Time unit: one day. The main flow reads strictly left → right, one direction:
green intake → green stock → allocation → roasting → roasted stock → sales → revenue / cost / profit
Only the essential branches:
- a daily green intake feeds a green-bean stock pool;
- allocation splits the stock: part is sold as green beans to other businesses (wholesale), the rest goes to own roasting.
- roast input draws from the green stock;
- a roast weight loss removes the moisture / chaff fraction (a drain);
- the remainder becomes roasted-bean stock (a pool).
- cafe drinks — beans consumed to make and sell drinks in the cafe;
- retail bagged beans — bags sold in-store;
- online bagged beans — bags sold online.
- a daily dessert prep quantity (
@dessert_prep); - in-store + takeaway dessert sales, bounded by that day's dessert demand;
- unsold dessert leaves the day's stock at end of day (a drain) — a simplifying assumption, surfaced as the signed dessert prep margin proxy (§CR3.5), not as a "waste" figure.
Changing any of the five levers (§CR6) is a real flow-simulation change — the
@parameter-id on that edge resolves to the new value and the stock
trajectory and channel sales move accordingly. All results are Registers
(loop-expr/1), read from the right-hand Summary block (§CR8):
| Register | English label | KO label | value | sign / kind |
|---|---|---|---|---|
| projected revenue | Projected daily revenue | 하루 예상 매출 | @cafe_retail_demand_kg·price + @online_orders·price + @green_wholesale_kg·price + fixed |
planning proxy — assumes all demand met; not realised revenue |
| planned cost | Planned daily cost | 하루 계획 비용 | (@daily_roast_kg + @green_wholesale_kg)·green-cost + @dessert_prep·dessert-cost + fixed |
planning proxy — planned spend at the current levers; not an accounting cost figure |
| projected operating margin | Projected daily operating margin | 하루 예상 운영차익 | @projected_revenue − @planned_cost |
planning proxy — + plan is in the black · − in the red; not accounting operating profit, and a fulfilment shortfall does not reduce it |
| roasted supply margin | Roasted supply margin | 로스팅 원두 수급 여유 | @roasted_stock − (@cafe_retail_demand_kg + @online_orders) × cover-days (rev 8 — reads the LIVE roasted-stock level, so a green shortage that starves the roaster genuinely moves it; §CR9.1) |
signed proxy — + the shelf covers the demand buffer (slack) · − demand exceeds roasting output / stock (running short) |
| dessert prep margin | Dessert prep margin | 디저트 준비 여유 | @dessert_prep − dessert_demand_per_day |
signed proxy — + prepared more than the day sold (leftover) · − demand outran prep (sold out) |
None of the five is a realised business figure.
- The three money Registers are planning proxies (rev 9). They are
computed from the ordered / planned lever values and assume every unit of
demand is met — so
Projected daily revenuekeeps rising as you raise@online_orderseven when roasted stock cannot fill those orders. The fulfilment shortfall shows up in the roasted-stock trajectory and the roasted supply margin going negative, not in these figures. Never labelledactual/realised/실제 매출/이익in a title, node label, blurb, Timeline series (§CR7), or §CR9 scenario.- Display unit (rev 10; dollars since rev 13). The three money proxies
carry the advisory
unithint$/day(example dollar amounts per day — thePprice constants are dollars per kg / per unit / per day: at the defaults the proxies read464/346.8/117.2$/day). It is one canonical, locale-common string shown as the Register's sub-line — it does not change the value, is not duplicated into the label, and does not make the proxy a realised or accounting figure. The two signed stock proxies stay unitless (they live in kg / dessert-unit space, not money).
- Display unit (rev 10; dollars since rev 13). The three money proxies
carry the advisory
- The two "… margin" Registers are signed proxies.
loop-model/2adds nomax/min(§M2-6), so they are plain signedsupply − demandreadings: a−roasted supply margin is an unmet-demand signal, not a counted lost-sale quantity; a+dessert prep margin is a leftover amount, not a confirmed discard weight (the "discard leftovers at end of day" rule, §CR3.4, is an assumption). Never labelledmissed sales,lost sales,waste, or폐기anywhere. impl PR (2) must use the exact EN / KO labels in the table above.
The physical roast weight-loss stays a real drain in the flow simulation
(§CR3.2) — expected process loss, not a signal or a Register.
A real roastery buys several origins / varietals. v1 aggregates them into one representative green-bean stock. Per-varietal comparison is noted here as a possible future extension only — it is not in the v1 graph and must not be added to hit a node count.
Explaining these keeps the impl PR from drifting back toward MMO scale:
- per-origin / per-varietal replication of the whole flow;
- blend ratios;
- seat turnover;
- staff / rota;
- delivery-platform commissions;
- tax / rent / depreciation;
- roast profiles;
- customer loyalty;
- seasonality;
- multiple stores;
- elaborate accounting invariants;
- any dependency on group frames or other not-yet-built features.
If the node budget (§CR5) has room to spare, cut a result item — do not add a feature.
- ≤ 20–25 nodes total, Summary Registers included.
- Readable left → right on one screen; the whole structure is distinguishable without zooming, and every node is readable + clickable when zoomed (this is the graph the LGR Slice-1 work is meant to serve).
- No connection passes through a node body; crossings minimised (hand-place on a clean grid).
- No bottom accounting mesh, no dozens of auxiliary Registers (the opposite of the MMO example).
Indicative breakdown (the impl PR finalises the exact set within the cap):
| group | ~count |
|---|---|
| Parameters (§CR6) | 5 |
| green side — delivery Source + green stock | 2 |
| green wholesale channel — one Drain | 1 |
| roasting — Gate, weight-loss drain, roasted stock | 3 |
| roasted sales — cafe/retail Drain, online Drain, staff/cupping Drain | 3 |
| dessert — prep Source, stock, sales Drain, day-end Drain | 4 |
| Summary Registers (§CR8) | 5 |
| total | 23 (rev 9 — was ~25; the disconnected footfall Source + tally Pool removed, §CR6.1) |
The five surfaced values are operational levers — real physical quantities that a roastery adjusts — laid out clearly in one row along the top. Every one sits on an edge the engine reads as a rate, so changing it moves a Pool trajectory, not just a Register readout (rev 9).
- Cafe & retail bean demand — kg of roasted beans the cafe counter + retail shelf sell per day (rev 9 — was "Daily customers" footfall).
- Daily roast amount — kg of green beans put to roast per day.
- Online bean orders — kg of roasted beans ordered online per day.
- Green wholesale orders — kg of green beans ordered by other businesses per day.
- Daily dessert prep — dessert units prepared per day.
Prices, unit costs, and the roast yield are stable fixed values (in node data or a couple of clearly-labelled constants) — not surfaced as hard-to-read expressions.
Each lever is one parameter node in the top row, referenced by exactly
one resource-edge flow string that is a single bare @<parameter-id> and
nothing else (no compound, no arithmetic — loop-model/2 M2-2). The ids below
are the canonical English node ids in examples/coffee-roastery.json; the impl
PR may only rename them, never change the wiring.
| # | Parameter (id) |
the one edge whose flow is @<id> |
engine role of that edge |
|---|---|---|---|
| 1 | cafe & retail bean demand (cafe_retail_demand_kg) |
the roasted-bean stock pool → cafe/retail-use drain edge | Drain pull amount — kg of roasted beans the cafe + retail counter sell per day (rev 9: this replaces the old daily_customers footfall lever, which fed only a tally Pool + the Register formulas — CR-D15) |
| 2 | daily roast amount (daily_roast_kg) |
the green-bean stock pool → roasting deterministic Gate input edge |
Gate pull / demand amount — kg of green beans put to roast per day (rev 8: Gate, was Converter) |
| 3 | online bean orders (online_orders) |
the roasted-bean stock pool → online-sales drain edge | Drain pull amount — kg leaving roasted stock to online sales per day |
| 4 | green wholesale orders (green_wholesale_kg) |
the green-bean stock pool → wholesale drain edge | Drain pull amount — kg of green beans leaving green stock to wholesale per day |
| 5 | daily dessert prep (dessert_prep) |
the dessert-prep Source → dessert stock pool edge |
Source push amount — dessert units prepared per day |
-
rev 9 — every lever moves a real Pool trajectory. The old lever 1 (
daily_customers) pushed into a stand-alonecafe_demandtally Pool and was read by the money-Register formulas, but no roasted-stock / dessert trajectory responded to it — a "Register numbers move only" pseudo-link (§CR16.2). It is nowcafe_retail_demand_kgon theroasted_stock → cafe_retailDrain edge, so raising it draws roasted stock down (§CR9.1 #1). The disconnectedcafe_footfallSource andcafe_demandPool are removed — 25 → 23 nodes. -
rev 8 — roasting is a deterministic
Gate. AConvertercannot carry lever 2: its output isf·outRatewithf ∈ [0, 1]and ≤ 1 activation/step (SEMANTICS.mdI2), so@daily_roast_kgon a Converter input edge stops changing the roasted output once green is not the binding constraint (and moves it backwards when it is). The deterministic Gate is the accurate model of "kg put to roast per day": it still uses exactly one@paramreference (its single input edge), pullsT = min(@daily_roast_kg, green available), and splits T by two fixed weights — 82 % → roasted-bean stock, 18 % → the roasting weight-loss drain (moisture / chaff — a real process loss, never "waste"). Mass is conserved (0.82·T + 0.18·T = T). -
Levers 2 and 4 both draw from the same green-bean stock pool. Raising
green_wholesale_kggenuinely removes green beans that would otherwise be available for roasting (§CR9.1 scenario 2) — the competition is real in the run, not a Register artefact, and the impl PR must not add a second green pool to sidestep it (§CR3.6 / §CR4). Thewholesaledrain's node id sorts beforeroasting, so on a short-green day the wholesale contract is filled first and the roaster takes what is left. -
Green short ⇒ input and output fall together. Because the roasting Gate's
Tismin(@daily_roast_kg, green available), a green shortage lowers both the green pulled and (proportionally) the roasted output and the weight-loss path — there is no regime where the roaster keeps producing on green it did not actually consume. -
A dangling / mistyped reference contributes
0+ a diagnostic (never1); the impl PR's fixture test asserts every@idresolves to a liveparameter(SEMANTICS-M2.md§M2-3). -
Prices / unit costs / roast yield stay literal constants — they are read by the Summary Registers (§CR3.5), not by a
flow.
rev 12 — narrowed to a 4-series default (legend-density pass). The legend
first shows one profitability read plus the three real stock trajectories, so it
sits on a single row in EN / KO / JA; the other four series are one +N more
click away and add their line back when selected. Every name uses the §CR3.5
wording — the money lines are projected / planned, never "revenue" /
"profit" bare.
Default visible (recommendedRunConfig.timelineSeries, kept sorted by the
store):
- projected daily operating margin (§CR3.5 — planning proxy; projected revenue − planned cost)
- green-bean stock
- roasted-bean stock
- dessert stock
Not drawn by default — reachable through the series selector (docs/timeline-series-contract.md
§6; the inline +N more chip this paragraph originally described was replaced by it):
- projected daily revenue (§CR3.5 — planning proxy)
- planned daily cost (§CR3.5 — planning proxy)
- roasted supply margin (§CR3.5 — signed proxy)
- dessert prep margin (§CR3.5 — signed proxy)
rev 13 — the money unit is dollars (2026-10-12, Hanrim / Lumi). The three
money proxies' unit hint is $/day instead of kKRW/day: shorter, and
the value and the flow read more cleanly beside it. No exchange conversion:
the P price constants keep their numbers and now read as example dollar
amounts ($30 / $17 / $9 per kg of roasted beans by channel, $5.5 per kg
of green beans, $1.4 per dessert, $60 / $140 fixed per day), so the
defaults still read 464 / 346.8 / 117.2 — a small roastery's day in
either currency, nothing became outsized. Values, relationships, trajectories,
labels and the planning-proxy framing (§CR3.5 / §CR8) are unchanged. Scope: the
BUNDLED example only (src/engine/coffee-roastery.fixture.ts →
examples/coffee-roastery.json, pinned by coffee-roastery.test.ts); a
document a user already saved keeps its own unit and values — nothing rewrites
it. The frozen legacy fixtures (examples/revision-legacy-v0.10.0/,
test/fixtures/templates-before-step4/) keep kKRW/day as the record they
are. The rev 10 note above stays as written: it describes the unit at rev 10.
rev 14 — re-placed for the node structure (2026-10-12, Lumi's candidate A).
The compact body with the name outside halves the bodies and frees the room the
old frames kept, so the diagram is placed again for reading order, not for the
old coordinates (share connection-editing-2/23-coffee-replace.md):
- one main row, Supply → Roasting → Sales, with the three sales drains in a fan
column (rows 196 / 324 / 452); the secondary outflows under their stock and
forward of it (Roasting weight loss forks forward of the Gate; Green wholesale
hangs UNDER Green bean stock through the node structure's ports —
e_green_wholesaleis stored bottom → top, the stock's bottomoutlane and the drain's topinlane aligned, so the line is a straight drop; the stock's name then sits on its TOP face); - the dessert flow on its own row (516), its two outflows fanning up and down from Dessert stock;
- the four bean levers on one row above the zones (y −28), each over the flow it drives; the dessert lever UNDER the dessert row (724) so it reads with that flow;
- Forecast metrics as one results column at the right, its frame from the lever row to the flow zones' bottom (720); the three frames share one top (112) and one bottom.
Placement: placeTemplate with boxes measured as body ∪ outside name over the 18
languages (docs/diagram-layout.md §DL5.2.1) and Coffee's own clearance 0
(§DL5.2.2). The start view (initialView) is the work flow, −64 / −40 / 1344 × 864 (about 0.71 at 1280 × 800); the whole view shows all three zones at about
0.52 there. §CR17 holds by the measured boxes (coffee-roastery.test.ts); the
lever rule of §CR5 is now "four on a row, the dessert lever below its row".
No value, flow, label, member or digest-relevant semantics changes — the revision
content digest moves (positions are content), the engine digest does not.
Verified in 18 languages: no name over a name, body or label; no name across a
frame border; members 5 / 8 / 5 everywhere. This placement presumes the node
structure as the product's default (a bundle holds one placement).
The legend renders in the component's structural order — every shown Pool, then
every shown Register, each in graph-node order — so the operating-margin
Register sits at the end of the row, read after the three stock trajectories.
The Timeline computation, the run CSV (every Pool column), and the Monte-Carlo
tracked set are all unaffected by this display default.
The right-hand result area: 5 Registers (§CR3.5 for the exact labels).
- Projected daily revenue — planning proxy; ordered/planned levers × price + fixed, assumes all demand met. Not realised revenue.
- Planned daily cost — planning proxy; planned spend at the current levers.
- Projected daily operating margin — planning proxy; projected revenue − planned cost. Not accounting operating profit; a fulfilment shortfall does not reduce it.
- Roasted supply margin — signed proxy;
+slack,−demand exceeds roasting output / stock (an unmet-demand signal, not a lost-sale count). - Dessert prep margin — signed proxy;
+leftover,−sold out.
None of the five is a realised figure. The two "… margin" proxies are never
clamped at zero (loop-model/2 has no max / min); the sign carries the
meaning. Human-readable titles take priority over the formulas.
At minimum, these changes must reproduce intuitively (using the §CR3.5
terms — a − roasted supply margin is an unmet-demand signal; a +
dessert prep margin is leftover; the money lines are projected / planned,
so they may rise even when stock cannot fulfil the plan):
- Cafe/retail demand up → the cafe/retail drain pulls more roasted beans → roasted stock falls → roasted supply margin turns negative.
- Roast amount too low → roasted supply margin turns negative (demand outruns roasting); roasted stock falls.
- Raise green wholesale orders → the roaster is starved → roasted stock falls; projected revenue rises (plan only — the roasted-stock line shows the plan is not fulfillable).
- Online orders rise → roasted stock draws down faster → roasted supply margin turns negative; projected revenue / operating margin rise (plan only).
- Dessert prep above demand → dessert prep margin turns more positive (bigger leftover); planned dessert-line cost rises.
- At a sensible roast amount (the shipped default) → roasted supply margin sits near zero — both the shortage signal and the overstock ease.
Each scenario is one @param change → a real Pool trajectory move, verified
by the impl PR's fixture test (a deterministic run before / after the change).
A build where a Register's text changes but the Pool trajectory does not does
not satisfy this section.
| # | change | Parameter (§CR6.1) | what must move (direction) |
|---|---|---|---|
| 1 | cafe/retail demand up | cafe_retail_demand_kg ↑ |
the roasted_stock → cafe_retail Drain pulls more ⇒ roasted-stock trajectory ↓ → roasted supply margin → negative |
| 2 | roast amount too low | daily_roast_kg ↓ |
the roasting Gate pulls less green ⇒ roasted inflow ↓ ⇒ roasted-stock trajectory ↓ → roasted supply margin → negative |
| 3 | more green wholesale | green_wholesale_kg ↑ |
green-stock drawdown ↑ → the roasting Gate is starved (wholesale filled first) → green-stock ↓ and roasted-stock ↓; projected daily revenue ↑ — plan only, the roasted-stock line shows it is not fulfillable |
| 4 | more online orders | online_orders ↑ |
roasted-stock outflow ↑ → roasted-stock trajectory ↓ → roasted supply margin → negative; projected daily revenue / operating margin ↑ — plan only |
| 5 | dessert prep above demand | dessert_prep ↑ |
dessert made per day ↑ while dessert sales are demand-bounded → dessert-stock trajectory ↑ → dessert prep margin → more positive; planned daily cost ↑ |
| 6 | roast amount at a sensible level | daily_roast_kg → the shipped default |
the roasting Gate output ≈ the day's roasted demand ⇒ the roasted-stock level holds near its buffer → roasted supply margin near zero — shortage signal and overstock both ease |
(rev 8 — the fixture test runs a real deterministic run before / after each
single @param change and asserts these directions on the committed
examples/coffee-roastery.json: src/engine/coffee-roastery.test.ts
§CR9.1 — each lever change moves a real trajectory in the stated direction.)
A build where the numbers only look like they moved (a Register's text
changes but the stock trajectory does not) does not satisfy this section
(loop-model/2 §M2-3 — the @id resolves to a real number the engine uses).
rev 6 — the success bar is re-set, not just lowered. It is not "usable in the field right now" (§CR2.0). A "this is not a real system" reaction is normal and does not fail the check. But "it's a toy" on its own is not a pass either. A pass still requires all of: (a) the simplified flow is recognisably realistic — a domain reader agrees it broadly matches how a roastery works; (b) the reader finds and operates the five levers unaided; (c) the reader can explain the result change each lever produces. "I can't tell what this means" remains a failure.
- the headline criterion — a reviewer with coffee-business experience, given no Loop Studio or graph-modelling instruction, can explain the core flow within 1–2 minutes and interpret the result change from adjusting at least one input value (§CR11), and recognises it as a simplified model, not mistaking it for an operations system;
- the five values to change are immediately identifiable with no prompting;
- for each value, the direction of the result change is predictable;
- the reviewer never needs the words Pool / Gate / Register to use it;
- ≤ 25 nodes;
- the whole structure reads without zoom; zoomed in, every node is stably readable and clickable;
- save / run /
loop-revision/*digest invariance and existing engine semantics are unchanged; - a deterministic-seed run plus the core stock ↔ revenue relationships are
pinned by tests (a fixture spec, like
mmo-progression.test.ts); - it loads from
파일 → 템플릿 → 커피 로스터리 운영 흐름(File → Templates; theTemplates ▾button until the 2026-10 workbench) and applies itsrecommendedRunConfig; no engine / schema / wire change in impl PR (2) itself, only the registration files (§CR2, §CR15) — the engine feature (loop-model/2, §CR16) merged separately as PR #103.
The engine tests verify the arithmetic is correct. This check verifies a person who knows the real business accepts the model as natural — the validation Loop Studio needs more right now.
Reviewer: an external, domain-informed reviewer — someone outside the team, with likely coffee-business familiarity. Their unprompted "real production / operations" read (§CR0) is what made this the first external check. A person who knows the domain saying "I can't tell what this means" is a failure; reading it briefly and explaining the flow + an adjustment result is a strong success signal. One reviewer is not the whole population, but a real outside domain reader is the sharpest single signal available. (The doc does not record the reviewer's identity; results are reported in aggregate.)
Send just the Loop Studio URL and one line: "open 파일 → 템플릿 → 커피 로스터리 운영 흐름" (File → Templates; Templates ▾ before the 2026-10 workbench).
No feature explanation, no Import / Share / file steps. Then
ask:
- What business flow does this screen represent?
- Point to where green beans enter and where they leave.
- Predict what happens if the roast amount is too low.
- What does raising green wholesale do to cafe / bean sales?
- Change cafe & retail bean demand or daily roast amount yourself and read the result.
- Name any node, term, or result you could not understand.
The questions are framed as "is the simplified model realistic and legible", not "is it usable in real work today" (rev 6, after §CR2.0). Ask, in addition to 1–6:
- does the overall flow broadly match how a roastery really works, or is it badly wrong somewhere?
- is a key step missing?
- are the five operational levers meaningful for an operating decision?
- are the result changes easy to understand?
A pass needs all of:
- explains the whole flow, roughly, in 1–2 minutes;
- confirms the simplified flow is recognisably realistic — broadly matches how a roastery works, with no badly-wrong step or missing key stage;
- understands the wholesale-vs-own-roasting competition for green stock;
- understands roasted beans split into cafe / retail / online;
- finds and operates the five levers with no guidance, and agrees they are meaningful for an operating decision;
- after changing a lever, can say why the result moved;
- uses it without knowing Loop Studio's internal node vocabulary.
Neutral (does not affect pass/fail): "this is not my real system" — expected for a simplified example.
Fails: "I can't tell what this means"; can't follow the flow; can't find or operate the levers; can't explain a result change; "it's just a toy" with no realism / legibility judgement behind it.
Not on the bar (rev 6): that the reviewer would use it in real work, or that it covers a full roastery operation.
- a core flow that is missing from an operations point of view;
- a part that is over-simplified;
- an inventory metric that should be read before revenue;
- whether the green wholesale ↔ roasting allocation is realistic;
- whether the dessert prep margin (leftover proxy) is a believable operating cue.
Read → adjust values → interpret results only. The reviewer is not asked to edit or "fix" the graph. Whether structural editing is viable is examined only after this check passes.
The comprehension check was sent and returned. Verdict against the §CR11.2 pass bar, at the current evidence:
| §CR11.2 item | Result |
|---|---|
| Explains the whole flow, roughly | pass — the reviewer follows the buy-green → roast → sell path |
| Naming and represented flow not badly wrong | pass for the Template's stated simplified scope — naming and the represented flow were not found materially awkward; the reviewer separately noted bean varieties, blending, and process differences as realities a broader operational model would need. This is not a finding that every core stage of a real roastery is present. |
| Finds the five levers with no guidance | pass — the five adjustable values are found easily |
| After changing a lever, can say why the result moved | not demonstrated — the reviewer understood the intended change → re-run → stock/result relationship, but did not report a completed before/after comparison or explain the direction of each lever's result change |
Two further reads from the same reviewer, outside the pass bar — one external reviewer's domain judgement and expectation, not a market finding:
- Real-operations usefulness — the reviewer expects limited real-work pull across small, mid-size, and large operators. A small self-roasting cafe: too complex, little reason to run a model in a busy shift. A large roaster: already has its own stock system, and this does not fully model reality. A mid-size roastery: carries far more item types (ingredients, packaging, consumables) than a few levers hold. Interesting to look at, not an operations tool.
- Education / explanation / scenario comparison — positive observation / potential identified (not verified market fit). Clear potential for teaching in a cafe / shop context, especially if a diagram at one point in time versus another could be exported cleanly for soft copy and print; a wider item set would want a summary report.
Status: external comprehension check complete; overall pass bar = partial / lever-to-result comprehension not demonstrated. This is not recorded as "field usefulness verified", "suitable for a real roastery", or "passed external validation". The §CR2.0 positioning — a simplified operating-flow simulation, not an ERP or real-time monitoring system — is consistent with this read.
Carried as separate follow-ups (each its own candidate; not this Template's scope; no roadmap slot or implementation approval implied):
- scenario / point-in-time diagram comparison;
- clean soft-copy and print export;
- multi-item summary report;
- long Korean-label node-box layout — an observation to check against the current ellipsis / LOD behaviour and real layout;
- before/after comparison experience and the Monte-Carlo explanation — review the current guidance (what to re-run, where to read the difference) before considering any new feature; the "you'd have to run it several times" reaction may be a guidance gap, not a missing capability.
Bean varieties, multi-item inventory, blending, and per-vendor process differences are not added to this Coffee Template. They are recorded here only as the basis for the scope limit: expanding toward a real operations system makes complexity spike and blurs the model's purpose.
The check must measure the model, not English comprehension: a Korean reader needs a Korean graph. But shipping a Korean-only JSON would leave every English user with an English menu entry that opens a Korean graph — a known mixed-language defect, not something to ship on purpose in the first external Template. So:
- One canonical graph, English labels —
examples/coffee-roastery.json. - Korean (and future locales') labels come from the shared fresh-open label
overlay (
docs/template-label-overlay.md), which is designed and implemented before this Template. The overlay applies the current locale'snodeId → labeldictionary once, when the Template is opened from the menu; after that the graph is a user document and is not re-translated on a language switch. - Translated: node
labelonly. Node ids (green_bean_stock,roasting,cafe_sales, …), expressions,resourceType,unit, edge data, and positions are not translated (they are English/stable everywhere). - Menu name / blurb are per-locale via the app i18n catalog, like every
Template — the impl PR adds two keys,
templates.coffeeRoastery.name/.blurb, toen.ts(EN) andko.ts(KO), mirroringtemplates.equilibrium.*. - Result: EN UI → English labels; KO UI → Korean labels, from the same
graph, with identical structure / expressions / run results (overlay
invariants, §TLO6). No
.ko.jsonfor this Template. - The Korean dictionary entries use the industry Korean below (§CR12.1).
Physical-flow terms use natural industry Korean; the two supply − demand
proxy read-outs (§CR3.5) use a neutral "여유 (margin)" wording with the sign
meaning spelled out — never 놓친 판매 / 품절 손실 / 폐기. The three
money Registers use 예상 / 계획 (projected / planned) wording, never
실제 매출 / 이익 bare (rev 9).
| English | Korean |
|---|---|
| green beans | 생두 |
| roasted beans / roasted stock | 로스팅 원두 (원두 재고) |
| roast yield | 로스팅 수율 |
| wholesale green beans | 생두 납품 |
| packaged beans | 포장 원두 |
| roasted supply margin (signed proxy) | 로스팅 원두 수급 여유 — 양수 = 여유, 음수 = 수요가 로스팅 공급을 초과(미충족 수요 신호) |
| dessert prep margin (signed proxy) | 디저트 준비 여유 — 양수 = 잔량, 음수 = 준비 부족(품절) |
| projected daily revenue (planning proxy) | 하루 예상 매출 — 주문·계획값 기준, 수요가 모두 충족된다는 가정. 실제 매출 아님 |
| planned daily cost (planning proxy) | 하루 계획 비용 — 현재 레버 기준 계획 지출. 회계상 비용 아님 |
| projected daily operating margin (planning proxy) | 하루 예상 운영차익 — 예상 매출 − 계획 비용. 회계상 영업이익 아님 |
The five surfaced Parameters (§CR6), in Korean (row 1 changed rev 9): 카페·리테일 원두 수요량 (kg/일) · 하루 로스팅량 · 온라인 원두 주문량 · 생두 납품 주문량 · 하루 디저트 준비량.
design PR — docs-only: this doc +Done (PR #99,docs/template-label-overlay.md+ one README line.d131948).implementation PR (1) — the shared label overlay + the Template-3 KO-label migration + EN-fallback allow-list for Templates 1 & 2.Done (PR #100,b938aed;mainCI green).design PR — rev 6 (docs-only): §CR2.0 repositioning + settled strings + §CR16 Direction-1 decision.Done (PR #101,26f4de5).PR (1.5) — the generalDone (PR #103, mergeparameter → simulation inputfeature: a spec (SEMANTICS-M2.md, Frozen,loop-model/2) + engine implementation + tests. General, not Coffee-specific; noloop-expr/1expansion, nomin/max, no Coffee file.c194629;mainCI green).fold PR (1.5) into this doc (docs-only, THIS PR): §CR3.5 / §CR6 (new §CR6.1) / §CR8 / §CR9 (new §CR9.1) lose the "blocked" notes and lock the fiveThis PR.@paramreferences; §CR2.1a marks Coffee the firstloop-model/2entry; CR-D12 / §CR16 → resolved.- implementation PR (2) — this Template, only after step 5:
examples/coffee-roastery.json—schema: "loop-studio/graph/2", English canonical labels + English ids; the five levers wired as the §CR6.1flow: "@<parameter-id>"references;- the
mmo-progression-style KO dictionary entries for its nodes (§CR12.1); - a fixture / deterministic-run test (like
mmo-progression.test.ts) + anexamples/README.mdentry that carries the §CR2 limitation marker (단순화한 시뮬레이션 예제이며 ERP나 실시간 모니터링 시스템이 아닙니다.); src/model/templates.ts— a 4thTEMPLATES[]item withrecommendedRunConfig(§CR7 series);openTemplate/loadGraphset the store'smodelVersionfrom the file'sschema(v2 — §CR2.1a);src/components/templateKeys.ts— a 4thTEMPLATE_KEYentry;src/i18n/templateLabels/ko.ts— acoffee-roasteryKO node-label block;src/i18n/locales/en.ts+ko.ts— the two menu keys (§CR2 strings —커피 로스터리 운영 흐름, never "운영 관리"). No engine / schema / wire change in PR (2) beyond whatloop-model/2landed; no Coffee-specific engine hardcoding.
- Hanrim pre-merge hands-on check — locally or in the PR-(2) preview, in both EN and KO: the entry loads, reads L→R, the five values are obvious, the §CR9.1 scenarios really move the trajectory (a before/after run), and EN/KO differ only in labels.
- merge + Production check.
- external comprehension check (§CR11) — ran; result in §CR11.5 (2026-09-03): partial pass. Flow, naming (within the simplified scope), and lever discovery passed; a completed before/after explanation of each lever's result direction was not demonstrated; real-operations suitability is not claimed. Recorded as CR-D16.
- README final check — the roadmap line + general positioning; states the check ran and is a partial pass, with no real-operations-suitability claim.
- after the check — if it exposes model problems, fix in a follow-up PR. LGR Slice 2 starts once step 9 is done (it has). The check running is not recorded as field-usefulness validation or a full §CR11.2 pass.
| id | question | decision |
|---|---|---|
| CR-D1 | replace an existing template? | No. New file + a 4th entry; 1 / 2 / 3 untouched (§CR2). |
| CR-D2 | Example or Template? | Template — editable, the user adjusts 5 values (§CR2). |
| CR-D3 | menu-registered when? | From the start — registered as the 4th entry in impl PR (2), so the reviewer just picks it from the menu (no Import / Share). Was "Import-only until validated" in rev 2; a menu pick is the fastest route for the external check (§CR2, §CR13). |
| CR-D4 | time unit | one day. |
| CR-D5 | multiple green-bean types in v1? | No — one aggregate green stock; per-varietal is a prose-only future extension (§CR3.6). |
| CR-D6 | node budget | ≤ 20–25 total, Summary Registers included; short on nodes ⇒ cut a result, never add a feature (§CR5). |
| CR-D7 | the 5 user values | cafe & retail bean demand (rev 9 — was "daily customers") · daily roast amount · online bean orders · green wholesale orders · daily dessert prep (§CR6). Each sits on an edge the engine reads as a rate (§CR6.1). |
| CR-D8 | prices / costs / yield | fixed, clearly-labelled constants in v1 — not surfaced as expressions (§CR6). |
| CR-D9 | src/ / wire / engine impact |
impl PR (2) touches only examples/coffee-roastery.json, its fixture test, templates.ts, templateKeys.ts, templateLabels/ko.ts, 2 menu keys × en+ko, and examples/README.md; no engine / schema / wire / loop-revision/N (§CR15). The engine change is loop-model/2 (SEMANTICS-M2.md, PR #103, merged c194629) — a separate, already-shipped, general feature. |
| CR-D10 | who verifies comprehension? | an external, domain-informed reviewer (identity not recorded) — read → adjust → interpret only; not asked to edit the graph (§CR11). |
| CR-D11 | language | one English-canonical examples/coffee-roastery.json; Korean (and later locales') node labels via the shared fresh-open overlay (docs/template-label-overlay.md), built first. label only — ids / expr / resourceType / positions stay English. No .ko.json for this Template. Menu name/blurb per-locale via the app catalog (§CR12). |
| CR-D12 | the five levers can't reach the frozen engine — what now? | RESOLVED (rev 7). Direction 1 shipped: loop-model/2 (SEMANTICS-M2.md, Frozen; PR #103, merge c194629) lets a v2 resource-edge flow be a single @parameter-id. The five stay operational levers — locked to concrete edges in §CR6.1 — never redefined as price / cost / yield. The feature added no Coffee-specific code, no loop-expr/1 expansion, no min / max. Direction 2 (redesign around the unchanged engine) was considered and not chosen. |
| CR-D13 | (rev 7) Coffee is the first bundled v2 Template — any risk? | No. openTemplate / loadGraph already accept a model version; a bundled v2 file loads as v2 as authored (not the "explicit user promotion" path). The label overlay is label-only, so it is unaffected. The v2 loop-revision / loop-workspace digest discriminator (§M2-8) means the Coffee graph's identity is distinct from any v1 graph — expected. |
| CR-D14 | (rev 8) lever 2 on a Converter input edge can't satisfy §CR9.1 under frozen semantics — what now? |
Model roasting as a deterministic Gate. SEMANTICS.md I2 fixes a Converter's output at f·outRate, f ∈ [0, 1], ≤ 1 activation/step, so @daily_roast_kg on a Converter input edge stops changing the roasted output once green is not the binding constraint. A deterministic Gate carries "kg put to roast per day" exactly — still one @param reference (the single input edge), T = min(@daily_roast_kg, green available), split 82 : 18 into roasted stock and the weight-loss drain, mass-conserving, and green-short limits input + output together. Not a work-around — it is the accurate model. Rejected: a second @param edge on the Converter output (breaks "exactly one edge per lever"); Coffee-specific engine code (§CR16.2); keeping the Converter and accepting a dead lever (fails §CR9.1). §CR3.5's roasted-supply-margin proxy now reads the live @roasted_stock level so a green-starvation move (§CR9.1 #2) is visible. |
| CR-D15 | (rev 9) Hanrim's screen review found lever 1 (daily_customers footfall) moved no product flow, and the three money Registers read like realised figures. |
(a) Replace lever 1 with cafe_retail_demand_kg on the roasted_stock → cafe_retail Drain edge — so it draws roasted stock down (§CR9.1 #1) instead of only feeding a tally Pool + the Register formulas (a "Register numbers move only" pseudo-link, §CR16.2). Remove the disconnected cafe_footfall Source + cafe_demand Pool (25 → 23 nodes). (b) Rename Total revenue / Total cost / Operating profit → Projected daily revenue / Planned daily cost / Projected daily operating margin, and state everywhere (titles, §CR3.5, §CR8, every scenario, §CR12.1) that they are planning proxies computed on the ordered/planned levers assuming all demand is met — not realised revenue / cost / accounting operating profit, and never shown as 실제 매출 / 이익. A fulfilment shortfall shows in the roasted-stock trajectory + roasted supply margin, not these figures. Rejected: computing an actual fulfilled-sales pool (adds nodes + complexity beyond §CR5, and the proxy framing is enough for a §CR11 comprehension check). |
| CR-D16 | (2026-09-03) the external comprehension check (§CR11) returned. | The check has run; it is not "external validation passed". Fixed: (1) the check returned; (2) flow, naming (within the Template's stated simplified scope), and lever discovery passed — naming / represented flow were not found materially awkward, with bean varieties / blending / process differences noted separately as a broader model's concerns, not a finding that every core stage is present; (3) lever-to-result explanation was not demonstrated — the reviewer grasped the change → re-run → result relationship but did not report a completed before/after comparison or state each lever's result direction; (4) real-operations suitability is not claimed — one reviewer expects limited real-work pull across small / mid / large operators, and sees education / explanation / scenario-comparison potential (a positive observation, not verified market fit). Overall §CR11.2 verdict: partial / lever-to-result comprehension not demonstrated. Follow-up candidates (scenario / point-in-time comparison, clean soft-copy + print export, multi-item summary report, long-KO-label layout, before/after + Monte-Carlo guidance review) are recorded in §CR11.5 and docs/product-direction.md §PD11.1 — none adopted. Bean varieties / multi-item / blending / per-vendor process are not Coffee-Template work items; they stand only as the basis for the scope limit. |
- This doc is the model + size + surfaced-value + result contract, the
comprehension-check protocol (§CR11), how this Template uses the label
overlay (§CR12), and the 4th Templates entry's fields (§CR2). The overlay
mechanism is
docs/template-label-overlay.md, not this doc. Neither is a spec for the module / template system (§PD8-B). - Impl PR (1) = the shared overlay (
docs/template-label-overlay.md§TLO)- the Template-3 KO-label migration. Merged (#100,
b938aed).
- the Template-3 KO-label migration. Merged (#100,
loop-model/2(SEMANTICS-M2.md, Frozen; PR #103, mergec194629) — the generalparameter → simulation inputengine feature. This is where the engine change lives; it is not Coffee-specific and not part of impl PR (2).- Impl PR (2) adds only:
examples/coffee-roastery.json(schemaloop-studio/graph/2), its fixture test, anexamples/README.mdentry (with the §CR2 limitation marker), thecoffee-roasteryblock insrc/i18n/templateLabels/ko.ts, aTEMPLATES[]item insrc/model/templates.ts, aTEMPLATE_KEYentry insrc/components/templateKeys.ts, and two menu keys inen.ts+ko.ts. No othersrc/change; no engine / schema / wire /loop-revision/Nbeyond whatloop-model/2already landed. - Templates 1 / 2 / 3 keep their graphs, behaviour, and digests unchanged (the overlay only affects a non-EN fresh-open, §TLO6).
- LGR Slice 2 does not start until the external comprehension check (§CR11) is complete.
- If the check fails, the file stays an
examples/reference and is not promoted — no menu entry, no further scope. - impl PR (2) starts now that
loop-model/2is merged and §CR3.5 / §CR6.1 / §CR8 / §CR9.1 state the real mechanism (rev 7, this PR). - README (the roadmap line and the general positioning text) is checked last — after the coffee Template is implemented and the external check has run.
Resolved. The constraint below is the reason
loop-model/2(SEMANTICS-M2.md, Frozen; PR #103, mergec194629) exists. In a v2 document aresource-edgeflowmay be a single@parameter-idthe engine resolves once per step — so the five §CR6 levers reach the run (§CR6.1). This section is kept as the design record; §CR6.1 / §CR9.1 / CR-D12 carry the settled outcome.
At the start of impl PR (2), the model as specified in rev 1–5 could not be built on the frozen engine. Three facts, from the current source and the frozen specs:
| # | fact | source |
|---|---|---|
| 1 | The engine skips parameter and register nodes entirely — no ports, no activation, they never fire. A Parameter's value changes no number the simulation computes. |
src/engine/step.ts (MODEL = new Set(['parameter','register']), excluded from every phase); SEMANTICS-M.md §M1.3 — "participates only by being referenced from expressions". |
| 2 | A resource edge's flow string is only const | all | percent | range | dice. There is no @id reference in a flow. So a Parameter cannot be a flow rate, a Source push rate, a Converter ratio, or a Gate weight. |
src/engine/flow.ts (parseFlow); SEMANTICS-M.md §M0 Out — "expressions on Gate / Source / Converter — a later amendment". |
| 3 | loop-expr/1 (Register expressions) has + - * /, unary -, @id, parentheses — and nothing else. No max / min / comparison / conditional (deferred to loop-expr/1.1). A Register cannot clamp a shortfall at zero. |
SEMANTICS-X.md §X0 / §X2. |
Consequence: with a parameter node, the five §CR6 levers can only change what a
Register displays — never green / roasted stock, channel sales, stockouts, or
waste in the run. The §CR9 scenarios, as "change a lever → a flow result moves",
are not achievable this way. SEMANTICS-M.md / SEMANTICS-X.md are
Frozen; §CR1 / §CR15 forbid any engine / schema / wire change in this
Template's PRs.
- Redefining the five levers as price / unit-cost / yield constants (so a Parameter only feeds a revenue Register). This keeps the letter of "5 Parameters" but drops the approved model: the point of the Template (§CR0) is that changing an operating condition moves stock / stockout / waste / profit. Silently swapping that for price-sensitivity is a different Template.
- A "simulation" where only Register text changes while the Timeline trajectory is fixed. An example that looks like a live operations model but whose numbers move only decoratively is the worst outcome — especially given the external reader's warning (§CR2.0) that partial-but-realistic-looking is worse than clearly-scoped.
- Coffee-specific engine hardcoding — a code path that special-cases this file.
- A flat wall of ~15 non-interactive Registers (a spreadsheet with no evolving state) — hard to read, fails "explain the flow in 1–2 min".
- "Select an edge, edit its
flowin the Inspector" as the five inputs — a first-time reader (the §CR11 reviewer) will not find them; fails "the five values are immediately identifiable".
Decided (rev 6 review); shipped as loop-model/2 (rev 7). A small, spec'd,
engine-level capability that lets a parameter value be referenced where a
rate is read — so a Parameter genuinely drives the run. It is general
(any graph benefits) and shipped as its own spec-first PR before impl PR (2)
(SEMANTICS-M2.md, PR #103, merge c194629). This
Template consumes it; it adds no engine code of its own. The as-shipped shape
below matches the sketch, with one narrowing: flow only (a Source's rate
is its out-edge flow), and a v2-document gate.
Shape (for the feature's own spec to pin down — sketch only here):
- a
parameterreference (@param_id) is accepted where a resource-edgeflowis read, and/or as a Source push rate; it evaluates once per step, like a constant (the parameter is fixed for the whole run, §M1.1); - likely touch points:
src/engine/flow.tsparseFlow(a newparamkind, or a resolve step feeding a numeric value in), the pull / push phases insrc/engine/step.ts,normalizeGraphhandling of a dangling / wrong-kind@ref(fill to a safe value + a notice, neverinvalidmid-run),loop-revisionfield projection + digest for the new reference form, and a minimal editor affordance so the field is discoverable; - the feature's spec decides how / whether it composes with
all/percent/range/dice(a plain constant-substitution that does not compose is an acceptable v1).
Explicitly excluded from this feature (kept out on purpose):
- Coffee-specific hardcoding — no code path that special-cases
coffee-roastery.jsonor any single file; - a Register-display-only workaround — a "simulation" whose trajectory is fixed and where only a Register's shown number moves is not this feature and does not satisfy §CR9;
- widening the
loop-expr/1expression language — no new operators, no functions, no general expressions on edges / sources; this is a reference to one parameter's value, not an expression layer; - adding
min/max/ clamping — out of scope here; the §CR8 roasted supply margin / dessert prep margin read-outs are plain signedsupply − demandproxies with an explicit sign meaning, per §CR3.5 — never labelled "missed sales" or "waste".
Also out of scope (different product, per §CR2.0 / §PD11): green-bean-variety CRUD, real-time stock entry, POS / sensor / order integration, statistics dashboards.
(Direction 2 — redesigning the Template around the unchanged engine — was considered and not chosen: the "edit a value the reader must first hunt for" mechanisms it depended on fail the §CR10 "five values immediately identifiable" bar, the same reason "select an edge, edit its flow" is rejected in §CR16.2.)
PR (1.5) — theShipped asparameter → simulation inputfeature.SEMANTICS-M2.md(Frozen,loop-model/2) + engine implementation + tests + theloop-revision/4digest discriminator (§M2-8). PR #103, mergec194629,mainCI green.fold the mechanism into this doc.This PR (rev 7) — §CR3.5 / §CR6.1 / §CR8 / §CR9.1 state the real mechanism; §CR2.1a; CR-D12 → resolved.- Then impl PR (2) per §CR13 — the JSON (schema
loop-studio/graph/2) + registration only, consumingloop-model/2.
Three GraphDoc.frames (docs/large-graph-readability-saved-frames.md §SF;
loaded via the §TLO12 template-frame path) give the left→right pipeline a
vertical three-band read:
| id | EN | KO | JA | contains |
|---|---|---|---|---|
zone_supply |
Supply & inventory | 공급·재고 | 供給・在庫 | green delivery + green / dessert stock + wholesale (5 nodes) |
zone_roasting |
Roasting & sales | 로스팅·판매 | 焙煎・販売 | the roast Gate + weight-loss + roasted stock + every sale Drain — cafe/retail, online, staff bleed, dessert sales, end-of-day leftover (8 nodes) |
zone_forecast |
Forecast metrics | 예측 지표 | 予測指標 | all five Registers (5 nodes) |
Rects (flow units, in the committed file):
| id | {x, y, w, h} |
box |
|---|---|---|
zone_supply |
{16, 176, 473, 432} |
[16, 176]–[489, 608] |
zone_roasting |
{516, 136, 719, 612} |
[516, 136]–[1235, 748] |
zone_forecast |
{1376, -24, 308, 634} |
[1376, -24]–[1684, 610] |
- Derived from the union of the rendered node AABBs across EN / KO / JA — Coffee nodes auto-size to their label, so the boxes differ per locale and each frame clears the widest. Every one of the 18 non-Parameter nodes keeps ≥ 24 px margin inside exactly one frame.
zone_forecast's bottom was extended +20 px (h614 → 634) on 2026-09-09 after the node-shell height fix (docs/node-shell-content-in-vessel.md): a Register now renders at its true content height, so the JA 2-line-titledessert_prep_margingrew ~16 px and needed the extra room to keep its ≥ 24 px bottom margin. Top / left / right and the other two zones are unchanged.- The five y = 0 Parameters sit outside all three (planning inputs, not a
pipeline stage): frames 1 & 2 start below the Parameter row; frame 3 starts to
its right. This is why the Register column moved +80 px (
rx1320 → 1400 in the fixture) — in JAdessert_preprenders 181 px wide (right edge ≈ 1341), which used to overlap the Register column and would sit inside any frame that reached the Registers. The move is visual-only: Registers have no ports or edges, so no engine / value / wiring effect. It also removes the old ~21 px Parameter↔Register overlap. - Frame-to-frame gaps: 27 px (
zone_supply→zone_roasting), 141 px (zone_roasting→zone_forecast— unchanged by the +20 px bottom extension); frame boxes never overlap. - Frame titles render as a chip above the frame's top-left corner: flow y ≈ 157.6 / 117.6 / -42.4. None is clipped by a node or by the top of the screen under the frame-less fit-all (verified at 1280 & 1920 in EN / KO / JA).
- Localisation: the titles follow §TLO12 — seeded on menu open, re-seeded
(live +
past/future) on a language switch, a user rename preserved. IDs + titles are pinned insrc/i18n/templateLabels/{ko,ja}.ts*Frames,known.generated.ts, andcheck:template-labels. - Pinned by
src/engine/coffee-roastery.test.ts(static geometry) ande2e/coffee-zone-frames.spec.ts(live per-locale re-measurement).