Constraint-control testbed for local LLM agents
Project description
What is this?
MorphSAT is a testbed for studying when a local LLM agent should stop gathering evidence and commit to an action.
An LLM agent with tool access can loop indefinitely — calling tools, reading results, calling more tools — without ever deciding. MorphSAT layers constraint-control mechanisms around the agent's loop and measures their effect on accuracy, tool usage, and commit timing.
The current checkpoint (v7) compares an evidence-pressure controller against an anticipatory posture controller that treats novelty as an orienting state rather than a scalar penalty.
For cognitive architecture researchers: See
docs/COGNITIVE_ARCHITECTURE_TRANSLATION.mdfor a term mapping between MorphSAT internal names and concepts from Soar, ACT-R, and active inference.
The proof chain
Seven versions tested on a 20-scenario security alert triage benchmark:
| Version | Mechanism | Accuracy | Key finding |
|---|---|---|---|
| v1 | Static FSA constraints | 55% | 0 useful interventions — too weak |
| v2 | Fixed tool-call counter | 67.5% | Any pressure helps |
| v3 | Adaptive budget (2/3/5) | 55–67.5% | Ceiling irrelevant, floor matters |
| v4 | Evidence-pressure gate | 65% | Best escalation (77.8%), best pre-v7 |
| v5 | + pattern memory | 62.5% | Mechanics work, accuracy fails — learned threats without tolerance |
| v6 | + bidirectional pressure | 55% | Novelty-as-penalty is the wrong abstraction |
| v7 | Anticipatory posture control | 70% | Best accuracy, benign recovery 78.6% (was 35.7%) |
v7 result
| Evidence-pressure (v4) | Anticipatory posture (v7) | |
|---|---|---|
| Overall accuracy | 62.5% | 70.0% |
| Tool-loop rate | 35.0% | 25.0% |
| Avg turns to decision | 5.4 | 4.8 |
| Benign accuracy | 35.7% | 78.6% |
| Suspicious accuracy | 75.0% | 62.5% |
| Escalation accuracy | 77.8% | 66.7% |
v7 fixes the tolerance problem (benign +42.9pp) at the cost of suspicious/escalate regression. The tradeoff is real and not yet resolved.
Key insight: Novelty handling is a posture problem, not a threshold problem. When novelty was treated as a penalty (raise the commit threshold), the agent over-investigated benign scenarios and never learned tolerance. When novelty was treated as an orienting state (enter protective posture, gather bounded evidence, relax on safe evidence), benign recovery improved dramatically.
Architecture
Layer 1: FSA lifecycle gate
Legal task-state transitions. Blocks impossible sequences.
Layer 2: Evidence-pressure gate (v4)
Sensor-driven commit timing. Se complexity threshold,
evidence quality, sidecar confidence, urgency decay.
Fires irreversibly when pressure crosses threshold.
Layer 3: Anticipatory posture controller (v7)
Hidden state machine wrapping the agent's loop.
Novelty → ORIENT → bounded investigation → decide.
Safe evidence decays protective posture (tolerance).
Multi-axis pressure → escalation signal.
Layer 4: Dual-store memory
Threat patterns and tolerance patterns stored separately.
Familiarity with known configurations speeds future decisions.
Layer 5: Episodic traces
Turn-by-turn audit records of state, evidence, posture,
and outcomes. Every decision is reproducible.
Shadow monitor states
NORMAL ──→ ORIENTING ──→ SAFE_DISTANCE ──→ NORMAL (safe recovery)
│
└──→ INVESTIGATING ──→ COMMIT_READY (clear evidence)
│ ESCALATE_READY (high threat)
│ ABSTAIN_READY (contradictory)
└──→ SWARM_CALL (multi-axis pressure)
Install
# From source
git clone https://github.com/echo313unfolding/MorphSAT.git
cd MorphSAT
pip install -e .
# Optional dev tools
pip install -e ".[dev]"
Quick start
FSA lifecycle gate
from morphsat import MorphSATGate, TaskState, TaskEvent
gate = MorphSATGate()
state, legal, action = gate.step(TaskEvent.NEW_TASK)
assert state == TaskState.PLANNING
assert legal is True
Evidence-pressure gate
from morphsat import CommitGate, SplitMemoryStore
memory = SplitMemoryStore("/tmp/memory.json")
gate = CommitGate(memory=memory)
gate.initialize(alert_text="Suspicious process spawned by cron")
# Feed tool results
action = gate.process_evidence("check_process", "PID 1234: /usr/bin/curl ...")
# action.action is "CONTINUE", "COMMIT", or "ABSTAIN"
# action.direction is "escalate", "benign", or None
Shadow monitor (v7)
from morphsat import ShadowMonitor, SplitMemoryStore
memory = SplitMemoryStore("/tmp/memory.json")
monitor = ShadowMonitor(memory=memory)
monitor.initialize(alert_text="Unknown binary in /tmp")
# Monitor enters ORIENT if alert is novel
print(monitor.state) # ShadowState.ORIENTING
# Feed evidence — monitor transitions through states
action = monitor.process_evidence("check_hash", "Hash not in VirusTotal")
print(monitor.state) # ShadowState.INVESTIGATING
print(action.action) # "CONTINUE"
action = monitor.process_evidence("check_parent", "Parent: systemd")
print(monitor.state) # ShadowState.COMMIT_READY
print(action.action) # "COMMIT"
print(action.direction) # "benign"
# Close episode — updates memory for next run
monitor.close_episode("benign", confidence=0.8)
Project structure
morphsat/
├── morphsat/
│ ├── __init__.py # Public API
│ ├── core.py # FSA gate, TaskState/TaskEvent, classify_event
│ ├── token.py # Token adjacency scoring (4-lane structure)
│ ├── pressure_gate.py # v4 evidence-pressure gate
│ ├── commit_gate.py # v6 bidirectional commit gate + split memory
│ ├── shadow_monitor.py # v7 anticipatory posture controller
│ └── receipt.py # Receipt wrapping with SHA256 content hash
├── tests/
│ ├── test_core.py # 31 tests: FSA structure, transitions, receipts
│ ├── test_token.py # 22 tests: lane scoring, temperature, masking
│ └── test_shadow_monitor.py # 22 tests: v7 posture predictions
├── docs/
│ ├── PRESSURE_GATE_SPEC.md
│ └── COGNITIVE_ARCHITECTURE_TRANSLATION.md
├── receipts/
│ └── v7_shadow_monitor/ # Benchmark receipts (single-seed + 3-seed)
└── pyproject.toml
109/109 tests passing (Python 3.10).
Caveats
- N=20 scenario benchmark with simulated tool responses
- Temperature=0 (deterministic) — no stochastic variance across seeds
- Qwen2.5-Coder-3B doing security triage — small model, not its strongest domain
- The shadow monitor is tested on one task type (alert triage)
- This is a research testbed, not a production system
Companion projects
| Project | Description |
|---|---|
| helix-substrate | Calibration-free neural network compression (HXQ). |
| sentinel-hybrid-stack | Hybrid SSM-Transformer security monitoring pipeline. |
| helix-codec | Standalone C99 tensor codec library. |
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file morphsat-0.3.0.tar.gz.
File metadata
- Download URL: morphsat-0.3.0.tar.gz
- Upload date:
- Size: 48.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1ea97faabba3818705e9d194d7b999b0ae0ac0601e2c5f21a8ccb9581c2df47d
|
|
| MD5 |
b370760fa05f05640b432f6e22ae227e
|
|
| BLAKE2b-256 |
491a9065a96d77a20dcc3be0cc62f3f41fdf69b78e611d47158e7a4a9ed03ae5
|
File details
Details for the file morphsat-0.3.0-py3-none-any.whl.
File metadata
- Download URL: morphsat-0.3.0-py3-none-any.whl
- Upload date:
- Size: 36.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0871c11355b35aa955144fd8d33aa7b69229abf4e65d7737f4467c748d5fb658
|
|
| MD5 |
6c72e9d39b99cafa9d03e41d3e73b2b4
|
|
| BLAKE2b-256 |
415e06c9db0465313cfddf35c1b6572beb5fd1fa2dfcb4e209d3610977113342
|