docs: agent platform v2.1 — hardening review (agreed 2026-07-21)
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled

Fixes two v2 design bugs and closes the review gaps alvis accepted:
- Transport rule: sync = direct A2A RPC (journaled to KB); async = KB
  polling; polling never on a sync path.
- GPU residency: local a(t)=f(VRAM), never-evict set, pre-load checks.
- NEW §5b Humans (plural): per-human private banks + shared household
  bank, interlocutor-scoped recall, cross-human leakage forbidden;
  human inbox design (priority-routed notifications, digest, no
  default-approve, vacation mode).
- NEW §6b fabric-keeper: scheduling decomposed (triggering/dispatch/
  admission/time); the keeper janitor owns leases, deadlines, cron
  catch-up, dead-letter, digests.
- NEW §6c KB as tier-0 (backup/monitoring/degraded mode).
- Completion vs verification: Done column = unverified, closed task =
  verified, producer never closes own task.
- A2A auth mandatory (LAN untrusted: VPN peers); fabric hygiene
  (creation quotas, ancestry cap, dead-letter); personas/Cards in git;
  golden-set eval gate on backbone swaps.
- Decision log entries 12-21.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Qg9gi7H7HtT8MncoXAB
This commit is contained in:
2026-07-21 07:34:19 +00:00
parent 7be30c7174
commit 714a9ca785

View File

