Files
boocode/openspec/changes/v1.13.20-drop-legacy-cols/tasks.md
indifferentketchup 211e903620 v1.13.20-drop-legacy-cols: final phase of v1.13.0 strangler-fig
Removes the dual-write into messages.tool_calls / messages.tool_results JSON
columns and drops the columns. message_parts is now the only source of truth
for tool calls and tool results.

10 dual-write sites stripped (5 in tool-phase.ts, 2 in routes/skills.ts, 2 in
routes/messages.ts, 1 in routes/chats.ts fork-clone). The recon-driven grep
caught 2 sites beyond the original v1.13.2 roadmap inventory and an extra
fixture file (tool_cost_stats.test.ts) with a direct legacy-column INSERT.

messages_with_parts view rewritten to parts-only subselects (COALESCE
fallbacks gone). View runs via CREATE OR REPLACE so it lands before the
column DROPs in startup DDL — Postgres rejects column-drop on view-referenced
cols. v1.12.1 cleanup DO block (DROP CONSTRAINT messages_status_check /
messages_role_check) removed; those one-shots have done their work.

Adversarial review caught a runtime bug the green test suite missed: the
discard_stale endpoint (chats.ts) had a RETURNING ... tool_calls, tool_results
clause that would have crashed on every 60s-no-token-activity recovery in
production. Fixed by switching to two-step UPDATE returning id, then SELECT
from messages_with_parts so parts-synthesized fields keep flowing on the wire.

Message API type retains tool_calls? / tool_results? — the view synthesizes
those keys from parts so the wire shape is unchanged; frontend reads need no
update. Override on the original v1.13.2 plan, captured in the openspec
proposal.

339/339 server tests passing (including 7 DB-integration tests that applied
the schema migration to a live DB and ran the parts-only view end-to-end).
tsc + web build clean.

Pairs with v1.13.0-ai-sdk-v6 (introduced the dual-write) and v1.13.1-B (moved
the read path to messages_with_parts). Umbrella v1.13 tag ships on this same
commit, marking the strangler-fig closed.

CLAUDE.md picks up Sam's pre-existing edits documenting tag-naming and
CHANGELOG conventions — both already in use by v1.13.19 / v1.13.20.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 13:03:51 +00:00

5.1 KiB

v1.13.20-drop-legacy-cols tasks

B1 — Recon (STOP after this step)

  • Grep apps/server/src/**/*.ts for every tool_calls and tool_results mention. Categorize each hit as:
    • dual-write (an UPDATE / INSERT that writes the JSON column)
    • read (a SELECT that reads the JSON column, or code that destructures it from a row)
    • type-only (interface / type field reference)
    • test fixture (literal in a test file)
    • comment / docs
  • Confirm the v1.13.2 roadmap inventory is complete:
    • tool-phase.ts: 3 sites
    • error-handler.ts (finalizeCompletion): 1 site
    • routes/skills.ts: 2 sites
    • routes/messages.ts (answer flow): 1 site
    • routes/chats.ts (fork): 1 site
    • Any extras the grep finds: list them
  • Confirm no READ sites still touching the legacy columns (everything should go through messages_with_parts). If reads remain, flag them — they need to migrate to the view BEFORE dropping the columns.
  • Hand back inventory as a per-file table: file, line, kind (dual-write / read / type / fixture), action (delete / migrate-to-view / type-prune).

B2 — Backups

  • cp <file> <file>.bak-v1.13.20-20260523 for every file in B1's action list before editing.

B3 — Remove dual-writes

  • Remove the JSON-column UPDATE / INSERT at every site identified in B1 as a dual-write. Keep the paired insertParts(...) call.
  • If a site only writes the JSON column with no parts pair (would indicate a bug from v1.13.0) — STOP, report as BLOCKED.
  • Verify by grep: zero remaining writes to tool_calls or tool_results outside of schema.sql and test fixtures.

B4 — Simplify messages_with_parts view

  • Open schema.sql. Find the view definition.
  • Drop the COALESCE fallbacks that read m.tool_calls / m.tool_results from messages.
  • View now reads only from message_parts joined to messages.
  • Confirm view's output column shapes are unchanged: tool_calls jsonb[], tool_results jsonb single object, reasoning_parts jsonb[].

B5 — Drop columns

  • ALTER TABLE messages DROP COLUMN IF EXISTS tool_calls;
  • ALTER TABLE messages DROP COLUMN IF EXISTS tool_results;
  • Idempotent on re-run.
  • Apply order in schema.sql: AFTER the view is updated (view depends on the columns; can't drop a column referenced by a view).
  • Actually verify the order — if the view references the columns, you must drop the view first OR change it before the ALTER.

B6 — Remove v1.12.1 cleanup block

  • Find the DO $$ DROP CONSTRAINT messages_status_check block in schema.sql (likely near the messages CHECK constraints).
  • Confirm it's safe to remove (the constraint should have been dropped long ago).
  • Delete the block.

B7 — Type pruning

  • apps/server/src/types/api.ts — remove tool_calls? and tool_results? from the Message interface.
  • apps/web/src/api/types.ts — mirror byte-for-byte.
  • Search for any other type references — ToolCallsField, ToolResultsField, etc.

B8 — Test fixture updates

  • Run pnpm -C apps/server test to see what breaks.
  • For each failing test that constructs a Message literal with tool_calls: null / tool_results: null — remove those fields.
  • For tests that exercised tool-call behavior via the legacy columns, rewrite to construct via message_parts rows.
  • Confirm: pnpm -C apps/server test — all green.

B9 — Type / build verification

  • npx tsc --noEmit -p apps/server — 0 errors.
  • npx tsc -p apps/web/tsconfig.app.json --noEmit — 0 errors.
  • pnpm -C apps/web build — green.

B10 — STOP checkpoint, hand back diff

  • Hand controller the diff for backend changes + test results.

B11 — Schema deploy

  • docker compose up --build -d rebuilds with new schema.
  • Boot twice in sequence — confirm idempotent (column DROP IF EXISTS is a no-op on the second boot).
  • docker exec boocode_db psql -U boocode -d boocode -c "\d messages" — confirm columns absent.
  • docker logs boocode 2>&1 | tail -50 — confirm no schema errors.

B12 — Smoke

  • Live-smoke: send a chat that triggers at least one tool call. Confirm:
    • Assistant message renders with content + tool_call ActionRow.
    • Tool result renders.
    • No console errors in browser or docker logs boocode.
  • Trigger a compaction-eligible turn (long context). Confirm rolling summary anchors correctly.

B13 — Docs

  • CHANGELOG.md entry for v1.13.20-drop-legacy-cols.
  • boocode_roadmap.md retrospective bullet on the v1.13.2 section (note the slug rename and ship date).
  • CLAUDE.md — drop the v1.13.0 dual-write notes that no longer apply. Audit the surrounding paragraphs.

B14 — Tag + push + rebuild

  • git add only the v1.13.20 batch files (per CLAUDE.md convention).
  • git commit with HEREDOC commit message.
  • git tag v1.13.20-drop-legacy-cols AND git tag v1.13 (umbrella, per original v1.13.2 plan).
  • Push: GIT_SSH_COMMAND="ssh -i /opt/boocode/secrets/boocode_gitea -o IdentitiesOnly=yes" git push origin main
  • Push both tags.
  • docker compose up --build -d.
  • Curl health check.