Skip to content
Go to console
Go to console

What Sluiceway writes for machines is documented, versioned, and kept small

Decision record 0096

Amended by 0110: the row table documents changed, creates, updates, replaces, tracking and behind, the generated row markers show each, and a section under the table gives the two reason words a reader writes for the reasons the markers do not carry.

Amends 0003 (the payload has a published schema and the writer checks every payload against it), 0009 (the marker keys are split into documented and left out, and the version rule is stated for readers outside Sluiceway), and 0041 and 0061 (the result file’s schema says it holds no secret, and where its one kind of value may be). Built as slice 5.33.

Sluiceway writes three things that a machine can read: the markers in the dashboard body (0009), the payload of each deployment record (0003), and the result file with the step outputs beside it (0041). The docs treated them as internals, and issue 233 proposed saying at 1.0 that they are not promised. The owner decided the opposite on 2026-09-24 (issue 234): people, scripts and future agents should be able to build on them on purpose. They already carried versions. What was missing was the writing down, the schemas and a rule.

Decision

  • One page, docs/what-sluiceway-writes.md, is the contract. It documents every key of each marker kind, every field of the record and of its payload, every field of both result files, which mode writes which file, and three worked examples: a step that reads the result file, the last deploy of a stack from the Deployments API, and what is waiting from the body alone.
  • Every example comes from what the code writes, never from a hand. scripts/written-examples.ts runs a scan of the example project, a tick, resolve and apply, with the real Pulumi adapter replaying what the real CLI printed and the fake GitHub of the mode tests, once with the deploy that went out and once with the one that failed. The body, the record, the result files and the outputs are that run’s. The marker kinds a run of one stack cannot show are lines of the example dashboard (0088), which the real renderer writes. The payloads of a queued stack, a drift repair, a deploy on merge and a merge come from the function every writer calls, with facts from the example dashboard, because a run of each is a flow of its own. bun run example:written writes them into the page. The only thing the generator changes is the result file’s directory, which it gives as the runner’s RUNNER_TEMP.
  • Tests hold the page to the code. The page is what the generator gives, byte for byte. Each marker example, parsed and written again by the marker functions, is itself. The keys the page documents for each marker kind are the keys the writer writes, minus the ones this record leaves out, and the page names those. The payload’s keys are the schema’s, the result files’ fields are the schema’s, level by level, and every example fits its schema. No example holds the canary value or secret of the example project. The “what is waiting” recipe, run on the example dashboard, gives what parseDashboard gives, and the result file recipe runs against the run’s file.
  • The payload has a published schema, schema/deployment-payload.schema.json, generated by bun run build:schema like the other two and checked in CI the same way. It is strict and the writer checks every payload against it before it sends one, as the result file renderer does (0061): a key it does not name cannot ride along. Its hash is 16 lower case hex characters, and run and attempt are run ids. The check leaves the keys in the order they were always written, so nothing that reaches GitHub changes.
  • The schemas say what they never hold. The payload’s: no property value, no secret, none of the tool’s words. The result file’s: no secret, none of the tool’s words, and no property value except at a path the repo lists in dashboard.showValues. The one field that may hold one, values on a change, says so in its own description, and a test holds both.
  • The rule, stated for readers. New keys, fields, marker kinds, row states and outputs may appear in any release without a new version. A documented key or field keeps its meaning while its version stands. A change that would make a reader misread raises the version: v on the root marker, v in the payload, version in the result file. Where it is cheap, both versions are written for at least one minor release, such as a second result file. A body and a payload have room for one, so there the release notes say it ahead. A reader of an older version checks the version first and stops on one it does not know, as Sluiceway does.
  • The published schemas stay strict. They say exactly what a version writes, so a file with a field that came later fails a strict check against an older schema. The page says to take the schema from the release tag of the version you run, or to ignore unknown fields rather than validate strictly.

Left out on purpose

Every documented key is a promise, and changing it costs a version and a migration that the project carries. So the surface is what a reader needs, not every key Sluiceway writes. Written and not promised, free to change or go in any release without a new version:

  • The text of the body. Rows, headings, the header, the counts line, notes, links and pictures. The rule of 0009 already said the text next to a marker is never parsed. It is now the reader’s rule too.
  • shortened on a row: the level the size budget cut it to (0028), a note for the layout.
  • run-waiting, run-waiting-since, run-waiting-more on the root marker (0086): a hint about the runners, not a fact about a stack, and a likely candidate to change shape.
  • note, added, gone, moved on a bulk box and confirm, stacks, hashes, scan-run on a confirm box (0083): the hand-over between one tick and the next. A script must never tick a confirm box, so it has no reason to read one, and documenting it would invite one to.
  • The description of a deployment status. The state is the fact. The words come from Sluiceway’s fixed lists and are for people, so they may be reworded.
  • Writing. The page documents what to read. A tick is an edit by a person, judged by who made it (0025), and a script that edits the body is that person.
  • The job summary, the job log, the preview pages and the notifications. They are for people. The webhook message has a version of its own and stays documented with the notifications (0078).
  • A schema for the markers. A JSON schema cannot describe an HTML comment in Markdown. The page, its tables and the tests that parse every example are the schema.

Considered

  • Saying they are not promised, as issue 233 proposed. It would leave every consumer to reverse-engineer a format that already has versions and rules, and the owner chose against it.
  • Documenting every key Sluiceway writes. Rejected for the reason above: the list of what is left out is what stops the promise from becoming a burden.
  • Lenient published schemas (additionalProperties allowed), so a strict validator of an older schema accepts a newer file. Rejected: the strict schema is the one the code checks itself against, and it is what makes “no field can quietly add a value” true. The page gives the reader the two ways to stay compatible instead.
  • Hand-written examples, as docs/notifications.md had one of a scan’s file. Replaced by the page’s generated ones: a hand example drifts, and a reader copies it.

Consequences

  • docs/notifications.md loses its hand-written example of a scan’s file and links the page. It also no longer says the file never holds a property value, which has not been true since 0052.
  • The docs index, docs/reference.md, the README’s list of what it does and the glossary name the page and the published shape.
  • The docs site needs a page for docs/what-sluiceway-writes.md, and its notifications and reference pages follow the changes above. Until it has one, the README reaches the page through the notifications page’s section on what Sluiceway hands over.