Skip to content

claude-code-chat-browser: Frontend tool registry - link to backend dispatch table via generated manifest#105

Merged
wpak-ai merged 4 commits into
masterfrom
feat/frontend-tool-types-manifest
Jul 1, 2026
Merged

claude-code-chat-browser: Frontend tool registry - link to backend dispatch table via generated manifest#105
wpak-ai merged 4 commits into
masterfrom
feat/frontend-tool-types-manifest

Conversation

@clean6378-max-it

@clean6378-max-it clean6378-max-it commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Closes #100

Summary

Links the frontend TOOL_USE_RENDERERS map to the backend KNOWN_TOOL_TYPES registry via a generated static/tool_types.json manifest. On SPA boot, the app fetches the manifest, cross-checks the renderer map, and logs console.warn when they diverge. Unknown tool fallbacks now show the raw type name and JSON payload instead of failing silently.

Sprint item #4 (Tuesday PR 2 of 2). Stacks on PR #1 (#104KNOWN_TOOL_TYPES registry + sync CI).

Problem

The SPA maintained its own tool renderer map with no runtime or build-time linkage to the backend. New backend tool types fell through to a generic fallback without any operator signal.

Changes

  • scripts/gen_tool_types_manifest.py (new) — writes static/tool_types.json from KNOWN_TOOL_TYPES
  • static/tool_types.json (new) — committed manifest (11 tool types)
  • static/js/render/tool_types_state.js (new) — module-level manifest Set (avoids circular imports)
  • static/js/render/tool_types_manifest.js (new) — fetch manifest on init; bidirectional drift warnings
  • static/js/render/tool_types_manifest.test.js (new) — vitest for init cross-check and fetch failure
  • static/js/app.jsawait initToolTypesManifest() before handleRoute()
  • static/js/render/registry.js — simplified fallback dispatch (drift warnings at init only)
  • static/js/render/tool_use/fallback.jsUnknown tool: {name} + JSON input
  • static/js/render/tool_result/fallback.jsUnknown tool result: {type} + JSON payload
  • tests/test_tool_dispatch_sync.py — manifest vs KNOWN_TOOL_TYPES, freshness check, registry vs manifest in one test
  • CONTRIBUTING.md — regen step: python scripts/gen_tool_types_manifest.py
  • Makefilegen-tool-types-manifest target
  • docs/architecture.md — manifest boot flow documented

Out of scope

  • GET /api/tool-types endpoint (Option B)
  • New tool type renderer implementations

Test plan

  • pytest tests/test_tool_dispatch_sync.py -q (6 passed)
  • pytest -q (419 passed)
  • npm test (84 passed)
  • ruff check . and ruff format --check .
  • mypy -p api -p utils -p models
  • Manual: temporarily add a fake type to tool_types.json without a TOOL_USE_RENDERERS entry — confirm console.warn on page load, then revert
  • Manual: open a session with an unknown tool type — confirm fallback shows type name and JSON payload

Summary by CodeRabbit

  • New Features

    • Added startup loading of a tool types manifest to keep the app’s tool handling in sync.
    • Introduced a generated manifest file that lists supported tool types.
  • Bug Fixes

    • Improved fallback messages for unknown tools and tool results, making them clearer and more informative.
    • Added checks that warn when the displayed tool registry and available tool types drift out of sync.
  • Documentation

    • Updated contributor guidance to include regenerating the tool types manifest when adding a new tool type.

Generate static/tool_types.json from KNOWN_TOOL_TYPES and load it at SPA
boot to cross-check TOOL_USE_RENDERERS with console.warn on drift. Improve
unknown tool/result fallbacks to show raw type names and JSON payloads.
Extend test_tool_dispatch_sync.py with manifest freshness and coverage
assertions. Await manifest init before first route render; document the
flow in CONTRIBUTING.md, Makefile, and docs/architecture.md.
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f45d9d02-7958-4561-8da7-332ff62ebf11

📥 Commits

Reviewing files that changed from the base of the PR and between 8475dc3 and e217de2.

📒 Files selected for processing (4)
  • docs/architecture.md
  • static/js/render/tool_types_manifest.js
  • static/js/render/tool_types_manifest.test.js
  • tests/test_tool_dispatch_sync.py
