Autonomous governance harness for Claude Code — now running as a service.
Your laptop ships diffs. The VPS judges, sandboxes, heals, and pushes the fix back. SQLite remembers everything.
Drop a creds file. Run one script. Done. → Quickstart
CI turned green. You merged. A week later: the bug was paved over, three tests are stubs, the diff references a pattern that no longer exists. Nobody flagged it because nobody graded the work — only returncode == 0.
Claude Code doesn't have standards. It has yours, but only when you're watching.
| # | Killer | Symptom |
|---|---|---|
| 1 | Silent failures | Tests pass, code is wrong. No grader on the diff. |
| 2 | Context decay | Auto-compaction erases the plan. Next commit is confidently wrong. |
| 3 | No TDD discipline | Implementation lands first. Tests written to match. Nothing prevents it. |
Plus what only shows at scale: no risk awareness, no over-investigation detection, no harness self-monitoring, no longitudinal signal across sessions.
devflow runs cloud-first. The Mac executes hooks and ships a tarball of the working tree. cloud.vinicius.xyz runs the judge, the hermetic Docker sandbox, and the Claude-driven heal proposer. Healed bytes come back inlined as base64, land on the local working tree, get committed and pushed automatically, and a Healing Journey PR comment summarizes what just happened.
flowchart LR
subgraph Mac[Mac · client]
Hooks[hooks/shadow_runner --cloud]
Promote[_auto_promote<br/>commit + push]
Comment[_pr_comment<br/>Healing Journey]
end
subgraph VPS[cloud.vinicius.xyz · VPS]
NPM[Nginx Proxy Manager<br/>TLS · Let's Encrypt]
Server[devflow-server.service<br/>uvicorn :8000 on 172.17.0.1]
Sandbox[devflow_sandbox<br/>Docker · hermetic per-job]
Proposer[Claude CLI<br/>OAuth token from /etc/devflow/claude.env]
Ledger[/var/log/devflow/jobs.jsonl<br/>real cost_usd]
Zelador[devflow-zelador.timer<br/>daily 03:00 UTC]
end
Hooks -->|HTTPS POST /v1/evaluate<br/>Bearer api_key + tarball| NPM
NPM -->|proxy_pass<br/>172.17.0.1:8000| Server
Server --> Sandbox
Sandbox --> Proposer
Proposer -->|verdict.json<br/>healing_patches[*].content_b64| Server
Server --> Ledger
Server -->|verdict + base64 patch| Hooks
Hooks --> Promote --> Comment
Zelador -->|prune · logrotate · budget_guard| Server
The loop is closed. Failure on the Mac → cloud heal → patch back → commit → push → PR comment. The dev sees ✨ Cloud-Healed: Patch applied to <files> and a green CI a moment later.
| Stage | Component | What happens |
|---|---|---|
| 1 | hooks/_cloud_client.pack_working_tree |
Gzipped tarball of the project, 50 MiB cap, drops .git/.venv/node_modules/build dirs. |
| 2 | cloud_server/runner.evaluate |
Unpacks into ephemeral workdir, runs devflow_sandbox heal --wiki, writes jobs.jsonl ledger entry with real cost_usd + token usage. |
| 3 | Sandbox + Claude CLI | OAuth token from EnvironmentFile=/etc/devflow/claude.env (0640 root:devflow); never visible in systemctl cat. HOME=/var/lib/devflow for a writable home inside ReadWritePaths. |
| 4 | cloud_server/runner post-process |
Inlines healed file bytes as healing_patches[*].files[*].content_b64; copies unified diff into top-level patch. Per-job artifacts dir is then deleted. |
| 5 | hooks/_cloud_client.apply_cloud_patch |
Validates base64 + path traversal, writes files to local working tree, prints ✨ Cloud-Healed: Patch applied to <files>. |
| 6 | hooks/_auto_promote |
Branch guard (refuses main/master), commit + push, honors --no-push / DEVFLOW_NO_PUSH. Detached HEAD blocked. |
| 7 | hooks/_pr_comment |
Posts (or PATCHes via HEALING_JOURNEY_MARKER) the Healing Journey on the open PR. Idempotent across retries. |
| Hook | Event | Impact |
|---|---|---|
| pre_task_profiler | PreToolUse | Scores probability × impact × detectability. Sets oversight: vibe / standard / strict / human_review. |
| post_task_judge | Stop | LLM judge (Haiku) over 11 rubric axes. Reads forced-fail signals from TDD, Shadow, Tier-4 swarm. PASS / WARN / FAIL with structured judge_reasoning. |
| shadow_runner --cloud | Stop dispatch (HIGH risk) | Ships tarball to cloud.vinicius.xyz, drives the heal loop, promotes patch, posts PR comment. |
| tdd_enforcer | PostToolUse | Detects implementation-without-tests. Writes tdd_violation signal. EDD hard-gate. |
| pre_push_gate | git push | 4 deterministic linters (import_boundary, file_size, coverage_gate, compile_check) + full pytest. Auto-push on non-protected branches when green. |
| pre_compact + post_compact_restore | PreCompact / SessionStart | Save plan + cwd + state. Restore on next session. Compaction stops erasing work. |
| stop_dispatcher | Stop | Single Stop entry point. Gate (spec_stop_guard) + fast (cost_tracker, task_telemetry) sync; boundary (judge, instinct_capture) sync or async by oversight. |
| task_telemetry | Stop | Records token cost per phase (PENDING → IMPLEMENTING → COMPLETED) into SQLite. 54-col schema, see docs/INTERNAL.md. |
| Worker | Looks for | Verdict effect |
|---|---|---|
| Worker-Sec | hardcoded creds, injection, unsafe deserialization | unsafe → HARD FAIL |
| Worker-QA | empty input, malformed input, race, overflow, timezone | missing/weak → soft warn |
| Worker-Doc | README + CLAUDE.md drift | drift → soft warn |
Fail-closed: if signal storage errors, a synthetic [sec] swarm-reports-unavailable HARD reason is emitted. Worker-Sec can never be silently skipped.
python3 -m mcp.server exposes three tools over JSON-RPC 2.0 stdio. Cursor, Claude Desktop, Zed, Continue — same governance.
| Tool | Purpose |
|---|---|
evaluate_task |
On-demand post_task_judge against a state_dir. |
get_task_health |
Session snapshot: cost_usd, pending TDD violations, active risk flags, last verdict. |
apply_devflow_governance |
The universal gate. Profiler + Shadow dispatch + last verdict → compact JSON with ready_to_push. |
apply_devflow_governance returns ready_to_push: true only when verdict == "pass" AND forced_fail_reasons == []. Prefix contract: [sec] / [shadow] / [tdd] block; [qa] / [doc] surface as soft notes.
Durable authorization. The agent acts directly without per-step approval. Auto-push on non-main/master branches when judge=PASS + lint green + tests green. Circuit Breaker (MAX_RETRIES=3 → post-mortem). Token Delta Guard (150k without PASS → emergency halt). Destructive ops still gated by devflow-wizard.
Prereqs: Claude Code CLI, Python 3.10+, curl, gh. Nothing else — no Docker on the laptop, the sandbox lives on the VPS.
Ask the maintainer to mint you a token. They will run, on the VPS:
sudo -u devflow /opt/devflow/devflow/.venv/bin/python <<'PY'
from cloud_server.auth import KeyStore
import secrets
ks = KeyStore("/etc/devflow/api-keys.json")
token = secrets.token_urlsafe(32)
ks.add_key(client_id="<your-handle>", api_key=token)
print(token)
PYThe plaintext token comes back once. The server only retains the argon2id hash.
mkdir -p ~/.devflow && cat > ~/.devflow/cloud-credentials.json <<'EOF'
{
"endpoint": "https://cloud.vinicius.xyz/v1/evaluate",
"api_key": "<token from step 1>",
"client_id": "<your-handle>"
}
EOF
chmod 600 ~/.devflow/cloud-credentials.jsongit clone https://github.com/viniciuscffreitas/devflow.git ~/.claude/devflow
bash ~/.claude/devflow/scripts/setup_client.shsetup_client.sh validates the credentials file, pings https://cloud.vinicius.xyz/v1/healthz, and runs install.sh to merge hooks into ~/.claude/settings.json. Override the endpoint with --endpoint URL or skip the hook install with --no-hooks.
cd <your-project>
claude
> /sync
> /spec add JWT refresh token rotationThe harness now scores every task, blocks unsafe pushes, dispatches Shadow V3 to the VPS on HIGH risk, applies any heal patch, commits, pushes, and posts a PR comment. Watch ~/.claude/devflow/telemetry/devflow.db accumulate evidence:
python3 ~/.claude/devflow/telemetry/cli.py tier1
python3 ~/.claude/devflow/telemetry/cli.py recent --n 10
python3 ~/.claude/devflow/telemetry/cli.py anxietycd ~/.claude/devflow/dashboard && streamlit run app.pyMission Feed timeline, healing toasts, project health, executive mode.
~/.claude/devflow/uninstall.shThe system carries three secret materials. Each has a defined custody and a defined leak posture.
| Secret | Where it lives | Custody | Leak posture |
|---|---|---|---|
| Client API key | ~/.devflow/cloud-credentials.json (mode 0600) on the laptop. |
User. | Sent as Authorization: Bearer over TLS only. Server stores argon2id hash in /etc/devflow/api-keys.json (0640 root:devflow). Plaintext is never logged. |
| SSH access to VPS | User's ~/.ssh/<key> + vinicius@vps authorized_keys. |
Operator. | Password auth disabled by /etc/ssh/sshd_config.d/01-devflow-hardening.conf. The 01- prefix is load-bearing: cloud images ship 50-cloud-init.conf which re-enables password auth, and sshd's drop-in semantics are first-occurrence-wins in lexical order. PermitRootLogin no + PubkeyAuthentication yes. |
| Anthropic OAuth token | EnvironmentFile=-/etc/devflow/claude.env (0640 root:devflow), loaded into devflow-server.service only. |
Operator. | Never appears in systemctl cat, never in journalctl, never in process env of unrelated services. The devflow user gets HOME=/var/lib/devflow so the Claude CLI has a writable home that lives inside ReadWritePaths. |
Server hardening (deploy/devflow-server.service):
NoNewPrivileges=yes
ProtectSystem=strict
ReadWritePaths=/var/lib/devflow /var/log/devflow
MemoryMax=2G
TasksMax=512
Path safety: apply_cloud_patch rejects any healed file path that escapes the project root or contains .. segments — base64 decode happens after the path check, so a malicious server reply can't write outside the working tree.
Container registry: the Shadow Runner V3 image is private on ghcr.io. The devflow user runs docker login ghcr.io once at provisioning; the credential persists at /opt/devflow/.docker/config.json until the PAT rotates. A 401 from docker pull is the canonical "verdict missing" failure mode — the runner surfaces sandbox stderr/stdout into the verdict so the client sees why heal didn't run.
The VPS runs devflow-zelador as a systemd timer:
deploy/devflow-zelador.timer OnCalendar=*-*-* 03:00:00 UTC
deploy/devflow-zelador.service Type=oneshot, User=devflow
ExecStart=/opt/devflow/scripts/vps_cleanup.sh
ExecStart=/usr/bin/python3 /opt/devflow/scripts/budget_guard.py
docker system prune -f --volumes— reclaims ephemeral sandbox layers and dead volumes.logrotate -f /etc/logrotate.d/devflow— rotates/var/log/devflow/*.logandjobs.jsonl.
Default tripwire: $10 / 24h. Reads /var/log/devflow/jobs.jsonl and resolves cost per entry in priority order:
cost_usd— true USD computed by the heal proposer from the Anthropic API response.usage+model— Anthropic-shape token counters multiplied byCLAUDE_PRICING(mirror ofhooks/cost_tracker.py; bump both when rates move).heal_attempts × $0.10— legacy heuristic, kept as a fallback so unenriched historical entries still contribute.
When the daily total crosses the budget the guard runs systemctl stop devflow-server.service and POSTs a JSON alert to $DEVFLOW_ALARM_WEBHOOK (5s timeout, best-effort — webhook failure never blocks the stop). Override the limit with --budget USD or alternative ledger with --ledger PATH.
Every /v1/evaluate call appends a JSONL row to /var/log/devflow/jobs.jsonl with the verdict, cost_usd, usage, model, heal_attempts, and the pre-heal stderr tail (inlined directly into the verdict for client-side rendering). This is the single source of truth the budget guard reads from.
One-shot, idempotent:
ssh devflow-vps 'sudo bash -s' < deploy/vps_provision.sh
ssh devflow-vps 'sudo bash -s' < deploy/sshd_hardening.sh
sudo cp deploy/devflow-server.service /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now devflow-server.serviceRun sshd_hardening.sh only after ssh-copy-id is verified — it disables password auth and will lock you out otherwise.
Full operational runbook (NPM ingress, UFW, ghcr.io login, key minting, recovery): deploy/README.md.
Auto-detected toolchains: Node.js, Flutter/Dart, Go, Rust, Java/Maven, Python. Trackers: Linear, GitHub Issues, Jira, TODO.md. Editors: Claude Code CLI, Claude Desktop, Cursor, Zed, Continue (via MCP).
pytest # full suite, 1405 tests
pytest hooks/tests # client-side hooks
pytest cloud_server/tests # server-side runner + auth
pytest deploy/scripts/tests # zelador + budget guard
pytest --cov # with coverageEnd-to-end smoke (Mac → VPS → heal → commit → push):
pytest tests/integration/test_cloud_heal_loop.py -v- Shadow Runner V3 — hermetic Docker sandbox + self-heal loop. Runs on the VPS, invoked by
cloud_server/runner.py. - paperweight — HTTP backend that consumes
hooks/paperweight_bridge.py.
deploy/README.md— VPS topology, NPM ingress, UFW, recovery runbook.docs/INTERNAL.md— 54-column telemetry schema, hook architecture, signal protocol.docs/universal_prompt.md— God Mode system prompt.docs/sg-rules.md— ast-grep structural rules.AGENTS.md— contract for any agent operating here.CLAUDE.md— durable authorization + workflow rules.
MIT.
The guardrails Claude Code never shipped with — now running as a service.