PRO-1298: migrate plugin onto @hydradb/sdk (bundled wrapper) + fix DX-G-002 and delete silent no-op - #9
Merged
Conversation
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PRO-1298 — Claude Code plugin (highest blast radius)
Migrates the plugin's hand-rolled v1 HTTP client (
scripts/lib/hydra-client.mjs) onto the generated@hydradb/sdkbehind a thin canonical wrapper, fixes the never-worked knowledge sync (DX-G-002) plus a second silent delete bug, and aligns user-facing names to the shared contract — without breaking installed copies.The runtime constraint that shaped the approach
The plugin ships via a public marketplace =
git clone, nonpm install;node_modules/is gitignored and hooks run on barenode. A bareimport "@hydradb/sdk"throwsERR_MODULE_NOT_FOUNDat load, before any guard — crashing every hook (reproduced). So the SDK is bundled, not depended on at runtime.Vendored SDK (Option A: bundle)
scripts/build-vendor.mjsesbuilds the exact-pinned@hydradb/sdk@2.1.2(+form-data,node-fetch) into one committed self-contained ESM file,scripts/vendor/hydradb-sdk.mjs, imported by relative path → runtime stays zero-dependency (esbuildis a devDependency only).package-lock.jsonpins transitive versions for reproducible bundles.check.mjsgates: (1) a freshbuild:vendormust be byte-identical to the committed bundle (drift guard — an SDK bump without a re-vendor fails CI); (2) the bundle loads and constructs on barenodefrom a dir with nonode_modules(the exact marketplace runtime). Bare-node gate: green.Canonical wrapper (
scripts/lib/hydra/, CONTRACT §2)Maps the canonical surface onto the vendored SDK, unwraps
HandlerEnvelopeby shape, translates SDK errors to plainErrors, caps retries at 0 (under the 15s/20s hook budgets), exposestenantId/subTenantIdas properties.HydraClientis now a thin adapter that keeps its method names + normalized output shape + null-when-unconfigured, soplugin.mjs/workspace-sync.mjsare otherwise unchanged and unconfigured installs stay inert.DX-G-002 — three stacked defects, all fixed
Knowledge ingest now routes through the SDK's multipart
context.ingest:application/json),tenant_id,app_knowledge(a JSON string) carrying each item's client-assignedid(claude-file:<sha1>) verbatim — neverapp_knowledge-as-JSON-body, never the v1-onlyapp_sources.Second silent bug — delete no-op
DELETE /contextanswers a zero-match delete with200 {success:false, deleted_count:0}; the caller counted that as success and dropped tracked state. The wrapper now inspects the body (success/deleted_count/user_memory_deleted) for both kinds and raises;workspace-syncsurfaces it intosummary.errorsand retains state for retry. Ingest and delete send the same collection so scopes match (server filters delete on database+collection).Preserved behaviour (all load-bearing)
Redaction on both legs;
Promise.allSettledsilent-failure recall;client=null+ guarded SDK construction when unconfigured; retry wall-time under hook budgets; dual read/write timeouts; the 340-line normalizer (kept verbatim — it now snake-cases input keys once at entry, since the SDK deserializes v2 responses to camelCase, so recall still surfaces chunks); the persistedstate.jsonshape; and the--jsonoutput shapes (golden-filed).Names (CONTRACT §3)
Canonical
/hydradb:query|ingest|doctor(slash + CLI + skills); every prior name (search,recall,status,remember,save-session,sync-workspace,reindex) keeps working with one stderr deprecation warning (stderr, so--jsonstdout is untouched). Env reads add theHYDRA_DB_*aliases (canonicalHYDRADB_*, one warning);HYDRA_OPENCLAW_*is not read (per-client scoping, CONTRACT §1).Tests (the repo had ZERO real HTTP-level coverage)
fetch): assert knowledge ingest is multipart + top-leveltenant_id+app_knowledge(notapplication/json, notapp_sources) with the client id preserved; delete-by-kind routing; the zero-match "deleted nothing" surfacing; and a recall round-trip through the camelCase SDK response.conformance/vectors.jsonthrough the wrapper against the real bundled SDK with a capturingfetch(16/16).--jsonshape snapshots forquery/doctor/last-recalllock the marketplace-parsed shapes.npm run checkandnpm pack --dry-run: green.Notes / follow-ups
setupskill env examples still teach the (working, deprecated)HYDRADB_TENANT_IDspelling — left to the Phase 3 docs pass per the plan.CONTRACT.md+conformance/vectors.jsonare the centrally-maintained masters, committed verbatim.🤖 Generated with Claude Code