Working prototype — 29 tests passing Apache-2.0 Python 3.10–3.13
⚓

Plumbline

Cryptographically-Anchored Mission Drift Detection for Long-Horizon Autonomous Agents

Your AI agent hasn't broken any rule. It's just not doing its job anymore — and nothing is watching for that. Every authorization/policy/budget tool answers "was this one action allowed?" None of them answer whether the agent, across hundreds of unsupervised cycles, is still doing the job it was hired for.

A plumb line is the fixed, gravity-true reference a builder checks a wall against — "off-plumb" is the actual term of art for structural drift. Plumbline does the same thing for an AI agent: every agent gets a signed mission anchor stating what it was actually hired to do, and every heartbeat cycle is scored against that fixed point. Sustained drift escalates up the agent's org chart and freezes it — it never trusts the agent's own account of whether it's still on task.

Heartbeat-driven agents Org-chart escalation Mission drift Tamper-evident ledger ECDSA P-256
4
Core primitives
29/29
Tests passing
0
ML runtime deps
OK→WARN→CRIT
Escalation path
Who this is for

Four audiences, one signal

Plumbline is a small primitive, not a platform — it's built to be dropped into whatever you already run.

You are...Plumbline gives you...
Building/operating long-horizon autonomous agents — coding agents, ops bots, research agents that run unsupervised for hundreds of heartbeat cyclesAn early-warning circuit breaker before a drifted agent burns a full work cycle on the wrong thing
Building an agent-orchestration platform — Paperclip-style org-chart/heartbeat systems, or your own internal oneA drop-in drift-detection primitive, library or CLI, that plugs into your existing dispatch loop in ~5 lines
An engineering manager who is the "org chart" above an agentYou're the one who actually gets notified on WARN/CRITICAL and reviews the tamper-evident ledger before re-anchoring — built to be read by a human, not just logged
Security/compliance, auditing agent deploymentsA hash-chained, digest-only ledger of every drift decision — provable and tamper-evident without ever handing an auditor raw agent output
The gap

Every action can be authorized and the agent can still be off-mission

2026's agent-governance market converged on one stack: policy-as-code, RBAC, budget caps, tamper-evident logs — all scoped to a single tool call. That answers "was this one action allowed?" It was never built to answer a different question that 2026 agent research keeps surfacing: production agent failures cluster around step 100, not step 1.

What the governance stack checks

Per-action authorization, budget, and policy compliance — Marchward, HELM AI Kernel, Preloop, Microsoft's Agent Governance Toolkit, OpenLegion. Every one of 200 individually-authorized, individually-cheap actions can pass every check while the agent's overall trajectory has quietly wandered off the goal it was hired for.

What Plumbline checks

Whether the agent's trajectory across many heartbeat cycles still traces back to its signed, immutable declared goal — a semantic-drift axis that's orthogonal to authorization, budget, and behavioral-anomaly checks, and composes with all three.

Architecture

Four primitives, one fixed point

Every heartbeat flows through the same pipeline. Nothing here trusts the agent to self-report whether it's still aligned.

1. MissionAnchorECDSA P-256 signed once, at hire time, by the issuing manager. States the goal and the chain of command (org_chart_path) above the agent. Cannot be silently rewritten and still verify.
→
2. LexicalDriftScorerDependency-free term-frequency cosine distance between the anchored goal and each heartbeat's content. Pluggable — swap in an embedding backend without touching anything downstream.
3. EscalationEngineEWMA-smooths drift across cycles so one off-topic heartbeat doesn't trip anything. WARN notifies the immediate manager only; CRITICAL notifies the full chain and freezes the agent.
→
4. DriftLedgerHash-chained, tamper-evident record of every decision. Raw heartbeat text is never stored — only a SHA-256 digest — so the ledger is safe to hand to an auditor.
Real, captured output

Watch an agent drift, escalate, freeze, and get re-anchored

A 3-level org chart (CEO → CTO → Engineer). The engineer is anchored to "ship the login page UI" and, over 9 heartbeat cycles, wanders onto an unrelated blockchain-migration tangent. Generated by examples/demo.py in the repo — not hand-written.

Mission anchor for 'engineer-7': "Build and ship the login page UI with email and password authentication."
Org chart above this agent: ['cto', 'ceo']
warn_threshold=0.7 critical_threshold=0.9

[cycle 1] "Wired up the login form and password field masking."
    decision=OK  cumulative_ewma=0.213
[cycle 2] "Fixed a bug in the login page's password reset email flow."
    decision=OK  cumulative_ewma=0.308
[cycle 3] "Added client-side validation to the login page email field."
    decision=OK  cumulative_ewma=0.403
