Skip to content

R6 · API contract against the screens

Done 2026-10-27 · 34 findings: 0 blockers, 29 majors, 5 minors · 0 merged · 5 questions for Thomas · 43 draft gaps placed

R6 round map: the 0.0.1 screens on the left, the API draft's resources on the right, cables where a screen is served, dashed lines where no resource exists, each gap pinned with its finding ID R6 round map: the 0.0.1 screens on the left, the API draft's resources on the right, cables where a screen is served, dashed lines where no resource exists, each gap pinned with its finding ID

Left: five 0.0.1 screens (Launch, Runs list, Run detail, Gate review, Relaunch), with 16 of the ux-reviewer's 22 findings pinned on them. Right: the draft's resources (workflows, runs, stages, tasks and logs, gates and checks, events and SSE, ingest), with the spec-reviewer's findings. Solid cables are served calls. Dashed lines end on "stages" and "ingest", which have no resource. Bottom right: six of the seven reads the api spec lists but the draft dropped (the seventh, gates:summary, is UX-275 in the table), with the other 6 UX findings. Red pins are the R3 blockers SR-062 and SR-054.

What changed

  • New review file: docs/reviews/R6-contract.md. It holds:
    • a summary with the round map, the counts by reviewer, the main themes and a table of all 34 findings with their Related pairs;
    • 34 findings: SR-080 to SR-091 (spec-reviewer, 12 majors) and UX-270 to UX-291 (ux-reviewer, 17 majors, 5 minors). Each has its spec passages, a proposal, "Found by" and Status: open;
    • 5 questions for Thomas, each with options and a recommendation;
    • Appendix A: all 43 x-spec-gap entries of the draft, each mapped to the finding that owns it;
    • Appendix B: for each of 11 boards, the draft operations it would call and whether they serve it.
  • Throwaway OpenAPI draft on branch autopilot/R6, commit b9c01fd: spikes/openapi-draft/openapi.yaml (1,502 lines) and spikes/openapi-draft/README.md. It is an OpenAPI 3.1 skeleton for the 0.0.1 cut only, written by hand from the specs. It is not merged into main and is not the contract. Its README says what it leaves out: pause and resume, the manifest, change sets, artefacts, webhooks and summaries.
  • Round map embedded at the top of this page and of the review file's summary. Script: tools/diagrams/r6_contract_map.py.
  • No spec was edited. This is a review round (see docs/reviews/README.md).
  • The review file, the round map and its script are in the working tree of autopilot/integration and are not committed yet. The mkdocs.yml nav entry "R6: API contract" points to the review file.

Why

Before code starts, the API must be able to serve the 0.0.1 screens. R6 tests that in two moves. First, write the 0.0.1 contract as OpenAPI from api-and-data-model.md. Every place where the specs are silent or disagree gets an x-spec-gap key. Then two reviewers read the draft from both sides: the spec-reviewer against the specs, the ux-reviewer against every number, list and action on the boards of mvp-0.0.1.md §2.

Prompt: next-phase.md · R6. The 0.0.1 scope comes from the decision log, 2026-10-27, first version is 0.0.1. The round map follows the decision log, 2026-10-27, a sketch for every step.

Evidence

How the round ran (from the header of the review file):

Item Value
Spec text read autopilot/integration at b2804e4; every spec line number in the file refers to it
Draft read openapi.yaml at b9c01fd (branch autopilot/R6); every draft line number refers to it
Reviewers two; both returned a full report, none failed
spec-reviewer the draft and its README against nine specs, the inbox and R5. It placed every one of the 43 gaps
ux-reviewer the ten boards of mvp-0.0.1.md §2 plus EnvLocal for context, each rendered at 1440×1180 with tools/canvas/render.js (renders in /tmp/r6-ux/, not kept)
External code read none. Claims about Nextflow and Snakemake behaviour (task totals, storeDir, Snakemake log:, the CloudEvents sequence extension) keep the reviewers' [to be validated] mark
Raw input 34 findings: spec-reviewer 12, ux-reviewer 22
Merge 0 merged, 0 dropped, 0 downgraded. Where an SR and a UX finding ask for the same thing from two sides, both stay and name each other in Related
Merger's check ten quoted spec passages reopened at b2804e4, all reproduced. The x-spec-gap lines and path list re-extracted from b9c01fd; both match the reports
Corrections at merge R3 references updated to R3's final IDs. The gap split corrected from "37 in new findings, 6 in R3" to 40 placed in an R6 finding and 3 only in an R3 finding

