fix(briefing): render briefing body into content so the model sees it#378
Merged
Conversation
nb__briefing put the full BriefingOutput only in structuredContent and a short status note in content. The engine feeds extractTextForModel(content) back to the model and never reads structuredContent, so a chat caller saw only "Briefing generated." with no body. The dashboard reads structuredContent directly and was unaffected. Render the briefing (greeting, lede, sections grouped by category) into content alongside the note, per the platform-tool contract (content = human-readable summary, structuredContent = typed payload). Fixes all three branches (cache / stale / generated). Category and field names anchor on the generator's output schema (recent/upcoming/attention, type).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Symptom
Calling
nb__briefingfrom chat returns one of"Briefing retrieved from cache.","Briefing (refreshing in background).", or"Briefing generated."— with no briefing body, including withforce_refresh: true. The dashboard renders the briefing fine.Root cause
nb__briefing'sok()helper put the entireBriefingOutputinstructuredContentand only the status note incontent(src/tools/core-source.ts). The engine feeds tool results back to the model viaextractTextForModel(finalResult.content)(src/engine/engine.ts:843), which reads onlycontenttext blocks and neverstructuredContent(src/engine/content-helpers.ts:100). So a chat caller (the model) saw only the note. The dashboard readsstructuredContentdirectly, so it was unaffected.nb__briefingis genuinely model-facing — noai.nimblebrain/internalannotation, and its description tells the model it "Returns a summary of recent activity, upcoming items, and anything needing attention." It must therefore carry the human-readable summary incontent, per the platform-tool contract (src/tools/platform/CLAUDE.md§2.1:content= human summary,structuredContent= typed/UI payload).Longstanding behavior (dates to the briefing tool's origin), not a recent regression.
Fix
Add a pure
renderBriefingText(briefing)helper (src/services/briefing-render.ts) that renders greeting + lede + sections grouped by category, and haveok()setcontentto${note}\n\n${rendered}.structuredContentis unchanged, so the dashboard is unaffected. Fixes all three branches (cache / stale / generated).Category and field names anchor on the generator's actual output schema (
category∈recent/upcoming/attention, fieldtype) fromsrc/tools/platform/schemas/home.tsandsrc/services/briefing-generator.ts— not the stale names in the inline dashboard script (see note below).Tests
test/unit/services/briefing-render.test.ts(5 cases): body text reaches the output, greeting/lede included, sections grouped under the correct schema-category labels in order, empty categories omit headings, quiet briefing renders without headings. The category-name test specifically guards the silent-drop failure mode (a renderer keyed on stale names would emit zero sections).bun run verify:staticpasses; all briefing unit tests pass.Follow-up (not in this PR)
The inline dashboard resource script
src/tools/core-resources/scripts/home-briefing-inline.tsuses stale category names (needs_attention/coming_up) and a non-existent field (item.sentiment) — it would render zero sections against real output. The primary React dashboard (src/bundles/home/ui/src/App.tsx) uses the correct names and is unaffected. Worth a separate issue/PR to align the inline script (and ideally share this PR's category-order source of truth).