DEV Community

Harper Xu
Harper Xu

Posted on

Give the Remote Critique Zero Merge Rights

A free coding server is only a critique plane. It must never become your merge plane. You keep merge authority on your side of the wire.

Cheap inference makes a remote box look like extra CI. That analogy breaks at the trust boundary. The box does not share your disk, your deploy keys, or your branch protections.

This is an architecture review of that split. Constraints first. Data flow second. Then the contract you should enforce before any diff leaves the laptop.

You are not reviewing a teammate. You are reviewing an untrusted compile-and-comment node. Treat it like a contractor with a badge that expires at the door.

Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode publishes free model access and a free server option. Those facts only justify a remote critique plane you still do not control.

Start with constraints, not prompts. The remote plane cannot see your full worktree. It cannot hold production secrets. It cannot fast-forward main.

A free server also cannot promise locality. Packets cross a network you do not operate. Latency, queueing, and disk policy sit outside your change window.

Those constraints force a two-plane design. The merge plane lives in your repo and CI. The critique plane lives on the free box. Only structured comments travel back.

Picture a museum with a glass wall. Visitors may look at the painting. They may write notes on a card. They never take the canvas home.

Your patch is the canvas. The remote critique is the visitor. The card is a typed review object. The guard is a local gate that ignores anything else.

Data flow stays boring on purpose. You cut a patch on a throwaway ref. You strip secrets and bulky binaries. You ship a bounded bundle, not a clone.

The remote node may compile, lint, or narrate risks. It returns JSON that matches a contract. Your gate verifies the shape, then you decide.

Nothing in that path writes a protected branch. Nothing in that path stores a cloud token. If the wire dies, the merge plane still works.

Here is a proposed extractor you can run locally. It is a sketch, not production CI. Label it as unexecuted until you test it on a dummy repo.

#!/usr/bin/env bash
# proposed: extract_patch.sh — local merge plane only
set -euo pipefail
BASE="${1:-origin/main}"
OUT="${2:-/tmp/critique-bundle}"
mkdir -p "$OUT"
git diff --binary "$BASE"...HEAD > "$OUT/patch.diff"
git rev-parse HEAD > "$OUT/head.sha"
git merge-base "$BASE" HEAD > "$OUT/base.sha"
# never pack .env, keys, or build caches into the bundle
Enter fullscreen mode Exit fullscreen mode

The extractor is intentionally dumb. Dumb tools fail closed. Clever wrappers start smuggling context the remote plane should never see.

Next you sanitize. Sanitizing is architecture, not politeness. A free critique node is a new copy of your diff. Copy only what the review question needs.

# proposed: sanitize_bundle.py — unexecuted example
from pathlib import Path
import re, sys

SECRET = re.compile(
    r"(api[_-]?key|secret|token|password)\s*=\s*['\"][^'\"]+",
    re.I,
)
DENY_SUFFIX = {".pem", ".p12", ".env", ".lock"}

def sanitize(diff_text: str) -> str:
    kept = []
    skip = False
    for line in diff_text.splitlines(True):
        if line.startswith("diff --git"):
            skip = any(line.strip().endswith(s) for s in DENY_SUFFIX)
        if skip:
            continue
        kept.append(SECRET.sub(r"\1=***", line))
    return "".join(kept)

if __name__ == "__main__":
    raw = Path(sys.argv[1]).read_text(errors="replace")
    Path(sys.argv[2]).write_text(sanitize(raw))
Enter fullscreen mode Exit fullscreen mode

That filter is a fence, not a vault. It will miss novel secret shapes. You still keep credentials out of the branch that produced the diff.

Now pin the return path. A critique without a schema is just chat. Chat does not belong in merge policy. Policy needs fields you can reject.