Counts, checked against the file and the draft:

$ for s in blocker major minor; do printf "%s " $s; grep -cE "^### (SR|UX)-[0-9]+ · $s ·" docs/reviews/R6-contract.md; done
blocker 0
major 29
minor 5
$ grep -oE "^### (SR|UX)-[0-9]+" docs/reviews/R6-contract.md | cut -c5-6 | sort | uniq -c
     12 SR
     22 UX
$ grep -c "^- \*\*Status:\*\* open" docs/reviews/R6-contract.md
34
$ sed -n "/^## Questions for Thomas/,/^## Findings: spec-reviewer/p" docs/reviews/R6-contract.md | grep -cE "^[0-9]+\. "
5

The draft and its gaps:

$ git show b9c01fd:spikes/openapi-draft/openapi.yaml | grep -cE "^\s*x-spec-gap:"
43
$ sed -n "/^## Appendix A/,/^## Appendix B/p" docs/reviews/R6-contract.md | grep -cE "^\| [0-9]+ \| [0-9]+ \|"
43
$ sed -n "/^## Appendix A/,/^## Appendix B/p" docs/reviews/R6-contract.md | grep -cE "^\| [0-9]+ \| [0-9]+ \| .* \| R3 SR-"
3
$ git show b9c01fd:spikes/openapi-draft/openapi.yaml | python3 -I -c "import sys,yaml; d=yaml.safe_load(sys.stdin); p=d['paths']; print(len(p), 'paths,', sum(k in ('get','post','put','patch','delete') for v in p.values() for k in v), 'operations,', len(d['components']['schemas']), 'schemas')"
24 paths, 26 operations, 67 schemas
$ sed -n "/^## Appendix B/,\$p" docs/reviews/R6-contract.md | grep -E "^\| [A-Z]" | grep -v "^| Board" | grep -oE "\*\*(partly|not)\*\*" | sort | uniq -c
      2 **not**
      9 **partly**
  • Every one of the 43 gaps in the draft has a row in Appendix A. 3 of them belong only to an R3 finding (SR-065, SR-064, SR-055), so 40 sit in an R6 finding.
  • No board is fully served. 9 are partly served. Manifest and EnvLocal are not served at all.
  • The basic journeys are served: launch with parameters, monitor, approve or stop, relaunch with parameter overrides, read the parameters per run (Appendix B, last paragraph). The findings are about what the boards show beyond that, and about N+1 calls.

Quotes re-checked by doc-writer at the commits the file cites:

$ git show b2804e4:docs/specs/mvp-0.0.1.md | sed -n 11p | grep -c "Shared login nodes are not covered (inbox #15, open)"
1
$ git show b2804e4:docs/reviews/inbox.md | sed -n 194p | grep -o "Not needed for 0.0.1 (single user, local engines; Thomas 2026-10-27)"
Not needed for 0.0.1 (single user, local engines; Thomas 2026-10-27)
$ git show b9c01fd:spikes/openapi-draft/openapi.yaml | sed -n 1492p
      x-spec-gap: "Whole envelope invented: api-and-data-model.md §14 (the ingest contract named by architecture-plugins-and-packaging.md §12-§13) does not exist."

These are the mvp l.11 and inbox l.194 quotes of SR-091, and gap #42 of SR-080.

Exit criterion. R6 is done when its review file exists, so that TRIAGE2 can read it (writes in pipeline.yaml):

$ ls docs/reviews/R6-contract.md
docs/reviews/R6-contract.md
$ wc -l docs/reviews/R6-contract.md
992 docs/reviews/R6-contract.md

The wave W2 exit check is python3 tools/docs/check.py. Its result for this page is under Findings and open questions.

