Skip to content

fix(web): align Kit routes with frontend transport contract#84

Merged
RealZST merged 1 commit into
mainfrom
fix/web-kit-routes
Jun 16, 2026
Merged

fix(web): align Kit routes with frontend transport contract#84
RealZST merged 1 commit into
mainfrom
fix/web-kit-routes

Conversation

@RealZST

@RealZST RealZST commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Problem

In web mode (hk serve β†’ browser), the Kits page showed empty kit lists and the new-kit dialog couldn't discover any existing skills/MCPs β€” while the desktop app worked fine on the same machine / same database.

Root cause

The frontend transport posts every command as POST /api/{snake_case_command} (src/lib/transport.ts), and all other features' routes follow that contract. The Kits block alone was registered REST-style:

Frontend sends Old web route Match
POST /api/list_kit_asset_candidates GET /api/kits/candidates ❌
POST /api/list_kits GET /api/kits ❌
POST /api/create_kit POST /api/kits ❌
…all 12 kit endpoints REST-style paths ❌

None matched, so every kit request fell through to the SPA fallback(serve_frontend) and returned 200 + text/html (index.html). The frontend then called response.json() on HTML β†’ threw β†’ .catch(console.error) swallowed it β†’ lists stayed empty, with no visible error. Desktop was unaffected because Tauri invoke dispatches by command name directly.

Verified empirically (oneshot against the real router): POST /api/list_kit_asset_candidates β†’ 200 text/html; GET /api/kits/candidates β†’ 200 application/json.

It went unnoticed because: zero router-level test coverage for kits, the failure was silent (200 + swallowed parse error), and the embedded dist/ was stale.

Fix

  • Align all 12 kit routes to POST /api/{command}, matching the frontend transport and the desktop Tauri command names.
  • Fix handler body/response shapes to match what the frontend actually sends:
    • unwrap { req } payloads (create/update/preview/sync/unsync),
    • read { id } from the JSON body instead of path params (get_details, delete_kit),
    • return JSON () instead of empty 204 bodies (which break the frontend's unconditional response.json()).
  • Add a regression test (kit_command_routes_return_json_not_spa_html) asserting kit commands reach a JSON handler, not the SPA HTML fallback.

Testing

  • cargo test --workspace β€” all green (hk-core 519, hk-web 8, …), including the new regression test.
  • npm run test β€” 231 passed.
  • biome check clean; no new clippy warnings from the change.
  • Manually verified in dev mode (npm run dev @ 1420): existing kits appear and skills are discoverable in the new-kit dialog.

πŸ€– Generated with Claude Code

Kit routes were registered REST-style (e.g. GET /api/kits/candidates) while
the frontend transport posts flat command names (POST /api/list_kit_asset_candidates).
Every Kit endpoint therefore fell through to the SPA fallback and returned
200 text/html, which the frontend failed to parse as JSON β€” so web mode
silently showed empty kit lists and no skill candidates while desktop worked.

Align all 12 kit routes to POST /api/{command}, and fix the handler body/
response shapes to match the frontend: unwrap { req } payloads, read { id }
from the body instead of path params, and return JSON () instead of empty
204 bodies (which would break the frontend's unconditional response.json()).
Add a regression test asserting kit commands return JSON, not the SPA fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@RealZST RealZST merged commit fc77deb into main Jun 16, 2026
3 checks passed
@RealZST RealZST deleted the fix/web-kit-routes branch June 16, 2026 18:35
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