Add start_print to the printer command set

Held back until there was a job path to drive it; the queueing work needs it
now. Three things are deliberate about its shape.

It carries a presigned `url`, not the file's bytes. A sliced project runs to
tens of megabytes and this is a JSON control channel, and a URL also keeps
object-store credentials out of every gateway on every farm. The schema says
to presign it at *dispatch* time rather than at job creation, because a queued
job may wait days and a URL minted when it was queued would be long expired.

It carries no filament or AMS field. The gateway reads the mapping out of the
archive's own `Metadata/slice_info.config`, because the archive is the only
thing that knows what each plate was sliced for. Passing one in would
reintroduce the guess that made a real P1P heat to 250C and pause itself at
1%.

And its description is blunt about what accepting it means, which is: very
little. A full end-to-end run on 2026-10-15 was accepted, uploaded
byte-perfect, reached RUNNING, and then the printer paused itself on a
physical broken-filament fault. Job state has to be driven by the telemetry
that follows, never by this reply - so the schema says so where a caller will
read it, rather than leaving it to be rediscovered.
This commit is contained in:
2026-10-15 22:43:03 +00:00
parent 7a9b19f72a
commit b454e30c87
2 changed files with 50 additions and 1 deletions
@@ -2,8 +2,33 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.octoturge.com/Continuum/continuum-schemas/raw/branch/main/schemas/continuum/v1/edge/printer_command.schema.json",
"title": "PrinterCommand",
"description": "The closed set of things the cloud may ask a printer to do. Deliberately small, and deliberately missing the obvious one: there is no 'start a print' variant. Starting an uploaded file has now been confirmed by hand on a real P1P (2026-10-11), so the earlier reason for leaving it out is spent; what is still missing is the rest of the path around it - nothing uploads a sliced file to a gateway, and no job-dispatch route decides which printer should run which job. A start variant belongs here once there is something to start, not before.",
"description": "The closed set of things the cloud may ask a printer to do. Deliberately small. It now includes start_print, which was held back until there was a job path to drive it; the thing to understand about that variant is that its acceptance says almost nothing - see its own description.",
"oneOf": [
{
"type": "object",
"title": "StartPrint",
"description": "Print a job's file. The gateway downloads it from `url`, transfers it to the printer, and starts it. Three things about this variant that a caller must not get wrong. (1) Its result means the gateway accepted the job, not that anything is printing: a real end-to-end run on a P1P (2026-10-15) was accepted, uploaded byte-perfect, reached RUNNING, and then the printer paused itself on a physical filament fault. Job state has to be driven by the telemetry that follows, never by this reply. (2) There is no filament or AMS field on purpose. The gateway reads the mapping out of the project archive's own Metadata/slice_info.config, because the archive is the only thing that knows what each plate was sliced for - guessing it instead is what made that same printer heat to 250C and pause itself at 1%. (3) `url` is short-lived and must be presigned at dispatch time, not at job creation: a queued job may wait days, and a URL minted when it was queued would be expired by the time anything sent it.",
"properties": {
"action": { "const": "start_print" },
"job_id": {
"type": "string",
"format": "uuid",
"description": "The cloud's print_jobs.id. The gateway does not need it to do the work - it is here so that a gateway's logs can be lined up against a job without reading its own telemetry back out of the cloud."
},
"file_name": {
"type": "string",
"minLength": 1,
"maxLength": 500,
"description": "What to call the file on the printer's own storage. A `.3mf` is treated as a sliced project (filament mapping derived from inside it); anything else is treated as bare gcode, which carries no mapping and prints with whatever its own tool changes select."
},
"url": {
"type": "string",
"format": "uri",
"description": "A presigned, expiring GET for the job file. Deliberately a URL rather than the bytes: the file is tens of megabytes, this is a JSON control channel, and handing the gateway a URL keeps object-store credentials out of every gateway on every farm. Expect it to be valid for minutes, and note that an upload to a printer is slow - 0.2 MiB/s measured over FTPS to a P1P - so the download must finish before the transfer starts, not stream through it."
}
},
"required": ["action", "job_id", "file_name", "url"]
},
{
"type": "object",
"title": "SetFanSpeed",