Skip to content

docs(activities): recipe formalization — the rewrite DEMOTED recipes from 20 fields to 4 (#274) - #2278

Open
joelteply wants to merge 2 commits into
canaryfrom
design/recipe-formalization-274
Open

docs(activities): recipe formalization — the rewrite DEMOTED recipes from 20 fields to 4 (#274)#2278
joelteply wants to merge 2 commits into
canaryfrom
design/recipe-formalization-274

Conversation

@joelteply

Copy link
Copy Markdown
Contributor

Design doc, nothing built. Written after auditing every recipe artifact in the tree.

The audit is the headline

recipes fields
legacy/src/system/recipes/ 28 uniqueId, version, name, displayName, description, tags, pipeline, ragTemplate, strategy, roles, tools, sentinelTemplates, layout, view, access, isPublic, locked, inputs, room, entityType
core/…/experience/recipes/ 4 purpose, regions, affordances, layout

The rewrite reduced a recipe to presentation. Every field describing how an activity behaves was dropped.

ai-debate-club.json ships roles: [{role, type, requires, prefers}] and strategy.responseRules. Those are precisely the two things found missing this session, when citizens with no roles and no participation rules looped in academy. Not unbuilt — designed, authored 28 times, lost in the port. Open cards re-deriving pieces of it: #371, #6, #264.

Identity was the loss that caused the live bug

uniqueId + version became one free-form purpose string doing two jobs — identity and taxonomy. Hence benchmark vs benchmark/hard-rs being indistinguishable, hence spawn's docs advertising a purpose that resolves to nothing (fixed separately in #2277), hence those rooms rendering as plain chat.

Design

  • RecipeId (UUID) + version — stable across content edits, so a bugfix to chat.json doesn't orphan every chat room. Deliberately not a content hash, for that reason.
  • Reproducibility moves to the run receipt(recipe_id, version, content_hash). A benchmark's exact bytes pinned forever without brittle identity. Same shape forge-alloy uses.
  • purpose demoted to a label, hierarchical. Grouping is then free: benchmark/* is a prefix query, rooms live at academy/bench/<suite>/<run>, web UI rewrites URLs from the taxonomy rather than a hand-kept nav.
  • One registry, two ways in — shipped (embedded, stable ids) and installed (recipe/install). Spawn resolves an id and doesn't care which. Install is the validation boundary.
  • Constants, not an enum — a closed set can't express a system where anyone writes a recipe, and reintroduces the compiler-to-author trap experience/source.rs already warns about.
  • Binding carries (id, version, content) so a partitioned node still renders its rooms.

Six-step build order. Steps 1–2 kill the silent-chat-room fallback, step 4 gives per-run benchmark rooms (#346 slice 3), step 5 restores roles (#264's open half), step 6 ports 28 activity types forward as data with zero Rust.

Also records what not to build: room field on work cards (retracted earlier in #274), an activity-type enum, hierarchical purpose fallback, automatic room expiry.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

…#274)

Design, not built. Written after auditing every recipe artifact in the tree, and
the audit is the headline.

THE FINDING: the recipe system did not fail to get built — it was DEMOTED in the
Rust rewrite.

  legacy/src/system/recipes/     28 recipes, 20 fields
  core/…/experience/recipes/      4 recipes,  4 fields

The live four (benchmark/hard-rs, chat, profile, video-chat) carry only
`purpose, regions, affordances, layout` — PRESENTATION. Everything describing how
an activity BEHAVES was dropped: pipeline, ragTemplate, strategy, roles, tools,
sentinelTemplates, access, isPublic, locked, tags, name/description, and — the one
that bit us — uniqueId + version.

`ai-debate-club.json` ships `roles: [{role, type, requires, prefers}]` and
`strategy.responseRules`. Those are exactly the two things found missing this
session when citizens had no roles and no participation rules and looped in
academy. Not unbuilt. Designed, authored 28 times, lost in the port. Several open
cards are re-deriving pieces of it: #371 (recipe-owned objective), #6 (ground
persona in room purpose), #264's role gap.

IDENTITY is the specific loss that caused the live defect. uniqueId + version were
replaced by ONE free-form `purpose` string doing two jobs — identity AND taxonomy.
That is why `benchmark` (family) and `benchmark/hard-rs` (instance) were
indistinguishable, why spawn's docs advertised a purpose resolving to nothing, and
why such rooms render as plain chat.

THE DESIGN:
- RecipeId (UUID) + version = identity. Stable across content edits, so a bugfix to
  chat.json does not orphan every chat room. NOT a content hash for that reason.
- Reproducibility moves to the RUN RECEIPT: (recipe_id, version, content_hash), so a
  benchmark's exact bytes are pinned forever without making identity brittle. Same
  shape forge-alloy already uses for models.
- purpose demoted to a free-form hierarchical LABEL. Grouping falls out of the path:
  `benchmark/*` is a prefix query, rooms live at academy/bench/<suite>/<run>, and
  the web UI rewrites URLs from the same taxonomy instead of a hand-kept nav.
- ONE registry, TWO ways in: shipped (embedded, stable ids) and installed
  (`recipe/install` at runtime). Spawn resolves an id and does not care which.
  Install is the validation boundary — serde, fail loud, assign identity.
- NOT an enum (a closed set cannot express a system where anyone writes a recipe,
  and it reintroduces the compiler-to-author trap experience/source.rs warns about).
  Named constants over an open registry: `shipped::BENCHMARK_HARD_RS`.
- Binding carries (id, version, content) so a partitioned node still renders its
  rooms — addressing by identity while keeping bytes local, as expert paging does.

Six-step build order; steps 1-2 remove the silent-chat-room fallback, step 4 fixes
per-run benchmark rooms (#346 slice 3), step 5 restores roles (#264's open half),
step 6 ports 28 activity types forward as pure data with zero Rust.

Explicitly recorded as NOT to build: room field on work cards (retracted), an
activity-type enum, hierarchical purpose fallback, automatic room expiry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
… open cards (#274)

Joel: 'Old shit was broken and a mess but many extra recipes we maybe miss.'

Enumerates every legacy recipe so they cannot be lost twice, and maps them to work
already underway. SEVEN describe activities we have OPEN CARDS re-deriving from
scratch: training-dashboard (#141, in progress), grid-overview (#257), universe
(#375), research (#93), academy-training, factory, inference-sample.

Sharpest instance: general-chat's description is 'Natural conversation where AIs
respond to humans thoughtfully, AVOIDING AI-TO-AI LOOPS.' That requirement was
recipe-level, authored, and lost in the port — and this session was spent watching
three citizens loop in academy.

Port the concept and field content, not the Node implementation. Each port is a
JSON file plus a test that its purpose resolves — zero Rust.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
@github-actions github-actions Bot added size: L and removed size: M labels Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant