From 2187c24d0de0facae26ca06bf37a443d332d6912 Mon Sep 17 00:00:00 2001 From: Don Garrett Date: Fri, 2 Oct 2026 19:33:17 +0000 Subject: [PATCH] Address review comments on the architecture Name the action classes harmless, contained and dangerous instead of numbering them; say what escalation doesn't depend on; say when a work item's Matrix thread starts and how threads route commands; add open questions for the state store's engine, ops issuing credentials, and releases. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE --- docs/Architecture.md | 68 ++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/docs/Architecture.md b/docs/Architecture.md index ef5d77a..7e4628a 100644 --- a/docs/Architecture.md +++ b/docs/Architecture.md @@ -66,14 +66,16 @@ injection, and one built to contain a hostile model makes the agent useless. ### P3. Actions by their effects +Every action is **harmless**, **contained** or **dangerous**: + | Class | What it is | Examples | Rule | |---|---|---|---| -| **1. No side effects** | Changes nothing outside a disposable sandbox | Reading, querying, searching, running tests in a throwaway environment | Always allowed | -| **2. Contained** | Stays within the agent's own area, or has a known undo | Editing the workspace, committing to the agent's branch, opening a PR, writing its own notes; for ops, an approved step with a known undo | Allowed in scope; logged and reported | -| **3. Dangerous** | Irreversible, visible beyond the agent's area, or outside what was agreed | Merging to a default branch, deleting data, changing a firewall, installing on a host the agent doesn't own, contacting third parties, spending beyond a budget | Needs discussion, unless an approved step sequence (O2) or the operator in conversation names the exact action | +| **Harmless** | Changes nothing outside a disposable sandbox | Reading, querying, searching, running tests in a throwaway environment | Always allowed | +| **Contained** | Stays within the agent's own area, or has a known undo | Editing the workspace, committing to the agent's branch, opening a PR, writing its own notes; for ops, an approved step with a known undo | Allowed in scope; logged and reported | +| **Dangerous** | Irreversible, visible beyond the agent's area, or outside what was agreed | Merging to a default branch, deleting data, changing a firewall, installing on a host the agent doesn't own, contacting third parties, spending beyond a budget | Needs discussion, unless an approved step sequence (O2) or the operator in conversation names the exact action | **In scope** means within what the operator agreed to (L3). The same action can be -class 2 in one work item and class 3 in another. **Move actions down the classes +contained in one work item and dangerous in another. **Make actions less dangerous wherever it is cheap** (P5). ### P4. Three layers of control @@ -82,7 +84,7 @@ wherever it is cheap** (P5). destinations it can't reach (X2), branches it can't push to or merge into, mounts it doesn't have, budget caps, the kill switch, idempotent external effects (L8). They hold even if every model is fooled. -2. **A deterministic fast path:** class-1 actions, and class-2 actions a static rule +2. **A deterministic fast path:** harmless actions, and contained ones a static rule recognises as in scope, proceed without asking any model. Most actions go here. 3. **The action classifier** (L4) for everything else. @@ -117,9 +119,10 @@ naming a milestone (D4). review), on the operator's own inference host behind LiteLLM (L9). - **Frontier models are helpers the driver calls:** a quick question (`ask_expert`) or a scoped sub-task (delegation), L9. -- **Escalation never depends on self-assessment alone.** The driver is prompted to - ask for help when unsure, and observable triggers (repeated failures, caps) - escalate too, since small models are poorly calibrated. +- **Escalation doesn't wait for the model to notice.** The driver is prompted to + ask for help when unsure, which is its own judgement; observable triggers + (repeated failures, caps) escalate as well, even when it doesn't ask, since small + models are poorly calibrated about their own competence. - **This is an experiment with a cheap exit:** if a local model falls short at some kind of work, a frontier model drives it instead, as a configuration change. @@ -210,7 +213,7 @@ again after a restart), permissions (L3), and work in progress as intents (L8). - **Dev work:** the assigned issue and what was agreed in its conversation (D1). Documents inform it but don't widen it (D2). The real check is the PR review. - **Ops work:** an approved step sequence, exactly (O2). -- **Always discussed first:** any class-3 action not named by an approved step +- **Always discussed first:** any dangerous action not named by an approved step sequence or by the operator in conversation. - **Labels and words steer, and are never required** (D1): leaving one off is never an error. @@ -261,12 +264,19 @@ executes nothing, so it runs on the agent host. room per agent, behind a `Transport` interface so another chat system is one new class. Existing Matrix clients are the UI. -**A thread per work item.** Each item's conversation is a Matrix thread. -- A reply in a thread goes to that item, even while it is parked. -- Commands in a thread apply to that item. At top level, `stop` applies to the - active item and `status` to the agent; any other top-level message starts a new - conversation in its own thread, and the agent offers to continue an existing item - if it clearly belongs there. +**A thread per work item.** Each item's conversation is a Matrix thread, and the +thread is how the agent knows which item a message is about. +- **When a thread starts:** with the item. For an assigned issue, the agent's + acknowledgement ("queued myproject#14") is the thread's first message. For a + conversation, the operator's own top-level message is, and the agent replies in + a thread under it. The item records its thread, so the mapping survives restarts. +- **Inside a thread,** every message and command (`go`, `stop`, `abandon`, an + answer) applies to that item, even while it is parked. +- **At top level,** `status` is about the agent and `stop` stops the active item. A + command that names its item applies to it ("abandon #14"). Any other command is + ambiguous, so the agent asks which item rather than guessing. Any other message + starts a new conversation, and the agent offers to continue an existing item if + it clearly belongs there. - **Any conversation about work is a work item from its first message,** however vague ("I'm having trouble with X…"). It may end with a project, an issue, or just an answer. @@ -638,10 +648,10 @@ the project needs and installs any gap **before any planning**; toolchain versio become facts (L6). **The build VM is the agent's to change:** installing packages and toolchains there, -cleaning caches or reinstalling dependencies to fix a broken build are class 2, +cleaning caches or reinstalling dependencies to fix a broken build are contained, reported but not asked about. Every install is recorded (as a fact and in scope memory) so the VM can be rebuilt; recurring ones go into the template (X4). -Installing on any other host is class 3. +Installing on any other host is dangerous. ### D4. Git, branches and PRs @@ -693,9 +703,9 @@ Installing on any other host is class 3. | Class | Examples | |---|---| -| 1 | Reading the repository, running builds and tests on the build VM, querying Gitea | -| 2 (in scope) | Editing the workspace; milestone branches, pushes and PRs; commenting on its issues and PRs; filing agreed issues (D1); changing its build VM (D3) | -| 3 | Merging to the default branch (unless the trust dial allows), deleting remote branches or others' work, changing repository settings, anything outside the item's repository or build VM | +| Harmless | Reading the repository, running builds and tests on the build VM, querying Gitea | +| Contained (in scope) | Editing the workspace; milestone branches, pushes and PRs; commenting on its issues and PRs; filing agreed issues (D1); changing its build VM (D3) | +| Dangerous | Merging to the default branch (unless the trust dial allows), deleting remote branches or others' work, changing repository settings, anything outside the item's repository or build VM | ### D7. Deployment and isolation @@ -754,7 +764,7 @@ stricter controls because live changes have no review step before they take effe |---|---|---| | Undo | discard a branch | often none (`zfs destroy`, a firewall lockout) | | Scope | the assigned issue and its conversation | approved step sequences (O2) | -| Class 3 | discuss | discuss, and exact-step approval | +| Dangerous actions | discuss | discuss, and exact-step approval | | Work shape | milestones → PRs | short reactive tasks → live changes | | Intake | issues in the project's repository, Matrix | issues in the infrastructure repository, alerts, duties, Matrix | | Reach | sealed build VMs | Tailscale, credentials, both sites | @@ -795,9 +805,9 @@ template with a trigger, rate limit and expiry, for a routine fix). | Class | Examples | |---|---| -| 1 | Metrics, logs, status, read-only MCP calls, issues, the world model | -| 2 (in scope) | Approved steps with a known undo; its own world model; commenting on and filing issues in the infrastructure repository | -| 3 | Every other infrastructure write; browser actions on admin UIs (each confirmed); **anything on network gear**, the most conservative case, since a bad change can sever the path to fix it | +| Harmless | Metrics, logs, status, read-only MCP calls, issues, the world model | +| Contained (in scope) | Approved steps with a known undo; its own world model; commenting on and filing issues in the infrastructure repository | +| Dangerous | Every other infrastructure write; browser actions on admin UIs (each confirmed); **anything on network gear**, the most conservative case, since a bad change can sever the path to fix it | ### O4. Interfaces @@ -917,6 +927,16 @@ shared between agents. - **Browser access to network gear:** standing, or handed off per task. - **Relocating a stateful MCP server** off the agent host, if one ever justifies losing attribution. +- **The state store's engine.** SQLite, one database per agent, for now. If + scaling or backups call for it, a server database such as PostgreSQL on the agent + host (still one database and role per agent). +- **`nisse-ops` issuing credentials** to other agents: an SSH key for a new VM, Gitea + credentials for a new project. Writing into another agent's secret set crosses + the isolation in L12, so it needs a designed channel and its own approval rule. +- **Releases.** Building and publishing artifacts at each release, usually by tag, + sometimes from release branches: what the agent may do (a release is visible + beyond its own area, so dangerous unless the trust dial says otherwise) and how a + build plan names one. - **Recovering the agent host.** It is a single point of failure for every agent, `nisse-ops` included, so its recovery can't depend on ops running. A recovery plan (rebuilding the host, restoring state from snapshots) is ops work still to