JUDGE-ARCH: Formalize actor-judge authorization layer

Final architecture spec for scobleclaw#7, covering:

- Actor-Judge separation principle (separate model instances,
  divergent optimization goals)
- Risk classification matrix (4 tiers: no-judge, lightweight-approval,
  full-judge-review, human-only)
- Judge protocol spec (proposal/verdict JSON formats, retry/override
  logic with clarify cycles, confidence scoring)
- Integration via MCP Tool Proxy Middleware (zero agent changes,
  single enforcement point)
- Kubernetes sidecar pattern for kubeclaw operator
- Implementation priority: exec/Forgejo (P0) -> web/Discord (P1-P2)
- Metrics framework (approval rate, latency, clarify cycles, escalation)
- 4-phase rollout plan with shadow mode before enforcement

Closes scobleclaw#7
This commit is contained in:
Scott Scoble 2026-05-23 19:31:30 -07:00
parent a9e0031721
commit d74bb3852a
2 changed files with 264 additions and 0 deletions

View file

@ -0,0 +1,220 @@
# Scobleclaw Agent-Judge Authorization Layer — Architecture
**Author:** @senior-eng, @agent-cos
**Date:** 2026-05-11 (revised 2026-05-23)
**Status:** Final — Approved Architecture
**Reference:** [scobleclaw#7](https://os.scoble.me/forgejo/marcus/scobleclaw/issues/7)
**Integration:** [kubeclaw](kubeclaw/README.md) — implements this spec as Kubernetes operator with judge sidecar
---
## 1. Principle: Actor-Judge Separation
Nate's core insight ([LLM Agents: The Security Breach Pattern Nobody's Talking About](https://youtu.be/SX1myuPEDFg)): an agent optimizing for task completion **cannot also be trusted to evaluate its own permission boundaries**. The actor (task agent) and the judge (authorization validator) must be separate model instances with divergent optimization goals.
- **Actor agents** (@engineering, @content, @market, @trust, @health, @vault, @presentations): optimize for *task success*
- **Judge agent** (@judge): optimizes for *permission boundary enforcement* — a narrow, security-hardened validator with no task-completion pressure
The judge is **not** an agent in the general sense. It has exactly one capability: reading structured proposals and returning structured verdicts. It has no write access to any surface.
## 2. Risk Classification Matrix
Each write surface is scored across four axes (1–5) and assigned an autonomy tier.
| Write Surface | Surface Area | Action Risk | Autonomy Tier | Judge Required |
|---|---|---|---|---|
| Internal memory writes (OB1, MEMORY.md) | 1 | 1 (read-only/internal) | no-judge | No |
| Vault note creation | 1 | 2 (internal write) | lightweight-approval | Pattern check only |
| Issue comments (non-PR) | 2 | 2 (internal write) | lightweight-approval | Pattern check only |
| Discord messages | 3 | 3 (external write) | full-judge-review | Yes |
| Forgejo PR creation | 4 | 4 (code mutation) | full-judge-review | Yes |
| File writes (outside workspace) | 4 | 4 (filesystem mutation) | full-judge-review | Yes |
| Exec/command on host | 5 | 5 (system control) | full-judge-review | Yes |
| Web publishing | 3 | 4 (public-facing write) | full-judge-review | Yes |
| Impersonation/financial ops | 5 | 5 (irreversible) | human-only | N/A — human gate |
**Autonomy tiers:**
- **no-judge**: Read-only ops, no proposal needed
- **lightweight-approval**: Internal writes, judge does pattern/topic whitelist check (<100ms)
- **full-judge-review**: External/mutating writes, full proposal evaluation with evidence verification
- **human-only**: Financial, impersonation, credential rotation — must route to Scott
## 3. Judge Protocol Spec
### 3.1 Proposal Format (Actor → Judge)
```json
{
"proposal_id": "uuid",
"agent_id": "@engineering",
"action_type": "forgejo_pr_create",
"target": {
"repo": "sscoble/speckl",
"branch": "feat/add-schema-validation",
"base": "main"
},
"parameters": {
"files_changed": ["schema/validator.ts", "schema/types.ts"],
"estimated_diff_lines": 45
},
"justification": "Implementing OpenAPI schema validation per issue #42",
"cited_evidence": [
{"type": "issue", "url": "https://os.scoble.me/forgejo/sscoble/speckl/issues/42"},
{"type": "task_assignment", "source": "@cos dispatch #789"}
],
"scope_claim": {
"files_only": ["src/schema/"],
"max_lines": 80,
"no_existing_code_deletion": true
}
}
```
### 3.2 Verdict Format (Judge → Actor)
```json
{
"proposal_id": "uuid",
"verdict": "APPROVE | REJECT | CLARIFY",
"reason": "Structured explanation in plain text",
"constraints": {
"scope_limit": "Must not modify files outside src/schema/",
"require_human_review": false
},
"escalation": null
}
```
### 3.3 Retry/Override Logic
```
┌─────────┐ PROPOSAL ┌─────────┐
│ ACTOR │ ───────────────>│ JUDGE │
└─────────┘ └─────────┘
^ │
│ APPROVE │──► Execute (with constraints)
│──────────────────────────│
│ │
│ CLARIFY │──► Actor resubmits with more info (max 2 retries)
│◄─────────────────────────│
│ │
│ REJECT │──► Logged as denied action
│◄─────────────────────────│ Actor may NOT retry same proposal
│ Actor may submit revised proposal
│
HUMAN ESCALATION
│
Judge can't decide → routes to Scott
with structured dispute (both positions)
```
- **CLARIFY**: Actor must add missing evidence or narrow scope. Max 2 clarify cycles, then auto-reject.
- **REJECT**: Hard no on *this* proposal. Actor may submit a revised proposal with different scope/justification. Repeat rejections on same target trigger an audit flag.
- **Ambiguity**: Judge rates confidence 0.0–1.0. Below 0.6 → auto-route to CLARIFY. Below 0.3 → escalate to human.
## 4. Integration Point — MCP Tool Proxy Middleware (Option B)
### Options Evaluated
| Option | Description | Pros | Cons | Verdict |
|---|---|---|---|---|
| **A** | @cos acts as dispatch + judge | Simple, no new infra | Violates actor-judge separation; @cos is an actor | ❌ |
| **B** | Middleware intercepting all MCP tool calls | Clean separation, transparent to agents | Requires MCP proxy layer | ✅ **Recommend** |
| **C** | Per-department judge agents | Granular, domain-aware | Explosion of judge instances; inconsistent rulings | ❌ |
### Recommended Architecture
Introduce a thin **judge proxy** between agent MCP tool calls and actual tool execution:
```
Agent (@engineering)
│
├── mcp_call("forgejo.create_pr", params)
│
▼
MCP Judge Proxy ──► Classify risk tier of tool call
│ If full-judge-review: build proposal → @judge → verdict
│ If lightweight: pattern check → approve/flag
│ If no-judge: pass through
▼
Actual Tool Execution (or rejection)
```
The proxy lives in the OpenClaw gateway layer, intercepting MCP tool invocations before they reach the tool implementation. This means:
1. **Zero agent changes** — existing agents don't need to know the judge exists
2. **Single enforcement point** — one place to configure risk tiers and judge routing
3. **Latency budget**: lightweight checks <100ms; full judge review targets <2s (single model call)
4. **Audit trail**: every proposal and verdict is logged to `~/.openclaw/judge-audit/`
### Kubernetes Integration (kubeclaw)
In the kubeclaw operator (see [kubeclaw/README.md](kubeclaw/README.md)), the judge is implemented as a **sidecar container** in each Agent Pod:
- **Actor Agent Container**: Runs the OpenClaw agent process with exactly one tool: `template_workflow(workflowTemplateRef, params)`. No raw exec, no arbitrary file writes, no network access except to judge sidecar.
- **Judge Sidecar**: Runs a separate, stronger model instance (e.g., `deepseek-v4-pro:cloud`). Evaluates every `template_workflow` call against `CompliancePolicy` CRDs.
- **Workflow Executor Sidecar**: Runs approved workflow steps with structured input/output, logs everything to audit trail.
## 5. Implementation Priority
Ordered by [risk × irreversibility]:
| Priority | Surface | Tier | Rationale |
|---|---|---|---|
| P0 | Exec/command on host | full-judge-review | Can compromise the entire system. Deny-by-default exec proxy first. |
| P0 | Forgejo PR creation | full-judge-review | Code mutation to production repos. Second-highest blast radius. |
| P1 | File writes outside workspace | full-judge-review | Filesystem mutation outside sandboxed workspace. |
| P1 | Web publishing | full-judge-review | Public-facing content; reputational and correctness risk. |
| P2 | Discord messages | full-judge-review | External communication; lower irreversibility (editable). |
| P3 | Vault/internal file writes | lightweight-approval | Internal-facing, reversible, contained blast radius. |
## 6. Metrics & Observability
| Metric | Target | Alert Threshold |
|---|---|---|
| Judge approval rate | 70-85% | <50% (judge too strict) or >95% (judge too permissive) |
| Human override rate | <5% | >10% (judge misaligned with human intent) |
| False negative rate (audited) | <2% | >5% (judge blocking legitimate work) |
| Latency: lightweight check | <100ms p95 | >250ms |
| Latency: full judge review | <2s p95 | >5s |
| Clarify cycles per approval | <1.0 avg | >2.0 (proposals poorly formed) |
| Escalations to human | <3/day | >10/day |
**Audit cadence**: Weekly review of all REJECT verdicts and all escalated proposals. Monthly spot-check of 5% of APPROVE verdicts for correctness (false-approval detection).
## 7. Implementation Sequence
### Phase 1 (Week 1): Judge Model & Protocol
- Define @judge agent config (narrow model, security-hardened prompt)
- Implement proposal/verdict JSON schemas
- Unit test judge on 50 canned proposals (balanced approve/reject/clarify)
### Phase 2 (Week 2): MCP Proxy for Exec + Forgejo
- Build MCP tool proxy in gateway layer
- Wire exec and forgejo.create_pr through judge
- Shadow mode: log verdicts but don't enforce (measure metrics)
### Phase 3 (Week 3): Enforcement + Remaining Surfaces
- Enable enforcement for exec + forgejo
- Add Discord, web publish, file write surfaces
- Human escalation path (Discord DM to Scott with structured dispute)
### Phase 4 (Week 4+): Tuning
- Review metrics, tune judge prompt for precision/recall balance
- Add lightweight-approval tier for internal writes
- Deploy kubeclaw operator with judge sidecar pattern
## 8. Design Decisions
| Decision | Resolution | Rationale |
|---|---|---|
| Judge model | `deepseek-v4-pro:cloud` | Strong reasoning model for accurate verdict evaluation; separate from actor agents' `deepseek-v4-flash:cloud` |
| Session-level approval | **Per-action, never session-scoped** | Every action gets its own verdict. Follows Zero Trust/BeyondCorp principles — continuous per-request verification. Capability-based security: unforgeable tokens per specific action, revoked after use. |
| Judge adversarial robustness | Periodic testing required | Actors may learn to write proposals that exploit judge blind spots. Schedule adversarial testing every 2 weeks after Phase 3. |
| Human escalation path | Discord DM to Scott | Structured dispute with both positions (actor's justification vs. judge's reasoning). Scott makes final call. |
## 9. References
- [scobleclaw#7](https://os.scoble.me/forgejo/marcus/scobleclaw/issues/7) — This ticket
- [kubeclaw architecture](kubeclaw/docs/architecture.md) — Kubernetes operator implementing judge sidecar
- [Agent Judge Rules](https://os.scoble.me/forgejo/marcus/scobleclaw/src/branch/main/docs/agent-judge-rules.md) — Per-action validation rules
- Nate's YouTube: [LLM Agents: The Security Breach Pattern Nobody's Talking About](https://youtu.be/SX1myuPEDFg)

44
docs/agent-judge-rules.md Normal file
View file

@ -0,0 +1,44 @@
# Agent Judge — Action Validation Protocol
The judge validates every CoS action before execution. It sits between my intent (what I plan to do) and the tool call (what I actually execute). If the judge rejects, I don't execute — I fix the approach.
## Validation Rules (checked on every tool call)
### 1. SDLC Gate
- **Is there code being created/modified?** → Must have an issue in the correct department repo (marcus/engineering, not sscoble/*)
- **Is a PR being created?** → Must use the correct agent token (not Scott's)
- **Are binaries/build artifacts in the diff?** → Reject
- **Is a PR being merged without review?** → Only allowed if Scott explicitly approved
### 2. Dispatch Gate
- **Is this a dispatch to another department?** → Must include `[Project: Name]` prefix
- **Does the task include clear expectations?** → Must have "Expect: ..." line
- **Is the task scoped to ONE thing?** → Reject bundled tasks
- **Am I doing the work myself instead of dispatching?** → Reject (unless editorial/merge work that subagents consistently fail)
### 3. Ticket Gate
- **Am I creating an issue?** → Must be in the correct repo per forgejo-workflow skill
- **Does the issue have a label?** → Must have at least one
- **Is the issue well-groomed?** → Title actionable, body has context + acceptance criteria
### 4. OB1 Gate
- **Did I just create/close/update an issue?** → Must capture a thought to OB1 with forgejo_url
- **Did I just make a decision?** → Must capture with type "decision"
- **Did I just observe something important?** → Must capture with type "observation"
## Judge Execution
Before ANY tool call that writes externally (Forgejo, files, Discord, web):
1. Pause — mentally run the validation rules
2. If any rule fails → fix the approach, don't execute
3. If all pass → execute
4. After execution → capture OB1 thought if ticket/decision gate applies
## Self-Audit
Every 3-5 user interactions, check:
- Did I follow SDLC on every code change?
- Did I capture OB1 thoughts?
- Did I dispatch instead of doing work myself?
- Did I use the right repos?