Revise the deployment model: an agent host and tool VMs #90

Merged
dev-agent merged 12 commits from deployment-model into main 2026-10-02 19:36:33 +00:00
2 changed files with 39 additions and 31 deletions
Showing only changes of commit 4ca8e1f550 - Show all commits

No files matched your search

+37 -29
View File
@@ -138,15 +138,16 @@ Everything in this part is used by every agent.
| **Scope** | What a work item operates on; context that outlives it | A repository | A site and service |
- **Sources of work items:** duties, the operator (always), and other agents. A
request from another agent arrives through the receiving agent's normal intake
and carries no authority of its own: the receiver applies its own rules.
request from another agent is an issue assigned to the receiving agent, and
carries no authority of its own: the receiver applies its own rules.
- **A duty run is read-only;** a finding that needs attention becomes a work item,
deduplicated by a stable fingerprint so a repeat attaches to the open item.
- **A work item is the unit of state:** its conversation, proposals, pending
questions, pinned roles (L9) and transcript (L7). Its locally minted ID is also
its correlation ID in logs.
- **An external record is linked when there is one** (a Gitea issue, a Vikunja
ticket); external truth wins on divergence (L8).
- **One tracker: Gitea issues,** for every agent. An issue assigned to an agent is
a work item for it, and the issue is the item's record (D1, O5). External truth
wins on divergence (L8).
- **Work items don't nest:** a milestone is a phase of one; a delegation is an
episode in one.
@@ -159,10 +160,11 @@ Everything in this part is used by every agent.
| **waiting on the operator** | an answer, a go-ahead, a review or a merge |
| **blocked** | a recorded dependency to close |
- **Blocked comes from external truth:** a Gitea issue dependency, or another
agent's ticket. It becomes ready only when the dependency actually closes (P6);
the operator can remove the dependency. An agent that finds a blocker mid-work
proposes recording it, and records it only with the operator's agreement.
- **Blocked comes from external truth:** a Gitea issue dependency, including on an
issue in another repository, such as a request to another agent. It becomes
ready only when the dependency actually closes (P6); the operator can remove the
dependency. An agent that finds a blocker mid-work proposes recording it, and
records it only with the operator's agreement.
**One active work item per agent; the rest are parked.**
- **Switching happens only at safe points:** the active item is waiting, blocked or
@@ -226,8 +228,8 @@ agreement: a restatement awaiting a go-ahead (D1), a design (D2), a step sequenc
model's description (L11).
- **Approvals expire** where the world can change underneath them (O2).
**Approval sources:** Matrix (L5), Gitea comments (D1) and Vikunja comments (O5),
each checked against its own operator allowlist. The first valid approval wins and
**Approval sources:** Matrix (L5) and Gitea comments (D1, O5), each checked
against its own operator allowlist. The first valid approval wins and
is echoed to the other channels. The knowledge vault is never one (L10).
**The trust dial:** how much the operator reviews after the fact, set by the
@@ -293,8 +295,8 @@ verified and left open); it says what happened to a message it can't act on yet,
rather than refusing; progress is prose at a readable rate, with raw tool activity
on request; and it asks when unsure, including mid-implementation.
**Other channels:** Gitea and Vikunja carry work and records, read as structured
JSON (never through a chat bridge). Comments from allowlisted operators are
**Gitea carries work and records,** read as structured JSON (never through a chat
bridge). Comments from allowlisted operators are
instructions or approvals; anything else is data (L11).
**Outages delay, never fail.** Intake retries indefinitely with capped backoff and
@@ -457,7 +459,7 @@ browses it in Obsidian.
receive-only on it. So conflicts can't arise.
- **Never an instruction or approval channel.** Work is never read out of the vault,
and no agent writes there to make another act; agents coordinate through the
operator, Matrix or tickets (L1). The operator points to a note in conversation
operator, Matrix or issues (L1). The operator points to a note in conversation
when it is input (L5).
- **Corrections go through conversation** (L6). Break-glass: stop the agent, edit on
the host, restart.
@@ -710,13 +712,14 @@ see it or only harness operations use it (the push key). Anything the model's
commands can see must be scoped for testing, never production.
**Provisioning:** build VMs come from a template via Terraform; eventually at
`nisse-dev`'s request to `nisse-ops` (O5), until then by the operator.
`nisse-dev`'s request to `nisse-ops` (an issue in the infrastructure repository,
O5), until then by the operator.
---
## Part O — `nisse-ops`
Operates infrastructure across both sites, driven by tickets and duties, with
Operates infrastructure across both sites, driven by issues and duties, with
stricter controls because live changes have no review step before they take effect.
### O1. How it differs
@@ -727,7 +730,7 @@ stricter controls because live changes have no review step before they take effe
| Scope | the assigned issue and its conversation | approved step sequences (O2) |
| Class 3 | discuss | discuss, and exact-step approval |
| Work shape | milestones → PRs | short reactive tasks → live changes |
| Intake | Gitea issues, Matrix | Vikunja tickets, alerts, duties, Matrix |
| Intake | issues in the project's repository, Matrix | issues in the infrastructure repository, alerts, duties, Matrix |
| Reach | sealed build VMs | Tailscale, credentials, both sites |
They are separate deployments over one library; one instance playing both would
@@ -766,8 +769,8 @@ template with a trigger, rate limit and expiry, for a routine fix).
| Class | Examples |
|---|---|
| 1 | Metrics, logs, status, read-only MCP calls, tickets, the world model |
| 2 (in scope) | Approved steps with a known undo; its own world model and tickets |
| 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 |
### O4. Interfaces
@@ -778,15 +781,20 @@ template with a trigger, rate limit and expiry, for a routine fix).
- **Event intake** as structured JSON (directly or via Alertmanager); curated
updates go to `#ops`.
### O5. Tickets and duties
### O5. Issues and duties
- **A Vikunja ticket is a work item's record;** Matrix is the live conversation.
Flow: filed → picked up → discussed → status updates → closed with the result.
- **Tickets that trigger action are operator-scoped.** Tickets from duties (findings)
and from other agents' requests (L1, e.g. `nisse-dev` asking for a build VM) may
be picked up and planned, but any write needs a step sequence the operator
approves. Until standing approvals exist, that means every provisioning request.
- **Vikunja comments approve plans on their own ticket** (`approve rev-N`).
- **One infrastructure repository for both sites,** holding ops' issues (it need
hold no code). Sites are labels (`garrett-hall`, `hartman-hut`), so cross-site
work is one issue. Intake works as in D1: an issue assigned to the ops agent is a
work item and its record, labels steer, and Gitea dependencies mark it blocked.
Matrix is the live conversation.
- **Action is operator-scoped.** Issues from duties (findings, opened by ops itself
and deduplicated by fingerprint) and requests from other agents (L1, e.g.
`nisse-dev` asking for a build VM, which then waits blocked on that issue) may be
picked up and planned, but any write needs a step sequence the operator approves
(O2). Until standing approvals exist, that means every provisioning request.
- **Comments on the issue approve its plan** (`approve rev-N`), as an alternative to
Matrix (L3).
### O6. Deployment
@@ -855,7 +863,7 @@ so every upstream action traces to one agent.
### X5. Supporting services
All self-hosted: Matrix (Synapse), Gitea, LiteLLM, the local inference host,
Vikunja, Vaultwarden, LLDAP with Authelia, and Syncthing for the operator's copy of
Vaultwarden, LLDAP with Authelia, and Syncthing for the operator's copy of
the vault. Each agent has its own account, token and room in each; nothing is
shared between agents.
@@ -872,8 +880,8 @@ shared between agents.
- **More than one active work item:** needs per-item serialisation, an inference
scheduler and per-item intents. Build VMs and threads already separate workspaces
and conversations.
- **Whether pure conversations need an external record** (dev work on a repository
and ops tickets already have one).
- **Whether pure conversations need an external record** (work on a repository
already has its issue).
- **Stacked PRs:** starting a milestone before the previous one merges, after
experience with waiting (D4).
- **Build VM collection:** how long idle, and whether it needs the operator's word.
+2 -2
View File
@@ -321,8 +321,8 @@ if the implementation differs.
on the operator defaults to 10 minutes. When the driver chooses the next ready
item and the choice fails or is invalid, fall back to the oldest.
- **L3 revision stamping:** in Matrix, a reply is stamped with the revision whose
message had been sent when it arrived; on a Gitea issue or a Vikunja ticket,
with the latest proposal comment older than the reply.
message had been sent when it arrived; on a Gitea issue, with the latest
proposal comment older than the reply.
- **L5 transport:** the `Transport` interface is send, ask and receive. Intake
backoff is exponential, capped at a few minutes.
- **L6 fitting order:** first shorten old tool results to their first and last