[Experiment] Two-tool surface: stable gitea dispatcher + gitea_help (9,734 B vs 65,159 B) #51
Labels
No labels
area/dsh
area/forgejo
experiment
priority/later
priority/next
priority/now
type/bug
type/task
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
dsh-plugins/dsh-gitea#51
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Idea
Instead of 96 typed tools, expose one stable dispatcher plus one schema fetcher:
gitea { operation, args }— runs any operation; validatesargsserver-side against the existing per-operation schema.gitea_help { operation }— returns that operation's description and JSON Schema as a tool result.This is the harness's own PTC "code mode" philosophy (one transport tool + generated SDK) implemented in plugin space for plain native function calling — without depending on PTC being composed.
Measured against the live 0.8.4 schemas
The catalog variant is the realistic one: the model has to know which operations exist, so the dispatcher description carries one line per operation. That catalog alone measured 9,146 B; the dispatcher skeleton is 183 B and
gitea_help210 B.Why this is harness-compatible (unlike #50)
tool-additionrecords ordeferLoadingprovider support, i.e. no bet on the least stable part of DSH before 1.0.Tradeoffs to measure
help→ call). Hot operations (the top 15 are 70 % of traffic) should be called directly from memory.gitea_helpshould stay under ~1.5 KB for the largest operation.Sketch
TOOL_DEFSstays the single source of truth: the dispatcher validatesargswith the same per-operation schema, andgitea_helprenders fromTOOL_DEFS— no second schema corpus that can drift. Shares thepresetplumbing from #49 rather than inventing a parallel switch.Impact
A 6.7× smaller permanent tool block (≈9.7 KB vs 65 KB, ≈2.6k vs ≈17.6k tokens) with no dependence on dynamic registration, so it is the fallback if #50's spike says dynamic injection is too expensive or too fragile.
Acceptance criteria (spike gate)
preset: dispatcher; default unchanged and byte-identicalcoreandfullgitea_helpoutput < 1.5 KB per operation and available for every operationinstanceparameter keeps working through the dispatcherBoundaries
Dependencies
Complementary to #50 and mutually exclusive with it in practice: both answer "how small can the surface get", one by dynamic injection, one by a stable dispatcher. #49's
presetplumbing is shared.Spike branch is up:
experiment/dispatcher(commitc921667, branched offv0.8.5). Not tagged, not published — master stays at 0.8.5 and this branch is rebased or dropped after the gate.What exists
preset: dispatcher(opt-in;fullstays the default and is unchanged) registers exactly two tools:gitea { operation, args }— validatesargsagainst that operation's own schema, then runs the same execution path as the typed tool (executeCanonicalTool(), extracted so the two cannot drift).gitea_help { operation }— returns the operation's description + compact JSON Schema as a tool result.lib/dispatcher.jsis pure and derives everything fromTOOL_DEFS: one-line catalog, help rendering, validation. No second schema corpus.Call gitea_help { operation: "gitea_issue_get" } for its schema.instancepasses through untouched, so multi-instance keeps working.Measured surface (wire bytes,
JSON.stringify({name, description, parameters}))fullfull(canonical, default)coredispatcher+ catalognoneBreakdown of the 8,973 B: catalog + instructions 8,531 B,
giteatool parameters ~300 B,gitea_helpdescription 163 B. Help payloads average 514 B, largest 947 B — every operation is inside the ~1.5 KB ceiling (enforced bytest/dispatcher.test.mjsover all 96).So the measured shape beats the issue's 9,734 B / 6.7× estimate, and — the important part for this versus #50 — the registry never changes, so the request prefix stays reusable.
Gate status
test/dispatcher.test.mjs)gitea_helpunder 1.5 KB for every operationinstanceflows through the dispatchercore/full— needs live sessionsThe missing piece needs a profile change to run:
preset: dispatcherin thedsh-gitearow of<profile>/cordis.patch.yml, which is outside this workspace (a profile write, so it needs operator approval — say the word and I'll do it with approval, or run a throwaway profile). The measurement itself is therequest/header+usagemethod from #48/#49 plus an accuracy column (first-call success vs retry after agitea_helpround-trip).Two tradeoffs to settle with the numbers, both from the issue: +1 round trip for unfamiliar operations, and weaker first-try arguments without schemas in context. The dispatch does not hide either — it turns them into measured columns.