💤 Files with no reviewable changes (1)
  • static/js/render/tool_types_manifest.js
✅ Files skipped from review due to trivial changes (1)
  • docs/architecture.md

📝 Walkthrough

Walkthrough

This PR generates static/tool_types.json from backend KNOWN_TOOL_TYPES via a new script and Makefile target, fetches the manifest during SPA boot to warn on drift against the frontend TOOL_USE_RENDERERS registry, updates unknown-tool/result fallback rendering to show raw type and JSON payload, and adds sync tests and docs.

Changes

Tool types manifest and frontend sync

Layer / File(s) Summary
Manifest generation and docs
scripts/gen_tool_types_manifest.py, Makefile, CONTRIBUTING.md, docs/architecture.md, static/tool_types.json
New script writes sorted tool_types from KNOWN_TOOL_TYPES to static/tool_types.json; a Makefile target and docs describe regenerating the manifest.
Manifest fetch and boot init
static/js/render/tool_types_manifest.js, static/js/render/tool_types_manifest.test.js, static/js/app.js, static/js/app.test.js
initToolTypesManifest fetches the manifest with a 5s abort timeout, normalizes types, and console.warns on drift versus TOOL_USE_RENDERERS; app.js calls it non-blocking on DOMContentLoaded.
Unknown tool and result fallback
static/js/render/tool_use/fallback.js, static/js/render/tool_result/fallback.js, static/js/render/registry.js, static/js/render/registry.test.js
Fallback renderers now show "Unknown tool: " / "Unknown tool result: " plus truncated JSON payloads; registry fallback lookup rewritten as an if block; tests updated accordingly.
Manifest consistency tests
tests/test_tool_dispatch_sync.py
New tests verify the committed manifest matches KNOWN_TOOL_TYPES and that regeneration produces identical committed JSON; existing registry test gains a docstring.

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant app.js
  participant tool_types_manifest.js
  participant Server
  participant TOOL_USE_RENDERERS

  Browser->>app.js: DOMContentLoaded
  app.js->>tool_types_manifest.js: initToolTypesManifest()
  tool_types_manifest.js->>Server: fetch /static/tool_types.json (5s timeout)
  Server-->>tool_types_manifest.js: { tool_types: [...] }
  tool_types_manifest.js->>TOOL_USE_RENDERERS: compare backend vs frontend types
  alt mismatch found
    tool_types_manifest.js-->>Browser: console.warn(drift message)
  else fetch fails or times out
    tool_types_manifest.js-->>Browser: console.warn(load/timeout error)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • timon0305
  • wpak-ai

A rabbit hops to check the list,
backend and frontend now can't miss,
manifest fresh, fallback bright,
warnings whisper if types don't fit right,
🥕 hop, sync, and ship tonight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.05% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: linking the frontend tool registry to the backend dispatch table via a generated manifest.
Linked Issues check ✅ Passed The PR matches the issue goals with a generated manifest, startup drift checks, warning logs, improved fallbacks, and sync-test coverage.
Out of Scope Changes check ✅ Passed The changes appear scoped to the manifest workflow, renderer drift checks, fallback improvements, tests, and supporting docs/build updates.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/frontend-tool-types-manifest

Comment @coderabbitai help to get the list of available commands.

@clean6378-max-it clean6378-max-it self-assigned this Jun 30, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
Makefile (1)

1-16: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add the new target to .PHONY.

gen-tool-types-manifest isn't a file-producing target by name, so omitting it from .PHONY is low-risk today, but it's inconsistent with the existing convention (line 1 lists every other target) and could bite if a same-named file ever appears in the repo root.

