From 9367d2b957bde3bf890f29b5d3b17cc4b180853a Mon Sep 17 00:00:00 2001 From: Ken Date: Tue, 26 May 2026 20:03:53 +0000 Subject: [PATCH] feat: context files for A2UI format, artifact conventions, researcher instructions --- bundle/context/a2ui-format.md | 145 ++++++++++++++++ bundle/context/artifact-conventions.md | 84 ++++++++++ bundle/context/researcher-instructions.md | 24 +++ tests/test_context_files.sh | 191 ++++++++++++++++++++++ 4 files changed, 444 insertions(+) create mode 100644 bundle/context/a2ui-format.md create mode 100644 bundle/context/artifact-conventions.md create mode 100644 bundle/context/researcher-instructions.md create mode 100755 tests/test_context_files.sh diff --git a/bundle/context/a2ui-format.md b/bundle/context/a2ui-format.md new file mode 100644 index 0000000..f723710 --- /dev/null +++ b/bundle/context/a2ui-format.md @@ -0,0 +1,145 @@ +# A2UI Block Format + +A2UI (AI-to-UI) lets you generate interactive UI controls directly in the chat. The +frontend parses `a2ui` code blocks and renders them as Tabler components — filter +toggles, sortable tables, card grids, and forms — without any page reload. + +## How to Generate + +Wrap a JSON object in a fenced code block with the language tag `a2ui`: + +```` +```a2ui +{ "type": "...", ... } +``` +```` + +The JSON must be a single object with a `type` field. The frontend selects the +renderer based on `type` and passes the remaining fields as props. + +--- + +## Block Types + +### filters + +Renders a row of toggle chips. Use for quick category or attribute filtering. + +```a2ui +{ + "type": "filters", + "options": [ + { "label": "Toyota", "active": true }, + { "label": "Honda", "active": false }, + { "label": "Hyundai", "active": false }, + { "label": "Under $5K", "active": false } + ] +} +``` + +| Field | Type | Description | +|-------|------|-------------| +| `options` | array | List of filter chips | +| `options[].label` | string | Display text for the chip | +| `options[].active` | boolean | Whether the chip is pre-selected | + +--- + +### table + +Renders a sortable data table. Use for structured comparisons across multiple items. + +```a2ui +{ + "type": "table", + "headers": ["Make/Model", "Year", "Price", "MPG", "Mileage"], + "rows": [ + ["Toyota Prius", "2019", "$8,500", "52 mpg", "61,000 mi"], + ["Honda Insight", "2018", "$9,200", "49 mpg", "55,000 mi"], + ["Hyundai Ioniq", "2019", "$8,900", "55 mpg", "48,000 mi"], + ["Toyota Camry Hybrid", "2017", "$7,800", "46 mpg", "72,000 mi"], + ["Ford Fusion Hybrid", "2018", "$7,200", "42 mpg", "68,000 mi"] + ] +} +``` + +| Field | Type | Description | +|-------|------|-------------| +| `headers` | string[] | Column header labels | +| `rows` | string[][] | Data rows; each row must match header count | + +--- + +### cards + +Renders a card grid. Use for item listings where each result deserves its own tile. + +```a2ui +{ + "type": "cards", + "items": [ + { + "title": "2019 Toyota Prius LE", + "description": "61,000 miles · Hybrid · Clean title · Seattle, WA", + "price": "$8,500", + "url": "https://www.cargurus.com/Cars/listings/prius-example" + } + ] +} +``` + +| Field | Type | Description | +|-------|------|-------------| +| `items` | array | List of card objects | +| `items[].title` | string | Card heading | +| `items[].description` | string | Supporting detail text | +| `items[].price` | string | Price label (formatted string, e.g. `"$8,500"`) | +| `items[].url` | string | Link destination for the card action | + +--- + +### form + +Renders a data-collection form. Use when you need user input to refine a search or +configure a follow-up action. + +```a2ui +{ + "type": "form", + "fields": [ + { "name": "max_price", "type": "number", "label": "Max Price ($)", "value": 10000 }, + { "name": "radius", "type": "number", "label": "Search Radius (miles)", "value": 25 }, + { + "name": "fuel_type", + "type": "select", + "label": "Fuel Type", + "options": ["hybrid", "electric", "any"] + } + ], + "submitLabel": "Search" +} +``` + +| Field | Type | Description | +|-------|------|-------------| +| `fields` | array | Ordered list of form fields | +| `fields[].name` | string | Field identifier (sent on submit) | +| `fields[].type` | string | `"number"`, `"text"`, `"select"` | +| `fields[].label` | string | Human-readable label | +| `fields[].value` | any | Default value (for `number`/`text` fields) | +| `fields[].options` | string[] | Choice list (for `select` fields) | +| `submitLabel` | string | Text on the submit button | + +--- + +## Rules + +- **Always use the `a2ui` language tag.** Any other tag (e.g. `json`) will render as + plain code; the frontend will not parse it. +- **JSON must be a single object with a `type` field.** Arrays and multi-root + documents are not supported. +- **Generate A2UI when the user benefits from interactive controls.** Filter chips, + sortable tables, card grids, and forms add real value when the user is comparing + options or needs to refine results. +- **Don't overuse.** Plain markdown lists and tables are fine for simple answers. Use + A2UI only when interactivity meaningfully improves the experience. diff --git a/bundle/context/artifact-conventions.md b/bundle/context/artifact-conventions.md new file mode 100644 index 0000000..bd623b4 --- /dev/null +++ b/bundle/context/artifact-conventions.md @@ -0,0 +1,84 @@ +# Artifact Conventions + +Artifacts are persistent markdown documents written to the filesystem. They appear +in the right panel and survive across sessions, making them ideal for research worth +saving: comparison guides, curated lists, reports, and actionable recommendations. + +## Writing Pattern + +Use the `write_file` tool to create artifacts: + +```python +write_file("artifacts//.md", content) +``` + +The `session_id` is available in your context. Use it to namespace files per session +so multiple sessions don't overwrite each other's work. + +## File Naming + +- **Format**: kebab-case +- **Extension**: `.md` +- **Style**: Descriptive, specific — say what the file contains + +``` +artifacts//hybrid-cars-under-10k-woodinville.md +artifacts//project-management-tools-comparison.md +artifacts//seattle-tokyo-flights-may-2025.md +``` + +## Content Format + +Each artifact should follow this structure: + +1. **H1 title** — Clear, specific heading +2. **Date and scope summary** — When the research was done and what it covers +3. **Markdown tables** for side-by-side comparisons (price, features, ratings) +4. **Bullet points** for recommendations, pros/cons, and key takeaways +5. **Source URLs** — Link to each page or listing referenced +6. **Closing section** — End with "Next Steps" or "Summary" + +```markdown +# Hybrid Cars Under $10K — Woodinville WA (May 2025) + +**Researched:** May 2025 · **Scope:** CarGurus, Autotrader, Craigslist within 30 mi + +## Comparison + +| Make/Model | Year | Price | MPG | Mileage | +|---|---|---|---|---| +| Toyota Prius LE | 2019 | $8,500 | 52 | 61,000 mi | +| Honda Insight EX | 2018 | $9,200 | 49 | 55,000 mi | + +## Recommendations + +- **Best value**: 2019 Prius LE — lowest price/mile ratio, clean title +- **Best mileage**: Hyundai Ioniq at 55 mpg city/highway combined + +## Sources + +- https://www.cargurus.com/Cars/new/nl-Hybrid-d2335 +- https://www.autotrader.com/cars-for-sale/hybrid + +## Next Steps + +- Schedule test drives for the Prius and Ioniq listings +- Verify CarFax reports before committing +``` + +## When to Create an Artifact + +Create an artifact when the output is: + +- A **guide, report, or summary** the user will want to reference later +- A **comparison table** across multiple options or products +- **Actionable recommendations** with supporting evidence +- Something **printable or shareable** with others + +## When NOT to Create an Artifact + +Skip artifact creation for: + +- **Simple answers** — a one-sentence reply doesn't need a file +- **Intermediate research steps** — notes you take while browsing, not the final output +- **Status updates** — progress messages mid-task diff --git a/bundle/context/researcher-instructions.md b/bundle/context/researcher-instructions.md new file mode 100644 index 0000000..8a159be --- /dev/null +++ b/bundle/context/researcher-instructions.md @@ -0,0 +1,24 @@ +# Research Workbench — Session Context + +This session runs inside the **Research Workbench**. You have access to the +following capabilities: + +## Available Capabilities + +**Browser** — Live Chromium browser controlled via `playwright-cli -s=research`. +The browser is persistent across commands and visible to the user via VNC (noVNC +panel). The user can take over at any time. + +**Artifacts** — Persistent markdown documents written to `artifacts//`. +They appear in the right panel and survive across sessions. + +**A2UI** — Inline interactive controls (filter chips, tables, card grids, forms) +rendered directly in chat via `a2ui` code blocks. + +**Inline Visuals** — Rich visual output (charts, maps, comparison tables with color +coding) rendered directly in chat via `visual` code blocks. + +## Full Command Reference + +For complete command syntax, workflow guidance, and best practices, see the +researcher agent system prompt. diff --git a/tests/test_context_files.sh b/tests/test_context_files.sh new file mode 100755 index 0000000..56ff51e --- /dev/null +++ b/tests/test_context_files.sh @@ -0,0 +1,191 @@ +#!/bin/bash +# test_context_files.sh - Verify bundle/context/{a2ui-format,artifact-conventions,researcher-instructions}.md (task-4) +# RED: Run before files are created (should fail) +# GREEN: Run after files are created (should pass) + +REPO=/home/ken/workspace/research-workbench +PASS=0 +FAIL=0 + +check_file_exists() { + local path="$1" + if [ -f "$REPO/$path" ]; then + echo "PASS: '$path' exists" + ((PASS++)) + else + echo "FAIL: '$path' should exist but is missing" + ((FAIL++)) + fi +} + +check_contains() { + local file="$1" + local description="$2" + local pattern="$3" + if grep -qF -- "$pattern" "$REPO/$file" 2>/dev/null; then + echo "PASS: $file contains '$description'" + ((PASS++)) + else + echo "FAIL: $file is missing '$description'" + ((FAIL++)) + fi +} + +check_regex() { + local file="$1" + local description="$2" + local pattern="$3" + if grep -qE "$pattern" "$REPO/$file" 2>/dev/null; then + echo "PASS: $file matches '$description'" + ((PASS++)) + else + echo "FAIL: $file is missing '$description' (pattern: $pattern)" + ((FAIL++)) + fi +} + +# ───────────────────────────────────────────────────────────── +# a2ui-format.md +# ───────────────────────────────────────────────────────────── + +A2UI="bundle/context/a2ui-format.md" + +echo "=== Checking $A2UI exists ===" +check_file_exists "$A2UI" + +echo "" +echo "=== Checking a2ui-format.md: fenced code block with language 'a2ui' ===" +check_contains "$A2UI" "a2ui fenced code block" '```a2ui' + +echo "" +echo "=== Checking a2ui-format.md: block type 'filters' ===" +check_contains "$A2UI" "filters type" '"type": "filters"' +check_contains "$A2UI" "filters options array" '"options"' +check_contains "$A2UI" "Toyota option" '"Toyota"' +check_contains "$A2UI" "Honda option" '"Honda"' +check_contains "$A2UI" "Hyundai option" '"Hyundai"' +check_contains "$A2UI" "Under \$5K option" '"Under $5K"' +check_contains "$A2UI" "active field" '"active"' + +echo "" +echo "=== Checking a2ui-format.md: block type 'table' ===" +check_contains "$A2UI" "table type" '"type": "table"' +check_contains "$A2UI" "table headers" '"headers"' +check_contains "$A2UI" "table rows" '"rows"' + +echo "" +echo "=== Checking a2ui-format.md: block type 'cards' ===" +check_contains "$A2UI" "cards type" '"type": "cards"' +check_contains "$A2UI" "cards items" '"items"' +check_contains "$A2UI" "card title field" '"title"' +check_contains "$A2UI" "card description field" '"description"' +check_contains "$A2UI" "card price field" '"price"' +check_contains "$A2UI" "card url field" '"url"' +check_contains "$A2UI" "Prius listing" 'Prius' + +echo "" +echo "=== Checking a2ui-format.md: block type 'form' ===" +check_contains "$A2UI" "form type" '"type": "form"' +check_contains "$A2UI" "form fields" '"fields"' +check_contains "$A2UI" "submitLabel field" '"submitLabel"' +check_contains "$A2UI" "max_price field name" '"max_price"' +check_contains "$A2UI" "radius field name" '"radius"' +check_contains "$A2UI" "fuel_type field name" '"fuel_type"' +check_contains "$A2UI" "select field type for fuel_type" '"select"' +check_contains "$A2UI" "hybrid option" '"hybrid"' +check_contains "$A2UI" "electric option" '"electric"' +check_contains "$A2UI" "any option" '"any"' + +echo "" +echo "=== Checking a2ui-format.md: Rules section ===" +check_regex "$A2UI" "Rules section" "^## Rules|^# Rules|Rules" +check_contains "$A2UI" "always use a2ui language tag" 'a2ui' +check_regex "$A2UI" "JSON must be single object with type field" 'type.*field|\"type\"' +check_regex "$A2UI" "generate when user benefits" 'benefit|interactive' +check_regex "$A2UI" "don't overuse" "overuse|don.t overuse|sparingly" + +# ───────────────────────────────────────────────────────────── +# artifact-conventions.md +# ───────────────────────────────────────────────────────────── + +ARTIFACT="bundle/context/artifact-conventions.md" + +echo "" +echo "=== Checking $ARTIFACT exists ===" +check_file_exists "$ARTIFACT" + +echo "" +echo "=== Checking artifact-conventions.md: write_file tool usage ===" +check_contains "$ARTIFACT" "write_file tool call" 'write_file' +check_regex "$ARTIFACT" "artifacts/ path pattern" 'artifacts/.*session' + +echo "" +echo "=== Checking artifact-conventions.md: file naming conventions ===" +check_regex "$ARTIFACT" "kebab-case naming" 'kebab.case|kebab-case' +check_contains "$ARTIFACT" ".md extension" '.md' + +echo "" +echo "=== Checking artifact-conventions.md: content format ===" +check_regex "$ARTIFACT" "H1 title requirement" 'H1|# title|heading' +check_regex "$ARTIFACT" "markdown tables for comparisons" '[Mm]arkdown.*table|table.*comparison|comparison.*table' +check_regex "$ARTIFACT" "bullet points for recommendations" 'bullet|recommendation' +check_regex "$ARTIFACT" "source URLs" '[Ss]ource.*URL|URL.*source|source url' +check_regex "$ARTIFACT" "Next Steps or Summary ending" 'Next Steps|Summary' + +echo "" +echo "=== Checking artifact-conventions.md: when to create ===" +check_regex "$ARTIFACT" "guides/reports/summaries" 'guide|report|summar' +check_regex "$ARTIFACT" "comparison tables" 'comparison' +check_regex "$ARTIFACT" "actionable recommendations" 'actionable|recommendation' +check_regex "$ARTIFACT" "printable/shareable" 'printable|shareable' + +echo "" +echo "=== Checking artifact-conventions.md: when NOT to create ===" +check_regex "$ARTIFACT" "not for simple answers" '[Ss]imple answer|not.*simple|simple.*answer' +check_regex "$ARTIFACT" "not for intermediate research" 'intermediate|research step|status update' + +# ───────────────────────────────────────────────────────────── +# researcher-instructions.md +# ───────────────────────────────────────────────────────────── + +RESEARCHER="bundle/context/researcher-instructions.md" + +echo "" +echo "=== Checking $RESEARCHER exists ===" +check_file_exists "$RESEARCHER" + +echo "" +echo "=== Checking researcher-instructions.md: Research Workbench context ===" +check_regex "$RESEARCHER" "Research Workbench mention" '[Rr]esearch [Ww]orkbench' + +echo "" +echo "=== Checking researcher-instructions.md: Browser capability ===" +check_regex "$RESEARCHER" "Browser with Playwright/Chromium" '[Bb]rowser|playwright|Chromium' +check_contains "$RESEARCHER" "playwright-cli session flag" '-s=research' +check_regex "$RESEARCHER" "VNC mention" 'VNC|vnc|noVNC' + +echo "" +echo "=== Checking researcher-instructions.md: Artifacts capability ===" +check_regex "$RESEARCHER" "Artifacts capability" '[Aa]rtifact' +check_regex "$RESEARCHER" "artifacts/ path" 'artifacts/.*session|session.*artifacts' + +echo "" +echo "=== Checking researcher-instructions.md: A2UI capability ===" +check_regex "$RESEARCHER" "A2UI capability" 'A2UI|a2ui' +check_regex "$RESEARCHER" "inline a2ui code blocks" 'inline|code block' + +echo "" +echo "=== Checking researcher-instructions.md: Inline Visuals capability ===" +check_regex "$RESEARCHER" "Inline Visuals" '[Ii]nline [Vv]isual|visual.*block' +check_regex "$RESEARCHER" "visual code blocks" 'visual.*block|code.*block.*visual' + +echo "" +echo "=== Checking researcher-instructions.md: pointer to researcher agent ===" +check_regex "$RESEARCHER" "pointer to researcher agent" 'researcher.*agent|researcher.*command|full.*command|command.*reference' + +echo "" +echo "==============================" +echo "Results: $PASS passed, $FAIL failed" +echo "==============================" + +[ $FAIL -eq 0 ] && exit 0 || exit 1