Skip to content
quarry Artifact [2434f4b93e]
Login

Artifact [2434f4b93e]

Artifact 2434f4b93e2cc399162e6ebd66fe19e7eca9c89aa36d1f7159557832fec216ad:


Summary

Requests leave the account page. They live at /me/requests, counted in the header beside the inbox, waiting ones first and answered in place; everything decided folds into History. Each request reads as the action it takes ("Create agent rajeev"), not the op that runs it. A burst from one agent is one email. And an agent may ask for a plan: several calls under one title, approved with one tap, run in order as the person, stopping at the first that fails.

Motivation

RFC 0027 shipped on 2026-11-08 and was used the same evening to set up a QA agent. Four calls were needed (an agent, its token, a role, a handover), so four cards, four emails and four approvals, named agent.new for rajeev and agent.token.new for rajeev, in an order only the agent knew, in a section between Password and Access that also kept a four-day-old decided request beside them. The owner: "this will get unmanageable fast".

Design

Where requests live

/me/requests, reached from a Requests entry in the header with a count of waiting requests (hidden at zero, as the inbox's is). Two tabs: Waiting, newest first, each row with Approve and Deny posted in place; History, everything decided or expired, newest first, with a filter by agent. The section on /me goes; /me/signoffs/{id} stays as the one request's page and redirects from the list where needed. Approving from a row needs the session, as now.

Requests read as actions

Each op may declare an Ask sentence, a template over its input: agent.new reads "Create agent {name}", agent.token.new "Mint a token for {agent} ({scopes}, {days} days)", agent.grant "Give {agent} {role} on {repo}", repo.claim "Hand {repo} to {account}". An op with none reads as its name and the account and repository it names, as today. The sentence uses only fields an op marks safe to show; a field holding a credential never appears.

From an email

Every button in a request's email opens exactly that request: its own page, /me/requests/{id} (a plan's page for a plan), with Approve and Deny on it, never the list (decision 6). The button itself cannot approve: a mail scanner opens every link in a message, so a link that acted on a GET would approve requests nobody read. Approval is the one tap on the page the button opens. Signed out, the button goes through sign-in and returns to that same request (/login?next=, as now). A burst's email lists each request with its own button to its own page.

One email for a burst

The first request from an agent is told at once (inbox, push, email). Requests from the same agent within the next five minutes are added to the waiting list and the inbox but not emailed separately; at the end of the window one email lists everything still waiting, each with its link, and a link to the Waiting tab. A plan is one request and one email.

Plans

An agent asks for a plan with POST /api/v1/requests:

{"title": "Set up Rajeev as QA tester",
 "reason": "Rajeev owns dantest-sandbox and tests the forge from outside",
 "steps": [
   {"op": "agent.new", "input": {"name": "rajeev"}},
   {"op": "agent.token.new", "input": {"agent": "rajeev", "name": "qa", "scopes": "api,web,sync", "days": 365}},
   {"op": "agent.grant", "input": {"agent": "rajeev", "repo": "dantest-sandbox", "role": "admin"}},
   {"op": "repo.claim", "input": {"repo": "dantest-sandbox", "account": "rajeev"}}
 ]}

At most 20 steps. Each step must be askable and inside the agent's scope on its own, by RFC 0027's rules, or the plan is refused whole with the step that fails named. A plan is one request: one row, one email, one Approve.

Approval runs the steps in order, each as the person, through the ordinary registry, each checked against the person's permissions as it runs. The first that fails stops the plan: earlier steps stand (they were each the person's act, and journalled as such), later ones do not run, and the request reads failed at step N with that step's error. Nothing is rolled back. A step's answer is kept per step; secret fields from any step go to the asking agent once, as RFC 0027 says.

A plan is approved whole (decision 2). To change it, the person denies it and the agent asks again.

Standing approvals (not now)

Turning a request into a rule ("always allow this") was weighed and held: it changes who decides, not how requests read, and is worth revisiting only when real volume shows which asks recur.

Build order

  1. /me/requests with Waiting and History, the header count, rows answered in place, and the /me section removed.
  2. Ask sentences: the template field on ops, filled for the agent, token, grant, claim, delete, invite and role ops; rows, pages and emails use them.
  3. Plans: POST /api/v1/requests, storage of steps and per-step answers, the in-order run that stops at the first failure, and the plan's page.
  4. One email per burst: the five-minute window per agent and the listing email.

Decisions

  1. The place is called Requests (owner, 2026-11-08).
  2. A plan is approved whole; no step can be unticked (decided by the implementer, stated to the owner, 2026-11-08: approval must stay exactly what runs, and a narrower plan is one Deny and one re-ask).
  3. Build A and B (the Requests page and plans); hold C, standing approvals (the design page's recommendation).
  4. A failing step stops the plan without rolling back earlier ones, which were each the person's own act.
  5. A burst window of five minutes per agent; at most 20 steps a plan.
  6. An email's button goes exactly to its request, where approving is one tap; it never goes to a list (owner, 2026-11-08: "If I can't push the button in my email and have it work, then I should go exactly to the request in the email").