@@ -1,6 +1,6 @@
# DESIGN — Agap Agent Platform v2: the agent algebra # DESIGN — Agap Agent Platform v2.1: the agent algebra
Status: **v2, agreed with alvis 2026-07-21** (supersedes v1 draft `2499218c`) Status: **v2.1, agreed with alvis 2026-07-21** (v2 `7be30c71` + hardening review)
Owner: alvis · Written with Claude Owner: alvis · Written with Claude
One design, two axioms, one verb. Everything alvis asked for — per-model queues, One design, two axioms, one verb. Everything alvis asked for — per-model queues,
@@ -23,6 +23,9 @@ rule. This document is the reference; the Kanboard A2A tasks implement it.
- **Backbone** — the concrete LLM an agent currently uses for reasoning. - **Backbone** — the concrete LLM an agent currently uses for reasoning.
- **Context ref** — a pointer (Hindsight bank id, git ref, KB task id, file - **Context ref** — a pointer (Hindsight bank id, git ref, KB task id, file
path) passed *instead of* pasted content. path) passed *instead of* pasted content.
- **fabric-keeper** — the janitor daemon owning time semantics (§6b): lease
sweeps, deadlines, cron catch-up, dead-letter, inbox digests. It never
assigns work.
## 1. Why ## 1. Why
@@ -75,6 +78,21 @@ Context travels **by reference, never by value** — the single most important
efficiency rule for inter-agent communication (A2A context-passing practice). efficiency rule for inter-agent communication (A2A context-passing practice).
`sync` vs `async` is not a second mechanism: sync = submit + await. `sync` vs `async` is not a second mechanism: sync = submit + await.
**Transport rule.** Sync and async share the algebra but not the transport:
**sync goes direct** — an A2A `message/send` RPC straight to the target agent's
endpoint, journaled to KB afterwards; **async/durable goes through KB** and is
drained by polling workers. KB polling must never sit on a sync path — a sync
call may not inherit poll-interval latency.
### Completion vs verification
KB convention (native semantics, no new machinery): the **Done column =
unverified completion** — the worker/agent finished and self-reported. **Closing
the task = verified completion.** The producer never closes its own task; the
submitter, a human, or (later) a reviewer-agent closes after checking the
task's acceptance criteria. Lifecycle: … → done (unverified) → closed
(verified). For code, the PR review is the verification; closing follows merge.
### Theorems — the old rules become consequences ### Theorems — the old rules become consequences
1. **"Queues are per model, not per agent."** Every agent has an inbox, but 1. **"Queues are per model, not per agent."** Every agent has an inbox, but
@@ -132,6 +150,30 @@ fabric free of micro-churn by construction.
└─────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────┘
``` ```
### GPU residency — a local model's a(t) is not 1
Local "free" models contend for VRAM with interactive components (measured on
the 8 GB GTX 1070: bge-m3 + gemma3:4b + tei-reranker ≈ 6.2 GB; loading anything
bigger evicts the reranker and silently regresses recall latency). So a local
model's availability is **a(t) = f(VRAM headroom)**, and the model registry
carries a **residency policy**: a never-evict set (embedder, reranker —
interactive-critical), allowed co-residency groups, and a pre-load check every
worker must pass before pulling a model onto a GPU. With more GPUs this becomes
a placement problem — same policy, more slots.
### Personas and Cards are code
SOUL.md files and agent Cards live **in git** and are deployed to runtimes —
never edited live in volumes. "Who changed Adolf's soul" must be a `git log`
answer. (The current SOUL.md in the adolf-state volume is migration debt.)
### Eval gate on backbone/routing changes
Backbone swap being "one constraint edit" is quality-blind. Each agent keeps a
**golden set** (1020 canonical exchanges); any backbone or routing change is
shadow-replayed against it and compared (Langfuse datasets/evals) before taking
effect. The algebra's flexibility must not become a silent-degradation machine.
## 4. A2A: the protocol, adopted now ## 4. A2A: the protocol, adopted now
The algebra maps 1:1 onto A2A v1.0 (Jan 2026), which is why we implement the The algebra maps 1:1 onto A2A v1.0 (Jan 2026), which is why we implement the
@@ -149,6 +191,10 @@ Claude loop, workers) exposes/consumes A2A; Kanboard remains the durable state
behind the endpoints. Scalability/extensibility later (remote nodes, third-party behind the endpoints. Scalability/extensibility later (remote nodes, third-party
agents) then needs zero redesign. agents) then needs zero redesign.
**Auth is mandatory on every A2A surface.** The LAN is **not trusted** — the
xray/3x-ui VPN terminates other people's peers on it. No unauthenticated
JSON-RPC listener, ever: shared tokens minimum, mTLS preferred.
## 5. Trust & sandboxing ## 5. Trust & sandboxing
**Trust classes** (on every Card): **Trust classes** (on every Card):
@@ -183,6 +229,43 @@ Reviewer = human, or later a reviewer-agent (just another persona).
(Adolf) outrank background ones (researcher, consolidation) — arbitration lives (Adolf) outrank background ones (researcher, consolidation) — arbitration lives
in the fabric (priorities + a small governor rule), not in LiteLLM. in the fabric (priorities + a small governor rule), not in LiteLLM.
**Fabric hygiene (runaway protection):** agents submit tasks that cause agents
to submit tasks — idempotency keys stop duplicates, not generative loops. So:
per-agent **task-creation quotas**; an **ancestry depth cap** on provenance
chains; cycle detection at submit; and a **dead-letter** state for poison tasks
after max-retries — never an infinite retry loop through paid quota.
## 5b. Humans (plural) and memory partitioning
There is more than one human already (alvis and elizaveta are both on Adolf's
Matrix allowlist) and there will be more. Every human is an agent with
trust=human, their own inbox, and — critically — **their own privacy domain**.
**Memory partitioning (hard rules):**
- **Per-human private banks**: `adolf-alvis`, `adolf-elizaveta`, … Everything
learned in conversation with human H goes to H's private bank by default.
**Content from one human's conversations must never surface to another
human.** This is a correctness property, not a preference.
- **One shared household bank** for facts that are explicitly household-wide
(addresses, devices, routines, shared plans). Trusted agents may write;
**promotion from a private bank happens only by that human's explicit action
or approval task** — never automatically.
- **Recall is interlocutor-scoped**: when Adolf talks to H it recalls from H's
private bank + the shared bank, nothing else. The recall/retain hooks select
the bank by interlocutor identity.
- Sandboxed agents (Torgash, researcher) read at most the shared bank; never
any private bank. This is the cross-human face of the memory matrix.
- The current single `adolf` bank is migration debt: split into
`adolf-alvis` + shared.
**Human inbox design:** notifications are priority-routed — gate/urgent tasks
ping the human via Matrix (Adolf initiates them; cf. proactive-messaging work),
everything else lands in a daily digest from the fabric-keeper. Ignored gate
tasks park and re-remind; **they never default-approve**. Vacation mode: a
human's a(t)=0 parks their inbox like any other scarce queue — gated flows
wait; predefined degraded defaults apply where explicitly configured.
## 6. Executor — thin KB-polling workers ## 6. Executor — thin KB-polling workers
No Temporal/Hatchet: at homelab scale (dozens of tasks/day) a durable-execution No Temporal/Hatchet: at homelab scale (dozens of tasks/day) a durable-execution
@@ -205,6 +288,32 @@ queued. Two workers on one queue never double-run a task (claim is atomic).
Idempotency keys on submission prevent duplicate proactive tasks. OpenClaw cron Idempotency keys on submission prevent duplicate proactive tasks. OpenClaw cron
is the proactive *submitter* (Adolf's schedule); workers are the *drainers*. is the proactive *submitter* (Adolf's schedule); workers are the *drainers*.
### 6b. Who is "the scheduler"? — decomposed, plus one janitor
There is deliberately **no central dispatcher**. Scheduling decomposes into
four concerns, each with its own owner:
| Concern | Question | Owner |
|---|---|---|
| Triggering | when do tasks appear? | OpenClaw cron, agents' delayed self-submissions, humans |
| Dispatch | which task runs next? | each queue's worker (claim by priority under its a(t)) |
| Admission | may it run now? | LiteLLM budgets/rate + the budget governor |
| **Time semantics** | deadlines, missed crons, stuck tasks? | **the fabric-keeper** |
The **fabric-keeper** is one tiny always-on daemon that never assigns work: it
sweeps expired leases back to queued, enforces deadlines (escalating to
inboxes), applies per-schedule **cron catch-up policy** (missed window →
run-once | skip, configured per schedule), moves poison tasks to dead-letter,
emits the human daily digest, and exports queue depths to Zabbix. Deadline and
catch-up semantics live here and nowhere else.
### 6c. Kanboard is tier-0 now
Promoting KB to the fabric's backbone promotes its ops class: **backups on par
with the vault**, Zabbix monitoring of the service and API, and a defined
**degraded mode** — if KB is down, Adolf still answers Matrix chat (no fabric
operations, no task memory), workers park, nothing crashes or data-loses.
## 7. Observability — Langfuse (kept), wired for real ## 7. Observability — Langfuse (kept), wired for real
Decision: keep **Langfuse** (already deployed; best-in-class self-hosted: Decision: keep **Langfuse** (already deployed; best-in-class self-hosted:
@@ -251,3 +360,24 @@ completion is traced here — this is where sub-Task granularity lives.
9. Executor = thin KB-polling workers; no Hatchet/Temporal at this scale. 9. Executor = thin KB-polling workers; no Hatchet/Temporal at this scale.
10. Sync routing = LiteLLM Auto Router v2; fabric owns async/parking. 10. Sync routing = LiteLLM Auto Router v2; fabric owns async/parking.
11. Sandbox = per-task workspace lease + container + PR-only merges. 11. Sandbox = per-task workspace lease + container + PR-only merges.
Added in v2.1 (hardening review, same day):
12. **Multi-human**: per-human private banks + shared household bank;
interlocutor-scoped recall; cross-human leakage forbidden (hard rule);
promotion to shared only by the owning human's action/approval.
13. A2A auth mandatory everywhere — the LAN is untrusted (VPN peers).
14. KB = tier-0 infrastructure (backup, monitoring, degraded mode).
15. Done column = unverified completion; closed task = verified; the producer
never closes its own task.
16. Scheduler = decomposed (cron/self-submission triggers; workers dispatch;
LiteLLM+governor admit); the **fabric-keeper** janitor owns time semantics;
cron catch-up policy is per-schedule config.
17. Personas/Cards live in git, deployed — never edited live.
18. Backbone/routing changes gated by golden-set shadow eval (Langfuse).
19. Fabric hygiene: creation quotas, ancestry depth cap, cycle detection,
dead-letter for poison tasks.
20. GPU residency policy: local a(t)=f(VRAM); never-evict set (embedder,
reranker); pre-load checks.
21. Transport: sync = direct A2A RPC (journaled); async = KB polling; polling
never on a sync path.