Skip to content
sunilgentyalaPublic

About

Detects when a long-running AI agent has silently drifted off its stated goal, even though every action it took was individually authorized. Cryptographically-anchored, tamper-evident drift detection for teams and platforms running heartbeat-driven autonomous agents.

Topics

Resources

Contributing

Security policy

Stars

6 stars

Watchers

0 watching

Forks

Repository files navigation

Plumbline

CI License: Apache 2.0 Python 3.10+ Zero ML dependencies

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 on the market today answers "was this one action allowed?" None of them answer "is this agent, across hundreds of unsupervised cycles, still doing the job it was hired for?" Plumbline is a small, dependency-light primitive that answers exactly that second question — and freezes the agent, up its own org chart, the moment the answer becomes no.

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. This tool 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 gets scored against that fixed point. Sustained drift escalates up the agent's org chart and freezes the agent; it never trusts the agent's own account of whether it's still on task.

Who this is for

You are... Plumbline gives you...
Building/operating long-horizon autonomous agents (coding agents, ops bots, research agents that run unsupervised for hundreds of heartbeat cycles) An 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 one) A drop-in drift-detection primitive — library or CLI — that plugs into your existing dispatch loop in ~5 lines, see Integrating drift detection
An engineering manager who is the "org chart" above an agent You're the one who actually gets notified on WARN/CRITICAL and reviews the tamper-evident ledger before re-anchoring — this is built to be read by a human, not just logged
Security/compliance, auditing agent deployments A hash-chained, digest-only ledger of every drift decision — provable and tamper-evident without ever handing an auditor raw agent output

Why this exists

2026's AI agent governance market (Microsoft's Agent Governance Toolkit, Marchward, HELM AI Kernel, Preloop, OpenLegion, and others) has converged on the same stack: policy-as-code, RBAC, budget caps, and tamper-evident audit logs for individual tool calls. That stack answers "was this one action authorized?" It does not answer a different, increasingly-cited failure mode in 2026 agent research: mission drift — an agent whose individual actions are each authorized and cheap, but whose trajectory across hundreds of autonomous heartbeat cycles has quietly wandered off the goal it was hired for. Production failures cluster around step 100, not step 1, and nothing in the governance stack above is watching for that, because authorization checks are inherently per-action, not per-trajectory.

Platforms built around heartbeat-driven, org-chart-shaped agent teams (e.g. Paperclip) are exactly where this shows up: an agent wakes on a schedule, checks in, and acts — for hundreds of cycles, with no human re-reading its original job description each time.

Plumbline is not an authorization or audit-log tool — it composes with those (see below), it doesn't replace them.

What it is not

  • Not an authorization/policy engine. It doesn't decide what an agent is allowed to do — pair it with a policy layer (Cedar, OPA, or a tool like the ones named above) for that.
  • Not an anomaly detector over raw telemetry. It only ever compares an agent's heartbeat content to that agent's own signed goal — there's no cross-agent baseline or behavioral model to poison.
  • Not identity-authenticated at the CLI layer yet. reanchor --approver checks org-chart membership, not a cryptographic signature from that approver — see the note in cli.py. A production integration should require the approver's own signing key.