{
  "$id": "local.critique.v1",
  "type": "object",
  "additionalProperties": false,
  "required": ["head_sha", "verdict", "findings"],
  "properties": {
    "head_sha": {"type": "string", "minLength": 40, "maxLength": 40},
    "verdict": {"enum": ["comment", "block_local", "nits"]},
    "findings": {
      "type": "array",
      "maxItems": 40,
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": ["path", "severity", "summary"],
        "properties": {
          "path": {"type": "string"},
          "severity": {"enum": ["info", "warn", "fail"]},
          "summary": {"type": "string", "maxLength": 240}
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Notice the missing field. There is no patch in the response. There is no git_push. The critique plane may talk. It may not mutate.

Your local gate is the real reviewer. It checks the SHA still matches HEAD. It checks the JSON still matches the contract. Then a human or CI applies judgment.

# proposed: local_gate.py — unexecuted example
import json, subprocess, sys
from jsonschema import validate

SCHEMA = json.loads(open("review_contract.json").read())

def head_sha() -> str:
    return subprocess.check_output(
        ["git", "rev-parse", "HEAD"], text=True
    ).strip()

def gate(path: str) -> int:
    payload = json.loads(open(path).read())
    validate(payload, SCHEMA)
    if payload["head_sha"] != head_sha():
        print("stale critique: sha drifted")
        return 2
    fails = [f for f in payload["findings"] if f["severity"] == "fail"]
    if payload["verdict"] == "block_local" or fails:
        print("local block; remote wrote nothing")
        return 1
    print("comments only; merge plane still yours")
    return 0

if __name__ == "__main__":
    sys.exit(gate(sys.argv[1]))
Enter fullscreen mode Exit fullscreen mode

Run a tiny test before you trust the fence. The test is the artifact that keeps this honest. If the sanitizer leaks a token, the design is already wrong.

# proposed: test_sanitize.py — run on a fixture, not prod secrets
from sanitize_bundle import sanitize

SAMPLE = """diff --git a/.env b/.env
+API_KEY=abcd
diff --git a/app.py b/app.py
+print(1)
"""

def test_drops_env_and_redacts():
    out = sanitize(SAMPLE)
    assert ".env" not in out
    assert "abcd" not in out
    assert "app.py" in out
Enter fullscreen mode Exit fullscreen mode

Failure domains in this design are few and named. Bundle leak is one. Stale SHA is two. Schema drift is three. Network partition is four, and it must be boring.

A bundle leak means the critique plane learned too much. You shrink the diff and rotate anything that slipped. You do not respond by giving the box write access.

A stale SHA means the review talks about a ghost commit. The gate drops the payload. You never cherry-pick remote advice onto a moved tree.

Schema drift means the model started chatting again. Extra keys fail additionalProperties. Missing keys fail required. Chatty failure is still a failure.

A network partition means the free server vanished. Your merge plane continues. That is the point of the glass wall. Critique is optional. Merge is not.

Compare that with the tempting one-plane design. People SSH into the free box and push from there. The badge now opens the vault. One phish, and the painting walks out.

You would not let a visitor merge from the gift shop laptop. Do not let a free critique node hold GIT_SSH_COMMAND. Do not let it store NPM_TOKEN.

What you would change next is tighter provenance. Sign the bundle with a local key. Carry a path allowlist, not a deny list. Version the schema in the repo.

You would also cap finding count. Forty items already strain a human. Unlimited findings become a second token furnace. The gate should refuse floods.

You would add a dry-run compiler on the remote plane only if the language is public. Proprietary protocols stay home. A free box is not your compliance zone.

Limitations are sharp. This workflow slows a solo spike. It rejects huge binary refactors. It will not catch secrets that never look like secrets.

The sanitizer is regex theater against a determined leak. If the branch already committed credentials, the remote copy still receives them. Clean history first.

Who should not use this approach is clear. Do not send regulated source to any free server. Do not use it as nightly CI for production deploys. Do not treat verdict JSON as a merge bot.

Teams with an air-gapped legal rule should skip the remote plane entirely. Local models on local disks exist for that constraint. Architecture follows the boundary you actually have.

If your problem is speed, this design will annoy you. The extra hop is the fee for isolation. Isolation is the feature you were about to delete.

Keep the story straight when tools get cheaper. Cheap critique does not cheapen authority. Authority stays with the plane that can lose the company.

You now have a contract, a fence, and a gate. Use them on a disposable repository first. Then point a free critique plane at a bundle you could publish anyway.

If you inspect MonkeyCode, inspect the bundle contract before the server. The useful question is not how fluent the model sounds. The useful question is which plane is allowed to write.

Top comments (0)