Story: Generate literate HTTP recipes that tangle to runnable Hurl

Table of Contents

This page documents a story in Sprint 26. It captures the goal, current status, acceptance criteria, and the tasks that compose it.

1. Goal

Every HTTP route the models declare has a generated literate recipe, each recipe tangles to a runnable Hurl file, the harness replays a recipe group against the live gateway and reports a verdict for each recipe, and the inventory is gated in CI.

2. Status

Field Value
State DONE
Parent sprint Sprint 26
Now Nothing.
Waiting on Nothing.
Next Nothing.
Last touched 2026-09-27

3. Acceptance

  • A codegen facet generates one org recipe per model that renders an HTTP route, with a tangle block per endpoint, and the generated output is byte-identical on a second run.
  • Every tangled Hurl file runs against the live gateway unchanged, behind a sign-in preamble the harness supplies.
  • A runner replays a named recipe group and records a verdict per recipe, and a recipe that does not answer fails the run.
  • The recipe inventory is generated from the recipes and checked in CI.

4. Tasks

Task State Start End Description
Scaffold story: Generate literate HTTP recipes that tangle to runnable Hurl DONE 2026-09-27 2026-09-27 Story scaffolding rides this task: documents, sprint wiring, and the scaffold PR. Close it before merging that PR.
Build the HTTP recipe facet and its runner DONE 2026-09-27 2026-09-27 Initial task for: Generate literate HTTP recipes that tangle to runnable Hurl

5. Result

The HTTP surface documents itself and tests itself from one document per model, the arrangement the shell surface already has. A codegen facet generates one literate recipe per model that renders a route, each endpoint tangles to a Hurl file in a committed library, a runner replays a recipe group against the live gateway and records a verdict for each, and the generated inventory is gated in CI.

Fourteen models generate today — seven entities, one junction and six operations — into 82 Hurl files. The task carries the item-by-item account.

The harness found a real defect on its first run, which is what it is for: the gateway rejects every token the login route issues, with JWT verification failed: wrong algorithm in its log. No authenticated recipe can assert a 200 until that is fixed.

6. Decisions

# Decision Why
1 Hurl is the tangle target. It is to HTTP what ores-shell is to the shell: a plain-text file that is both the readable document and the runnable test, with assertions. The existing verb blocks were already most of the way to its syntax.
2 A recipe asserts the endpoint's status, not its payload. A payload assertion would pin the recipe to whatever the populated database happens to hold.
3 REJECTED is a verdict of its own. The gateway refusing the caller says something different about the deployment than the endpoint answering wrongly, and the harness exists to tell those apart.
4 The generated inventory is its own document. The curated doc/recipes/http/http.org catalogues the hand-written recipes that predate the facet. Regenerating it from filetags would replace headings like "RBAC (Role-Based Access Control)" with "Rbac verb".
5 The runner is not a CI job. CI has no running gateway, and check_shell_recipes.py is a manual lever for the same reason. What CI gates is the inventory.

7. Out of scope

Recorded rather than done, each with its reason:

  • The JWT algorithm defect. It needs a diagnosis of how the gateway and iam agree on a signing key, which is security work rather than recipe work.
  • The hand-written recipes in doc/recipes/http/ are stale in shape as well as style: login takes principal rather than username, and the paths are namespaced.
  • The runner replays writes by default. A destructive recipe must be named in --skip, which is recorded on the task rather than enforced here.

Emacs 29.3 (Org mode 9.6.15)