Skip to content

feat(0.x): pluggable knowledge sources + freshness + change detection#7

Merged
drewstone merged 1 commit into
mainfrom
feat/pluggable-knowledge-sources
May 14, 2026
Merged

feat(0.x): pluggable knowledge sources + freshness + change detection#7
drewstone merged 1 commit into
mainfrom
feat/pluggable-knowledge-sources

Conversation

@tangletools
Copy link
Copy Markdown
Contributor

Why

Static knowledge bundles ship information rot — FTC non-compete rule vacated by Ryan LLC v. FTC (2024), CA Beverly-Killea → RULLCA, DTSA § 1836 confusion. Today every consumer of @tangle-network/agent-knowledge has to hand-roll its own ingestion against external authorities. This PR ships the primitives that close the loop: live authority → fragment snapshot → freshness state → change detection → eval re-runs tagged by the exact dimensions that moved.

Scope

KnowledgeSource contract (src/sources/types.ts)

```ts
interface KnowledgeSource {
id: string
name: string
description: string
fetch(opts: FetchOpts): Promise<KnowledgeFragment[]>
}
```

Every KnowledgeFragment carries: canonical URL, source-attested timestamp (Last-Modified → Date → fetch time), jurisdiction tag, verifiable flag (false on 4xx / captcha / parse miss — consumers MUST refuse to promote verifiable: false content), and dimensionHints (which eval dimensions a change here should re-score, e.g. jurisdictional_accuracy, citation_hygiene, tax_compliance).

Three shipped sources

Source Surfaces Default dimensions
createCornellLiiSource US Code text + Wex jurisdictional_accuracy, citation_hygiene
createIrsPublicationsSource publications index, pubs, rev-procs tax_compliance, regulatory_currency, citation_hygiene
createStateSosSource per-state config (URL pattern + selector) jurisdictional_accuracy, corporate_formation, citation_hygiene

KnowledgeFreshnessStore (src/freshness.ts)

last() / mark() / stale() / list(), keyed by (workspaceId, sourceId). Per-tenant isolation enforced by keying — no global mutable state. Filesystem adapter ships in-package; createD1FreshnessStoreStub(adapter) anchors the D1 / Postgres shape for production.

detectChanges(prev, next) (src/changes.ts)

Diffs two fragment snapshots. Each KnowledgeChange carries affectedDimensions — the eval scheduler reads this set to re-run only the campaigns tagged with those dimensions. Unverifiable fragments are filtered before diffing (otherwise a captcha snapshot vs a real one would falsely fire every fragment as removed).

Polite HTTP fetcher (src/sources/http.ts)

1 req/sec/host throttle, polite UA, disk cache keyed by URL hash, block-page heuristic on 200 responses, never throws on network/HTTP failure — returns verifiable: false with reason.

Worked example: Cornell LII → eval cron re-runs

  1. Cornell LII updates the Wex entry to reflect Ryan LLC v. FTC.
  2. createCornellLiiSource returns a KnowledgeFragment tagged dimensionHints: ['jurisdictional_accuracy'].
  3. detectChanges(prev, next) emits { kind: 'modified', fragmentId: 'wex:...', affectedDimensions: ['jurisdictional_accuracy'], diff: { before, after } }.
  4. Eval cron reads affectedDimensions, schedules ONLY the jurisdictional_accuracy campaigns to re-run. No global re-eval, no eval rot.

DX bar parity with agent-eval 0.24.0

  • biome.json + pnpm lint / pnpm format
  • noUncheckedIndexedAccess: true
  • @stable / @experimental JSDoc tags on every public re-export
  • .github/workflows/ci.yml runs lint + typecheck + test + build on every PR
  • pnpm.minimumReleaseAge: 4320 (supply-chain hardening)
  • README "Pluggable Knowledge Sources" worked example
  • AGENTS.md integration-boundary note

Tests (69 total)

Each test names the bug class it defends against.

  • tests/sources-types.test.ts (15) — factory id stability, htmlToText entity decoding, block-page heuristic
  • tests/http-cache.test.ts (11) — verifiable=true/false matrix, disk cache write/read, TTL expiry, host throttle ≥900ms gap, network-error path
  • tests/sources-mocked.test.ts (10) — Cornell / IRS / SOS parsers against HTML snippets that match live structure (verified 2026-05-14)
  • tests/freshness.test.ts (7) — TTL math, workspace isolation, concurrent writes serialise, D1 stub round-trip
  • tests/changes.test.ts (9) — added/removed/modified, dimension union dedup, filterDimensions narrowing, unverifiable-fragment drop, duplicate-id warning
  • tests/sources-live.test.ts (5) — real HTTP against Cornell LII (DTSA + Wex restraint_of_trade), IRS (index + p15), CA SOS, 30s timeout, skip-with-warning on rate-limit. Gated on AGENT_KNOWLEDGE_RUN_NETWORK_TESTS=1.

Test plan

  • pnpm typecheck clean
  • pnpm lint clean (0 errors)
  • pnpm test — 64 unit pass, 5 live skipped without env flag
  • AGENT_KNOWLEDGE_RUN_NETWORK_TESTS=1 pnpm test — 69/69 pass
  • pnpm build clean
  • Live HTTP verified against Cornell LII, IRS, CA SOS — all return verifiable: true

…tion

Static knowledge ships information rot — FTC non-compete rule vacated by Ryan
LLC v. FTC (2024), Beverly-Killea → RULLCA, DTSA section confusion. This adds
the primitives that bridge "live authority" to "eval re-runs":

- `KnowledgeSource` contract (`src/sources/types.ts`): pluggable `fetch(opts)
  → KnowledgeFragment[]`. Every fragment carries canonical URL, source-attested
  timestamp, jurisdiction tag, `verifiable` flag, and `dimensionHints`.
- Three shipped sources:
  - `cornell-lii.ts` — federal US Code + Wex from law.cornell.edu
  - `irs-publications.ts` — IRS index + named publications + revenue procedures
  - `state-sos.ts` — generic adapter (state config + URL pattern + selector)
- `politeFetch`: 1 req/sec/host throttle, polite UA, on-disk content cache,
  block-page heuristic (verifiable=false rather than throwing).
- `KnowledgeFreshnessStore`: per-(workspaceId, sourceId) last-refresh tracker,
  filesystem adapter + D1 adapter scaffold. Per-tenant isolation by keying.
- `detectChanges(prev, next)`: added/removed/modified diff, each change
  carries `affectedDimensions` so an eval scheduler knows which campaigns to
  re-run when an authority shifts.

DX bar = agent-eval 0.24.0:
- biome.json + `pnpm lint`/`pnpm format`
- `noUncheckedIndexedAccess: true`
- @stable/@experimental JSDoc tags on every public re-export
- README "Pluggable Knowledge Sources" worked example
- CI workflow (lint + typecheck + test + build)
- pnpm `minimumReleaseAge` block

Tests: 69 total, 64 pure-unit (vitest) + 5 live (gated on
AGENT_KNOWLEDGE_RUN_NETWORK_TESTS=1, skip with a warning rather than fail on
rate-limit). Each test names the bug class it defends against. Verified
against real Cornell LII (18 USC § 1836 + Wex restraint_of_trade), IRS
publications index + p15, and CA SOS forms page on 2026-05-14.
@drewstone drewstone merged commit 4daba13 into main May 14, 2026
1 check 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