2026-09-29 10:49:10 +02:00
# DRAFT - not loaded by the engine. Written against docs/pack-spec-v0.2.md.
2026-09-29 10:36:29 +02:00
# Purpose: an acceptance test for the proposed model. If the model cannot express
# this story without special cases, the proposal must change. When v0.2 is adopted
# this file becomes pack.yaml (and the v0.1 file is retired or kept as a
# compatibility fixture).
spec : runbook-pack/v0.2
id : rtut.showcase-lighthouse
title : "Showcase: a night at the lighthouse"
owner : runbook-tutorials
outcome : You have played a short interactive story that touches every feature of the runbook engine, using only harmless echo, printf and a scratch file under /tmp/rtut-.
exercise_status : unexercised
engine : native
confirm_phrase : I understand this cannot be undone
framing : # shown first; things to keep in mind, not gates
- This showcase never handles a secret. Datapoints hold references and facts, never secret values.
- Only /tmp/rtut-* is ever written or removed.
- Several things below can be done in any order; the guide recommends one.
actors :
- id : keeper
role : Lighthouse keeper
description : Runs the lamp and the signals. Does the everyday work.
privileges : [ light-lamp, sound-signal, write-logbook]
- id : harbourmaster
role : Harbour master
description : Approves the irreversible closing of the night's log.
privileges : [ close-log, waive-lamp-check]
datapoints :
- {id: keeper_name, label: Keeper's name, type: string, pattern : "[A-Za-z]+" , default: Ada, dimensions : {actor: keeper, kind: identity}, ask_in : dusk}
- {id: lamp_fuel, label: Lamp fuel, type: enum, values: [whale-oil, paraffin, electric], default: paraffin, dimensions : {actor: keeper, kind: equipment}, ask_in : dusk}
- {id: weather, label: Tonight's weather, type: enum, values: [clear, fog, storm], default: fog, dimensions : {kind: condition}, ask_in : dusk}
- {id: motto, label: Motto, type: text, default: Steady light for those at sea, dimensions : {actor: keeper, kind: identity}, ask_in : dusk}
- {id: logdir, label: Scratch folder, type: path, pattern : "/tmp/rtut-[a-z0-9-]+" , default: /tmp/rtut-lighthouse, dimensions : {kind: location, subsystem: logbook}, ask_in : dusk}
2026-09-29 11:34:07 +02:00
- {id: lamp_lit, label: Lamp is lit, type: bool, dimensions : {actor: keeper, kind: state}, produced_by: light-lamp, confirmed_by : light-lamp}
2026-09-29 10:36:29 +02:00
stages :
- id : dusk
title : Dusk - prepare
goal : The lamp is lit and the weather is known before dark.
steps: # no requires among these : they are parallel; list order is the recommended order
- id : read-the-sign
2026-09-29 12:19:58 +02:00
idempotent : idempotent
2026-09-29 10:36:29 +02:00
title : Read the poem over the door
actor : keeper
owner : the-lighthouse
command : printf '%s\n' 'Tower of stone, and a patient flame,' 'you keep no name, yet call each name' 'across the dark, across the foam:' 'come home, come home, come home.'
reversible : true
verify : {done_when: You read the poem, expect : manual}
- id : light-lamp
2026-09-29 12:19:58 +02:00
idempotent : idempotent
2026-09-29 10:36:29 +02:00
title : Light the lamp
actor : keeper
owner : the-lighthouse
uses : [ keeper_name, lamp_fuel]
produces : [ lamp_lit]
command : echo "{{keeper_name}} lights the {{lamp_fuel}} lamp."
reversible : true
2026-09-29 10:49:10 +02:00
downstream_risk : Without a lit lamp the night watch has no purpose and ships are not warned.
2026-09-29 10:36:29 +02:00
verify : {done_when: The lamp is lit, expect : manual}
- id : read-the-sky
2026-09-29 12:19:58 +02:00
idempotent : idempotent
2026-09-29 10:36:29 +02:00
title : Read the sky
actor : keeper
owner : the-lighthouse
uses : [ weather]
command : echo "Weather tonight - {{weather}}."
reversible : true
verify : {done_when: You know the weather, command : 'echo "Weather tonight - {{weather}}."' , expect: output-contains, contains : "{{weather}}" }
gates :
- id : lamp-check
title : The lamp burns steadily
goal : safe-navigation # quality objective; a canon standard id where one exists
blocking : true
2026-09-29 10:49:10 +02:00
timeout : {seconds: 120, on_timeout : stop}
downstream_risk : Ships may steer by a light that is not burning.
2026-09-29 10:36:29 +02:00
checks :
- {done_when: The light is visible from the water, expect : manual}
- {datapoints_ok : [ lamp_lit]}
waivable : {by: harbourmaster, needs_reason : true }
- id : night
title : Night watch - give warning
goal : Ships are warned in a way that suits the weather.
requires : [ gate:lamp-check]
steps :
- id : warn
2026-09-29 12:19:58 +02:00
idempotent : idempotent
2026-09-29 10:36:29 +02:00
title : Warn the ships
actor : keeper
owner : the-lighthouse
uses : [ keeper_name, weather]
one_of: # alternatives : any one satisfies "warn"; the guide recommends by weather
- id : sound-horn
title : Sound the fog horn
recommended_when : {weather : [ fog]}
command : echo "{{keeper_name}} sounds the horn through the {{weather}} night."
reversible : true
verify : {done_when: The horn has sounded, expect : manual}
- id : wave-flags
title : Signal with flags
recommended_when : {weather : [ clear]}
command : echo "{{keeper_name}} signals with flags in the {{weather}} night."
reversible : true
verify : {done_when: The flags were seen, expect : manual}
- id : shelter
title : Bar the door and wait it out
recommended_when : {weather : [ storm]}
command : echo "{{keeper_name}} bars the door. Nobody is warned tonight."
reversible : true
verify : {done_when: The door is barred, expect : manual}
- id : write-logbook
2026-09-29 12:19:58 +02:00
idempotent : non-idempotent
idempotent_note : Appends a line every time it runs; a second run leaves a duplicate entry.
2026-09-29 10:36:29 +02:00
title : Write tonight's entry
actor : keeper
owner : the-lighthouse
requires : [ warn]
uses : [ keeper_name, weather, lamp_fuel, motto, logdir]
2026-09-29 10:49:10 +02:00
produces : [ logbook-file]
2026-09-29 10:36:29 +02:00
command : mkdir -p {{logdir}} && echo "Night watch by {{keeper_name}} - {{weather}}, {{lamp_fuel}} lamp. {{motto}}." >> {{logdir}}/logbook.txt
reversible : true
rollback : rm -rf {{logdir}}
verify : {done_when: The logbook exists, command : 'test -s {{logdir}}/logbook.txt' , expect : exit-0}
- id : dawn
title : Dawn - close the night
goal : The night is on record and the scratch space is clean.
requires : [ night]
steps :
- id : close-log
2026-09-29 12:19:58 +02:00
idempotent : conditional
idempotent_note : Safe to repeat only while the log file exists; a second run fails on the read.
2026-09-29 10:36:29 +02:00
title : Close the log for good
2026-09-29 12:19:58 +02:00
auto_confirm : manual-only # never confirmed by the engine on its own
2026-09-29 10:36:29 +02:00
actor : harbourmaster
owner : the-lighthouse
uses : [ logdir]
command : cat {{logdir}}/logbook.txt && rm -rf {{logdir}}
reversible : false # engine demands the typed confirm_phrase; no renderer can skip it
verify : {done_when: The scratch folder is gone, command : 'test ! -e {{logdir}}' , expect : exit-0}
gates :
- id : night-on-record
title : Nothing left behind
goal : clean-handover
blocking : true
checks :
- {done_when: No files remain under the scratch folder, command : 'test ! -e {{logdir}}' , expect : exit-0}
artefacts :
2026-09-29 10:49:10 +02:00
- id : logbook-file
2026-09-29 10:36:29 +02:00
title : The night's logbook entry
location : {datapoint: logdir, path : logbook.txt} # a reference, never a value
produced_by : write-logbook
lives_until : close-log
ownership :
- {concern: The story, owner : runbook-tutorials}