Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/compute-mcp/docs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ func TestSkillsMatchDocumentedSet(t *testing.T) {
"/runbooks/referenced-data-triage.md": true,
"/runbooks/placement-triage.md": true,
"/runbooks/stalled-transient.md": true,
"/runbooks/workload-create.md": true,
}
got := docs.paths()
if len(got) != len(want) {
Expand Down
26 changes: 20 additions & 6 deletions docs/agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,25 @@ assistant owns the document schema that carries it.
## Status

Landed here: the reason catalog, the diagnosis walk, the knowledge and skills
above, and `cmd/compute-mcp` — the MCP server that publishes the five read-only
tools (`workloads_list`, `workloads_get`, `instances_list`, `workload_diagnose`,
`reason_explain`) over Streamable HTTP.
above, and `cmd/compute-mcp` — the MCP server that publishes compute's tools
over Streamable HTTP:

| Tools | Names |
|---|---|
| Diagnosis, read-only | `compute_workloads_list`, `compute_workloads_get`, `compute_instances_list`, `compute_workload_diagnose`, `compute_reason_explain` |
| Creation, writes nothing | `compute_instance_types_list` (the sizes a workload may ask for), `compute_workload_render` (inputs to a Workload manifest, pure) |

Every tool is prefixed `compute_`, so the assistant can compose tools from
several services in one conversation without names colliding; the capability
document must register the prefixed names.

Compute publishes no mutating tool. Everything else creating a workload needs
comes from the assistant's base tools, which it gives every project turn and
which act as the caller: `locations_list` with service `compute` for where
compute is offered, `quota_get` for what is left, `resources_list` for
Networks and RuntimeClasses, and `resources_validate`, `resources_plan` and
`resources_apply` for the change itself. The plan token and the confirmation
step live there, once, for every service.

## HTTP surface

Expand Down Expand Up @@ -59,9 +75,6 @@ Three properties of the server are worth knowing before you deploy it:
prompt injection away from another tenant's workloads. The caller sets
`X-Datum-Project` after authenticating the user.

Compute publishes no mutating tool. Allow-list enforcement is the gateway's job,
but a tool that does not exist cannot be called through any path.

## Why the knowledge leads with "how to read conditions"

Compute's top-level condition reasons are deliberately **pointers, not causes**.
Expand Down Expand Up @@ -90,6 +103,7 @@ orientation and classification; the procedures live here and nowhere else.
| `referenced-data-triage` | Missing, unauthorized, or oversized ConfigMaps/Secrets |
| `placement-triage` | `NoMatchingLocation`, `AmbiguousServingLocation`, `LocationMismatch` |
| `stalled-transient` | A transient reason that has outlived its expected window |
| `workload-create` | Deploying something new: prerequisites, the choices that are final at create, and render → plan → show → confirm → apply |

A skill never grants privileges. It can only direct the model toward tools that
are independently on the enforced allow-list, which is why these go through the
Expand Down
77 changes: 63 additions & 14 deletions docs/agent/llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ Pointer reasons you must read *through*, never report as the answer:
PendingQuota -> look at Instance.QuotaGranted
SchedulingGatesPresent -> something else is holding it; find what

The `workload_diagnose` tool does this walk for you and returns the leaf cause.
Prefer it over assembling the tree by hand.
The `compute_workload_diagnose` tool does this walk for you and returns the
leaf cause. Prefer it over assembling the tree by hand.

## Kinds of cause

Expand Down Expand Up @@ -121,8 +121,8 @@ reason, and only the elapsed time tells them apart.
Every tool result that names a cause carries how long the state has held:
`rootCauseFor` / `rootCauseSince` in the fleet view, `inStateFor` /
`lastTransitionTime` on a diagnosis. Every transient reason carries the window
it should clear inside (`expectedWithin` from `reason_explain`). Past that
window the actionability comes back as `stalled`.
it should clear inside (`expectedWithin` from `compute_reason_explain`). Past
that window the actionability comes back as `stalled`.

`stalled` is not the same as a platform fault. Nothing reported a cause; the
classification has merely been contradicted by the clock. Report the duration
Expand Down Expand Up @@ -182,21 +182,70 @@ Instance runtime
infrastructure that runs the container, not by compute. Load
`instance-not-ready`.

## Creating a workload

A Workload is three things: a name, one instance template, and one or more
placements. The template says what runs — either containers in a sandbox, or a
virtual machine booting a full operating system — at one instance type, on one
network interface, with whatever volumes the containers or the machine attach.
Each placement says where in one of two ways — a list of location names, or a
selector over the topology those locations declare (city code, region), which
places at every location matching it and picks up new ones as they appear — and
a replica count. Where it runs and how many of it there are live in the
placement, not in the template.

Some of that is settled for good at create. The workload's name, the runtime
kind, and the network interface — its name, the address families it carries,
any extra addresses such as a public IPv4 one, and what becomes of those
addresses afterwards — cannot be changed later. Getting one of them wrong means
a new workload rather than an edit, which is why the create procedure asks
about them explicitly rather than defaulting them quietly.

Two things are outside your reach entirely. You cannot build or push a
container image: it must already exist in a registry, fully qualified, and be
built for the runtime Datum runs it on — `datumctl compute build` is the step
the customer runs. And you cannot enable Compute for a project or grant it
quota; both are Datum's to give.

Writing is a sequence, not a call. `compute_workload_render` turns inputs into
a Workload manifest and touches nothing. The rest is the platform's own change
path, the same for every service: `resources_plan` validates the manifests,
settles create versus update, orders a Network ahead of the Workload that names
it, and returns them with a plan token. `resources_apply` takes those manifests
and that token and nothing else, so the only thing that can be created is what
you showed the customer and they agreed to.

Load `workload-create` before any of this. The prerequisites, the inputs, the
rejections that are worth pre-empting, and what to do at each failure are all
there, and this section deliberately does not restate them.

## What the tools give you

workloads_list fleet view, worst first, with root-cause reason,
actionability, and how long that cause has held
workloads_get raw condition tree for one workload
instances_list per-instance conditions, to see failure distribution
workload_diagnose the walk, the leaf cause with its age, and next steps
reason_explain any reason, explained, classified, and — when transient —
the window it should clear inside
compute_workloads_list fleet view, worst first, with root-cause reason,
actionability, and how long that cause has held
compute_workloads_get raw condition tree for one workload
compute_instances_list per-instance conditions, to see failure
distribution
compute_workload_diagnose the walk, the leaf cause with its age, and next
steps
compute_reason_explain any reason, explained, classified, and — when
transient — the window it should clear inside
compute_instance_types_list the instance types a workload may ask for
compute_workload_render inputs to a full Workload manifest; writes
nothing, reads nothing

The platform's base tools fill in the rest of a create. Call locations_list
with service "compute" for the locations this project may place a workload in —
a location absent from it is one compute is not offered in, and placements name
these verbatim or select them by their topology. quota_get says how much
compute is left, resources_list reads Networks and RuntimeClasses, and
resources_plan and resources_apply make the change.

Skills (load on demand) carry the procedures: workload-not-available,
quota-triage, instance-not-ready, referenced-data-triage, placement-triage,
stalled-transient. This document is orientation; it deliberately does not
restate what a skill covers, so reach for the skill rather than answering a
triage question from what is in the prompt.
stalled-transient, workload-create. This document is orientation; it
deliberately does not restate what a skill covers, so reach for the skill
rather than answering a triage or creation question from what is in the prompt.

## Telling compute what it could not do

Expand Down
4 changes: 2 additions & 2 deletions docs/agent/skills/instance-not-ready.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ it — the logs are there either way.
unpacked. Say to wait. Only if it persists well beyond a few minutes should
you treat it as Datum's problem.

6. **Check whether every instance fails the same way.** `instances_list` for the
6. **Check whether every instance fails the same way.** `compute_instances_list` for the
workload: all of them failing the same way points at the workload or the
image; one failing among healthy siblings points at one machine or one
location, which is Datum's.
Expand Down Expand Up @@ -81,7 +81,7 @@ When that happens:
"capability": "container log retrieval for a crashing instance",
"kind": "UnactionableGuidance",
"evidence": {
"tool": "instances_list",
"tool": "compute_instances_list",
"observed": "InstanceCrashing; remediation points at the logs",
"contradictedBy": "log retrieval fails outright on this instance:
the port answers plain HTTP where encrypted
Expand Down
10 changes: 5 additions & 5 deletions docs/agent/skills/placement-triage.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ end.
- `LocationMismatch` — the workload asked for one location and was sent to
another. It was routed to the wrong place.

2. **Confirm the scope.** `workloads_list` shows whether other workloads in the
same placement are also failing. Several failing in one place is a
location-wide problem and is worth reporting as such; a single one may be a
leftover deployment.
2. **Confirm the scope.** `compute_workloads_list` shows whether other
workloads in the same placement are also failing. Several failing in one
place is a location-wide problem and is worth reporting as such; a single one
may be a leftover deployment.

3. **Check whether other placements are serving.** A workload with several
placements may be fully available elsewhere. Say so — the customer's service
may be up even though this part is broken.

4. **Escalate with specifics.** Datum needs: the WorkloadDeployment name, its
`location`, and the status message. Pull these from `workloads_get`.
`location`, and the status message. Pull these from `compute_workloads_get`.

## Reporting

Expand Down
28 changes: 16 additions & 12 deletions docs/agent/skills/quota-triage.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ service that evaluates it, and not the request compute files against it.
## Procedure

1. **Get the real reason.** `QuotaNotGranted` on the Workload or
WorkloadDeployment is a pointer. Call `workload_diagnose`, or read the
Instance's `QuotaGranted` condition via `instances_list`. Never report
WorkloadDeployment is a pointer. Call `compute_workload_diagnose`, or read the
Instance's `QuotaGranted` condition via `compute_instances_list`. Never report
`QuotaNotGranted` as the cause.

2. **Separate the four cases.** They look alike and lead to opposite advice:
Expand All @@ -34,13 +34,14 @@ service that evaluates it, and not the request compute files against it.
3. **For `QuotaExceeded`, quantify it.** The status message carries the amount
requested and the amount left. Quote both. Then give the customer the three
real options: fewer replicas, less CPU or memory per instance, or ask Datum
to raise the project's quota.
to raise the project's quota. If the message leaves out what is left,
`quota_get` has it.

4. **For `PendingEvaluation`**, check how long. Minutes is normal. If it stays
there, the checking service itself is stuck — treat it as
`QuotaBackendUnavailable` and hand it to Datum.

5. **Check the split.** `instances_list` shows how many instances were cleared
5. **Check the split.** `compute_instances_list` shows how many instances were cleared
and how many were not. Partial is the common case: the workload is serving at
reduced capacity, which is worth saying explicitly.

Expand All @@ -52,19 +53,22 @@ burn time trying.

## When the numbers are not there

Step 3 rests entirely on the status message. Nothing else in these tools carries
the project's compute quota, how much of it is in use, or how much is left. So
when a `QuotaExceeded` message arrives without figures — or carries what was
requested but not what remains — you cannot tell the customer how much smaller
to go, and "ask for less" without a number is not something they can act on.
When a `QuotaExceeded` message arrives without figures — or carries what was
requested but not what remains — call `quota_get` with service
`compute.datumapis.com`. It reports the project's compute quota per resource
type: the limit, how much is in use, and how much is left. That is the number
the customer needs to know how much smaller to go.

Say which half you have and which is missing, then file `InsufficientDetail`
against the tool you read it from, quoting the message you were given:
If `quota_get` cannot answer either, you cannot tell the customer how much
smaller to go, and "ask for less" without a number is not something they can act
on. Say which half you have and which is missing, then file
`InsufficientDetail` against the tool you read it from, quoting the message you
were given:

"capability": "how much of the project's compute quota is left",
"kind": "InsufficientDetail",
"evidence": {
"tool": "instances_list",
"tool": "compute_instances_list",
"observed": "QuotaGranted=False, QuotaExceeded, \"quota exceeded\"",
"contradictedBy": "no requested or remaining amount in the response" }

Expand Down
10 changes: 5 additions & 5 deletions docs/agent/skills/stalled-transient.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ and neither is licence to rule the customer's own workload out.

## Procedure

1. **Quantify it, and use the larger number.** `workload_diagnose` gives two
1. **Quantify it, and use the larger number.** `compute_workload_diagnose` gives two
ages on the root cause and they answer different questions:

- `inStateFor` — how long the *status* has said this.
Expand All @@ -54,7 +54,7 @@ and neither is licence to rule the customer's own workload out.
an object broken for nine days means something is rewriting the status
without ever finishing. Say so.

Then call `reason_explain` for `expectedWithin` — how long this step should
Then call `compute_reason_explain` for `expectedWithin` — how long this step should
take. "Nine days, against thirty minutes" is the whole finding.

Two things the tools will not give you, on purpose. An age is omitted rather
Expand All @@ -76,13 +76,13 @@ and neither is licence to rule the customer's own workload out.
something is working on this and never saying how it turned out. It does
**not** name a culprit — see step 5.

3. **Check whether it is one object or all of them.** `instances_list` for the
3. **Check whether it is one object or all of them.** `compute_instances_list` for the
workload. Every instance stuck the same way points at the place they all
run; one stuck among healthy siblings points at that object. Say which — it
decides who Datum wakes up.

4. **Look underneath before escalating.** Read `contributingConditions` from
`workload_diagnose`. A stalled pointer reason (`InstancesProvisioning`,
`compute_workload_diagnose`. A stalled pointer reason (`InstancesProvisioning`,
`PendingQuota`, `SchedulingGatesPresent`) often has a real cause below it
that arrived after the stall began. If one is there, that is the answer —
follow its skill instead.
Expand Down Expand Up @@ -166,7 +166,7 @@ copied out of the tool result:
"capability": "duration-aware classification of transient reasons",
"kind": "MisleadingOutput",
"evidence": {
"tool": "workload_diagnose",
"tool": "compute_workload_diagnose",
"observed": "actionability: transient, remediation \"Wait.\"",
"contradictedBy": "failingFor: 9d, inStateFor: 9h30m, expectedWithin: 30m" }

Expand Down
Loading
Loading