/// /** * TEST PATTERN: Template Strings Require derive() * * CLAIM: Template strings with multiple properties need derive() wrapper * SOURCE: folk_wisdom/llm.md - "When to Use derive() for generateObject Prompts" * * WHAT THIS TESTS: * - That derive() wrapper enables template strings with OpaqueRef properties * - The broken approach (direct template strings) fails at compile time * * BROKEN APPROACH (does not compile): * ```tsx * const summaries = articles.map((article) => ({ * summary: generateObject({ * prompt: `Title: ${article.title}\nContent: ${article.content}`, // ERROR! * }), * })); * ``` * This fails with: "Tried to directly access an opaque value" * Because JavaScript evaluates ${article.title} IMMEDIATELY, but article is OpaqueRef. * * WORKING APPROACH (this pattern): * ```tsx * const summaries = articles.map((article) => ({ * summary: generateObject({ * prompt: derive(article, (a) => `Title: ${a.title}\nContent: ${a.content}`), * }), * })); * ``` * derive() defers evaluation until reactive context is established. */ import { Cell, Default, derive, generateObject, handler, NAME, pattern, UI, } from "commontools"; interface Article { title: string; content: string; } interface Summary { mainPoints: string[]; sentiment: string; } interface Input { articles: Default; } const addArticle = handler< { detail: { message: string } }, { articles: Cell } >( ({ detail }, { articles }) => { const text = detail?.message?.trim(); if (!text) return; // Parse as "Title | Content" const parts = text.split("|"); const title = parts[0]?.trim() || "Untitled"; const content = parts[1]?.trim() || text; articles.push({ title, content }); }, ); const clearArticles = handler }>( (_, { articles }) => { articles.set([]); }, ); export default pattern(({ articles }) => { // WORKING: derive() wrapper defers template evaluation // Framework tracks dependencies, evaluates inside reactive context const summaries = articles.map((article) => ({ article, summary: generateObject({ system: "Summarize the article. Return main points and overall sentiment.", // derive() defers template evaluation until reactive context prompt: derive(article, (a) => { if (!a) return ""; return `Title: ${a.title}\n\nContent: ${a.content}`; }), model: "anthropic:claude-sonnet-4-5", }), })); return { [NAME]: "Test: Template Strings Need derive()", [UI]: (

Template Strings Require derive()

Folk Wisdom Claim:{" "} Template strings accessing multiple OpaqueRef properties need a derive() wrapper.
{/* Broken code example (documentation only) */}

BROKEN (fails to compile)

{`// This code fails at compile time:
const summaries = articles.map((article) => ({
  summary: generateObject({
    prompt: \`Title: \${article.title}\\nContent: \${article.content}\`,
  }),
}));

// Error: "Tried to directly access an opaque value"`}
          

JavaScript evaluates ${`{article.title}`}{" "} immediately, but article{" "} is an OpaqueRef that requires reactive context.

{/* Working code example */}

WORKING (this pattern)

{`// This code works:
const summaries = articles.map((article) => ({
  summary: generateObject({
    prompt: derive(article, (a) =>
      \`Title: \${a.title}\\nContent: \${a.content}\`
    ),
  }),
}));`}
          

derive(){" "} defers template evaluation until reactive context is established.

Clear Articles {derive({ articles }, ({ articles: arr }) => `${arr.length} article(s)`)}
{/* Live results */}

Live Results (using derive() approach):

{summaries.map((item, idx) => (
{item.article.title}
{item.article.content}
{derive( [item.summary.pending, item.summary.result, item.summary.error], ([pending, result, error]) => { if (pending) { return (
Generating summary...
); } if (error) { return (
Error: {String(error)}
); } const summaryResult = result as Summary | undefined; if (summaryResult) { return (
Main Points:
    {summaryResult.mainPoints.map(( point: string, i: number, ) =>
  • {point}
  • )}
Sentiment: {summaryResult.sentiment}
); } return (
Waiting for input...
); }, )}
))}

Why derive() Is Needed:

  • JavaScript evaluates template strings immediately
  • `${article.title}`{" "} tries to access the property NOW
  • But article{" "} is an OpaqueRef requiring reactive tracking
  • derive(){" "} defers evaluation until reactive context exists
), articles, summaries, }; });