# 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}