🔧 Proposed fix
-.PHONY: seed-baselines-local update-baselines check-benchmarks clean-benchmark-artifacts
+.PHONY: seed-baselines-local update-baselines check-benchmarks clean-benchmark-artifacts gen-tool-types-manifest
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Makefile` around lines 1 - 16, The Makefile target gen-tool-types-manifest is
missing from the .PHONY declaration, which is inconsistent with the other
non-file targets. Update the .PHONY list at the top of the Makefile to include
gen-tool-types-manifest so make always runs the gen-tool-types-manifest rule
even if a same-named file appears.
tests/test_tool_dispatch_sync.py (1)

88-97: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Non-string manifest entries are silently coerced rather than rejected.

frozenset(str(t) for t in raw) stringifies every entry instead of validating that all entries are strings. The frontend's tool_types_manifest.js filters non-string entries out entirely (types.filter((t) => typeof t === 'string')), so a malformed manifest (e.g. containing a number) would pass this backend test (coerced to a numeric string) while being silently dropped on the frontend — masking a real generator bug.

🔧 Proposed fix
 def _load_manifest_tool_types(path: Path) -> frozenset[str]:
     if not path.is_file():
         msg = f"Missing manifest: {path} (run python scripts/gen_tool_types_manifest.py)"
         raise ValueError(msg)
     data = json.loads(path.read_text(encoding="utf-8"))
     raw = data.get("tool_types")
     if not isinstance(raw, list):
         msg = f"Invalid tool_types in {path}: expected a JSON array"
         raise ValueError(msg)
-    return frozenset(str(t) for t in raw)
+    if not all(isinstance(t, str) for t in raw):
+        msg = f"Invalid tool_types in {path}: expected all entries to be strings"
+        raise ValueError(msg)
+    return frozenset(raw)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_tool_dispatch_sync.py` around lines 88 - 97, The manifest parser
in _load_manifest_tool_types is coercing every entry with str(t) instead of
validating types, so malformed non-string entries can slip through. Update this
helper to reject any non-string item in raw by checking each element before
building the frozenset, and raise a ValueError that clearly points to the
invalid tool_types content in the manifest. Keep the same validation flow used
by the existing JSON array check, but make the final return only include entries
that were already confirmed as strings.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@static/js/app.js`:
- Around line 52-56: The DOMContentLoaded bootstrap in app.js is blocking
initial render by awaiting initToolTypesManifest(), even though this diagnostic
load does not gate route handling or UI correctness. Update the bootstrap flow
to call initToolTypesManifest() fire-and-forget instead of awaiting it, keeping
applyTheme() and the footer year update synchronous so the app can continue
booting immediately. Use the existing initToolTypesManifest() symbol as the
non-blocking background task and leave route handling off the critical path.

In `@static/js/render/tool_types_manifest.js`:
- Around line 10-20: The manifest load in initToolTypesManifest can hang
indefinitely because fetch(MANIFEST_URL) has no timeout, which blocks app.js
startup before handleRoute() runs. Add an AbortController-based timeout around
the fetch in initToolTypesManifest so the request is canceled after a bounded
wait, and handle the abort case with a warning similar to the existing HTTP
failure path. Keep the existing manifest parsing and setManifestToolTypes flow
unchanged when the request succeeds.

---

Nitpick comments:
In `@Makefile`:
- Around line 1-16: The Makefile target gen-tool-types-manifest is missing from
the .PHONY declaration, which is inconsistent with the other non-file targets.
Update the .PHONY list at the top of the Makefile to include
gen-tool-types-manifest so make always runs the gen-tool-types-manifest rule
even if a same-named file appears.

In `@tests/test_tool_dispatch_sync.py`:
- Around line 88-97: The manifest parser in _load_manifest_tool_types is
coercing every entry with str(t) instead of validating types, so malformed
non-string entries can slip through. Update this helper to reject any non-string
item in raw by checking each element before building the frozenset, and raise a
ValueError that clearly points to the invalid tool_types content in the
manifest. Keep the same validation flow used by the existing JSON array check,
but make the final return only include entries that were already confirmed as
strings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 94c23888-412a-4ef5-bcfa-0521c644e25a

📥 Commits

Reviewing files that changed from the base of the PR and between 7f685e4 and 6c450fd.

📒 Files selected for processing (15)
  • CONTRIBUTING.md
  • Makefile
  • docs/architecture.md
  • scripts/gen_tool_types_manifest.py
  • static/js/app.js
  • static/js/app.test.js
  • static/js/render/registry.js
  • static/js/render/registry.test.js
  • static/js/render/tool_result/fallback.js
  • static/js/render/tool_types_manifest.js
  • static/js/render/tool_types_manifest.test.js
  • static/js/render/tool_types_state.js
  • static/js/render/tool_use/fallback.js
  • static/tool_types.json
  • tests/test_tool_dispatch_sync.py

Comment thread static/js/app.js Outdated
Comment thread static/js/render/tool_types_manifest.js Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
static/js/render/tool_types_manifest.test.js (1)

50-73: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Fake timers leak on assertion failure.

vi.useRealTimers() runs only after the expect() calls. If either assertion throws, fake timers stay active and bleed into subsequent tests in the file, causing hard-to-diagnose flaky failures elsewhere in the suite.

♻️ Proposed fix using try/finally
     it('warns when fetch times out', async () => {
         vi.useFakeTimers();
-        const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
-        vi.stubGlobal(
-            'fetch',
-            vi.fn((_url, init) =>
-                new Promise((_resolve, reject) => {
-                    init?.signal?.addEventListener('abort', () => {
-                        reject(Object.assign(new Error('aborted'), { name: 'AbortError' }));
-                    });
-                }),
-            ),
-        );
-
-        const promise = initToolTypesManifest();
-        await vi.advanceTimersByTimeAsync(5000);
-        await promise;
-
-        expect(getManifestToolTypes()).toBeNull();
-        expect(warn).toHaveBeenCalledWith(
-            '[tool registry] Could not load /static/tool_types.json: timed out after 5000ms',
-        );
-        vi.useRealTimers();
+        try {
+            const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
+            vi.stubGlobal(
+                'fetch',
+                vi.fn((_url, init) =>
+                    new Promise((_resolve, reject) => {
+                        init?.signal?.addEventListener('abort', () => {
+                            reject(Object.assign(new Error('aborted'), { name: 'AbortError' }));
+                        });
+                    }),
+                ),
+            );
+
+            const promise = initToolTypesManifest();
+            await vi.advanceTimersByTimeAsync(5000);
+            await promise;
+
+            expect(getManifestToolTypes()).toBeNull();
+            expect(warn).toHaveBeenCalledWith(
+                '[tool registry] Could not load /static/tool_types.json: timed out after 5000ms',
+            );
+        } finally {
+            vi.useRealTimers();
+        }
     });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@static/js/render/tool_types_manifest.test.js` around lines 50 - 73, The fake
