From dd760d0f2b3daeba1bcf25cc5cdb8b8c0cfee4d7 Mon Sep 17 00:00:00 2001 From: Chili Palmer Date: Sat, 30 May 2026 13:39:57 +0200 Subject: [PATCH] B1-1: distill 9 inline surface types into editor_chat.allium spec --- SPECGAPS.md | 2 +- specs/ai.allium | 8 +++- specs/editor_chat.allium | 80 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 2 deletions(-) diff --git a/SPECGAPS.md b/SPECGAPS.md index 3ba9fc7..03231c8 100644 --- a/SPECGAPS.md +++ b/SPECGAPS.md @@ -60,7 +60,7 @@ Gap categories: **SC** = spec correct, fix code | **CS** = code correct, update | ID | Behavior | Code Location | Path | |---|---|---|---| -| B1-1 | Chat inline surfaces (9 types: card, chart, form, list, metric, mindmap, table, tabs, text/json) | `lib/bds/ui/chat/tool_surfaces.ex:6-15` | Distill into spec | +| ~~B1-1~~ | ~~Chat inline surfaces (9 types: card, chart, form, list, metric, mindmap, table, tabs, text/json)~~ | `tool_surfaces.ex` | **Resolved:** added InlineSurface value type with all 9 discriminators, supporting value types (SurfaceAction, ChartSeries, MindmapNode, FormField, FieldOption, TabPanel), inline_surfaces on ChatMessage, InlineSurfaceRendering guarantee, and surface_form_debounce_ms config to editor_chat.allium; updated StructuredRenderTools invariant in ai.allium to list all 9 types | | B1-2 | Auto-translation system (AutoTranslation.maybe_schedule, media cascade, batch fill) | `lib/bds/posts/auto_translation.ex` | Distill into spec | | B1-3 | 3 extra settings sections (Technology, MCP, Data Maintenance) | `lib/bds/ui/settings_editor/` | Distill into spec | | B1-4 | Style/Theme as separate tab (`:style`), not settings section | `lib/bds/ui/style_editor.ex` | Distill into spec | diff --git a/specs/ai.allium b/specs/ai.allium index 9173e7d..6923319 100644 --- a/specs/ai.allium +++ b/specs/ai.allium @@ -396,8 +396,14 @@ invariant ChatCancellation { } invariant StructuredRenderTools { - -- Chat may emit structured render payloads for charts, tables, and forms. + -- Chat may emit structured render payloads via tool calls prefixed "render_". + -- 9 inline surface types: card, chart, form, list, metric, mindmap, table, tabs, text, json. + -- Render tool names: render_card, render_chart, render_form, render_list, + -- render_metric, render_mindmap, render_table, render_tabs. + -- Unrecognized render tool names render as JSON (raw dump). + -- Tab content may nest any surface type including plain text. -- These payloads are data contracts, not arbitrary HTML strings. + -- Surface data contracts are defined in editor_chat.allium InlineSurface value. } invariant BlogStatsPromptAugmentation { diff --git a/specs/editor_chat.allium b/specs/editor_chat.allium index 8df4567..7d612ed 100644 --- a/specs/editor_chat.allium +++ b/specs/editor_chat.allium @@ -23,9 +23,72 @@ value ChatMessage { role: String -- user | assistant | system content: String -- user: plain text; assistant: GFM markdown tool_markers: List + inline_surfaces: List is_streaming: Boolean -- true while accumulating } +value InlineSurface { + id: String + type: "card" | "chart" | "form" | "list" | "metric" | "mindmap" | "table" | "tabs" | "text" | "json" + title: String? + subtitle: String? -- card only + body: String? -- card, text + actions: List -- card only + columns: List -- table + rows: List> -- table + chart_type: String? -- chart: bar/pie/line + series: List -- chart + max_value: Integer? -- chart + label: String? -- metric + value: String? -- metric + items: List -- list + nodes: List -- mindmap + fields: List -- form + submit_label: String? -- form + submit_action: String? -- form + tabs: List -- tabs + selected_index: Integer? -- tabs + raw: Map? -- json catch-all +} + +value SurfaceAction { + label: String + action: String + payload: Map +} + +value ChartSeries { + label: String + value: Integer + segments: List +} + +value MindmapNode { + id: String? + label: String + children: List +} + +value FormField { + key: String + label: String + input_type: String + placeholder: String? + value: String? + options: List + required: Boolean +} + +value FieldOption { + label: String + value: String +} + +value TabPanel { + label: String + content: List -- nested surfaces or text +} + value ToolMarker { tool_name: String args_preview: String -- string args truncated to config.chat_tool_args_max_length @@ -66,6 +129,7 @@ value ModelEntry { config { chat_tool_args_max_length: Integer = 30 chat_input_max_height: Integer = 200 + surface_form_debounce_ms: Integer = 500 } surface ChatPanelSurface { @@ -85,6 +149,9 @@ surface ChatPanelSurface { tm.tool_name tm.args_preview tm.is_complete + for sfc in msg.inline_surfaces: + sfc.type + sfc.id provides: ChatSendMessage(panel.conversation_id, panel.input_text) @@ -138,6 +205,19 @@ surface ChatPanelSurface { -- Actions dispatched through store, same as user clicks. -- Navigation actions open tabs with pin intent. + @guarantee InlineSurfaceRendering + -- Assistant render-tool calls produce structured inline surfaces + -- rendered between message content and tool markers. + -- 9 surface types: card, chart, form, list, metric, mindmap, table, tabs, text, json. + -- Render tool names: render_card, render_chart, render_form, render_list, + -- render_metric, render_mindmap, render_table, render_tabs. + -- Unrecognized render names render as json (raw dump). + -- Tab content nests any surface type including plain text. + -- Form surfaces persist field values in conversation surface_state. + -- Tab surfaces track selected_index in conversation surface_state. + -- Surfaces can be dismissed, persisted in conversation surface_state. + -- Form debounce: config.surface_form_debounce_ms. + @guarantee TokenTracking -- Token usage tracked per conversation. -- Displayed in status bar, not in the chat panel itself.