Skip to content
C.W.K.
Lesson 04 of 05 · published

The Kitchen: Ingredients to Interfaces

~13 min · interfaces, matching, kitchen, prompting

Level 0Mise en Place
0 XP0/43 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

A thread with no id

The kitchen is the one thread whose key carries no id. It is just kitchen, so Sizzle has exactly one, and slot_for_thread sends its turns to a brain slot of their own (lesson 5). On the web, the Kitchen page renders the same Talk component inline, with a greeting and a placeholder that reads At home I have…. That page hides the docked Sidekick, because the conversation is already on the page. On pages with no plate or class of their own, such as the plate list, the Sidekick opens this same kitchen thread. The phone has a Kitchen tab with a refrigerator icon, and it opens the same thread too.

Sizzle's Kitchen page: Pippa's chef avatar, Dad's list of squid, bean sprouts, pork belly, tofu and leftover rice, and Pippa's Korean reply with two numbered options, each naming the principle it uses; the first says that principle is not in Dad's tree yet, and the second names the tree's seafood jjamppong as its parent.
The kitchen thread: a list of what is at home, answered with options matched to principle roles. One option says plainly that its principle is not in the tree yet; the other names the dish in the tree it inherits from.

An interface is a list of roles

Track 2 showed where a principle keeps its interface: a JSON list of roles on the principle's own row, each with a name and a few examples. By Dad's naming ruling, principle names and role names are Korean. In the engine's test fixture, the gambas reading binds three roles: garlic as 향채 (the aromatic), olive oil as 기름 (the fat) and shrimp as 주재료 (the main ingredient). The principle above them is the design's worked example: move aroma into fat first, then cook in that fat.

A reading climbs from a dish to its principle. The kitchen asks the reverse question. It starts from what is in the house and asks which principles have roles those ingredients can fill. The architecture doc says it in object terms: find the classes whose interface those ingredients satisfy, and offer options, each with why it works. OO Quest teaches interfaces and abstraction in Track 7, The Complete Toolkit. Its Track 4 reads cooking through flavor axes, while Sizzle cuts cooking along techniques, and a technique's role list is the thing you can hold up against a shelf.

What I receive

The kitchen's subject is the longest of the three. It tells me to find the principles whose ingredient roles Dad's ingredients can fill, offer a few options with why each one works, and name the dishes or variants in his tree they would become. Its last sentence covers an awkward moment: plates still being read have not reached the tree yet, so I should say that instead of calling the tree empty.

The data is cut for exactly that job. Every principle arrives with its id, name, summary, roles and status, plus the names of the dishes directly under it. Dad's compact tree comes along with the id of every dish and variant, and so does the list of plates still being read. There is no pantry anywhere in it. Sizzle has no table of what is at home and no field that knows a word is an ingredient. The ingredients are written down all the same, as text: Sizzle records Dad's message as the turn's question, in the journal's turn.asked and the mirror's turns.question, and cwkPippa keeps it in the conversation. Nothing reads them back as an inventory, so the next kitchen turn starts from whatever Dad says then.

Two details of that shape matter in practice. The principles list holds proposals as well as adopted classes, each with its status, so I can say when an option rests on a principle Dad has not adopted yet. And the dish names under a principle are its direct children only. Dad's variants hang under their dishes, so they reach me through the catalog, which lists every dish and variant with its id.

One turn, sketched

Here is the shape of a turn with an invented kitchen. Say the message is "eggs, a bowl of leftover rice, scallions and some neutral oil". In a tree that holds the aroma-into-fat principle, scallion can play the aromatic and the oil can play the fat, and the rice is a main ingredient. The option I would name is scallion-oil fried rice, the Korean sibling in the design's own worked example, with the why in one sentence: the scallion's aroma moves into the oil first, then the rice cooks in that oil. Exact amounts stay out unless Dad asks, as the addendum's register says. Anything the tree has no role for, I say plainly.

