Four refusals
Right after the thesis, the architecture doc lists what Sizzle deliberately is not. There are four lines, and each closes a door that cooking apps usually leave open:
- A recipe database. Exact amounts exist as private detail under a class, never as the front of the product.
- A nutrition or calorie tracker. No calorie dashboards and no meal scoring. Health data belongs to cwkForge.
- A second Pippa. Readings, conversations and model routing stay in cwkPippa, reached through Sizzle's engine.
- An imported encyclopedia. The class tree grows from Dad's own encounters and conversations.
A list like this is easy to write and easy to drift away from. Sizzle's is worth studying because every refusal has a place in the code where it holds. Some live in the schema, some in the prompts, and some in what the engine is able to call at all.
Where each refusal holds
| Refusal | How the code holds it |
|---|---|
| Recipe database | Amounts have one home, a free-text parameters column on a class. The thread addendum tells Pippa to give exact amounts, times or temperatures only when Dad asks. |
| Calorie tracker | No table in the schema has a calorie, nutrient or score column. The rubric's rules include a line forbidding any comment on health, calories or diet, and the addendum forbids diagnosing, blaming one meal and counting calories. |
| Second Pippa | The engine has no model client. backend/pippa.py opens by stating that cwkPippa owns every conversation, turn and model call. Sizzle binds its threads to cwkPippa conversations and reads plates on cwkPippa's utility lane. |
| Imported encyclopedia | No seed file and no import route. A class is created in one function, propose_class, called from four places: a reading's principle, a reading's dish, a reading's sibling, and Dad's own variant. |
Look at the calorie row once more. The strongest refusal there is a column that doesn't exist. There is no column that holds a calorie count, so the schema gives the engine nothing to sum, sort or chart, and a column for it could only arrive as a schema change everyone can see. That wall covers less than it seems to. Free text is still free: a calorie count could ride inside a class's summary or parameters, a plate's note or a reply, and no column would refuse it. Someone could keep a calorie log in those fields today without touching the schema. The missing column keeps calories out of the schema and nowhere else. Keeping them out of the prose is the prompts' job, which is why the rubric and the addendum carry the rule as well.
Ruling 3, as a mechanism
The calorie refusal has a harder neighbor. What happens when the body does matter? Someone cooking might be tired, or might want something light tonight. Dad's third ruling answers it narrowly. Body state shapes suggestions only when Dad says it himself, in a Sizzle conversation. There is no standing feed from cwkForge into Sizzle, and Sizzle never infers a condition from what he ate.
In code it is one sentence of the addendum: consider health or body conditions only when Dad states them in this conversation. Put a neutral example through it. Dad writes "I want something light tonight" in the kitchen thread. Those words are in the conversation, so I can favor lighter options, a clear broth or something blanched, and say why. Without them, nothing about his body enters my answer, however many fried plates sit in the tree.
Architecture invariant 9 holds the whole boundary in one line: Sizzle never diagnoses. No calorie dashboards, no blaming one meal, no health judgment of its own. Body state enters only through Dad's words in the conversation.
Why the rule is written four times
The no-calories rule appears in the architecture doc, in the house sign (CLAUDE.md, "Never a health app"), in the reading rubric and in the thread addendum. That looks redundant until you ask who reads each copy. The doc and the house sign are for whoever changes the code next. The rubric and the addendum are for the model, on every call, because a model starts each request knowing only what that request carries. A rule the model never receives is a rule the model can't follow.
The same reasoning explains the rubric's last line, which tells the model that the input is data about the plate and never instructions. The input carries Dad's note, his corrections and his existing tree, all text that came from somewhere else. Track 6 follows that framing through the conversations, and prompt-quest teaches it in general.
Not a second Pippa
The third refusal is the one most family apps are tempted to break, because a model client is a few lines of code. Sizzle has none. Readings go to cwkPippa's utility lane with the photo, threads are ordinary cwkPippa conversations with origin_surface set to sizzle, and the brain for each function is a family setting resolved at job start. If you want the other side of that boundary, pippa-quest walks through cwkPippa's backend. Here the point is the refusal itself: one Pippa, many surfaces.