Skip to content

feat(graph): let the caller name the machinery, and stop asking the question twice#529

Merged
samchon merged 5 commits into
masterfrom
fix/graph-benchmark-index-block
Jul 14, 2026
Merged

feat(graph): let the caller name the machinery, and stop asking the question twice#529
samchon merged 5 commits into
masterfrom
fix/graph-benchmark-index-block

Conversation

@samchon

@samchon samchon commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Intent

The tour asked for the question twice. IProps.question and ITtscGraphTour.IRequest.query were the same string, two lines apart, and a schema that asks twice gets it once: GPT-5.6 sent { "type": "tour" } with no query in 31 of 32 cells, the validator rejected it, and every Codex tour paid a full extra round trip. The tour now ranks against props.question.

The tour landed on the wrong machinery, and the model paid for it. A tour ranks its seeds on words, and a codebase names many things alike: "dependency tracking" matches Vue's devtools hook onRenderTracked as readily as track, the function that records the dependency, and "request" matches NestJS's message listener as readily as its HTTP router. The model notices — "the tour surfaced the microservices listener path, not the HTTP router path" — and then spends two more traces fetching the router by hand.

The tour request now carries reinterpretations: a list of symbol names, never a sentence, naming the machinery the caller expects the answer to be made of. Each name resolves the way a handle does. The ones the graph holds take half the entrypoints; the other half stays with what the graph finds central, untouched by the names. A name the graph does not know — or knows several of — is dropped, so a wrong guess is free, prose costs nothing, and [] is the right answer when the question names no machinery.

Also in this PR: the benchmark harness now rejects a tool arm that never called its tool, publish.mjs stops dropping the index block, and the charts sit under the claims they support in the README, the design guide, and the blog.

Results

All 64 agent cells re-measured on today's clients. 0 file reads, 0 shell commands, and every Codex cell answers in a single call with no validation failures.

axis before after
Sonnet / common 93% 92%
Sonnet / dedicated 83% 87%
Opus 4.8 / common 93% 93%
Opus 4.8 / dedicated 90% 91%
GPT-5.6 terra / common 89% 87%
GPT-5.6 terra / dedicated 85% 90%
GPT-5.6 sol / common 94% 96%
GPT-5.6 sol / dedicated 93% 95%

Sonnet's Vue tour goes from seven calls to one, Opus's NestJS tour from three to one.

Deferred

Whether the tour should ask the caller for symbol names at all is open. The tour is the request you make when you do not yet know them, and the defect it works around is in the ranking: runtimeEntryScore grants +70 for an English verb in the name, so Vue's onRenderTracked — a devtools hook with one execution edge — outranks track, which has twelve. Fixing that is question-only, needs no priors from the model, and may replace this field.

Test plan

  • pnpm -F @ttsc/graph build and tsc --noEmit clean.
  • tests/test-graph end-to-end MCP case updated for the new tour request.
  • Benchmark: 64 ttsc-graph cells, plus one re-measurement each for the three cells that came back anomalous.

samchon and others added 5 commits July 14, 2026 15:41
`publish.mjs --from` folds the agent cells and rewrites the document, and the
`index` block — the cold index build time, one cell per tool per repository —
was not carried across. It went out of `graph.json` with the republished grid,
and the two charts that read it, `graph-index-build-time` and
`graph-time-to-answer`, stopped being generated at all: the site kept serving
the stale SVGs left on disk from the last build, which is worse than serving
nothing, because nothing is visible.

The 32 index cells are restored from the document as it stood before the
republication. They are unchanged measurements — the dump path did not move in
this release — and the chart count goes back to 74.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XmPDmgakdzQQP9it9HZMen
…e model to restate the question

Three things were out of date at once.

**The `index` axis was gone from the published document.** `publish.mjs --from`
folds the agent cells and rewrites `graph.json`, and it did not carry the
`index` block across, so the republished grid dropped the 32 cold-index cells
with it. The two charts that read them — `graph-index-build-time` and
`graph-time-to-answer` — then stopped being generated at all, and the site kept
serving the stale SVGs left on disk, which is worse than serving nothing because
nothing is visible. Restored; the chart count goes back to 74.

**Every code snippet still told the model to restate the question.** The tool's
schema says the opposite, and says why: the tour ranks its seeds against the
words in the query, so a rewrite ranks a different answer. The README, the
design doc and the blog all carried `restate the code question`, which is the
line the server itself stopped saying. They now read as the schema does.

**The benchmark section had no charts, and the time axis was unreadable.** Each
`###` now carries the chart that makes its claim — shared question, project
questions, and the wait before either of them. The time-to-answer chart labels
every bar `index / answer` in the order you wait for them, in seconds
throughout rather than seconds beside minutes, with the two shades named in the
legend; and the repositories no longer run together into one wall, because a
group of five bars was 75px tall in a 76px row.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XmPDmgakdzQQP9it9HZMen
…p publish dropping the index

