@delfini/drift-engine
v0.3.0
Published
Pure-logic drift analysis core shared by @delfini/action and @delfini/cli. No I/O, no LLM client, no credentials, no fetch. Runtime deps: zod + picomatch (both pure CPU). Public API: buildPrompt, validateAndReconcile, estimatePromptTokens, analysisSchema,
Readme
@delfini/drift-engine
The pure-logic analysis core behind Delfini — the tool that detects when a code change has made your documentation wrong and proposes the fix.
This package is the brain, with none of the plumbing. Given a diff and a set of documents, it builds the analysis prompt, defines the schema the model must answer in, and reconciles the model's output back to exact line numbers. It is shared by both Delfini surfaces — the Skill (local, @delfini/cli) and the Action (CI) — so the analysis is identical wherever it runs. A finding surfaced locally is the same finding the Action would surface on the eventual PR.
What it does
A drift analysis is three pure steps, and this package owns all three:
buildPrompt— assemble the LLM prompt from a diff, the in-scope docs, and PR metadata. Every doc line is prefixed with its number so the model can cite exact ranges.analysisSchema— the schema the model's JSON output must satisfy: structured findings of three kinds —drift(replace these lines),additive(insert this content), andclarification(uncertain — surface for a human).validateAndReconcile— validate the model's JSON and verify each finding's quoted text actually matches the doc at the cited lines. Mismatches (model hallucinations) are discarded before they reach the caller.
Install
npm install @delfini/drift-enginePublic API
import {
buildPrompt, // (input, template, options?) => string
validateAndReconcile, // (rawJson, docs) => AnalysisResult
estimatePromptTokens, // (prompt) => number
analysisSchema, // Zod schema for the model's output
// doc-scope matching:
normalizeDocScope,
validateDocScopeEntry,
classifyEntry,
isFileInDocScope,
// types:
type AnalysisInput,
type AnalysisResult,
type DocFile,
type Contradiction,
type Addition,
} from '@delfini/drift-engine'Both the Action and the CLI follow the same flow: gather inputs (diff + docs + PR metadata) → buildPrompt → send to an LLM → validateAndReconcile on the JSON → render the result. Internal helpers are not re-exported; callers compose only through the surface above.
Relevance retrieval (optional)
buildPrompt(input, template, options?) accepts an optional third argument to keep large prompts focused:
buildPrompt(input, template, { relevanceThreshold: 5 })Each doc section is scored against the diff and sections below the threshold are dropped before rendering:
| Signal | Points | |---|---| | The doc itself appears in the diff | +20 | | A code-file path from the diff appears in the section | +10 per file | | An identifier from the diff appears in the section | +3 each, capped at +30 | | A heading overlaps a diff identifier | +5 per heading |
A threshold of 5 keeps any section with a single file-path or heading match — a safe default that typically cuts prompt size ~40% on doc-heavy inputs with no measurable recall loss. Omit options (or pass 0) to keep every section.
Runtime constraints
The package is intentionally pure so it can run unchanged in CI and on a developer's laptop:
- No I/O — never reads files, never touches the network.
- No LLM client — never imports an Anthropic, OpenAI, or LangChain SDK. It builds the prompt and validates the response; calling the model is the caller's job.
- No environment reads — pure functions of explicit arguments. Same input → byte-identical output, every time.
Runtime dependencies are exactly two, both pure CPU: zod (schema validation) and picomatch (glob matching).
