Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

321 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

devflow

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.

Python Tests Cloud License: MIT Shadow V3 Claude Code

Drop a creds file. Run one script. Done.Quickstart


L1 — The illusion of green

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.


L2 — The three killers

# 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.


L3 — DevFlow as a Service (DaaS)

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
Loading

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.


L4 — What's inside

The autonomous heal loop

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.

Core hooks

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.

Tier-4 Orchestrator-Workers (HIGH risk only)

Worker Looks for Verdict effect
Worker-Sec hardcoded creds, injection, unsafe deserialization unsafeHARD 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.

Universal MCP integration

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.

Autômato Seguro

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.


Quickstart (2 minutes)

Prereqs: Claude Code CLI, Python 3.10+, curl, gh. Nothing else — no Docker on the laptop, the sandbox lives on the VPS.

1. Get a credential

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)
PY

The plaintext token comes back once. The server only retains the argon2id hash.

2. Drop the creds file

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.json

3. Clone + run the installer

git clone https://github.com/viniciuscffreitas/devflow.git ~/.claude/devflow
bash ~/.claude/devflow/scripts/setup_client.sh

setup_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.

4. Start a session

cd <your-project>
claude
> /sync
> /spec add JWT refresh token rotation

The 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 anxiety

Dashboard

cd ~/.claude/devflow/dashboard && streamlit run app.py

Mission Feed timeline, healing toasts, project health, executive mode.

Uninstall

~/.claude/devflow/uninstall.sh

Security model

The 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.


Maintenance

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

Cleanup pass (vps_cleanup.sh)

  • docker system prune -f --volumes — reclaims ephemeral sandbox layers and dead volumes.
  • logrotate -f /etc/logrotate.d/devflow — rotates /var/log/devflow/*.log and jobs.jsonl.

Budget guard (budget_guard.py)

Default tripwire: $10 / 24h. Reads /var/log/devflow/jobs.jsonl and resolves cost per entry in priority order:

  1. cost_usd — true USD computed by the heal proposer from the Anthropic API response.
  2. usage + model — Anthropic-shape token counters multiplied by CLAUDE_PRICING (mirror of hooks/cost_tracker.py; bump both when rates move).
  3. 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.

Job ledger

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.


Provisioning a new VPS

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.service

Run 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.


Compatibility

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).


Tests

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 coverage

End-to-end smoke (Mac → VPS → heal → commit → push):

pytest tests/integration/test_cloud_heal_loop.py -v

Pairing


Deeper


License

MIT.

The guardrails Claude Code never shipped with — now running as a service.

⭐ Star if you're building with Claude Code

About

Language-agnostic spec-driven development plugin for Claude Code

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages