2026-09-29 12:19:58 +02:00
---
id: RBT-WP-0008
type: workplan
title: "Runbook maturity, projects and idempotency"
domain: infotech
repo: runbook-tutorials
2026-09-29 21:45:24 +02:00
status: finished
2026-09-29 12:19:58 +02:00
flavor: implementation
owner: claude
topic_slug: stack
created: "2026-09-29"
updated: "2026-09-29"
depends_on: [RBT-WP-0007]
2026-09-29 12:20:49 +02:00
state_hub_workstream_id: "8f11966b-1e6c-5b12-8bc4-a13c52307c4c"
2026-09-29 12:19:58 +02:00
---
## Goal
Separate what a runbook is (characteristics), how far it can be trusted (maturity, earned and bound to a
pack revision), and what state an environment is in (projects). Replaces the environment-dependent
`exercise_status` . Model and candidate terminology: `docs/maturity-and-projects.md` .
## Decide the terminology and model
```task
id: RBT-WP-0008-T01
2026-09-29 12:36:34 +02:00
status: done
2026-09-29 12:19:58 +02:00
priority: high
2026-09-29 12:20:49 +02:00
state_hub_task_id: "904939c5-ed69-53a0-8fe1-2a0ae6d5337d"
2026-09-29 12:19:58 +02:00
```
2026-09-29 12:36:34 +02:00
Decided 2026-09-29 (Bernd: go with the suggestions, keep *project* ): `docs/maturity-and-projects.md` .
2026-09-29 12:19:58 +02:00
## Idempotency as a declared characteristic
```task
id: RBT-WP-0008-T02
status: done
priority: high
2026-09-29 12:20:49 +02:00
state_hub_task_id: "911ef051-ee55-5c65-b134-57fa9f1fddce"
2026-09-29 12:19:58 +02:00
```
Done 2026-09-29: `idempotent: idempotent | non-idempotent | conditional` (+ `idempotent_note` ) per step in the v0.2
schema, validator rule for `conditional` , derived runbook value (`flow.pack_idempotency` ), declared honestly in the
showcase. Values are words because YAML reads unquoted yes/no as booleans. Follow-ups: show it in the UI, and use
it for the retry policy (T07).
## Bind runs to a pack revision
```task
id: RBT-WP-0008-T03
2026-09-29 12:36:34 +02:00
status: done
2026-09-29 12:19:58 +02:00
priority: high
2026-09-29 12:20:49 +02:00
state_hub_task_id: "a79e5b3d-221b-5ac6-a7a1-44737139c8f3"
2026-09-29 12:19:58 +02:00
```
2026-09-29 12:36:34 +02:00
Done 2026-09-29: `pack.digest()` ; every run records `pack_digest` and `context` (project, target, environment class).
2026-09-29 12:19:58 +02:00
## Derive maturity from run history
```task
id: RBT-WP-0008-T04
2026-09-29 12:36:34 +02:00
status: done
2026-09-29 12:19:58 +02:00
priority: high
2026-09-29 12:20:49 +02:00
state_hub_task_id: "53fe81cd-6b67-5508-bdbd-38073a0d77e7"
2026-09-29 12:19:58 +02:00
```
2026-09-29 12:36:34 +02:00
Done 2026-09-29: `rtut/maturity.py` , review records (`REVIEWS.yaml` , bound to a revision), `rtut maturity` , `rtut review` .
`proven` = reviewed + rehearsed + clean in two environments by two operators. Not yet done: retiring `exercise_status` and the
"Record pack as exercised" click in the UI (T09).
2026-09-29 12:19:58 +02:00
## Project specification and shared datapoints
```task
id: RBT-WP-0008-T05
2026-09-29 12:36:34 +02:00
status: done
2026-09-29 12:19:58 +02:00
priority: high
2026-09-29 12:20:49 +02:00
state_hub_task_id: "d022c7fb-1421-5738-a00f-5b18593fb1a2"
2026-09-29 12:19:58 +02:00
```
2026-09-29 12:36:34 +02:00
Done 2026-09-29: attribute sets (`attribute-set/v0.1` : composition with `extends` , requirement, derived state, provenance,
`modified` flag), projects with targets and members (`project/v0.1` ), project templates (`project-template/v0.1` ),
`rtut/attrs.py` , `rtut/project.py` , schemas, CLI (`rtut project ...` ), examples under `attribute-sets/` and `project-templates/` .
Runs started from a project take the resolved attributes as defaults and record the project as context.
2026-09-29 12:19:58 +02:00
## Project state
```task
id: RBT-WP-0008-T06
2026-09-29 12:36:34 +02:00
status: done
2026-09-29 12:19:58 +02:00
priority: medium
2026-09-29 12:20:49 +02:00
state_hub_task_id: "41f2b781-3699-5d21-bdfd-4ff3c2c0b777"
2026-09-29 12:19:58 +02:00
```
2026-09-29 12:36:34 +02:00
Done 2026-09-29: `project.state()` : member status `not-applied | applying | applied | stale | failed` , project state
`planned | applying | applied | stale` , plus the members that can start next (`after` ) and the attributes still needed.
2026-09-29 12:19:58 +02:00
## Retry policy for non-idempotent steps
```task
id: RBT-WP-0008-T07
2026-09-29 14:42:09 +02:00
status: done
2026-09-29 12:19:58 +02:00
priority: medium
2026-09-29 12:20:49 +02:00
state_hub_task_id: "8b34c57b-3380-5f7b-aaf5-6227a47dcf60"
2026-09-29 12:19:58 +02:00
```
2026-09-29 14:42:09 +02:00
Done 2026-09-29: `run2.retry(..., acknowledge_partial=True)` is required unless the step is declared `idempotent` (undeclared
counts as not idempotent); the UI asks with a checkbox; the acknowledgement is recorded on the step. v0.1 runs have no idempotency
declaration and keep their free retry.
2026-09-29 12:19:58 +02:00
2026-09-29 12:36:34 +02:00
## UI for projects, maturity and attributes
```task
id: RBT-WP-0008-T08
2026-09-29 13:12:47 +02:00
status: done
2026-09-29 12:36:34 +02:00
priority: high
2026-09-29 12:37:10 +02:00
state_hub_task_id: "913c46fa-7790-5d32-ada1-8dec430d4ff5"
2026-09-29 12:36:34 +02:00
```
2026-09-29 13:12:47 +02:00
Done 2026-09-29 (tested over HTTP, not yet reviewed in a browser): the runbook list shows maturity per runbook; the front page lists
projects (environment, state, goal) and templates; a template page shows every attribute with requirement, note and source and
creates a project; the project page shows goal, state, still-needed attributes (fillable, validated against the runbooks that
consume them), members with status, maturity, next member and a Start run button, and per-member attribute tables with state,
source, modified mark and problems; runs started from a project take its attributes and link back to it; the pack page shows
maturity evidence, idempotency and a review form. Not covered: walking v0.2 runs (RBT-WP-0004). Original scope: show maturity and idempotency next to each runbook; a project view (goal, targets, member status, next member, needed
2026-09-29 12:36:34 +02:00
attributes with state, source and modified marks); start a run from a project so its attributes fill the form; a
template picker that instantiates a project.
## Retire exercise_status
```task
id: RBT-WP-0008-T09
2026-09-29 21:45:24 +02:00
status: done
2026-09-29 12:36:34 +02:00
priority: medium
2026-09-29 12:37:10 +02:00
state_hub_task_id: "54749c4e-69d8-54ee-a1f7-5e9f781a9053"
2026-09-29 12:36:34 +02:00
```
2026-09-29 21:45:24 +02:00
Done 2026-09-29 (RBT-WP-0009-T01): the click, `apply_exercise` and `exercise_update` are removed, the field is deprecated and ignored, and the shipped packs no longer carry it.
Original scope: replace the "Record pack as exercised" click and the `exercise_status` field with derived maturity; migrate the packs.
2026-09-29 12:36:34 +02:00
## Templates as products: auto-confirm and auto-pilot on a project
```task
id: RBT-WP-0008-T10
2026-09-29 20:08:17 +02:00
status: done
2026-09-29 12:36:34 +02:00
priority: medium
2026-09-29 12:37:10 +02:00
state_hub_task_id: "917961d5-1571-52a5-b138-a15163176ed4"
2026-09-29 12:36:34 +02:00
```
2026-09-29 20:08:17 +02:00
Done 2026-09-29: `rtut/projectrun.py` and `rtut/ui_projectrun.py` . A project run snapshots the resolved attributes, then runs the members
member by member in `after` order. Manual: you start each member (out of order needs a recorded override). Auto-confirm: the engine starts
the next ready member, ticks its run, closes it when it finishes cleanly, continues through push-through, and stops wherever a person is needed
(v0.1 member, manual-only or irreversible step, failed check). Closing locks and seals it, and abandons an open member run. Auto-pilot with
execution remains RBT-WP-0006. Attribute states to explore in use: needed, optional, modified,
2026-09-29 12:36:34 +02:00
checked; add what real use shows.
2026-09-29 12:19:58 +02:00
## Acceptance Criteria
- "Exercised" has a precise, environment-aware meaning; a production project is never called "exercised".
- Maturity is derived from runs, bound to a revision, and expires when the pack changes.
- A value shared by several runbooks is entered once, in the project.