Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 295952@bnt-lap001 Assistant-Session: e93f64ad-516c-46eb-9666-aad8d300c477
159 lines
7.5 KiB
YAML
159 lines
7.5 KiB
YAML
# DRAFT - not loaded by the engine. Written against docs/pack-spec-v0.2.md.
|
|
# 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}
|
|
- {id: lamp_lit, label: Lamp is lit, type: bool, dimensions: {actor: keeper, kind: state}, produced_by: light-lamp, confirmed_by: light-lamp}
|
|
|
|
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
|
|
idempotent: idempotent
|
|
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
|
|
idempotent: idempotent
|
|
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
|
|
downstream_risk: Without a lit lamp the night watch has no purpose and ships are not warned.
|
|
verify: {done_when: The lamp is lit, expect: manual}
|
|
- id: read-the-sky
|
|
idempotent: idempotent
|
|
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
|
|
timeout: {seconds: 120, on_timeout: stop}
|
|
downstream_risk: Ships may steer by a light that is not burning.
|
|
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
|
|
idempotent: idempotent
|
|
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
|
|
idempotent: non-idempotent
|
|
idempotent_note: Appends a line every time it runs; a second run leaves a duplicate entry.
|
|
title: Write tonight's entry
|
|
actor: keeper
|
|
owner: the-lighthouse
|
|
requires: [warn]
|
|
uses: [keeper_name, weather, lamp_fuel, motto, logdir]
|
|
produces: [logbook-file]
|
|
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
|
|
idempotent: conditional
|
|
idempotent_note: Safe to repeat only while the log file exists; a second run fails on the read.
|
|
title: Close the log for good
|
|
auto_confirm: manual-only # never confirmed by the engine on its own
|
|
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:
|
|
- id: logbook-file
|
|
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}
|