Two holes, and the second one hid the first.

**A graph-arm run that never opens the MCP is not a graph-arm measurement.**
`validateArmSample` was an empty function in both drivers. So when GPT-5.6 was
asked how RxJS carries a value from `subscribe` through the operators, opened
with "I'll trace the subscription path through the repository's implementation",
and ran ten PowerShell commands without naming the MCP once — the server
mounted, the other seven repositories of the same sweep calling it twice each,
the same tool line in the prompt — that run went into the table as a 39% saving
with five shell commands against it. Re-run, the same cell called the graph
twice, opened no file, and spent 72,174 tokens against the 153,954 it had spent
shelling: 39% becomes 72%, and the axis 80% becomes 82%.

The retry loop already re-runs a sample it marks `ok: false`. Now a tool arm
with zero graph calls is marked that way, in both drivers, so the harness
re-runs it instead of publishing a model that did not look as if it were a
measurement of the tool.

With that cell corrected, `@ttsc/graph` answers **all 64 cells** — four models,
two prompt families, eight repositories — with **zero file reads and zero shell
commands**, and no cell that skipped the tool.

**And `publish.mjs` deleted the `index` axis every time it ran.** It rebuilt the
document from `structural` and `agent` alone, so folding agent cells silently
dropped the 32 cold-index cells, and the two charts that read them stopped being
generated at all — the site kept serving the SVGs left on disk from the last
build, which is worse than serving none, because a missing chart is visible and
a stale one is not. Every block the document carries now survives a publish that
does not write it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XmPDmgakdzQQP9it9HZMen
… cells

`IProps.question` and `ITtscGraphTour.IRequest.query` asked for the same
string two lines apart. A schema that asks twice gets it once: GPT-5.6 sent
`{ "type": "tour" }` with no query in 31 of 32 cells, the validator rejected
the call, and every Codex tour paid a full extra round trip. The duplicate
also lost what it asked for -- `question` keeps the user's backticks and
dotted names, which the mention resolver reads to find the symbol the user
named, and a model paraphrasing into `query` drops them.

The tour now ranks against `props.question`. Every Codex cell is down to a
single call and zero validation failures (was 8 of 8 failing on terra/common).

Re-measured all 64 ttsc-graph cells on the current client:

  axis                 before   after
  sonnet / common       93%      93%
  sonnet / dedicated    82%      83%
  opus / common         93%      93%
  opus / dedicated      92%      90%
  terra / common        80%      89%
  terra / dedicated     82%      85%
  sol / common          90%      94%
  sol / dedicated       91%      93%
…udicate

A tour ranks its seeds on the question's words, and a codebase names many
things alike: "dependency tracking" matches Vue's devtools hook
`onRenderTracked` as readily as `track`, the function that records the
dependency, and "request" matches NestJS's message listener as readily as its
HTTP router. A tour that lands on the wrong machinery is not a wrong answer --
it is a tour the model then spends calls correcting, and it says so: "the tour
surfaced the microservices listener path, not the HTTP router path", then two
more traces.

The tour request now carries `reinterpretations`: a list of symbol names, never
a sentence, naming the machinery the caller expects the answer to be made of.
Each name resolves the way a handle does. The ones the graph holds take half the
entrypoints; the other half stays with what the graph finds central, untouched
by the names, so a caller cannot lose the tour on a bad guess or crowd out the
symbol it did not think to name. A name the graph does not know -- or knows
several of -- is dropped, so prose costs nothing, and `[]` is the right answer
when the question names no machinery at all.

Two things the schema could not do on its own. A required field the instruction
never mentions is not sent: GPT-5.6 submits the smallest object that could be a
request, so eight of eight tours failed validation and were re-sent. And a field
whose description opens with prose gets prose -- the same model sent one
sentence to a `string[]` until the description opened with "a list of symbol
names, not a sentence". A model reads the description, not the type.

All 64 cells re-measured, 0 file reads, 0 shell:

  axis                 before   after
  sonnet / common       93%      92%
  sonnet / dedicated    83%      87%
  opus / common         93%      93%
  opus / dedicated      90%      91%
  terra / common        89%      87%
  terra / dedicated     85%      90%
  sol / common          94%      96%
  sol / dedicated       93%      95%

Sonnet's Vue tour goes from seven calls to one, Opus's NestJS tour from three to
one, and every Codex cell answers in a single call with no validation failures.
@samchon samchon force-pushed the fix/graph-benchmark-index-block branch from 74917e6 to fc02332 Compare July 14, 2026 06:42
@samchon samchon changed the title fix(benchmark): restore the index axis, and put the charts where the claims are feat(graph): let the caller name the machinery, and stop asking the question twice Jul 14, 2026
@samchon samchon merged commit fab5304 into master Jul 14, 2026
43 of 48 checks passed
@samchon samchon deleted the fix/graph-benchmark-index-block branch July 14, 2026 06:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant