Skip to main content
Glama

detect_drift

Compares current workflow fingerprints to prior runs, detecting silent rewrites, surface changes, and oscillating refactors to catch behavioral regressions after PR merges.

Instructions

Compares the current workflow fingerprints (SHA-256 hash of sorted members + edges + signals per workflow) against fingerprints from prior Veris runs persisted in .veris/state.db. Surfaces three classes of drift: silent rewrites (member set identical but internal topology changed — most dangerous because nobody's watching), surface expansion/contraction (members added/removed), and oscillating refactors (same workflow flips back and forth across runs — usually a sign of unresolved indecision). Run after every PR merge to catch behavioral regressions before users do.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • Handler function that executes the detect_drift tool. It computes workflow fingerprints via WorkflowFingerprintEngine, then passes them to DriftDetector.detect() which compares against historical fingerprints in VerisState. Returns a DriftReport with summary and per-workflow drift details.
    private handleDetectDrift() {
        const wf = this.ensureWorkflows();
        const graph = this.ensureGraph();
        const fps = new WorkflowFingerprintEngine().fingerprintAll(wf.workflows, graph);
        this.lastFingerprints = fps;
        if (!this.lastRunId) this.lastRunId = this.state.newRunId();
        const drift = new DriftDetector().detect(this.lastRunId, fps, this.state);
        return this.text(drift);
    }
  • Registration of the detect_drift tool in the CallToolRequestSchema switch-case, routing to handleDetectDrift().
    case "detect_drift": return this.handleDetectDrift();
    case "generate_adversarial_probes": return this.handleGenerateProbes();
  • Tool definition with name, description, and input schema for detect_drift. No input parameters required.
    { name: "detect_drift",
      description: "Compares the current workflow fingerprints (SHA-256 hash of sorted members + edges + signals per workflow) against fingerprints from prior Veris runs persisted in .veris/state.db. Surfaces three classes of drift: silent rewrites (member set identical but internal topology changed — most dangerous because nobody's watching), surface expansion/contraction (members added/removed), and oscillating refactors (same workflow flips back and forth across runs — usually a sign of unresolved indecision). Run after every PR merge to catch behavioral regressions before users do.",
      inputSchema: { type: "object", properties: {}, required: [] } },
  • WorkflowFingerprintEngine computes a SHA-256 fingerprint for each workflow based on sorted member node IDs and internal edge signatures. Used by handleDetectDrift to generate current fingerprints.
    export class WorkflowFingerprintEngine {
        public fingerprint(domain: WorkflowDomain, graph: BehavioralGraph): WorkflowFingerprint {
            const memberSet = new Set(domain.memberNodeIds);
            const members = [...domain.memberNodeIds].sort();
            const internalEdges = graph.getEdges()
                .filter(e => memberSet.has(e.sourceId) && memberSet.has(e.targetId))
                .map(e => `${e.sourceId}>${e.targetId}:${e.type}`)
                .sort();
    
            const h = crypto.createHash('sha256');
            h.update(domain.kind);
            for (const id of members) h.update('\n' + id);
            h.update('\n---\n');
            for (const sig of internalEdges) h.update('\n' + sig);
    
            return {
                workflowId: domain.id,
                workflowName: domain.name,
                fingerprint: h.digest('hex'),
                memberCount: members.length
            };
        }
    
        public fingerprintAll(domains: WorkflowDomain[], graph: BehavioralGraph): WorkflowFingerprint[] {
            return domains.map(d => this.fingerprint(d, graph));
        }
    }
  • DriftDetector compares current workflow fingerprints against historical fingerprints from VerisState to detect silent rewrites, surface expansion/contraction, and oscillation. Produces a DriftReport with per-workflow narratives and a summary.
    export class DriftDetector {
        public detect(
            runId: string,
            current: WorkflowFingerprint[],
            state: VerisState | null
        ): DriftReport {
            const out: WorkflowDriftReport[] = [];
            let driftedCount = 0;
            let silentRewriteCount = 0;
    
            for (const cur of current) {
                const history: FingerprintRecord[] = state && state.enabled
                    ? state.fingerprintHistory(cur.workflowId, 20)
                    : [];
                // history is newest-first per VerisState
                const previous = history[0] || null;
                const previousFp = previous ? previous.fingerprint : null;
                const distinct = new Set(history.map(h => h.fingerprint));
                distinct.add(cur.fingerprint);
                const changed = previousFp !== null && previousFp !== cur.fingerprint;
                const memberChange = previous ? cur.memberCount - previous.memberCount : 0;
                const oscillation = history.length >= 3 &&
                    history[0].fingerprint !== history[1].fingerprint &&
                    history[1].fingerprint !== history[2].fingerprint &&
                    history[0].fingerprint === history[2].fingerprint;
    
                let narrative: string;
                if (!previousFp) {
                    narrative = `${cur.workflowName}: first observation. ${cur.memberCount} members.`;
                } else if (!changed) {
                    narrative = `${cur.workflowName}: stable since last run (${cur.memberCount} members, fp unchanged).`;
                } else if (memberChange === 0) {
                    silentRewriteCount++;
                    narrative = `${cur.workflowName}: silent rewrite — same members, different internal topology. Inspect for unannounced refactors.`;
                } else if (memberChange > 0) {
                    narrative = `${cur.workflowName}: surface expanded by ${memberChange} member${memberChange === 1 ? '' : 's'} (now ${cur.memberCount}). Verify scope creep.`;
                } else {
                    narrative = `${cur.workflowName}: surface contracted by ${Math.abs(memberChange)} member${memberChange === -1 ? '' : 's'} (now ${cur.memberCount}). Verify regression / extraction is intentional.`;
                }
                if (oscillation) {
                    narrative += ` Oscillating fingerprint across last runs — likely refactor instability.`;
                }
                if (changed) driftedCount++;
    
                out.push({
                    workflowId: cur.workflowId,
                    workflowName: cur.workflowName,
                    currentFingerprint: cur.fingerprint,
                    previousFingerprint: previousFp,
                    changedSinceLastRun: changed,
                    distinctFingerprintsObserved: distinct.size,
                    memberCountTrend: [cur.memberCount, ...history.map(h => h.memberCount).slice(0, 9)],
                    oscillationDetected: oscillation,
                    memberChange,
                    narrative
                });
            }
    
            const summary = driftedCount === 0
                ? 'No workflow drift detected vs prior runs.'
                : `${driftedCount} workflow${driftedCount === 1 ? '' : 's'} drifted since last run` +
                  (silentRewriteCount > 0 ? `; ${silentRewriteCount} appear to be silent rewrites.` : '.');
            return { runId, workflows: out, summary };
        }
    }
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. It explains the comparison mechanism (SHA-256 hashes), three drift classes, and warns about dangerous silent rewrites, providing valuable behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, each adding value: mechanism, drift classes, usage recommendation. Slightly verbose but well-structured and front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given zero parameters and no output schema, description fully explains what the tool does, how it works, and when to use it, making it complete for this tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Input schema has zero parameters, so baseline is 4. Description adds no parameter info, which is appropriate as none exist.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states it compares current workflow fingerprints against prior runs to detect drift, using specific verb 'compares' and resource 'workflow fingerprints'. It distinguishes from sibling tools like analyze_workflow by focusing on drift detection.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says 'Run after every PR merge to catch behavioral regressions before users do', providing clear when-to-use guidance. Does not explicitly mention alternatives but context makes it distinct.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/vighriday/Veris'

If you have feedback or need assistance with the MCP directory API, please join our Discord server