Figures and media

  • Round map (sketch pair, at the top of this page and of the review file's summary): docs/assets/diagrams/r6_contract_map_{light,dark}.{svg,png}, from python3 tools/diagrams/r6_contract_map.py (r6-contract-map in docs/assets/media.yaml). It groups the boards into five screens and pins every R6 finding, plus the two R3 blockers it leans on (SR-062 at runs, SR-054 at ingest).
  • No screenshots or casts: a review step needs only the round map (documentation-system §3.1). The ux-reviewer's board renders stayed in /tmp/r6-ux/.

Findings and open questions

No new blocker. The blockers that hold the 0.0.1 contract are already open in earlier rounds. Several R6 findings extend them (summary):

Blocker Round What it holds R6 findings that lean on it
SR-054 R3 ingest contract (api §14) parked as "not needed for 0.0.1" SR-080
SR-062 R3 relaunch with changed parameters from a gate is two flows SR-081, SR-085, UX-284
SEC-102 R5, inbox #15 single mode trusts loopback SR-091
SR-001 R1, inbox #11 ingest credential and contract SR-080

R6 findings also lean on the R3 blockers SR-055 (local environment, default environment_id) and CH-042 (run state machine).

Main themes, as the review file's summary groups them:

  1. No ingest contract. The draft had to invent the batch envelope, the acknowledgement, the heartbeat and the engine event names (SR-080).
  2. No contract for the run's inner structure. No stages resource, no stage or task state enum, and the Legend board does not match the draft's enums (SR-082, SR-083, UX-290).
  3. Run actions and relaunch are under-specified. Source states, a fourth spelling of the relaunch modes, lineage, resource overrides and the preview (SR-081, UX-280 to UX-282, UX-289).
  4. The 0.0.1 gate cannot be served end to end. No run filter, check values without thresholds, no read of a decided gate, and the gate board's main button needs a change set (SR-085, UX-283 to UX-285).
  5. The Run resource lacks the fields the boards show. Workflow name, progress, task totals and timestamps cost 3 to 5 calls per list row (UX-271, UX-270, UX-272, UX-273, UX-288, SR-088).
  6. Wire conventions, events and SSE frames are not stated (SR-090, SR-084).
  7. Endpoints in api §5 that the draft dropped, which boards need: runs:summary, gates:summary, /version, the manifest, samplesheets, gate items, artefacts (UX-274, UX-275, UX-278, UX-279, UX-285, UX-286).
  8. Security. Any web page can drive a token-less loopback API through DNS rebinding or CSRF (SR-091).

Questions for Thomas. They go to TRIAGE2 first. TRIAGE2 copies the ones it cannot settle to inbox.md. The full wording and options are in questions for Thomas.

# Refs Question Reviewers recommend
1 SR-091 Browser protection in 0.0.1? Host allow-list, Origin check on writes and JSON-only writes, now
2 SR-080, SR-084 Where do engine events go, and what is published? one events log; engine types under spectio.engine.*, internal, with a seq; heartbeats kept out
3 SR-088, UX-272 What is the "% tasks" denominator? show "% tasks" only when the engine gives a total (expected for Snakemake [to be validated]), otherwise "N done · M running"
4 SR-085, SR-062, UX-284 Which gate verbs does the 0.0.1 API expose? approve and stop only; relaunch through SR-062 path B; apply and go_back return 422
5 UX-274, UX-275, UX-278, UX-279, UX-285, UX-286, UX-291 Widen the draft's cut to the api §5 endpoints the boards need? widen to the reads a 0.0.1 board needs; hide the rest on the boards

Overlap with earlier rounds. Findings from R1, R3, R4, R5, R7 and R12 are cited, not repeated. Where an R6 finding extends one, its Where or Related line names it, so TRIAGE2 can sort the pair together.

Open on this page.

  • Fixed before review: the round map's gap bar was redrawn at 40 placed in R6 findings and 3 only in R3 findings, and the map's alt text now says five 0.0.1 screens.
  • review is null until doc-reviewer scores the page. Until then, python3 tools/docs/check.py reports "a done step needs review: approve" for this page.

Next. TRIAGE2 sorts the R3 and R6 findings with the triage policy. APPLY2 then changes the specs and adds the decision-log entries.