Operation requests
The problem this solves. An agent working for you often reaches an operation it is not allowed to perform — removing a Space the repo owns, rewriting nodes across a partition, asking a NodeType to recompile — because on a system-synced Space no human or agent identity holds Delete, by design. Until 2026-09-04 the agent's only honest answer was a list of GUI links, one per node, one per instance. The maintainer's verdict on that: "I should get a screen with your request where I can approve and then it executes."
An Essentials/OperationRequest node is that screen.
How it works
- Propose. The agent (or you) creates a request node anywhere it may write — its own home is
the natural place — with a title, the reason, and the operation as a C# script written
against the fluent
PlanDSL below. Filing needs no rights beyond writing the node. - Read the script. It is on the page in full — that is what runs. A script is general C#, so it may do more than the DSL table lists, which is why the script is the thing to read and why even a preview is admin-gated (it runs the code).
- Approve. A global admin — not the requester — presses Approve & run as System, the page's primary action. It needs no preview: the platform runs the script in execute mode as System — the identity that owns synced Spaces — strictly in order, fail-fast, one log line per step, recording the plan as it goes. The outcome lands on the request; the full kernel log is the activity under it.
- Preview (optional). Pressing it first runs the same script in preview mode: every DSL call records a step, each target is probed (does it exist? what type? how many nodes below it?), and the plan comes back as a table. Nothing is executed. Worth it for anything destructive or large — the chunk count of a partition drain is how you learn it will take eight minutes.
- Reject at any time; a rejected request keeps its history.
🚨 An approval is bound to the script the approver saw. The Approve button carries the hash of
the text it rendered (approvedScriptHash); if the stored script no longer matches it, nothing runs
and the request says so. A request that WAS previewed additionally refuses a script changed since
the preview. So editing a script under an approver cannot get it executed.
Who pressed what is never a field anyone types: the control plane reads the framework-stamped
author of the write that set the action (lastModifiedBy), and refuses a blank one outright.
The DSL
The script sees one object, Plan, and chains what it wants. Every call records; nothing runs
until the plan completes at the end of the script (the platform appends that line).
Plan.Because("UWDeepfield and ClaimsDeepfield are retired legacy modules (#159)")
.Space("UWDeepfield").Delete()
.Space("ClaimsDeepfield").Delete();
| Call | Records |
|---|---|
Plan.Because(reason) |
why — shown first on the page |
Plan.Note(text) |
a line of explanation between steps; never executed |
Plan.Space(id).Delete() |
two steps: the Space's _GitSync source first (so no later sync re-imports it), then the Space root, recursively — absent parts are no-ops, so a re-run heals a half-removed Space |
Plan.Node(path).Read() |
reads the node; the safest first step in any plan |
Plan.Node(path).Delete() |
deletes the node and its subtree |
Plan.Node(path).Update(n => n with { … }) |
rewrites the node through a record with; content included (n.ContentAs<T>(…) to type it) |
Plan.Node(path).Create(nodeType, content, name) |
creates the node; an existing one is left as is |
Plan.Query(query).Read() / .Delete() / .Update(…) |
the same over every match of an anchored mesh query (namespace:X scope:descendants nodeType:Y) — the store refuses one that names no partition |
Plan.NodeType(path).Recompile() |
flips the type's compilationStatus to Pending; the framework rebuilds it |
Plan.Node(path).Recycle() |
disposes that node's hub, so the next access re-reads it from the mesh |
Plan.NodeType(path).Recycle() |
disposes the TYPE's hub and cascades to the types sharing its sources and every existing instance of each — prefer this |
Plan.Do(kind, target, effect, run, probe?) |
anything the verbs above do not cover — still recorded. run executes only in execute mode; probe is read-only and renders in preview. A step with no run is refused when it is written. |
Because the script is C#, the fluent APIs of the records themselves compose with it: a
.Update(n => n with { Content = (n.ContentAs<Workbench>(opts)) with { Partitions = ["Acme"] } })
is an ordinary record expression.
🚨 Everything that changes the mesh goes through the DSL — and since Do exists, everything
can. A raw Mesh call in the script still runs in both modes, but it is INVISIBLE to preview:
it does not probe, it does not appear in the plan table, and the approver approves a list that
does not mention it. That was the one hole in the promise "you approve the steps that will run",
and it was open only because an unforeseen operation had nowhere else to go. It now does:
Plan.Because("retire the anonymous grant on Edu")
.Do("revoke", "Edu/_Access/Anonymous", "removes the anonymous viewer grant",
run: ct => Plan.DeleteAt("Edu/_Access/Anonymous", ct),
probe: async ct => OperationPlan.Describe(await Plan.Find("Edu/_Access/Anonymous", ct)));
An unforeseen operation should cost a step in the table, never a step outside it.
🚨 A plan that rewrites nodes is only half a migration — recycle in the same plan
Recycle and dispose are one act under two names. The platform's verb is a RECYCLE; the wire
carries a DisposeRequest. Plan.…Recycle() is that act, recorded as a step like any other.
A per-node hub binds its NodeType and its compiled assembly once, while activating, and is then
pinned by address — nothing re-reads it while it lives. So a plan that retypes nodes changes the
STORE and reaches every running activation through nothing at all. They keep answering from what
they hold, with no error and no log line. Measured 2026-09-21: a Crm/Client → Crm/Counterparty
retype of eight partition roots wrote correctly — the query flipped to 8 Counterparty, 0 Client —
and the pages went on serving through hubs whose type registry had never heard of
CounterpartyContent, so the content read as ABSENT and the views rendered empty.
Plan.Because("Crm/Client is retired; Crm/Counterparty replaces it")
.Node("ATIOZ").Update(toCounterparty)
// … the other seven …
.Note("The writes are done; now make the live hubs notice.")
.NodeType("Crm/Counterparty").Recycle();
Why it has to be a DSL verb rather than a follow-up click. A recycle needs Update on the
target, and on a system-synced space no account holds that — not the maintainer, not a global
admin, by design. So the MCP recycle tool and the node's Recycle menu both refuse, and the only
route left is a proposal executed as System. That made recycling the one step of this shape the DSL
could not express, which is why the retype above shipped without it.
🚨 What a recycle cannot reach. It re-READS the mesh, so it picks up node content and an in-place recompile — and it does not touch a compiled MODULE assembly or the portal image, which are pinned at PROCESS start and need a roll through CD. If a recycle changes nothing, the activation was never the problem and a second one proves nothing the first did not. Never pin an image instead.
Full account: /recycle-after-deploy and
Hub Disposal Model.
Where the pieces live
Essentials/OperationRequest— the NodeType: the record, the control plane, the screen, the tests.Essentials/OperationRequest/Source/OperationDsl— the DSL. It is compiled into the type (so the Tests area pins it) and read off the mesh as text to prefix every submission: a script cannot reference a NodeType's compiled assembly, so the DSL travels as source. It stays script-compatible on purpose (no namespace, no extension methods).{request}/Script— the composed submission, aCodenode the platform writes as System.{request}/_Activity/*— every preview and run, with the kernel's full log.- The skill
/operation-request(Essentials/Skill/operation-request) — what an agent reads before filing one.
When a request looks inert
A request that keeps its pending action and never previews is not a request the API could not file — filing one through the API and watching it preview and run takes under a second, measured. It is a request whose watcher stopped, and why that could happen, what the control plane now guarantees, and the one gap still open is written up beside this page.
Why both buttons need a global admin
A preview runs the script. The DSL records instead of executing in preview mode, but the script
is not confined to the DSL — a line of raw Mesh code runs in both modes. So the gate is on
running code at all, and it is the same gate Store/Publishing's SystemRemoval puts in front
of System impersonation: SystemAuthorization.Authorize, fail-closed (a probe that answers
nothing is a no). The request's own page tells the approver exactly that.
The first use
The Deepfield retirement of 2026-09-04 (MeshWeaver.Reinsurance#159): two Spaces on memex, then twenty-two reinsurance and education Spaces on systemorph — twenty-four removals that were twenty-four links before this existed, and are two requests now.