From aebecb43b73f329716d901442aed4b18a2a9b650 Mon Sep 17 00:00:00 2001 From: Ryan Dombrowski Date: Fri, 10 Jul 2026 19:04:11 -0400 Subject: [PATCH 1/5] feat: add 'structured-editing' intent + ex.recipe-creator worked example (owner-approved) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Additive, v0.4-safe: no rules added (per owner decision — no failure evidence supports one), no existing intents or examples change. The worked example is the dspack-studio recipe-creator surface, verbatim; it lints S1/S2/S3 clean. Few-shot contexts are intent-scoped, so destructive-action and scheduling contexts are byte-stable. Evidence basis: the 216-run scheduling matrix (192/216 passed vs 67/216 baseline, zero emitter-gate failures) — with the recorded limitation that the v0.1.4 alignment and the scheduling example were not separately ablated. Co-Authored-By: Claude Fable 5 --- examples/astryx.dspack.json | 107 ++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/examples/astryx.dspack.json b/examples/astryx.dspack.json index d7527d1..e0abb4d 100644 --- a/examples/astryx.dspack.json +++ b/examples/astryx.dspack.json @@ -401,6 +401,11 @@ "id": "scheduling", "name": "Scheduling", "description": "Surfaces that collect a time choice and confirm it." + }, + { + "id": "structured-editing", + "name": "Structured editing", + "description": "Surfaces the user and agent edit together: a structured object rendered for co-editing, with explicit actions to change it." } ], "rules": [ @@ -669,6 +674,108 @@ ] } } + }, + { + "id": "ex.recipe-creator", + "intent": "structured-editing", + "name": "Recipe creator", + "prompt": "an editable weeknight recipe with servings and dietary constraints", + "description": "A card presenting a co-edited structured object: a titled recipe with a dietary badge, labeled servings controls, an ingredients table, a labeled constraint input, a bound status line, and explicit edit/regenerate actions.", + "surface": { + "dspackSurface": "0.1", + "system": "Astryx", + "intent": "structured-editing", + "root": { + "component": "card", + "id": "recipe-card", + "children": [ + { + "component": "text", + "id": "title", + "props": { + "type": "display-3" + }, + "text": "Weeknight pasta" + }, + { + "component": "badge", + "id": "diet-badge", + "props": { + "label": "no constraints", + "variant": "neutral" + } + }, + { + "component": "text", + "id": "servings-label", + "props": { + "type": "body" + }, + "text": "Servings: 2" + }, + { + "component": "button", + "id": "servings-down", + "props": { + "label": "Fewer servings", + "variant": "secondary" + } + }, + { + "component": "button", + "id": "servings-up", + "props": { + "label": "More servings", + "variant": "secondary" + } + }, + { + "component": "table", + "id": "ingredients", + "props": { + "columns": [ + "Ingredient", + "Amount" + ], + "data": [], + "density": "compact" + } + }, + { + "component": "text-input", + "id": "constraint-input", + "props": { + "label": "Dietary constraint", + "placeholder": "vegetarian, vegan, or gluten-free" + } + }, + { + "component": "button", + "id": "apply-constraint", + "props": { + "label": "Apply constraint", + "variant": "secondary" + } + }, + { + "component": "text", + "id": "status", + "props": { + "type": "supporting" + }, + "text": "Edit servings or add a constraint." + }, + { + "component": "button", + "id": "regenerate", + "props": { + "label": "Regenerate recipe", + "variant": "primary" + } + } + ] + } + } } ] } From 3d6819d6f56c5c897e391ce45992e2077b76d3b4 Mon Sep 17 00:00:00 2001 From: Ryan Dombrowski Date: Fri, 10 Jul 2026 19:07:41 -0400 Subject: [PATCH 2/5] =?UTF-8?q?fix(example):=20populate=20the=20recipe=20t?= =?UTF-8?q?able=20rows=20=E2=80=94=20teach=20the=20row=20shape?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generated tables were A3-refused (columns/data shape unknown to models taught by an empty table). The worked example now carries four literal rows ({cells: [name, amount]}), matching the surface the studio ships. Co-Authored-By: Claude Fable 5 --- examples/astryx.dspack.json | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/examples/astryx.dspack.json b/examples/astryx.dspack.json index e0abb4d..2485d74 100644 --- a/examples/astryx.dspack.json +++ b/examples/astryx.dspack.json @@ -737,7 +737,32 @@ "Ingredient", "Amount" ], - "data": [], + "data": [ + { + "cells": [ + "Spaghetti", + "180 g" + ] + }, + { + "cells": [ + "Pancetta", + "80 g" + ] + }, + { + "cells": [ + "Parmesan", + "50 g" + ] + }, + { + "cells": [ + "Cherry tomatoes", + "240 g" + ] + } + ], "density": "compact" } }, From ab837f9d89a1e339d1cd31939e3ff9c7914ac8b7 Mon Sep 17 00:00:00 2001 From: Ryan Dombrowski Date: Fri, 10 Jul 2026 19:36:50 -0400 Subject: [PATCH 3/5] fix(examples): serialize example nodes in generation-schema key order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grammar-constrained decoders (Ollama structured outputs / llama.cpp) enforce the schema's declared property order: component, id, text, props, children. The examples serialized 'text' after 'props', so few-shot generation taught models an order the grammar then forbids — measured as text-less nodes in every generated surface and early-closed minimal surfaces. Pure key reordering; no semantic change to any example. Co-Authored-By: Claude Fable 5 --- examples/astryx.dspack.json | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/examples/astryx.dspack.json b/examples/astryx.dspack.json index 2485d74..e5b3703 100644 --- a/examples/astryx.dspack.json +++ b/examples/astryx.dspack.json @@ -551,17 +551,17 @@ "children": [ { "component": "text", + "text": "Danger zone", "props": { "type": "display-3" - }, - "text": "Danger zone" + } }, { "component": "text", + "text": "Deleting a project removes all of its data permanently.", "props": { "as": "p" - }, - "text": "Deleting a project removes all of its data permanently." + } }, { "component": "button", @@ -601,18 +601,18 @@ { "component": "text", "id": "title", + "text": "Book a consultation", "props": { "type": "display-3" - }, - "text": "Book a consultation" + } }, { "component": "text", "id": "intro", + "text": "Pick a time that works. Your name is required to hold the slot.", "props": { "type": "body" - }, - "text": "Pick a time that works. Your name is required to hold the slot." + } }, { "component": "text-input", @@ -626,10 +626,10 @@ { "component": "text", "id": "status", + "text": "Pick a time to begin.", "props": { "type": "supporting" - }, - "text": "Pick a time to begin." + } }, { "component": "button", @@ -692,10 +692,10 @@ { "component": "text", "id": "title", + "text": "Weeknight pasta", "props": { "type": "display-3" - }, - "text": "Weeknight pasta" + } }, { "component": "badge", @@ -708,10 +708,10 @@ { "component": "text", "id": "servings-label", + "text": "Servings: 2", "props": { "type": "body" - }, - "text": "Servings: 2" + } }, { "component": "button", @@ -785,10 +785,10 @@ { "component": "text", "id": "status", + "text": "Edit servings or add a constraint.", "props": { "type": "supporting" - }, - "text": "Edit servings or add a constraint." + } }, { "component": "button", From 89524af3cd6b8d2760138e21cbbc7c5e0b0927fc Mon Sep 17 00:00:00 2001 From: Ryan Dombrowski Date: Fri, 10 Jul 2026 19:54:55 -0400 Subject: [PATCH 4/5] Revert "fix(examples): serialize example nodes in generation-schema key order" This reverts commit ab837f9d89a1e339d1cd31939e3ff9c7914ac8b7. --- examples/astryx.dspack.json | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/examples/astryx.dspack.json b/examples/astryx.dspack.json index e5b3703..2485d74 100644 --- a/examples/astryx.dspack.json +++ b/examples/astryx.dspack.json @@ -551,17 +551,17 @@ "children": [ { "component": "text", - "text": "Danger zone", "props": { "type": "display-3" - } + }, + "text": "Danger zone" }, { "component": "text", - "text": "Deleting a project removes all of its data permanently.", "props": { "as": "p" - } + }, + "text": "Deleting a project removes all of its data permanently." }, { "component": "button", @@ -601,18 +601,18 @@ { "component": "text", "id": "title", - "text": "Book a consultation", "props": { "type": "display-3" - } + }, + "text": "Book a consultation" }, { "component": "text", "id": "intro", - "text": "Pick a time that works. Your name is required to hold the slot.", "props": { "type": "body" - } + }, + "text": "Pick a time that works. Your name is required to hold the slot." }, { "component": "text-input", @@ -626,10 +626,10 @@ { "component": "text", "id": "status", - "text": "Pick a time to begin.", "props": { "type": "supporting" - } + }, + "text": "Pick a time to begin." }, { "component": "button", @@ -692,10 +692,10 @@ { "component": "text", "id": "title", - "text": "Weeknight pasta", "props": { "type": "display-3" - } + }, + "text": "Weeknight pasta" }, { "component": "badge", @@ -708,10 +708,10 @@ { "component": "text", "id": "servings-label", - "text": "Servings: 2", "props": { "type": "body" - } + }, + "text": "Servings: 2" }, { "component": "button", @@ -785,10 +785,10 @@ { "component": "text", "id": "status", - "text": "Edit servings or add a constraint.", "props": { "type": "supporting" - } + }, + "text": "Edit servings or add a constraint." }, { "component": "button", From 0ea1ac20d6beda0987390e98477cfd64faae2cf8 Mon Sep 17 00:00:00 2001 From: Ryan Dombrowski Date: Fri, 10 Jul 2026 19:55:07 -0400 Subject: [PATCH 5/5] =?UTF-8?q?fix(contract):=20type=20the=20table's=20col?= =?UTF-8?q?umns/data=20items=20=E2=80=94=20align=20grammar,=20example,=20a?= =?UTF-8?q?nd=20profile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The columns description claimed [{key, header}] objects while the approved worked example and the A2UI projection both use header-label strings; the untyped arrays also left grammar-constrained generation free to emit shapes the A3 gate then refuses (measured: every rich structured-editing run failed on '/columns must be array' or missing data). Items are now declared: columns as strings, data as { cells: string[] } records. The full Astryx TableColumn shape remains future contract work. Co-Authored-By: Claude Fable 5 --- examples/astryx.dspack.json | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/examples/astryx.dspack.json b/examples/astryx.dspack.json index 2485d74..79b21a9 100644 --- a/examples/astryx.dspack.json +++ b/examples/astryx.dspack.json @@ -283,11 +283,28 @@ "props": { "data": { "type": "array", - "description": "Row objects." + "description": "Row records: each { cells: string[] } aligned with columns.", + "items": { + "type": "object", + "properties": { + "cells": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "cells" + ] + } }, "columns": { "type": "array", - "description": "Column definitions: [{key, header, ...}]." + "description": "Header labels for the data columns, e.g. [\"Ingredient\", \"Amount\"]. (The full Astryx TableColumn object shape is not yet contract-specified.)", + "items": { + "type": "string" + } }, "density": { "type": "enum",