feat: license facts drive the gate, providers emit / core completes, accuracy defaults - #27
Merged
Merged
Conversation
…s; indeterminate comparator
… relevance and limit
…c, Wikimedia, Rijksmuseum Met emits description from objectName/medium/culture/period and tags from classification + tags[].term; Artic requests short_description, medium_display and the three title vocabularies, emitting description, deduped tags and sourceScore from _score; Wikimedia Commons emits description from ImageDescription and tags from pipe-separated Categories; Rijksmuseum emits the EDM record's localized description. Adds a shared `plainText` helper to core's provider-helpers (strip markup, collapse whitespace, cap at 500 chars) so the HTML-stripping logic has one definition — wikimedia-commons' local stripTags is replaced by it. Also pins the previously unasserted tag mappings (freesound, nailbook, pixabay image + video, polyhaven) and the second-factory ids (pexels-video, pixabay-video, openverse-audio) with regression assertions.
…, deadlineMs, gateContext
…ver richer text, minRelevance - mergeReferences takes `weights` parallel to `perSource` and multiplies each RRF contribution by it (missing/invalid → 1; an all-zero pool scores 0, not NaN) - new confidence.ts: sourceConfidence = floor + (1 − floor)·hitRate over the batch's title/description/tags/excerpt, reported per provider as `meta.providers[].confidence`; `RefkitOptions.sourceConfidence` (default on) - lexicalReranker scores all four text fields, penalises same-source near-duplicate titles (sticky flag, so the greedy pass stays O(n²)) and can weight the source's own score (per-source min-max, default off) - `RefkitOptions.rerank` defaults to lexicalReranker(), `SearchInput.rerank` overrides per call, `false` on either level restores raw fusion - `SearchInput.minRelevance` + `meta.threshold` as a pipeline stage between rerank and gate; non-finite bars are ignored - runPass composed of uniform stages (confidence/merge/rerank/threshold); rights-conflict warnings share one prefix; the deadline handle is acquired inside the try that cancels it - MCP `rerank` is a boolean defaulting to true (false → raw fusion)
…is 0; no confidence for empty batches
… is 0; no confidence for empty batches
… polyhaven token matching
- `ReferenceProvider.accepts?({ text, modalities })` lets a source decline a query
it cannot answer; selectProviders consults it last (after modality/sources/kind)
and only on an unscoped search — naming a source in `sources` overrides the
source's own judgement
- `PROVIDER_SKIP_REASONS` gains 'declined' (MCP's output schema tracks it via
core's export); a declined provider reports `status: 'skipped', reason: 'declined'`
in meta.providers and raises no warning — routing, not failure
- the empty-selection throw now fires only when nothing declined: an all-declined
search runs a no-provider pass and returns an empty result set, while the
modality/sources/kind misses still fail loudly
- nailbook accepts only nail-vocabulary queries (/nail|manicure|ネイル|ジェル|美甲|指甲|甲油/i);
motif-only searches stay reachable via `sources: ['nailbook']`
- polyhaven's client-side filter matches each whitespace token independently over
id/name/categories/tags and ranks assets by matched-token count (stable sort),
instead of substring-matching the whole phrase
…eal query The nearDuplicateThreshold case used titles whose Jaccard was 0.6, so its expected order was identical at the default 0.7 and at 1 — the assertion proved nothing about the threshold. Replace the fixture with a 0.8 repeat plus a weaker-matching sibling and assert both orders: penalised at the default, spared at 1. The "lexical-only" case ran with fusionWeight live at 0.5 and passed only because the fixture's incoming relevance was 0; pass fusionWeight: 0 so the title is honest. Add a diversity case with the fusion term live (incoming relevance 1.0 for three refs of source a, 0.9 for one of source b, all lexical 1): under the shipped weights the 0.1 source penalty still outweighs the 0.05 fusion edge, so b surfaces second. Add a client case pinning that a provider's accepts() receives the caller's query text verbatim and then runs.
…ds once per asset The acceptance regex matched `nail` inside `snail`, so "snail shell macro" — exactly the generic image query nailbook has nothing for — was accepted. Use a lookbehind, and add the katakana spelling of manicure plus a word-bounded `gel` to the term list (amending the design spec's D8 list). polyhaven rebuilt the per-asset match fields once per query token inside the filter; hoist it into the map callback so it is computed once per asset.
search_references now exposes the three SearchInput surfaces added by the redesign: minRelevance (post-rerank relevance bar, reported as meta.threshold), deadlineMs (whole-call deadline, cursor advances included) and gateContext.userJurisdiction (forwarded to the gateFor gate). The search input object is typed as core's SearchInput, so every parameter is checked against the contract it is forwarded into and not just against zod. Each parameter's description states what the value means in practice: the threshold is graded against the reranker's blend (a result matching no query term lands near 0.3) and does not transfer to rerank: false, where max-normalised fusion puts the top result at exactly 1; the deadline is a hard bound while per-source resilience is on.
…and the accuracy defaults Every sample now matches the types on this branch. Root README: reranking and source-confidence weighting are documented as defaults (with the full LexicalRerankOptions table), minRelevance gets a calibration note against the reranker's blend, rights conflicts are described as facts-keyed with the actual warning wording, deadlineMs is bounded against per-source resilience, and a new "Source routing & acceptance" section covers accepts / reason: 'declined' / explicit sources bypassing it. Architecture and the invariants now state that providers emit EmittedReference while core stamps id, provenance, relevance and the limit, that facts drive every rights decision, and that rehostPolicy is host-facing metadata core never acts on. core README: the same, plus a worked provider-contract sample and a controls table that matches what the 23 provider ids actually declare. mcp README: the real zero-config source list and every search_references parameter. The semantic-rerank cookbook imports refText from core and replaces the default rather than adding an optional one. provider-roadmap: real package count. nailbook README: its query-acceptance behaviour. The changeset lists every published package with its removed and added surfaces; the breaking changes ship as minor under semver's pre-1.0 rule.
…urrogate-range entities
…ports; self-contained README provider sample
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
Summary
Redesign of the core contract and the default search path. The library has no external users, so removed surfaces are removed, not aliased.
LicenseIdis an open string; an id without aLICENSE_FACTSrow resolves tounknown, which grants nothing.RightsRecord.facts?lets a source declare terms narrower than its label. The gate, merge conflict resolution, attribution and the reranker readfactsOf(rights); the id only appears in reason text.CC0-1.0vsPDno longer reports a phantom conflict; the same id with narrower facts now does.stricterLicenseand the hand-writtenLICENSE_PERMISSIVENESStable are replaced bycompareRestrictiveness(factsA, factsB)and a derivedpermissivenessScore(facts)(obligation credit scaled by the grant fraction, so proprietary/unknown never outrank a real CC grant).ReferenceProvider.searchreturnsEmittedReference[]; core stampsid,source,canonicalUrl,verifiedAt,relevanceand applies the per-provider limit.okJson(res, label)replaces the per-provider status check. A defaultUser-Agentis injected (Art Institute of Chicago rejects Node's default).lexicalReranker()over title + description + tags + excerpt (CJK bigrams) with the fused relevance blended back atfusionWeight 0.5, a same-source near-duplicate-title penalty, and an opt-inminRelevancethreshold.description/tags(Art Institute alsosourceScore).ReferenceProvider.accepts?({ text, modalities })skips a provider withreason: 'declined'; nailbook declines non-nail queries; an explicitsourceslist bypasses acceptance; an all-declined search returns empty instead of throwing. polyhaven matches per token.SearchFilters,SearchInput.filters,NormalizedQuery.filters,SearchMeta.appliedFilters,QueryFeature,ReferenceProvider.queryFeatures, the legacy feature→control routing, the MCPfiltersparameter,stricterLicense,LICENSE_PERMISSIVENESS.CONTROL_PATHSregistry from which the key union, accessors and zod schema derive;searchMetaSchema/buildSearchControlsSchemaexported from core and consumed by@refkit/mcp.selectProviders,runPasswith named stages;SearchInput.deadlineMsandSearchInput.gateContext.userJurisdiction; cursor passes accumulatemeta.passes, summed latency, per-pass failure warnings and URL-deduped rights conflicts. Cache namespace bumped torefkit:v3.minRelevance,deadlineMs,gateContextsearch parameters;rerankdefaults to true.Design spec:
docs/superpowers/specs/2026-09-14-accuracy-and-contract-redesign-design.md.Migration notes for provider authors
Return
EmittedReference[]: dropid,source,verifiedAt,relevanceand any.slice(0, limit);canonicalUrldefaults tosourceUrl. UseokJson. ReplacequeryFeatureswithcapabilities.controls. Populatedescription/tagswhere the source has them.licenseVersionis only valid on the six versioned CC families.Opting out of the accuracy defaults
createRefkit({ sourceConfidence: false }),rerank: false(client or per call),lexicalReranker({ fusionWeight: 0 })for pure lexical order.minRelevanceis off by default and is calibrated against the blended score; it does not transfer torerank: false.Verification
pnpm typecheck,pnpm lint,pnpm test:run: 509 passed, 23 skipped (live smokes).pnpm test:liverun with keys.Follow-ups (out of scope)
MCP
agentRefstill omitsdescription/tags; Wikimedia housekeeping categories add reranker noise; no test combinesdeadlineMswith a signal-ignoring provider.