feat(desktop): cut model round-trips — bind-template fast-path on the slim surface, Call-2 auto-apply, result-carried readback#554
Merged
Conversation
…ce (discoverability)
Live root cause (2026-07-19, two banked receipts): on calc-heavy asks the agent
searched 'underlying-metadata' -> 0 hits -> concluded 'not available in this
build' -> fell back to hand-writing workbook XML and drowned. The verbs exist
(executor maps them to GET/POST /v0/workbook/document) but had zero entries in
the JSON that search-commands, validateKnownCommand, and paramContractGuard all
read — undiscoverable AND uninvocable through the tool.
Adds honest entries for tabui:save-underlying-metadata / load-underlying-metadata
(load declares required in-param 'text'), allowlists both, and names the verbs in
the zero-hit recommendation. Empirically gated: her exact failing query now
returns the pair at ranks 1-2; 'calculated field' puts save at rank 1; guard
rejects load{} and load{filepath} with actionable steers.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…document or nothing) Adversarial-review P1 mitigation for surfacing the round-trip verbs: a pre-dispatch guard on execute-tableau-command rejects fragment loads (non- <workbook> root, no datasources/worksheets), loads that would DROP live worksheets (steer: delete-worksheet), and gross-shrink documents (<50% of live) — with fail-open on live-read infrastructure failure. Guard built by GPT-5.5 minion, adjudicated + full suite re-run firsthand (4453 green). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ot prove application Live-proven 2026-07-19 (Desktop main.26.0715, three probes): document loads that ADD columns or rewrite worksheet content apply; loads that REMOVE a datasource column are silently ignored — completed envelope, no effect. Teach: after every load, re-read with save-underlying-metadata and confirm the change landed. Applied to the load entry's description and the notional-spec-calc-authoring knowledge module. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…fore the workbook root
FATAL caught by live audition probe: Desktop documents open with a build
comment between the XML prolog and <workbook> ('<!-- build main.26.0715 -->');
the root regex forgot comments, so the guard rejected EVERY legitimate
whole-document load as a fragment — blocking the exact path it exists to
protect. Live receipt: verse 2's 'fragment' rejections were false positives.
Regression fixture uses the verbatim real head. Lesson (now in the stack
skill): probe the guard's ACCEPT path against a real artifact, not only its
reject paths against synthetic fixtures.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t in the agent's hands Live evidence (two drown receipts, 2026-07-19): agents on the manual round-trip fragment the 50KB document re-emission. author-calc takes caption+formula primitives and does the round-trip internally: read → unique-name splice into the target datasource (single non-Parameters default, candidates listed on ambiguity) → guard-validate → load → READBACK-VERIFY (completed does not prove applied). Caption collisions rejected pre-write. Built by GPT-5.5 minion, adjudicated firsthand. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…be trims, ratchets tightened 46,691 → 45,753: prose-only trims across the six fattest describes; per-tool ratchets lowered to the new sizes. Load-bearing semantics restored after adjudication where the trim cut meaning (Call-2 proposal provenance on bind-template, plan-dashboard-creation pointer and applies-to-LIVE warning on build-and-apply-worksheet). Full suite 4,460 green, tsc clean, combined-lean under cap with headroom. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s human-in-loop only open-calc-editor-with-custom-calc returns completed WITHOUT committing (opens the editor pre-filled; open editor blocks the UI thread — live-proven twice 2026-07-19). Knowledge now routes: author-calc tool first, manual round-trip as fallback, calc-editor commands never in unattended runs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s real datasources Live verse receipt (2026-07-19): worksheet views clone <datasource> elements, so a Superstore workbook offered 'Sample - Superstore, Sample - Superstore' as candidates, and splicing into the clone was silently discarded by Tableau (the readback-verify caught it). Selection now scans only the first <datasources> block. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… columns are a position trap Live untaught-verse receipt (4 failed author-calc calls, readback-verify caught every one): <connection>/<relation>/<columns> holds schema <column ordinal=.../> nodes; insert-after-last-column landed the calc inside the relation block and Tableau silently discarded it. End-of-datasource is the position every successful live splice used tonight. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sheet law in spec knowledge Every author-calc bug tonight was invisible to synthetic fixtures and cost a live verse to find (datasource clones; relation-column position trap). The replay test runs the tool against a real saved Desktop document (username scrubbed) offline. Knowledge: generate-viz renders on the ACTIVE sheet — goto-sheet immediately before, or it clobbers a neighbor (live receipt: the agent detected the clobber via readback, restored the sheet, and rerouted — correct behavior, avoidable trap). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…o internal names Live finale receipt (2026-07-19): all six author-calc calls succeeded, but 5 of 6 layered formulas referenced sibling calcs by CAPTION ([Member Profit], [Profit Tier]) — Tableau formulas resolve internal names, and authored calcs get [Calculation_N] names the agent cannot know, so every dependent calc flagged invalid and the chart leg died. The tool now rewrites bracketed caption tokens to internal names at splice time (base fields where caption equals name pass through untouched). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nt round-trip Wraps the proven set shape (Segno's golden groupfilter + CODA's live param-link proof) as an author-* verb: primitives in (caption/dimension/orderBy/count/end), golden <group><groupfilter> XML server-side, spliced at datasource-end, readback- verified. count accepts a literal N or a [Parameters].[X] token — the melody plays over the key signature. Offline replay (5/5) incl. real 49KB Superstore document. Live-mapped this session (receipts): the document round-trip is the whole dialect — calcs/sets MERGE; parameters are OPEN-only (frozen to merge, all headless create/edit commands hit dlg.DoModal). Set authoring needs no parameter mutation: it references the param by token and Tableau resolves it at runtime. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…round-trip Live-proven this session (CODA): a workbook-level <actions> block MERGES via the round-trip — spliced <edit-parameter-action> targeting [Parameters].[Parameter 1] survived readback, link intact. This refuted the map-agent's OPEN-only assumption (the same trap sets fell into) — a live probe beat the reasoning. Wraps it as a verb: primitives in (caption/sourceWorksheet/sourceField/ targetParameter/activation), golden <edit-parameter-action> XML server-side, creates the workbook <actions> block if absent (between </datasources> and <worksheets>) or appends with a fresh [ActionN] name, readback-verified. Offline replay 4/4. The interactivity layer over the key signature. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…der the 46k cliff format-labels: turns mark labels ON/OFF for a worksheet via the document round-trip (PROVEN live 2026-07-19 — <format attr='mark-labels-show'> in a pane MERGES and survives readback). Idempotent: rewrites an existing rule or inserts one; readback- verified. Offline replay 4/4. Funding: author-set/author-action/format-labels pushed the combined tool surface past the 46k tools/list auto-deferral cliff (and the combined-lean profile's). Per the ratchet doctrine (never raise the cap — trim to fit), emptied redundant single- token .describe() stubs across the fattest tools (plan-dashboard-creation, build-and- apply-dashboard, validate-proposal, dashboard-health-check, inject-template, dashboard -auto-apply, add-field, apply-worksheet, resolve-field, build-and-apply-worksheet). Field names + enums already carry the meaning; empty describe drops the JSON key entirely (verified). Lowered the grandfather caps to lock the wins in; removed the tools that dropped under the 1200B per-tool budget. Full suite 4476 green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… OPEN, reopen explicit)
Parameters are the ONE shape frozen to live merge (PROVEN live 2026-07-19, CODA:
create/add/value-edit all silently refused; every headless create/edit command is a
blocking dlg.DoModal). A parameter is born ONLY at OPEN. So this verb does the
document surgery — splices the <column param-domain-type> into the Parameters ds
(creating that ds if absent; an empty one is dropped on load, one with a real column
survives) — writes a reopen-ready stage, and returns { stagePath, reopenRequired:
true }. The reopen + re-pin is the serving layer's job, surfaced explicitly, NEVER
hidden inside the tool (ghost gen-18: it's a session-lifecycle event). The reopen
preserving merged calcs/sets/actions/formatting is live-proven. Parameters = the key
signature; everything else is the melody merged over it. Offline replay 4/4.
Funds the 5th verb under both 46k cliffs by emptying redundant describe stubs
(get-worksheet-xml, apply-dashboard, remove-field). Full suite 4480 green, tsc clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…s + key-signature/melody law New tactics/data/notional-spec-dynamic-authoring.md is the routing map that steers the agent to author-calc/author-set/author-parameter/author-action/format-labels instead of hand-XML, with the one governing law (params=key signature born at OPEN; calcs/sets/ actions/formatting=melody merged live) and the full WW44-shape recipe in build order. Cross-linked bidirectionally with the calc-authoring doc. Every claim live-proven this session. This is the teach-Sonnet layer — the verbs are the capability, this is discovery. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… the reference marks safe but block The command reference marks 15 *DialogCommand-sourced commands as opens_blocking_dialog= false + agent_can_invoke=true; invoking them unattended pops a modal that wedges the session (live-proven: edit-existing-parameter hung 12s while health stayed OK). Names the 15, gives the trust rule (source name > derived flag), routes to author-* verbs instead, and records the generator-fix heuristic (source =~ /Dialog/ => force blocking). Refines the ghost's 'reference omission' into the real bug: misclassification, not omission. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…no-XML surface Answers 'make it shorter': instead of trimming describe stubs to squeeze verbs under the 46k tools/list cliff, define a lean profile that IS the whole authoring language. TOOL_PROFILE=dynamic-authoring = spec-loop's 5 (execute-tableau-command for the NotionalSpec chart/dashboard spine + discovery/readback) + the 5 author-* verbs (calc/set/parameter/action/format-labels). Ten semantically-named tools cover the full WW44 dynamic dialect; zero agent-visible XML/cache/validation/template tools. Measured wire surface: 9,644 bytes vs the 46,000 cliff — 79% headroom (vs the full 45-tool surface that fights the cliff). The cliff stops being a fight structurally. Full set stays reachable via TOOL_PROFILE=full. Tests: registers exactly the 10, banishes XML/template tools, asserts <30k. Full suite 4482 green, tsc clean. Ghost gen-19 Move 1 (the ruthless surface already half-existed as spec-loop; this bolts the write half on and names it). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…he 12-tool singable surface CODA's joy-cut overshot: a singer who cannot ask for help or discover the execute-tableau-command dialect is not lean, she is mute. Re-measured at 11,500 wire bytes — 75% headroom under the 46k cliff.
…y signature no longer stops the song CODA's one seam, closed tool-side: getExecutor discovers manifests dynamically and getDesktopConfig() re-reads env per call, so the tool itself relaunches the seeded stage (open -n, live-probed: manifest 2s, API-ready 3s), readback-verifies the parameter in the reopened doc, re-pins TABLEAU_DESKTOP_SESSION_ID in-process (only when a pin already existed), then SIGTERMs the old instance (probed: dies clean, no recovery sibling). Any failure degrades to the honest reopenRequired stage-on-disk result. stagePath is now optional — untaught agents never invent filesystem paths; the default stages beside the live workbook's own file.
…ses the document among instances Dress rehearsal caught two live defects the offline suite could not: (1) with multiple instances of the bundle running, 'open -n -a' starts a fresh instance but LaunchServices routes the document Apple Event unpredictably — the reopened instance came up EMPTY; spawning <bundle>/Contents/MacOS/<binary> <stage> detached carries the document deterministically (and puts it in argv). (2) The default stagePath cannot derive from the old process's argv for the same reason — it now stages under ~/Documents/My Tableau Repository/Workbooks (real user path; Desktop has crashed saving from sandboxed tmp dirs). E2E-proven live: reopened 78197→78328, param verified in the new instance's readback.
…tches up to the shipped verb
…erbs are named where the singer reads first Finale autopsy: untaught Sonnet landed both parameters through the in-call reopen but burned 55 of 65 calls spelunking raw commands for the set and dashboard, and never reached author-set — the tool list carried the verbs but the initialize instructions never routed a dynamic ask to them. Segno's discoverability law, one level up: ink the sign in the score she reads at note one.
…n 46k cliff The prior commit pushed combined-lean to 46,116 — the full suite caught it after an ungated commit chain (the lesson is the gate order, re-learned).
… modal dies here revert-workbook-ui fired Error 47BF7751 on Matt's screen three times in one verse: the reference says safe, CODA's probe said modal, the knowledge doc knew, but nothing ENFORCED it. Now the guard refuses all 16 live-proven dialog commands outright, each with a FIX redirecting to the sanctioned alternative (author-parameter, author-action, NotionalSpec keys, or author-forward). Every annoyance dies exactly once.
…arameters].[N] refs — the empty-sheet killer Verse-3 live receipt: the singer's filter calc named two parameters by caption; resolution only covered the target datasource's columns, so the refs never bound and the sheet rendered empty on Matt's screen. Parameter captions now resolve from the Parameters datasource to the qualified cross-datasource form, set after the field map so a caption collision resolves to the parameter — which is what a dynamic-ask formula means.
…ls the silent-empty-sheet class Live eval receipt (2026-07-20 TUTTI Blake-set wave): a spec with aggregation:'none' (not v0.2) and a rangeFilter using max: (v0.2 is start/end) passed the guard, codegen generated NOTHING, and the /v0 success envelope shipped an empty sheet as a PASS. The guard now validates per-field keys/enums (data/type/role/aggregation/encoding, 'shelf' redirect, detail/tooltip v0.3 gate), sort, rangeFilters, relativeDateFilters (plural periods, next/previous), and categoricalFilters — every rejection carries a FIX line that teaches the correct vocabulary in-context. Accept fixtures are the six live-proven TUTTI golden specs; reject fixtures are the live failing payloads. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…or the semantic-loop pair The dynamic-authoring profile has no knowledge tools, so search-commands is the singer's only reachable teacher — expertise:// pointers are dead ends there. The generate-viz entry now carries the v0.2 cheat-sheet (chart enum, field props, filter/sort shapes), the author-calc-then- reference-by-caption idiom (aggregation 'default', never 'none'), the string-date DATE() idiom, and the write-blind law (probed raw 2026-07-20: /v0 invokeCommand returns state:SUCCEEDED with no result, no operations route — verify by readback, always). The read-spec twin's entry now states its output is NOT returned by the /v0 API (product gap) and routes readers to document readback. Both entries get real parameter metadata (was []). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… test helper Green firsthand: lint clean, build ok, 4531 tests pass. CI was red on lint only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… omitted it CI Tests step failed (both node matrices): the timeout + 2 success-path reopen tests fell through to defaultSpawnDetached, which spawned the real macOS Tableau binary — ENOENT on the Linux runner fired as an async unhandled rejection after the test passed, exiting the process 1. Passed locally only because the app path exists on macOS. All other reopen tests already mock it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…E now selects the 12-tool dynamic-authoring surface Flips the desktop default from the full 40-tool surface (raw XML get/apply + templates + cache) to the lean native-authoring surface: semantic loop via execute-tableau-command + the five author-* verbs + discovery/readback, zero agent-visible XML. 'full' is the explicit opt-in escape hatch; demo/spec-loop/ combined-lean unchanged. Consumers that need the XML surface set TOOL_PROFILE=full. TAS + the eval kit already set the profile explicitly, so the singer is unaffected; this makes native the DEFAULT for every unset consumer (ghost gen-19 standing move). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…lt surface The TOOL_PROFILE default flip (unset -> dynamic-authoring) changes what both the standalone desktop server and the combined bundle's desktop half register when no profile is set. Assert the lean 12-tool native surface (raw XML tools opt-in via TOOL_PROFILE=full). Desktop list-tools verified locally; combined needs CI web creds. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…was failing open) Review P1: WORKSHEET_NAME_RE only matched name='...' (single quotes), so a live worksheet with a double-quoted name was invisible to the dropped-sheet check and could be silently dropped on reload — the exact data-loss this guard exists to prevent, failing OPEN. Match both quote styles (the pattern formatLabels.ts already uses for the same attribute). Regression test with a double-quoted live sheet. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ace, coherent instructions, census, live-session fields Live receipts (2026-07-20 slim battery) root-caused the slow eval: ~9 model turns/song at ~5s each while every tool result returns in <0.1s. Three of those turns were manufactured by the surface itself: - instructions advertised bind-template and XML fallback tools the dynamic-authoring profile does not register (m1 receipt: two search-commands calls literally hunting for 'bind template'), so bind-template joins the slim profile (13 tools) as the deterministic ~0.3s fast-path for plain chart shapes; route prose no longer names unregistered tools; - a command census prose entry lists the load-bearing tabdoc/tabui commands so search-commands is reserved for the uncommon path (m1: 13 of 23 turns were discovery thrash); - list-available-fields required a cached workbookFile and its error pointed at cut get-*-xml tools (e1 turn 1 burned on 'File not found'); workbookFile is now optional and omitting it reads the live session workbook. Instructions stay under the 46k tools/list deferral cliff (combined-lean budget test green). Full suite 4535 green, tsc clean, lint clean. Known tradeoff: DESKTOP_INSTRUCTIONS is profile-independent, so full-profile consumers lose the dashboard-auto-apply routing prose; instructions now match the default surface. Profile-aware instructions are a follow-up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…te-viz-from-notional-spec Live re-time receipt (e1, 60s/13 calls): a Call-2 bound result stranded the slim surface (auto-apply was Call-1-only and the manual apply tools are cut), costing 4 search-commands thrash turns; then 5 more turns re-verified a write-blind spec apply. - bind-template auto_apply now applies any bound fast-path-eligible result. A Call-2 proposal is binder-validated against the live workbook and applies under the same events-anchor user-change guard; the freehand fallback the model otherwise takes has fewer guards. - tabdoc:generate-viz-from-notional-spec success results now append a compact server-side readback (sheet, Rows/Cols shelves, mark, sort) resolved from the active window; fail-open, one metadata round-trip. The command reference's verify guidance now points at the result readback. - bind-template byte cap ratcheted down to 1629 (describe trim funded the gate-comment rewrite). Full suite 4537 green, tsc clean, lint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…emplate + author-* carry the song, refine-worksheet joins the slim surface NotionalSpec is retired (Matt, 2026-07-20). Three live pointers still steered the model at the dead command (jam gen-21): the plain-chart/dashboard/dynamic/ edit-in-place route prose, the command census, and the success-path hint strings author-calc and author-set return on every call. All re-pointed at the post-spec dialect: author the semantic object first, then bind-template the chart naming the authored caption; refine-worksheet (primitives-only top-N/sort) joins the 14-tool dynamic-authoring profile to carry edit-in-place. The spec command itself stays registered and functional — the door closes when the replacement chain is live-proven, not before. Full suite 4537 green, tsc clean, lint clean. Instructions +13 bytes, cliff-safe (refine-worksheet was already in the full surface the combined budget measures). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… call
jam gen-22 Move 1 (receipt: the author-calc→bind chain costs ~9-13s of model
turns per calc song). bind-template accepts calcs[{caption,formula,datatype,
role}]: formulas validate BEFORE any document mutation (atomic error, zero
apply on a bad calc), authoring reuses author-calc's splice/load/readback via
the new shared authorCalcCore, and the bind + auto-apply see the post-calc
workbook. authored_calcs surfaces in every outcome. Route prose updated.
Per-tool cap raised 1591→1725 for the capability (slim surface is ~11.6k of
46k; combined-lean cliff stays the hard gate, green at ~45.9k).
Full suite 4540 green, tsc clean, lint clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… as absent jam gen-21/22 Move 2 (receipt: m1 waterfall — tabdoc:sort 500'd on seven wire shapes, ~150s of blind model turns, before a manual computed-sort splice landed direction/using correctly). sort_by_field produces that same computed-sort shape through refine-worksheet's readback-verified apply path, with caption resolution matching the existing ops. resolveSession now treats empty/whitespace session strings as absent (was a wasted error turn: session:'' against a pinned Desktop). Full suite 4548 green, tsc clean, lint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ach the true sort contracts Live receipts (2026-07-20): tabdoc:sort popped blocking modals on the user's screen (47BF7751 'missing: global-field-name') after seven opaque 500s. The monolith's own sort-cmd.data settles it: SortCommand drives the sort UI dialog (updateSortDialog notification) — it was never agent-safe — and SortNested is the programmatic sort that existed all along. - execute-tableau-command now refuses tabdoc:sort pre-dispatch with a FIX line redirecting to refine-worksheet sort_by_field or tabdoc:sort-nested. - Reference corrected: Sort marked dialog-risky with the true wire contract (global-field-name, Type-required-unless-clear, computed-sort MeasureName); sort-nested pinned with its required params. - paramContractGuard enforces sort-nested's required params pre-dispatch. Full suite 4556 green, tsc clean, lint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Base automatically changed from
claude/segno-roundtrip-discoverability
to
feature/desktop
July 20, 2026 20:12
…osal (#555) * feat(desktop): shared slot vocabulary — sort and top_n on the bind proposal jam gen-23 Move 1 (receipts: m1's order-by strand; the ranking family cannot say 'top 10'). The proposal schema gains two optional concepts — sort {by, direction} and top_n — advertised in PROPOSAL_OUTPUT_SCHEMA so Call-2 models learn them from the propose payload itself, validated against the workbook schema BEFORE apply, spliced via the computed-sort/top-N helpers extracted from refine-worksheet (built earlier today — reused, not duplicated), atomic failure on splice errors. One bind call now carries ordering and limits; no refinement tail. Growth law honored: receipt-backed concepts only, binder- validated, riding the one-call path. Full suite 4567 green, tsc clean, lint clean. combined-lean 45,992/46,000 — 8 bytes headroom; next growth must trim first. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(desktop): complete the slot-vocabulary commit — validate seam + describe trims These two files were part of the sort/top_n packet but missed the explicit staging list; the working-tree suite masked the gap (adjudication lesson: git-status-clean check BEFORE declaring a packet landed). Without them the pushed tree fails its own validate-proposal byte cap. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(desktop): literal names at every tool boundary — the ampersand bug dies Receipt (m1, 2026-07-20): a sheet named 'P&L Waterfall…' surfaced entity- escaped through list-worksheets, made refine-worksheet miss by literal name, and silently confirm-miss (refined:false) by escaped name — any &-titled sheet was unreachable. Now: names decode exactly once at output boundaries (list-worksheets/dashboards, bind-template sheet_name/guidance), name inputs resolve normalized-to-normalized with escaped-input tolerance, apply gates compare normalized, and refine uses the canonical literal name from fetched XML for apply/readback/output. Coverage for & < " in literal and escaped paths. (Includes a sliver of the prior commit's file overlap — the two commits were disentangled from one worktree; suite green across both.) Full suite 4582 green, tsc clean, lint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
mattcfilbert
added a commit
that referenced
this pull request
Jul 21, 2026
…ll discovery hints CI tsc caught warnings? missing on BindTemplateToolResultBase (vitest transpiles without typecheck — the gate that matters is npx tsc). Live re-sing receipt (m1, judge 34 both pre/post-slot): the slot shipped but was never FOUND — a confident bind has no Call-2, so manifest descriptions are never read, and the routeTable teaching was trimmed for the 46k cliff. A capability without a discovery channel is a no-op. Result-carried teaching (the #554 pattern, zero tools/list bytes): waterfall bind results now hint (a) the anchor_category re-call naming the schema's category-like column when the slot is unbound, (b) the one-call sort override when no sort proposal was passed. 6 tests pin presence/absence per case. tsc 0, suite 4616 green (316 files), lint 0 — pipe-proof exits. Co-Authored-By: Claude <noreply@anthropic.com>
mattcfilbert
added a commit
that referenced
this pull request
Jul 21, 2026
…er template sort (#560) * feat(desktop): waterfall anchor_category slot + sort proposal wins over template sort Live eval receipts (m1 waterfall FAIL 34/100, 2026-07-20): (1) the singer's sort override 'hit a template limitation' — part-to-whole-waterfall hard-codes computed-sort DESC by SUM(Profit) and the sort field wasn't declared in datasource-dependencies, so the proposal was inert; the singer burned 3 sort-nested 500s + a doc round-trip forcing ASC-by-display_order. (2) subtotal/ total rows were running-totaled with the increments they summarize — final bar ~2x true Net Income. - sort proposal now replaces the built-in computed-sort (declares the sort column/column-instance when missing); every failure path is fail-open with a warnings[] channel on the bind result — template default kept, bind never blocks - new optional virtual slot anchor_category: when bound, splices a categorical EXCLUDE filter (subtotal/total) so anchors leave the bridge; unbound binds are byte-identical (characterization-pinned) - teaching lives in the manifest slot description (model-facing at bind time), NOT tools/list: bind-template/validate-proposal ratchets TIGHTENED 1908->1886 / 1555->1533; combined-lean 45,952 of 46,000 - served-corpus knowledge twins: waterfall subtotal/total law + zero-denominator ratio-sort guard (both analytics paths — legacy twin is live) Suite 4608 green (316 files), lint clean, invariants unchanged. v2.27.0. Co-Authored-By: Claude <noreply@anthropic.com> * fix(desktop): warnings typing on result base + result-carried waterfall discovery hints CI tsc caught warnings? missing on BindTemplateToolResultBase (vitest transpiles without typecheck — the gate that matters is npx tsc). Live re-sing receipt (m1, judge 34 both pre/post-slot): the slot shipped but was never FOUND — a confident bind has no Call-2, so manifest descriptions are never read, and the routeTable teaching was trimmed for the 46k cliff. A capability without a discovery channel is a no-op. Result-carried teaching (the #554 pattern, zero tools/list bytes): waterfall bind results now hint (a) the anchor_category re-call naming the schema's category-like column when the slot is unbound, (b) the one-call sort override when no sort proposal was passed. 6 tests pin presence/absence per case. tsc 0, suite 4616 green (316 files), lint 0 — pipe-proof exits. Co-Authored-By: Claude <noreply@anthropic.com> * fix(desktop): sort splice handles the serializer's paired-empty computed-sort form Third live sing receipt: the singer finally passed the full correct proposal (anchor_category + sort by Display Order asc) and the splice refused it — 'only the safe self-closing <computed-sort/> can be changed'. Root cause: the SOURCE template is self-closing but inject serialization expands it to <computed-sort ...></computed-sort>; the safe-form detector missed our own serializer's output. Safe target detection now covers self-closing, paired-empty, and plain-wrapper forms; truly nested <sort-computation>/manual forms still refuse (the computed-sort-crash class — Desktop crash guard intent preserved, evidence in computedSortCrash.ts). bind-template and refine-worksheet sort_by_field share the helper, so both inherit the fix. Characterization test pinned to the real inject-serialized waterfall shape (failed before, passes after). tsc 0, suite 4620 green (316 files), lint 0 — pipe-proof exits. Co-Authored-By: Claude <noreply@anthropic.com> * fix(desktop): waterfall seam-4 — sort the shelf axis with anchor_category bound; surface knowledge tools Two coupled fixes so a P&L waterfall can carry BOTH an anchor exclude-filter AND a proposal sort, and so the singer can actually reach the corpus that already documents how. Seam 4 (planSortByFieldOnCategoricalAxis): the categorical-axis count included filter-only dimension CIs. A waterfall's anchor_category exclude-filter adds a nominal/None CI that lives only in <filter>; it was counted as a rival axis and tripped "more than one categorical axis is present; sort is ambiguous." Restrict axis candidates to dims whose DS-qualified ref appears on a rows/cols shelf, mirroring planSortInsertion's proven membership check. Substring membership is exact for CI refs (the :suffix] grammar means one ref is never a substring of a longer one; ds + ci.name share the shelf text's escaping domain, e.g. P&L Data). Fail-before/pass-after test uses the real part-to-whole-waterfall template + spliceWaterfallAnchorFilter; adds a prefix-boundary case (RegionName vs Region) and keeps the genuine two-shelf-axis refusal. Knowledge tools: the default dynamic-authoring profile omitted list-knowledge-resources + read-knowledge-resource, yet the singer's system prompt instructs "consult the expertise library BEFORE authoring" — so the corpus (which already states the waterfall subtotal/total exclusion rule and the Top-N-needs-a-context-filter rule) was unreachable on every sing. Add both to the profile (14→16) and sharpen their descriptions to say WHEN to reach (net -27 bytes; combined-lean tools/list surface stays under the 46k cliff with more headroom than before). No a2td port owed — a2td's refine is a different (operation-based) architecture and always registers the knowledge tools. Gates: tsc 0, vitest 4623 passed/1 skipped, lint 0. Andy-review addressed. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
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.
Stacked on #552. Live-receipt-driven: the slim-surface eval was slow because of model round-trips (~9/song at ~5s each; every tool result returns in <0.1s), and three of those turns were manufactured by the surface itself.
What changed
dynamic-authoringprofile as the deterministic fast-path for plain chart shapes. The instructions advertised it while the profile cut it — live receipts show the model literally searching for "bind template" and thrashing.search-commandsis reserved for the uncommon path. Stays under the 46k tools/list deferral cliff (combined-lean budget test).auto_applynow applies Call-2 bound results (was Call-1-only). A Call-2 proposal is binder-validated against the live workbook and applies under the same events-anchor user-change guard; on the slim surface the manual apply tools don't exist, so the alternative was the model freehand-building the same chart with fewer guards.tabdoc:generate-viz-from-notional-specresults append a compact server-side readback (sheet, Rows/Cols, mark, sort; fail-open, one metadata round-trip) so verification stops costing model turns.list-available-fieldsworkbookFileis optional — omitted reads the live session workbook; its error no longer references cutget-*-xmltools.Live receipts (same ask, same stage, same judge)
Verification
Full unit suite 4537 green, tsc clean, lint clean. Byte ratchets respected (bind-template cap ratcheted down to 1629).
Known tradeoff:
DESKTOP_INSTRUCTIONSis profile-independent, so full-profile consumers lose the dashboard-auto-apply routing prose; instructions now match the default surface. Profile-aware instructions are a follow-up.🤖 Generated with Claude Code
Update (same day): NotionalSpec retired from routing — post-spec dialect live-proven
NotionalSpec was retired by decision on 2026-07-20. Commit
ff11aecere-points every live pointer (route prose, command census, and thehint:strings author-calc/author-set return) at the post-spec dialect: author the semantic object first, then bind-template the chart naming the authored caption. refine-worksheet (primitives-only top-N/sort) joins the slim profile (now 14 tools) to carry edit-in-place. The spec command itself stays registered — the door closes only after full coverage is proven.Live receipts on this build:
author-calc("Quota Attainment %") →bind-templatenaming the caption → Call-1 bound + auto-applied in 0.2s.tabdoc:sortreturned 500 on seven wire-shape attempts (~150s) before the guarded document round-trip landed the sort correctly. Known follow-ups: fix the tabdoc:sort contract or extend refine-worksheet with a sort-by-field op; treatsession: ""as absent.🤖 Generated with Claude Code