From 2d2bf2ff83bb293135f3de64293edacd74870f2c Mon Sep 17 00:00:00 2001 From: Jeff Ober Date: Wed, 9 Sep 2026 10:54:34 -0600 Subject: [PATCH 1/2] Trim conversation_search; land deferred gating-column drops; recheck tokens conversation_search: 2460 -> ~1990 chars. Drops the 'summary is auto-generated' restatement (implied by the return shape), the passage-absence clause (a negation the model can infer), and tightens the within_days/prefer_recent param prose. The RANKING-IS-BY-TOPIC guard - the observed-failure guard that made this tool big - stays verbatim. recipe_save rebuilt: 2181 -> 1718 chars. The accreted prose rules collapse into a one-line grammar + inline-reference example + terse wrong-vs-right guards. Every observed-failure guard intact; the full cooklang spec stays in docs/user/cookbook.md for humans. toolboxes_enabled / tools_enabled drops go live: two releases have deployed since the deferral, so no cached pre-gating-retirement bundle can still be in service. Verified dropped against prod. Token recheck (30h post-merge window, small n): declare-everything did NOT raise cold prompts by the predicted +10k (24,865 vs 31,393 gated era); continuation marginal growth is 17.3% of full prompt - the cache rides as designed; zero empty completions since #543 (both 900-1000 ct turns delivered full content). Full writeup in scratch/. --- src/lib/tools/conversation_search.schema.ts | 44 +++++++--------- src/lib/tools/recipe_save.schema.ts | 57 ++++++++++----------- supabase/schema.sql | 20 +++----- 3 files changed, 50 insertions(+), 71 deletions(-) diff --git a/src/lib/tools/conversation_search.schema.ts b/src/lib/tools/conversation_search.schema.ts index 124ae1d4..59c727de 100644 --- a/src/lib/tools/conversation_search.schema.ts +++ b/src/lib/tools/conversation_search.schema.ts @@ -15,22 +15,15 @@ export const conversationSearchSchema = { 'over what was actually said in them as well as their title and ' + 'summary. Returns {id, title, summary, updated_at, archived, ' + 'match_kind, similarity?, passage?}[]. ' + - 'Search in the words the user would have used, not in the words a ' + - 'title would use - the message text is indexed, so "ran out of ' + - 'lentils" finds the conversation that says it however it is ' + - 'titled. ' + - '`passage` is the excerpt that matched, when the hit came from the ' + - 'message text; pass it back as conversation_get\'s `query` to open ' + - 'the thread at that point rather than at its end. Absent on threads ' + - 'matched only by title/summary. ' + - 'summary is auto-generated after the first terminal assistant turn ' + - '(null on brand-new threads). Archived threads are included; weigh ' + - 'the archived flag lower if freshness matters. ' + - 'RANKING IS BY TOPIC ONLY unless you say otherwise. If the user ' + - 'anchors their request in time - "yesterday", "last week", "the ' + - 'other day", "recently" - a plain query will happily return the ' + - 'best topical match from a year ago and nothing recent at all. Use ' + - '`within_days` when the time frame is a requirement, and ' + + 'Search in the words the user would have used, not title words - ' + + 'the message text is indexed, so "ran out of lentils" finds the ' + + 'conversation that says it however it is titled. `passage` is the ' + + 'matched excerpt; pass it back as conversation_get\'s `query` to ' + + 'open the thread at that point. ' + + 'RANKING IS BY TOPIC ONLY unless you say otherwise: if the user ' + + 'anchors the request in time ("yesterday", "last week", "recently"), ' + + 'a plain query may return the best topical match from a year ago. ' + + 'Use `within_days` when the time frame is a requirement, ' + '`prefer_recent` when it is only a lean.', shortDescription: 'search past conversations by topic', parameters: { @@ -52,20 +45,19 @@ export const conversationSearchSchema = { maximum: CONVERSATION_SEARCH_MAX_WITHIN_DAYS, description: 'Only consider conversations with activity in the last N days. ' + - 'A hard filter, so a great match outside the window is dropped ' + - 'entirely - use it when the user made the time frame a ' + - 'requirement ("the conversation from yesterday"), not when they ' + - 'merely implied freshness. "the last few days" is about 3; "last ' + - 'week" about 7.', + 'A hard filter - a great match outside the window is dropped ' + + 'entirely. Use when the user made the time frame a requirement, ' + + 'not when they merely implied freshness ("the last few days" is ' + + 'about 3; "last week" about 7).', }, prefer_recent: { type: 'boolean', description: - 'Break near-ties toward more recent conversations, without ' + - 'excluding anything. Deliberately gentle: it reorders results ' + - 'that already score similarly and will NOT lift a weak match ' + - 'above a strong one. Use for "did we talk about this recently"; ' + - 'use within_days when the time frame is the actual requirement.', + 'Break near-ties toward more recent conversations without ' + + 'excluding anything - it reorders similar scores, it will NOT ' + + 'lift a weak match above a strong one. Use for "did we talk ' + + 'about this recently"; use within_days when the frame is a ' + + 'requirement.', }, }, required: ['query'], diff --git a/src/lib/tools/recipe_save.schema.ts b/src/lib/tools/recipe_save.schema.ts index 8f99c3c1..575940b3 100644 --- a/src/lib/tools/recipe_save.schema.ts +++ b/src/lib/tools/recipe_save.schema.ts @@ -65,37 +65,32 @@ export const recipeSaveSchema = { 'Omit id to create (title + cooklang required); pass id (from ' + 'recipe_list) to update, providing only the fields that change - ' + 'pass null for source / source_url to clear them. cooklang is the ' + - 'raw Cooklang source (https://cooklang.org/docs/spec/): ' + - '@ingredient{qty%unit}, #cookware{}, ~timer{d%unit}, ' + - `>> metadata: value (max ${MAX_RECIPE_COOKLANG_CHARS} chars). Group ` + - 'long recipes with `== Section ==` or `# Section` headers. Two ' + - 'authoring styles supported and mixable: (a) pure Cooklang (each ' + - 'line is an instruction with inline ingredients); (b) cookbook-style ' + - "(a line whose first non-whitespace char is `@` is an ingredient " + - 'DECLARATION, not numbered as an instruction; a dash-only line like ' + - '`--` ends the declaration block so prose instructions below render ' + - 'as a flat numbered list). Wrap a long instruction across lines by ' + - 'prefixing continuations with `> `. Inline emphasis is supported in ' + - 'step text: `**bold**`, `*italic*`, and `_italic_` render as styled ' + - 'spans. Backtick code spans are NOT rendered - they show as literal ' + - "backticks, so don't use them. For durations, prefer the Cooklang " + - 'timer syntax `~{N%unit}` (e.g. `~{4-5%hours}`) so the duration ' + - 'also contributes to the timers list; wrapping it in `**...**` for ' + - 'emphasis is fine but the `~` is what makes it a timer. For an ' + - 'ingredient with a modifier, write the whole phrase as a single ' + - 'multi-word braced name: `@pre-minced garlic{1%tbsp}`, NEVER ' + - '`@pre-minced @garlic{1%tbsp}` (which creates two separate ' + - 'ingredient entries). Mark an OPTIONAL ingredient with `?` right ' + - 'after the `@` (`@?cilantro{2%tbsp}`, bare `@?cilantro`) - it ' + - 'renders with an "(optional)" tag in the ingredient list. For ' + - "alternatives (\"use X or Y\"), only the primary ingredient gets " + - '`@`; write the substitute as plain prose. The star rating is the ' + - "user's own verdict and is not editable here - only they can set " + - 'or clear it, from the recipe card. change_message: optional on ' + - 'create (defaults to "Initial version"), required on update - it ' + - 'lands in the recipe history the user reviews. Returns the saved ' + - 'row plus the current photo list, which this tool never changes - ' + - 'use the recipe_photos_* tools to edit photos.', + `raw recipe source (max ${MAX_RECIPE_COOKLANG_CHARS} chars). ` + + 'Grammar per line: ' + + 'step | ingredient-declaration | section | metadata | comment; ' + + 'ingredient := "@" "?"? name ("{" qty "%" unit? "}")?; ' + + 'cookware := "#" name "{}"; ' + + 'timer := "~" name? "{" qty "%" unit "}" ' + + `(e.g. ~{4%hours}); metadata := ">>" key ":" value. ` + + 'Two authoring styles, mixable. Pure style puts references inline ' + + 'in step prose ("Season the @pork{}, add @soy sauce{2%tbsp} to the ' + + '#wok{}, cook ~{3%minutes}"). Cookbook style: `@`-first lines are ' + + 'not numbered as steps); a dash-only line ends the declaration block so ' + + 'prose below renders as a flat numbered list. Guards: ' + + 'a modifier+ingredient is ONE multi-word braced name - ' + + '`@pre-minced garlic{1%tbsp}`, NEVER `@pre-minced @garlic{...}`; ' + + 'optional ingredients take `?` after the `@` (`@?cilantro{2%tbsp}`); ' + + 'alternatives get one `@` with the substitute as prose; ' + + 'prefer timer syntax `~{N%unit}` over prose durations (it feeds the timers list); ' + + "wrap long steps with a `> ` continuation line; " + + '`== Soup ==` / `# Soup` start sections; ' + + 'emphasis `**bold**` / `*italic*` renders, backticks do NOT. ' + + "The star rating is the user's verdict and is not editable here - " + + 'only they can set or clear it, from the recipe card. ' + + 'change_message: optional on create (defaults to "Initial version"), ' + + 'required on update - it lands in the recipe history the user reviews. ' + + 'Returns the saved row plus the current photo list, which this tool ' + + 'never changes - use the recipe_photos_* tools to edit photos.', shortDescription: 'save a recipe to the cookbook', parameters: { type: 'object', diff --git a/supabase/schema.sql b/supabase/schema.sql index 45f2fbd0..d3b84843 100644 --- a/supabase/schema.sql +++ b/supabase/schema.sql @@ -1152,20 +1152,12 @@ grant execute on function public.mark_second_thoughts_acted(uuid) -- gate existed. `toolboxes_enabled` held that set (written by the -- retired toggle_toolbox meta-tool and composer popover); -- `tools_enabled` was its boolean predecessor. No code reads --- either any more. --- --- The drop is DEFERRED one release on purpose. The deploy pipeline --- applies this file BEFORE the new frontend reaches GitHub Pages, --- and the previously-deployed frontend still writes --- `toolboxes_enabled` in its createThread / forkThread inserts and --- its toolbox-popover UPDATE - dropping the column now makes the --- old bundle's first-send INSERT fail with PGRST204 for the --- minutes-wide window until Pages finishes publishing. A dropped --- column harms nothing while the new code (which never selects or --- writes it) is live, so the drop lands in a follow-up PR once no --- cached old bundle can still be in service. --- alter table public.threads drop column if exists toolboxes_enabled; --- alter table public.threads drop column if exists tools_enabled; +-- either any more. The drop was deferred one release so the +-- pre-gating-retirement frontend (which wrote toolboxes_enabled in +-- its createThread / forkThread inserts) survived the mixed-version +-- window; two releases have since deployed, so the drop is live. +alter table public.threads drop column if exists toolboxes_enabled; +alter table public.threads drop column if exists tools_enabled; -- Soft-hide flag for the "Archive" drawer section. Archived threads still -- load into the sidebar and remain viewable, but the composer is disabled From da4cc658368e85b5e0be65c7c94b7d0f757b1285 Mon Sep 17 00:00:00 2001 From: Jeff Ober Date: Wed, 9 Sep 2026 12:53:30 -0600 Subject: [PATCH 2/2] recipe_save: note-after-reference guard clause QA surfaced the miss on first attempt: the model wrote prep hints inside the ingredient name (@finely chopped basil) - syntactically legal cooklang, so the validator accepts it and the hint renders as part of the name instead of a note. The rebuilt spec's guards now name it explicitly with the wrong-vs-right pair, matching the other observed-failure guards. --- src/lib/tools/recipe_save.schema.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/tools/recipe_save.schema.ts b/src/lib/tools/recipe_save.schema.ts index 575940b3..9dcbfaf2 100644 --- a/src/lib/tools/recipe_save.schema.ts +++ b/src/lib/tools/recipe_save.schema.ts @@ -79,6 +79,8 @@ export const recipeSaveSchema = { 'prose below renders as a flat numbered list. Guards: ' + 'a modifier+ingredient is ONE multi-word braced name - ' + '`@pre-minced garlic{1%tbsp}`, NEVER `@pre-minced @garlic{...}`; ' + + 'prep hints are a note AFTER the reference, not inside the name - ' + + '`@basil{1%tbsp} (finely chopped)`, never `@finely chopped basil{...}`; ' + 'optional ingredients take `?` after the `@` (`@?cilantro{2%tbsp}`); ' + 'alternatives get one `@` with the substitute as prose; ' + 'prefer timer syntax `~{N%unit}` over prose durations (it feeds the timers list); ' +