From c1a365ad31849aa6bd86fb7083d555398126942a Mon Sep 17 00:00:00 2001 From: Max Abouchar Date: Thu, 9 Jul 2026 16:04:09 -0700 Subject: [PATCH 01/33] feat(prompts): move backend + HTML-dashboard contracts into built-in skills The 15.3k-char BACKEND_GENERATION_PROMPT and 10.7k-char VISUALIZATIONS_HTML_OUTPUT_FORMAT_PROMPT were re-sent in the system prompt on every LLM call. They now ship as read-only built-in skills (build-fullstack-backend, build-html-dashboard) served by SkillStore and are recalled on demand; the always-sent prompt carries short mandatory recall hints instead. create_artifact/launch_backend descriptions reinforce the recall. System prompt drops from ~43.5k to ~22.6k chars. Co-Authored-By: Claude Fable 5 --- anton/core/llm/prompt_builder.py | 4 +- anton/core/llm/prompts.py | 464 +----------------- .../build-fullstack-backend/SKILL.md | 178 +++++++ .../build-html-dashboard/SKILL.md | 92 ++++ anton/core/memory/skills.py | 115 +++-- anton/core/tools/tool_defs.py | 11 + tests/conftest.py | 14 + tests/test_builtin_skills.py | 122 +++++ uv.lock | 2 +- 9 files changed, 526 insertions(+), 476 deletions(-) create mode 100644 anton/core/memory/builtin_skills/build-fullstack-backend/SKILL.md create mode 100644 anton/core/memory/builtin_skills/build-html-dashboard/SKILL.md create mode 100644 tests/test_builtin_skills.py diff --git a/anton/core/llm/prompt_builder.py b/anton/core/llm/prompt_builder.py index 3c3d429d..fca3a57c 100644 --- a/anton/core/llm/prompt_builder.py +++ b/anton/core/llm/prompt_builder.py @@ -163,7 +163,9 @@ def build( conversation_started=conversation_started, ) - prompt += "\n\n" + BACKEND_GENERATION_PROMPT.format(output_dir=output_dir) + # Short pointer only — the full backend/fullstack contract lives in the + # built-in `build-fullstack-backend` skill (recalled on demand). + prompt += "\n\n" + BACKEND_GENERATION_PROMPT tool_prompts = self._build_tool_prompts_section(tool_defs) if tool_prompts: diff --git a/anton/core/llm/prompts.py b/anton/core/llm/prompts.py index 3271504a..2b924cb6 100644 --- a/anton/core/llm/prompts.py +++ b/anton/core/llm/prompts.py @@ -336,164 +336,16 @@ VISUALIZATIONS_HTML_OUTPUT_FORMAT_PROMPT = """\ -LIST THE INSIGHTS (terse — one line each, not an essay): -Before coding, list the insights you want to present/convey/highlight as `1 - : ..` -Example: `1 - Line chart of weekly signups: shows growth inflection after the March launch, flags whether momentum is sustained.` -This is a checklist, not a brief — no narrative prose, no design discussion. - -BUILD THE DASHBOARD — use multiple scratchpad cells, but produce ONE single self-contained HTML file: - -Before the first write, call `create_artifact(type="html-app", \ -name=..., description=..., primary="dashboard.html")` and use the returned \ -`` for every file you write (the HTML, any sibling data files, \ -images, etc.). All paths below referring to "the output directory" mean \ -``. The final dashboard MUST be a single .html file with ALL \ -data, CSS, and JS inlined. Do NOT reference external local files (like \ -data.js) — browsers block local file:// cross-references for security \ -reasons and the dashboard will silently fail to load data. - - REROUND DISCIPLINE (critical — most "round-cap exhaustion" failures we've \ -seen on real dashboards come from drifting off one or more of these): - 1. ONE scratchpad, ONE name. Pick a name on the first cell (e.g. `dash`) \ -and reuse it for the entire build. Switching names (`build_pres` → `write_html` \ -→ `pres1` …) creates *separate isolated environments* — variables in one don't \ -exist in another — and burns rounds on recovery. - 2. WRITE TO DISK INCREMENTALLY. Open the output `.html` once in 'w' mode, \ -then `open(path, 'a')` to append head → body skeleton → each chart section → \ -nav/JS → closing tags. Each cell appends a small chunk you can sanity-check. \ -Do NOT build a single 20KB+ HTML string in memory and write it at the end. - 3. CAP STRING SIZE PER CELL at ~5KB. Large-string scratchpad calls are the \ -single biggest cause of silent failures (the tool occasionally drops the \ -`code` payload on oversized inputs and the cell comes back with an empty-code \ -error, which still counts against the round cap). If a section is too big, split it. - 4. NEVER re-emit the full HTML mid-build. Append deltas, don't re-print \ -the world. Assembly is a one-line concat at the end, not a re-render of \ -everything you've written so far. - 5. KEEP READS SMALL. To verify what landed, `os.path.getsize(path)` or \ -`open(path).read(2000)` — never `open(path).read()` on a multi-KB HTML. - - SECURITY (critical): Dashboards may be published to the web. NEVER embed API keys, tokens, \ -passwords, connection strings, or any credentials in the HTML, JS, or inline data. Fetch data \ -in scratchpad cells using credentials from environment variables, then serialize only the \ -resulting data into the dashboard. If the user explicitly asks to embed a credential \ -(e.g. for a live-updating dashboard), warn them that publishing will expose it and get \ -confirmation before proceeding. - - Build the parts in separate cells, then assemble at the end: - - CELL 1 — Serialize data to a JS string variable (programmatic, no HTML): - Serialize all computed data (dataframes, metrics, KPIs) into a Python string. Build a \ -Python dict with keys like "kpis", "tables", "charts" — each containing the relevant data. \ -Convert DataFrames with df.to_dict(orient='records'). Use json.dumps(data, default=str) to \ -handle dates, Decimal, numpy types. Store as a Python variable: \ -`data_js = 'const D = ' + json_string + ';'` — do NOT write to a separate file. - - CELL 2 — Build CSS + HTML structure as a Python string variable: - Write the HTML head (styles, CDN script tags) and body structure (header, KPIs, chart divs, \ -tabs, tables) as a Python string variable `html_body`. This cell builds the template. - - CELL 3+ — Build JS chart rendering logic as Python string variables: - Write the JavaScript that initializes charts, populates tables, handles tabs, etc. \ -Split across multiple cells if needed to avoid token limits. Store as `js_charts` etc. - - FINAL CELL — Assemble and write the HTML file: - Combine: `html = html_body.replace('', f'')` \ -or similar. - - SELF-CONTAINED OUTPUT (critical): - Prefer inlining everything — CSS in `