How it works

  1. MissionAnchor — an ECDSA (P-256) signed, immutable record of an agent's declared goal and its chain of command (org_chart_path, immediate manager first). Signed once, at hire time, by the issuing manager's key. Neither the agent nor anything downstream of it can rewrite its own mandate and have it still verify.
  2. LexicalDriftScorer — a dependency-free, deterministic term-frequency cosine-distance scorer between the anchored goal and each heartbeat's content. No ML runtime dependency, so it's fast and reproducible in CI. It implements a DriftScorer protocol, so a stronger embedding-based backend can be swapped in for production without touching the anchor, ledger, or escalation logic.
  3. EscalationEngine — EWMA-smooths drift across heartbeats (so one off-topic message doesn't trip anything) and classifies each cycle as OK / WARN / CRITICAL against the anchor's own thresholds. WARN notifies the agent's immediate manager only; CRITICAL notifies the entire chain of command and freezes the agent — further heartbeats are rejected until a manager in that chain calls reanchor().
  4. DriftLedger — a hash-chained, tamper-evident log of every decision (LeaseGuard/HWAAM-style sealed chain). Raw heartbeat text is never stored, only a SHA-256 digest, so the ledger is safe to hand to an auditor. Editing, deleting, or reordering a past entry is detectable by walking the chain.

Demo

$ python examples/demo.py

Simulates a 3-level org chart (CEO -> CTO -> Engineer) where the engineer is anchored to "ship the login page UI" and slowly drifts onto an unrelated blockchain-migration tangent over 9 heartbeat cycles. Real output from this 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

CLI

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

Integrating drift detection into an orchestration platform

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

Library integration (Python orchestrators)

Call EscalationEngine.submit_heartbeat() right after the platform records an agent's heartbeat/work-log entry, 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

submit_heartbeat itself raises RuntimeError if called while frozen, so a platform that just always calls it before dispatch gets the block enforced for free, without needing its own separate frozen-state check.

CLI integration (any language, process-based orchestrators)

For orchestrators that aren't Python, or that shell out to worker processes, 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

Either way, Plumbline never needs to know anything about the platform's own task/ticket model — it only ever sees the anchor, the ledger, and the raw heartbeat text.

Installing

pip install -e ".[dev]"
pytest

Python 3.10+. Single runtime dependency: cryptography.

Threshold calibration

The bundled LexicalDriftScorer's default warn_threshold=0.70 / critical_threshold=0.90 are calibrated against its own output distribution, not picked arbitrarily: paraphrased-but-on-topic heartbeats typically land around 0.5-0.65 distance against it (bag-of-words has no synonym understanding), while genuinely off-topic work saturates near 1.0. If you swap in an embedding-based DriftScorer, recalibrate the thresholds against its distribution — cosine distances from different scorers are not comparable.

Relationship to prior work

  • Authorization propagation via signed delegation chains for human+agent hierarchies is a solved problem in this author's own HWAAM — Plumbline deliberately does not re-solve it; a MissionAnchor states a goal, not a permission grant.
  • LeaseGuard bounds cumulative damage magnitude under disconnection. Plumbline bounds cumulative semantic drift from a stated goal — orthogonal axes; an agent can be well within its damage budget while still off-mission.
  • GSH Framework watches MCP traffic for anomalous/malicious behavior. Plumbline never classifies behavior as good or bad in isolation, only as aligned or drifted relative to that specific agent's own declared goal.

Contributing

Forks and PRs welcome — this is a young, deliberately small primitive with real open extension points, not busywork:

  • Swap in an embedding-based DriftScorer. The bundled scorer is a dependency-free bag-of-words baseline on purpose (see Threshold calibration); it implements a documented DriftScorer protocol precisely so a semantic/embedding backend can drop in without touching the anchor, ledger, or escalation code.
  • Cryptographically-signed reanchor approval. reanchor --approver currently checks org-chart membership, not a signature — see the note in cli.py. Requiring the approver's own signing key (the same pattern anchor-create already uses for the issuer) is the single biggest hardening gap in the repo.
  • More platform integrations. The integration guide covers Python-library and CLI/process integration; adapters for specific orchestrators (Paperclip, LangGraph, custom cron runners) are welcome as examples under examples/.

Before opening a PR: pip install -e ".[dev]", then ruff check plumbline tests, mypy plumbline --ignore-missing-imports, and pytest -q all need to stay clean — CI runs the same three across Python 3.10-3.13.

How to Cite

If you use Plumbline in your research, please cite the software:

@software{gentyala2026plumbline,
  author    = {Gentyala, Sunil},
  title     = {Plumbline},
  year      = {2026},
  version   = {0.1.0},
  url       = {https://github.com/sunilgentyala/Plumbline}
}

Machine-readable metadata is in CITATION.cff; GitHub shows it under "Cite this repository".

License

Apache-2.0

About

Detects when a long-running AI agent has silently drifted off its stated goal, even though every action it took was individually authorized. Cryptographically-anchored, tamper-evident drift detection for teams and platforms running heartbeat-driven autonomous agents.

Topics

Resources

Contributing

Security policy

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages