Skip to content

Delete the core tool-name tables; rows render from persisted data and structure (WS3 layer 5) - #2232

Closed
SawyerHood wants to merge 1 commit into
ws3-l4-open-requestsfrom
ws3-l5-delete-tool-name-tables
Closed

Delete the core tool-name tables; rows render from persisted data and structure (WS3 layer 5)#2232
SawyerHood wants to merge 1 commit into
ws3-l4-open-requestsfrom
ws3-l5-delete-tool-name-tables

Conversation

@SawyerHood

@SawyerHood SawyerHood commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Stack #2207, layer 5 of 5 (on #2209#2208#2206#2192). Spec: WS3 "projection + renderers + mobile", delete layer.

What was wrong

Core still knew provider tool names. packages/thread-view carried the Agent/Task/spawnAgent delegation set, the Read/Grep/Glob exploration sets, the TodoWrite/ToolSearch/AskUserQuestion suppression set, and a TodoWrite-by-name todo reader; @bb/domain shipped claude-task-tools.ts; the server copied plugin statusLabels into every tool-call event; the events table had a tool_name virtual column with an index for name lookups; and turn/plan/updated sat on the noise list so legacy codex plans never rendered. Layers 1–4 taught every client to read the bridge-owned presentation and the grammar v3 kinds beside those tables. With the tables still there, a plugin provider could not hide, label, or nest a row unless its tool happened to share a name with a first-party one.

What changed

Deleted (core knows no tool names).

  • packages/thread-view/src/tool-call-parsing.ts: the DELEGATION/READ/SEARCH/LIST name sets, their predicates, baseToolName, stripAgentOutputMetadata. Shell wrapper handling, parseShellCommandIntents, and formatToolCallCommand stay.
  • tool-call-suppression.ts honours presentation.suppress only. todo-snapshot-extraction.ts reads planSteps items only. exec-lifecycle.ts projects every tool call as a generic tool call.
  • packages/domain/src/claude-task-tools.ts and the claudeTaskTool* schemas in @get-bb/plugin-sdk/provider-bridge.
  • statusLabels: off the toolCall item, the timeline tool row, the projection message, and apps/server/src/internal/events.ts (withPluginToolStatusLabels). The plugin-facing experimental_statusLabels field still exists; the server folds it into the tool's resolved presentation, which the bridge stamps on every call (docs/api_to_audit.md entry updated).
  • events.tool_name virtual column and its index. Migration 0107_kind_based_indexes replaces them with events_delegating_item_lookup_idx (item_kind IN ('toolCall','delegation'), covering) and events_plan_steps_thread_sequence_idx (planSteps completions or turn/plan/updated). Generated with drizzle-kit, then IF EXISTS / IF NOT EXISTS added so a rewound database replays.
  • turn/plan/updated leaves timeline-noise-events.ts.

Replaced by structure or persisted data.

  • A persisted tool call that other rows name as their parentToolCallId becomes the delegation row (normalize-event-projection.ts). It keeps the :delegation: row id it had under the name set, so nested rows keep their ids, and takes description/subagentType from conventional argument keys (argument shape, never a name). A v3 delegation item does not pass through this path.
  • packages/domain/src/legacy-thread-events.ts converts a persisted codex turn/plan/updated into a planSteps item at read time (deterministic id from turn + payload), so old codex threads show their plans and feed the todo banner.
  • packages/db/src/data/events.ts: delegating-item and plan-snapshot queries select by item_kind/type literals with the new indexes (query-plans.test.ts pins both plans).
  • Pi keeps its read/grep/find/ls rows: the knowledge moved into packages/agent-runtime/src/pi/delta-translation.ts, which now emits fileRead/search items with presentation (bridge-owned, like the other bridges).

Wire. HOST_DAEMON_PROTOCOL_VERSION 150 → 151: the toolCall item lost its optional statusLabels key and the daemon contract's guard for it, so an older daemon could still send the key. Compatibility was not tested, so the version bumps.