The kitchen can propose changes, within limits. correct and link belong to plate threads, so validation refuses them here. An adopt of a proposed class, a rename, a verdict, or a variant of a dish whose id is in the data all pass. The rest of the loop happens away from the screen, the way the architecture doc's flow describes it: Dad cooks one and photographs it, and the plate closes the loop as a cooked encounter, and as a variant when he changed something.

Principle: Store the interface, and matching becomes a question you can ask from either side. A dish asks which class it belongs to. A shelf asks which classes it can make.

Code

cwkSizzle, backend/threads.py: the kitchen's subject and data, principles with their roles (verbatim, lines 214-223)·python
        else:
            subject = ("This is Dad's kitchen conversation. When he says what he has at home, find the principles "
                       "whose ingredient roles those ingredients can fill, offer a few options with why each one "
                       "works, and name the dishes or variants in his tree they would become. Plates still being "
                       "read have not reached the tree yet; say so rather than calling the tree empty.")
            data = {"principles": [{k: p[k] for k in ("id", "name", "summary", "roles", "status")}
                                   | {"dishes": [d["name"] for d in p["dishes"]]}
                                   for p in store.tree()["principles"]],
                    "dads_tree": store.catalog(),
                    "plates_still_being_read": store.plates_being_read()}
cwkSizzle, frontend/src/pages/Kitchen.tsx: the whole Kitchen page, one Talk bound to the kitchen key (verbatim, lines 1-23)·typescript
import { Talk } from "../Talk";

// The kitchen: tell Pippa what is at home, and she finds the principles
// whose roles those ingredients can fill.

export function Kitchen({ onChanged }: { onChanged: () => void }) {
  return (
    <div className="page-inner kitchen">
      <header className="page-head">
        <h1>Kitchen</h1>
        <p className="muted">Say what you have. Pippa matches it to the roles of the principles in your tree and tells you why each option works.</p>
      </header>
      <div className="kitchen-talk">
        <Talk
          threadKey="kitchen"
          placeholder="At home I have…"
          greeting="What's in the kitchen? Name the ingredients and Pippa finds the principles they can play."
          onChanged={onChanged}
        />
      </div>
    </div>
  );
}
Hand-written illustration, not from the repository: an invented, abbreviated kitchen data block in the shape threads.py builds·json
{
 "principles": [
  {
   "id": "01JZZZZZZZZZZZZZZZZZZZZP01",
   "name": "향을 기름에 먼저 옮기고 그 기름에서 익히기",
   "summary": "향채의 향을 기름에 먼저 녹인 뒤 그 기름으로 주재료를 익힌다.",
   "roles": [
    {"role": "향채", "examples": ["마늘", "파"]},
    {"role": "기름", "examples": ["올리브유", "식용유"]},
    {"role": "주재료", "examples": ["새우", "파스타", "밥"]}
   ],
   "status": "adopted",
   "dishes": ["Aglio e olio", "Gambas al ajillo", "파기름 볶음밥"]
  }
 ],
 "dads_tree": {
  "principles": [{"id": "01JZZZZZZZZZZZZZZZZZZZZP01", "name": "향을 기름에 먼저 옮기고 그 기름에서 익히기", "summary": "…", "status": "adopted"}],
  "dishes": [
   {"id": "01JZZZZZZZZZZZZZZZZZZZZD02", "kind": "dish", "name": "파기름 볶음밥", "gloss": "파기름 볶음밥", "cuisine": "한국", "status": "proposed"}
  ]
 },
 "plates_still_being_read": []
}

External links

Exercise

Model one family of things in your own domain as an interface of roles, the way a principle lists ingredient roles: a workout that needs a push, a pull and a carry, or a trip that needs a place to sleep, a way to get there and something to do. Store the role list on the class row. Then answer the reverse question: given what a user has, which classes can be made from it, with which item filling which role? Do it twice, once as plain code and once by handing the raw role lists to a model as labelled data, and compare the options each one finds.
Hint
The code version is exact and blind to near fits; the model version finds near fits and needs the ids beside the names so its suggestions can be checked. Keep the user's list in the conversation, and add a stored inventory only if you have a reason to keep one.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.