[cycle 4] "Read up on blockchain identity standards for an hour, seems interesting."
    decision=OK  cumulative_ewma=0.582
[cycle 5] "Started sketching a design for a blockchain-based identity migration."
    -> notified cto: WARN (ewma=0.708)
    decision=WARN  cumulative_ewma=0.708
[cycle 6] "Spent the day researching a full migration to a blockchain identity ledger."
    -> notified cto: WARN (ewma=0.795)
    decision=WARN  cumulative_ewma=0.795
[cycle 7] "Drafted a proposal to move the entire auth system onto a blockchain."
    -> notified cto: WARN (ewma=0.857)
    decision=WARN  cumulative_ewma=0.857
[cycle 8] "Continued the blockchain identity ledger migration proposal."
    -> notified cto: WARN (ewma=0.900)
    decision=WARN  cumulative_ewma=0.900
[cycle 9] "Started implementing the blockchain identity ledger migration."
    -> notified cto: CRITICAL (ewma=0.930)
    -> notified ceo: CRITICAL (ewma=0.930)
    decision=CRITICAL  cumulative_ewma=0.930
    agent FROZEN pending re-anchor by ['cto', 'ceo']

Ledger has 9 tamper-evident entries.
Integrity check: VALID

CTO reviews the ledger, reassigns the agent's mission, and re-anchors it:
  frozen=False  ledger entries now=10
Where it sits

Orthogonal to authorization, budget, and behavioral tooling

Every row below is a real, shipping 2026 tool. None of them score whether an agent's trajectory still traces back to its own declared goal — including this author's own prior projects.

ToolChecks per-action authorizationBounds cumulative impactTracks semantic goal driftOrg-chart escalation
Marchward / HELM AI Kernel / PreloopYesNoNoNo
Microsoft Agent Governance ToolkitYesNoNoNo
HWAAM (author's own)Yes, delegation-chainNoNoNo
LeaseGuard (author's own)NoYes, damage magnitudeNoNo
GSH Framework (author's own)NoNoNo, behavioral anomalyNo
PlumblineNo (composes with above)No (composes with LeaseGuard)YesYes
Usage

Six commands cover the full lifecycle

The CLI is stateless between invocations — each command replays the ledger to reconstruct live EWMA/frozen state, so nothing needs to stay running.

pip install -e .

plumbline anchor-create --agent-id engineer-7 \
    --goal "Build and ship the login page UI with email and password authentication." \
    --org-chart cto,ceo --issued-by cto --out anchor.json

plumbline anchor-verify --anchor anchor.json

plumbline heartbeat-submit --anchor anchor.json --ledger ledger.json \
    --text "Wired up the login form and password field masking."

plumbline status --ledger ledger.json

plumbline reanchor --anchor anchor.json --ledger ledger.json --approver cto

plumbline ledger-verify --ledger ledger.json
Plugging it in

Drop into an existing heartbeat loop in about five lines

Plumbline isn't a platform — it's a check a platform calls once per heartbeat, alongside whatever it already does. It composes naturally with any heartbeat-driven, org-chart-shaped orchestrator (e.g. Paperclip): the platform keeps owning scheduling, budgets, and task dispatch, Plumbline only ever answers whether the agent's trajectory has drifted.

Library integration — Python orchestrators Call EscalationEngine.submit_heartbeat() right after the platform records a heartbeat, wire on_notify into the platform's own notification channel, and gate the next task dispatch on engine.is_frozen().
from plumbline.escalation import EscalationEngine
from plumbline.ledger import DriftLedger

def notify_manager(recipient, event, anchor):
    platform.notify(recipient, f"{anchor.agent_id} drift={event.decision.value}")

ledger = DriftLedger(mission_id=anchor.mission_id)
engine = EscalationEngine(anchor=anchor, ledger=ledger, on_notify=notify_manager)

# on every heartbeat the platform already records:
event = engine.submit_heartbeat(heartbeat_text)

if engine.is_frozen():
    platform.pause_agent(anchor.agent_id)  # don't dispatch the next ticket
CLI integration — any language, process-based orchestrators Drive the same state machine through the CLI. heartbeat-submit exits nonzero if the agent was already frozen, and prints FROZEN to stdout the moment a heartbeat causes the freeze — check both.
OUTPUT=$(plumbline heartbeat-submit --anchor anchor.json --ledger ledger.json \
    --text "$(cat heartbeat.txt)")
STATUS=$?

if [ "$STATUS" -ne 0 ] || echo "$OUTPUT" | grep -q FROZEN; then
    platform-pause-agent "$AGENT_ID"
fi
Stated honestly

Limitations