Skip to content

Architecture hardening: conservative rights merge, unified pagination cursor, CJK ranking, bounded fan-out, leaner MCP#17

Merged
MyPrototypeWhat merged 5 commits into
mainfrom
claude/architecture-review-lqvmac
Jul 14, 2026
Merged

Architecture hardening: conservative rights merge, unified pagination cursor, CJK ranking, bounded fan-out, leaner MCP#17
MyPrototypeWhat merged 5 commits into
mainfrom
claude/architecture-review-lqvmac

Conversation

@MyPrototypeWhat

Copy link
Copy Markdown
Collaborator

Implements the findings of an architecture review of core/providers/mcp, then a follow-up code review of the branch itself (10 findings, all fixed, the 3 release-blocking ones verified A/B against the pre-fix commit).

Core (@refkit/core, minor)

  • Conservative cross-source rights merge — when two sources disagree about the license of the same canonical URL, the stricter license wins; incomparable claims collapse to unknown (→ needs-review). Conflicts are reported once per URL via meta.warnings and the new merge.onRightsConflict observer (RightsConflict, stricterLicense exported). Previously whichever provider was registered first silently won — fail-open by accident.
  • Unified pagination cursorSearchInput.cursor + meta.nextCursor: drains the current provider page's overfetched pool before advancing the provider-local page internally (up to 3 pages per call), dedupes against everything already returned (seen-set capped at 500), and validates cursors with zod. Verified by runtime repro: pre-fix load-more returned 20/60 results and terminated early on a duplicate page; now 60/60 with page-3 reachability.
  • Collision-proof cache keys — short fixed-shape hashed keys (safe for strict KV backends: no raw query chars, bounded length) with the full normalized-query fingerprint embedded in the cached value and verified on read, so a key collision degrades to a miss (previously a bare 32-bit hash could silently serve another query's results). Key prefix bumped to refkit:v2:.
  • Single-track capability routingqueryFeatures / filters deprecated; routing driven by capabilities.controls, with the deprecated NormalizedQuery.filters channel derived from routed controls so both channels always agree. Capabilities-less providers declaring legacy queryFeatures keep receiving their filters (loud deprecation, not silent degradation).
  • CJK-aware tokenize — CJK runs tokenize into character bigrams so lexicalReranker scores Chinese/Japanese/Korean queries instead of dropping them.
  • Bounded fan-out — new concurrency option (default unlimited); a queued provider's timeout starts only when its slot starts.
  • cacheRaw: false option; runProviderSearch/providerCacheKey/stableStringify/offsetForPage extracted and exported; createRefkit rejects non-array providers (un-awaited async factory) at construction.

MCP (@refkit/mcp, minor)

  • search_references gains rerank: true (CJK-aware lexical reranking) and cursor; the continuation token is returned as top-level nextCursor on every call, independent of explain.
  • BYOK providers moved to optionalDependencies, loaded lazily and concurrently only when their key is set; load failures are classified (ERR_MODULE_NOT_FOUND → "not installed" hint, anything else surfaces the real error). defaultProviders() is now async.
  • tsup explicitly externalizes @refkit/* — pre-fix builds bundled all 9 optional providers into dist/cli.js (220K, --omit=optional ineffective, provider patches snapshotted at publish time); verified post-fix dist resolves them from node_modules (80K, real package imports) and the built cli answers the MCP initialize handshake.

Providers (13 × patch)

  • controls.page wired through shared validated helpers (setIfPositiveInt / offsetForPage): native page params (openverse, pixabay, unsplash, flickr, freesound, gutendex, pexels, artic, internet-archive), offset translation (wikimedia gsroffset, smithsonian/europeana start, jamendo/ambientCG offset), windowed slices (met, polyhaven). Brave/PoetryDB/Rijksmuseum expose no usable offset pagination and keep page undeclared.
  • internet-archive escapes Lucene syntax in the user query (unescaped ) broke the expression; ) OR (mediatype:... could escape the movies/texts scope).
  • Dead useLegacyFilter fallbacks removed from pexels/pixabay/unsplash.

Tooling

  • eslint (typescript-eslint syntactic recommended) + pnpm lint + CI lint step; 4 existing violations fixed.

Verification

pnpm typecheck (0 errors) · pnpm lint · pnpm test:run (357 passed, live suites skipped) · pnpm build · A/B repros for the three release-blocking findings against pre-fix commit a88f105 · built MCP cli boot + initialize handshake with a BYOK key set. Changesets included (core/mcp minor, 13 provider patches).

🤖 Generated with Claude Code

https://claude.ai/code/session_018hoN6wKWxcs8WbUNCM9rtt


Generated by Claude Code

claude added 5 commits July 14, 2026 08:03
- merge: resolve cross-source license conflicts conservatively (stricter
  license wins; incomparable claims collapse to unknown), surfaced in
  meta.warnings and via merge.onRightsConflict
- client: unified load-more cursor (SearchInput.cursor + meta.nextCursor)
  with cross-page dedup; cache keys embed the full normalized query
  (no more 32-bit hash collisions); new cacheRaw option; runProvider
  extracted to provider-run.ts
- query: single-track capability routing — queryFeatures/filters
  deprecated, capabilities.controls is the only routing source, and the
  legacy NormalizedQuery.filters channel is derived from routed controls
- rerank: CJK-aware tokenize (character bigrams) so CJK queries score
- mcp: BYOK providers become lazily-loaded optionalDependencies;
  search_references gains rerank + cursor
- providers: wire controls.page in openverse, pixabay, internet-archive,
  wikimedia-commons (gsroffset), artic

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hoN6wKWxcs8WbUNCM9rtt
- providers: wire controls.page across every source with usable
  pagination (unsplash/flickr/freesound native page; smithsonian/
  europeana start; jamendo/ambientcg offset; met/polyhaven windowed
  slice) — the unified cursor now advances every paginatable source
- core: RefkitOptions.concurrency bounds in-flight provider searches
  (default unlimited; queued providers keep their full timeout)
- tooling: eslint (typescript-eslint syntactic recommended) + pnpm lint
  + CI lint step; fix the four existing violations

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hoN6wKWxcs8WbUNCM9rtt
- mcp/tsup: externalize @refkit/* so optionalDependencies providers are
  imported from node_modules at runtime instead of being bundled into
  dist (--omit=optional now works; provider patches reach users)
- cursor: drain the overfetched pool of the current provider page before
  advancing (no more skipped ranked results); advance internally up to 3
  pages when a page is fully seen/gated; cap the seen set; validate the
  cursor with zod; bind fixes with regression tests
- mcp: return nextCursor unconditionally at the top level of
  search_references output — pagination no longer requires explain
- mcp/cli: classify BYOK load failures (ERR_MODULE_NOT_FOUND vs real
  errors, real error surfaced) and load sources concurrently
- merge: report each cross-source rights conflict once per URL with only
  source-declared licenses (no phantom 'unknown' claims, no duplicates);
  RightsConflict.licenses is now LicenseId[]
- cache: fixed-shape hashed keys (strict-KV-safe) + query fingerprint
  verified on read, so a key collision degrades to a miss
- createRefkit: reject non-array providers (un-awaited async factory)
  with a clear error at construction time
- query: capabilities-less providers declaring legacy queryFeatures keep
  receiving their filters (loud deprecation, not silent degradation)
- internet-archive: escape Lucene syntax in the user query
- provider-helpers: shared offsetForPage; providers use validated
  setIfPositiveInt/offsetForPage for all page wiring; drop the
  dead useLegacyFilter fallbacks

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hoN6wKWxcs8WbUNCM9rtt
Verified the three release-blocking findings A/B against the pre-fix
commit (a88f105) with runtime repros: overfetch gap (20/60 vs 60/60
results returned), false exhaustion (page-3 unreachable vs reached),
and optional-provider bundling (9 bundled chunks/220K vs 0/80K with
imports resolving from node_modules); built cli answers the MCP
initialize handshake with a BYOK key set. This adds the missing unit
test for top-level nextCursor without explain + cursor round-trip.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hoN6wKWxcs8WbUNCM9rtt
- scripts/smoke-artifact.mjs: pack every package (publishConfig applied,
  like a real publish), install the tarballs into a clean project, and
  boot the installed @refkit/mcp cli through three scenarios — keyless
  handshake, BYOK dynamic import from node_modules, and graceful skip
  when the optional package is removed. Guards the layer source suites
  cannot see (a bundler regression re-inlining optionalDependencies
  fails scenario 3). Wired into CI after build as `pnpm smoke:artifact`.
- mcp: BYOK_SOURCES exported + test pinning it 1:1 to package.json
  optionalDependencies (and out of hard dependencies)
- core: offsetForPage unit tests (offsets, 1-based origin, rejection of
  page 1 / non-integer / negative input)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hoN6wKWxcs8WbUNCM9rtt
@MyPrototypeWhat MyPrototypeWhat merged commit c45a121 into main Jul 14, 2026
1 of 2 checks passed
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.

2 participants