Corpus gate. A pointer allowlist cannot describe a change that adds, removes, or moves rows. apps/server/test/provider-corpus/row-diff-classes.ts adds an identity-based mode to the A4 gate (BB_PROVIDER_CORPUS_ROW_CLASSES=<file>): rows match by callId/itemId/interactionId/turn id, every change must fall into a named class (added, removed, moved, resegmented, reshaped, changed + fields), and a class that claims nothing fails the run. This PR's classes live in allowlists/ws3-layer5-row-classes.json; scripts/provider-corpus/classify-row-diff.ts runs the same engine offline over two snapshot dirs. Documented in docs/debugging-and-qa.md and the allowlists README. Relative BB_PROVIDER_CORPUS_* file paths now resolve against the repo root as well as turbo's apps/server cwd.

Parity harness. maskPath accepts the root pointer (/) to empty one layer of one cell. One entry uses it (codex/user-question rows): the immutable v2 recording's thread/start predates the server-resolved tool presentation, so the replayed AskUserQuestion call renders as a generic row where the old leg hid it by name; the live server sends suppress: true on the definition. The cell's row pin moves 1 → 2 for the same reason. Re-recording the cell drops both.

How you verified

  • pnpm exec turbo run test for @bb/thread-view (365), @bb/domain (150), @bb/db (406), @bb/client-core (239), @bb/cli (453), @bb/app (3209), @bb/provider-bridge-protocol (217), @bb/provider-parity (44), @bb/host-daemon-contract, @bb/agent-runtime, @bb/server; typecheck for @bb/server and the above. New/changed tests: structural delegation (build-thread-timeline.test.ts: arbitrary tool name becomes a delegation only when referenced, keeps its id and argument metadata), todo-snapshot-extraction.test.ts (planSteps only; a TodoWrite-named call decides nothing), row-diff-classes.test.ts (5), query-plans.test.ts (both new indexes), migrate.test.ts rewind, Pi translation (read/grep/find/ls → fileRead/search), host-daemon contract 151, parity self-suite root mask.
  • A4 corpus (307 threads, main-minted baseline, class mode): 333 tests pass; 220 threads change; every change falls into 9 classes, 0 unclassified, 0 idle classes:
    exploration-intent-by-name 4183 · container-bounds 712 · unsuppressed-by-name 567 · legacy-plan-rows 356 · delegation-output-unstripped 160 · delegation-from-children 145 · parented-rows-surface 141 · delegation-rows-gain-v3-fields 123 (Project grammar v3 items to presentation-driven rows (WS3 layer 1) #2192) · turn-segments-rejoined 24. Each class's reason is in the JSON file. Legacy threads render from persisted data: delegation rows keep their ids, nesting, description and subagent type; old codex plans appear as plan-steps rows.
  • Perf gate: 21/21 pass at load 1.2 on 16 cores; walk vs base 0.73–1.04× (normalized min), synthetic 10k-event thread 161 ms full walk.
  • Parity A2: pnpm parity --old ~/.bb/parity-old-main --new . → 39 passed, 0 failed, 13 skipped (pi in-process, model-list), no stale entries.
  • G1: node scripts/check-provider-literal-ratchet.mjs 148 → 148. The ratchet counts provider-ID literals and its header excludes tool-name keying, so this layer cannot move it; the remaining hits are plan-mode comments and the Claude model-fallback raw-event parser. Substitute measure, reproducible: git grep -c -E '"(Read|Grep|Glob|Edit|Write|Bash|Task|Agent|TodoWrite|TodoRead|ToolSearch|AskUserQuestion|TaskOutput|TaskStop|TaskCreate|TaskUpdate|TaskGet|TaskList|Monitor|WebFetch|WebSearch|spawnAgent|resumeAgent|update_plan|exec_command|apply_patch|read_file|list_dir|grep_search|file_search|shell)"' over packages/thread-view/src packages/domain/src packages/db/src packages/server-contract/src apps/server/src/services/threads: main 31 → this PR 3, and the three left are the English words "Read"/"Reading" in row titles and the terminal "shell" mode.
  • Known local-only failure unrelated to this PR: internal-skill-trees 0644 vs 0664 under umask 0002 (passes in CI).

Part of the WS3 stack; no issue number.

AGENT GENERATED: by Claude Opus 5

@SawyerHood
SawyerHood force-pushed the ws3-l5-delete-tool-name-tables branch from 1df241b to 8f319cf Compare August 21, 2026 17:50
… structure (WS3 layer 5)

Core no longer keys any projection on a tool name. The thread-view
DELEGATION/READ/SEARCH/LIST name sets, the suppression name set, the
TodoWrite-by-name todo reader and @bb/domain's claude-task-tools are gone;
statusLabels leave the event, the timeline row and the daemon wire
(HOST_DAEMON_PROTOCOL_VERSION 150 -> 151); the events.tool_name virtual
column and its index are replaced by kind-based partial indexes (migration
0107); turn/plan/updated leaves the noise list and decodes at read time into
a planSteps item so legacy codex plans still render.

A persisted tool call that other rows name as their parentToolCallId becomes
the delegation row structurally (same row id as before, label metadata from
conventional argument keys), so old threads keep their nesting. Pi's
read/grep/find/ls knowledge moves into its own translation as fileRead/search
items.

The corpus gate gains an identity-based row-class mode
(BB_PROVIDER_CORPUS_ROW_CLASSES) because a change that adds, removes or moves
rows cannot be expressed by pointer allowlist; this layer's nine classes
live in allowlists/ws3-layer5-row-classes.json.

Co-Authored-By: Claude <noreply@anthropic.com>
@SawyerHood
SawyerHood force-pushed the ws3-l5-delete-tool-name-tables branch from 8f319cf to 03479c3 Compare August 21, 2026 18:09
@SawyerHood

Copy link
Copy Markdown
Collaborator Author

Coordinator review — WS3 stack #2207

Layers 1–4 (#2192, #2206, #2208, #2209): PASS. Layer 5 (#2232): PASS WITH ONE CONDITION — a layer 6 is required before the stack is complete. Do not merge: the stack accumulates until the full migration is done.

Verified

The condition: old threads must not regress

Four classes are improvements and stay: legacy-plan-rows, delegation-from-children, delegation-rows-gain-v3-fields, parented-rows-surface.

Five classes are regressions on threads the user already has, because those rows were persisted before bridges sent presentation, and layer 5 deleted the only knowledge that rendered them:

class rows what the user sees on an old thread
exploration-intent-by-name 4,183 Read { file_path: … } instead of "Explored N files"
unsuppressed-by-name (3 variants) 567 ToolSearch / TodoWrite / Task* rows appear
turn-segments-rejoined 24 a consequence of the above
delegation-output-unstripped 160 agentId: / <usage> lines in the expanded body
container-bounds 712 needs the same classification: improvement or regression

The owner's standing constraint is "do not regress current behavior". A core with no tool-name tables is the target for the live path; it does not require history to change.

Layer 6 — legacy read-time adapter (required)

In packages/domain/src/legacy-thread-events.ts (your own read-time module), upgrade a persisted tool item only when it carries no presentation (a row minted before grammar v3): the Read/Grep/Glob/ls/find names → fileRead / search item shape; the TodoWrite/TodoRead/ToolSearch/Task*/AskUserQuestion names → presentation.suppress: true; the Agent/Task result → strip the agentId: / <usage> lines into summary. Rules:

  1. Keyed on the absence of presentation, never on a provider id. One file, with a header that states it is a legacy-data adapter and names the migration that will delete it.
  2. A test asserts the adapter is never consulted for a row that has presentation.
  3. A4 against the same baseline: the five regression classes go to zero; the four improvement classes remain; 0 unclassified. Classify container-bounds and state which side it lands on.
  4. Report the tool-name literal count honestly as "core: 3 + legacy adapter: N (quarantined)". Do not exclude the file from any metric.
  5. In the PR body, name the follow-up: a one-time backfill migration that stamps the adapter's output onto the old rows, after which the adapter is deleted. That migration is not part of this stack.

Then gh stack submit --auto so #2232's tip stays put and layer 6 becomes the chain base.

Left for the owner: iOS simulator QA (no simulator on this box; Maestro flow extended in layer 3).

AGENT GENERATED: by Claude (coordinator)

@SawyerHood SawyerHood closed this Aug 21, 2026
@SawyerHood
SawyerHood deleted the ws3-l5-delete-tool-name-tables branch August 21, 2026 21:41
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