timer setup in initToolTypesManifest's timeout test can leak if an assertion
fails because vi.useRealTimers() is only reached at the end of the test. Wrap
the timer-enabled test body in a try/finally so the cleanup always runs, and
keep the assertions around getManifestToolTypes() and the warn spy inside the
try block to ensure subsequent tests are never affected by leftover fake timers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@static/js/render/tool_types_manifest.test.js`:
- Around line 50-73: The fake timer setup in initToolTypesManifest's timeout
test can leak if an assertion fails because vi.useRealTimers() is only reached
at the end of the test. Wrap the timer-enabled test body in a try/finally so the
cleanup always runs, and keep the assertions around getManifestToolTypes() and
the warn spy inside the try block to ensure subsequent tests are never affected
by leftover fake timers.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 511d1910-d41a-4f63-b3f4-6b5858255c67

📥 Commits

Reviewing files that changed from the base of the PR and between 6c450fd and d6f4cfd.

📒 Files selected for processing (6)
  • Makefile
  • static/js/app.js
  • static/js/app.test.js
  • static/js/render/tool_types_manifest.js
  • static/js/render/tool_types_manifest.test.js
  • tests/test_tool_dispatch_sync.py
💤 Files with no reviewable changes (1)
  • static/js/app.test.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • Makefile
  • tests/test_tool_dispatch_sync.py

Wrap the initToolTypesManifest timeout test in try/finally so
vi.useRealTimers() runs even when assertions fail. Also ruff-format
test_tool_dispatch_sync.py manifest validation helper (CI gate).
Comment thread static/js/render/tool_types_state.js Outdated
Comment thread static/js/app.js
Comment thread tests/test_tool_dispatch_sync.py Outdated
@clean6378-max-it clean6378-max-it requested a review from wpak-ai June 30, 2026 23:18
@wpak-ai wpak-ai merged commit 554b2cf into master Jul 1, 2026
16 checks passed
@wpak-ai wpak-ai deleted the feat/frontend-tool-types-manifest branch July 1, 2026 00:34
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.

claude-code-chat-browser: Frontend tool registry — link to backend dispatch table via generated manifest

3 participants