prim.process

Six months after an edition ships, somebody will ask who was allowed to publish it and which checks actually ran. If the only copy of that procedure is the runner at HEAD, you are reading today’s code and calling it last August. The runner will have changed. Put the procedure in a file the edition can point at.

Listing the tools is the easy part, and people stop there. The rest is who may draft, fail a check, kill the piece, or hit publish. If the writer can also publish, stop.publish: human is lying.

Newspapers print a masthead for this. Camunda draws it as swimlanes. A wiki page of the procedure will get edited in place, and then you cannot say which wording an old edition used. The edition needs an id and a hash, which means the procedure has to live in a file you can hash.

People reach for UML here. Class diagrams describe objects, so they’re the wrong drawing. Swimlanes are the useful one: writer, machine, checker, desk, publisher. Draw those off the JSON if you want them on a wall.

Calling it

You ask for work under a named process. Something like: draft the 26 August waste volume under ledger-monthly-v1. A connector opens that pack, runs the tools in order, and writes process plus process_hash on the new edition. If the pack says a human publishes, it stops, and the writer can’t override.

Tighten the monthly checks in September and that’s a new pack with a new hash. The August edition still points at the old one. Daily specials that skip the long check need their own pack. A flag in the runner feels neat until October, when you can no longer tell which branch ran.

Checkers have limits. A URL grader will miss a date that isn’t on the page it opened. That belongs in the checker’s notes. The process only has to say whether that checker ran, and whether anything ran after it.

What we actually store

The in-tree example is small on purpose. desk-docket-v1 is an editor, a connector, and a human publish stop. That validates today.

{
  "id": "desk-docket-v1",
  "version": "1",
  "cites_type": "docket",
  "steps": [
    { "tool": "docket-editor", "as": "editor" },
    { "tool": "docket-webmcp", "as": "webmcp" }
  ],
  "stop": { "publish": "human" }
}

Ledger’s monthly walk needs more than that: named roles, a gate after verify so blockers send it back, desk can kill the line, publish is a different person, and a record of the run. Those keys type-check if you put them in. They aren’t required yet. The connector that would enforce them isn’t finished.

{
  "id": "ledger-monthly-v1",
  "version": "1",
  "cites_type": "edition",
  "roles": {
    "writer": { "may": ["draft"] },
    "desk": { "may": ["edit", "kill"] },
    "verifier": { "may": ["grade"] },
    "publisher": { "may": ["publish"] }
  },
  "steps": [
    { "id": "draft", "tool": "volume-shell", "role": "writer" },
    { "id": "lock", "tool": "lock-gate", "role": "system" },
    { "id": "verify-cited", "tool": "verify-prompt", "role": "verifier" },
    { "id": "census", "tool": "sentence-census", "role": "verifier" },
    { "id": "desk", "tool": "draft-desk", "role": "desk", "human": true },
    { "id": "publish", "tool": "draft-desk", "role": "publisher", "human": true }
  ],
  "gates": [
    { "after": "verify-cited", "pass": "blockers == 0", "fail": "return:draft" },
    { "after": "desk", "pass": "desk.approve", "fail": "kill" }
  ],
  "stop": { "publish": "human", "kill": "desk" },
  "record": ["job_runs", "verdicts", "approvals"]
}

Bump version when the walk changes. Leave the old file on disk. cites_type is the kind of piece this is for, and it can’t be process. Don’t copy the steps onto the edition:

process: ledger-monthly-v1
process_hash: sha256:…

If you paste the steps into the docket, editing the docket looks like editing the procedure. The procedure stays in profiles/process.

Most prims never point at one of these. Siliconfall Score is tracks and a player. Don’t mint prim.pipeline. The order and the permissions already live in this file.