passages
v0.2.2
Published
Miadi Passages - Narrative Formulations for the Miadi Chronicle
Maintainers
Readme
Passages
Passages is the narrative formulation engine for the Miadi Chronicle. It transforms chronological events and branch-map data into interactive story formats (like Twine/Twee).
Overview
In the Miadi platform, a Passage is a structured unit of narrative that represents a specific branch, chapter, or moment in time. This package provides the tools and wrappers to manage these formulations across the distributed ecosystem.
Features
- Chronicle to Form: Transforms raw Miadi Chronicle episodes into structured narrative units.
- Twine Integration: Supports the promotion of episodes into interactive Twinery story worlds.
- Narrative Motion: Applies the RISE framework and structural tension to ensure meaningful story progression.
Installation
Python
pip install passagesNode.js
npm install passagesUsage
passages --helpmkepisode — Chronicle episode vessel
Creates a minimal Chronicle episode vessel (one directory + one episode.yaml
carrying the goal and references) under the chronicle root. See
rispecs/mkepisode.spec.md for the full contract.
mkepisode \
-n 133 \
-t honcho-eval-adequate-memories \
-g "Establish adequate memories for the honcho evaluation." \
-r "https://example.test/reference-one" \
-r "jgwill/Miadi#133"--adopt — the one repair path
A directory born by hand (mkdir, a clone, an agent writing notes straight into
the chronicle) resolves by number but carries no episode.yaml.
inquiry-weave lineage cannot author an edge on it and the /chronicle surface
cannot render it — both read the manifest — and mkepisode used to refuse the
number without offering any way forward, leaving the vessel permanently
unreachable.
The medicine wheel is not one of those readers, which is what makes this
shape dangerous: registerEpisodeNode derives the card from the directory
name, so a manifest-less vessel registers successfully and the wheel then
reports it healthy. Two episodes in the live chronicle are in exactly that
state. A green wheel is not evidence that a vessel has a manifest.
--adopt writes the missing manifest in place, keeping the directory's own
date, number and slug (read off its name) and touching nothing else in it:
mkepisode --adopt \
-n 67 \
-t "Renaud meeting" \
-g "The relationship becomes provenance the chronicle can see." \
-r "provenance/nicolas-renaud-260619.md"It refuses when an episode.yaml is already present — adoption only ever fills
a missing manifest, it never overwrites or merges one — and it refuses an
ambiguous number (several directories carrying it), because there is no single
vessel the manifest would belong to. On a free number it simply creates the
vessel normally. Registration and the .mw-registration.json receipt run
exactly as they do at birth.
Three refusals worth knowing, because each one is a case where guessing would have been quieter than stopping:
- A directory whose name carries no
YYYY-MM-DDis refused rather than stamped with today. The date is part of the identity being preserved; minting one would rewrite a June episode as an August one under the directory's own authority. - An
episode.yamlthat cannot be read — a dangling symlink, an unreadable parent — is refused rather than treated as absent.existsSyncanswersfalsefor both, which would let adoption replace a manifest pointer with a file, the one thing this flag promises never to do. --statusexists because adoption runs, by definition, on directories that already hold work. The defaultvesseldescribes an empty one; pass--status woven(or whatever is true) rather than stamping finished work as empty.
Flags: -n/--number (required positive integer), -t/--title (required; kebab-case
preserved as slug, otherwise normalized), -g/--goal (required), -r/--reference
(required, repeatable, order preserved), --chronicle-root (explicit override;
defaults to $MIADI_CHRONICLE_ROOT), --adopt and --status <text> (above),
--register [url] / --no-register (below). It never overwrites or merges into an
episode that already has a manifest, and fails without writing when the number
already resolves and --adopt was not given. A created directory is stamped with the
local calendar date; an adopted one keeps its own.
Vessels visible at birth (jgwill/Miadi#490): when MW_API_URL is set or
--register is given (--register <url> overrides the env; no other default url is
assumed; --no-register always wins), mkepisode registers the episode card into
medicine-wheel /api/nodes via @miadi/inquiry-weave's registerEpisodeNode —
naming and shining become one gesture. A truthful receipt lands beside
episode.yaml as .mw-registration.json (state
registered | already-registered | pending, node_id, timestamp, url, and
error when pending). An existing card is already-registered success and is never
overwritten. Registration is fail-open: vessel creation never fails because of it.
Composition: passages owns creation and narration; directory allocation and
collision rules are consumed from @miadi/inquiry-weave's scaffoldEpisode —
relating and syncing inquiries stays inquiry-weave behavior. The JavaScript
distribution is the canonical implementation; the Python distribution does not
expose mkepisode yet.
sketch — derive a book from an episode vessel
mkepisode owns birth; sketch owns the modality switch. It reads a vessel and
emits Twee source derived from it — source only, no HTML render, and no write
outside --out. The chronicle stays canonical and read-only.
passages sketch --episode 311 # to stdout
passages sketch --episode 311 --out book.twee # to a file
passages sketch --episode 311 --json # the derivation reportA passage is never invented, it is read. Each vessel unit becomes one passage:
every inquiry/weave.yaml entry, every ## section of the narrative document,
every *audio-manifest.json, every lineage.relates_to exit, plus a board and a
provenance page. Every emitted passage carries a passages-sketch stamp naming
its source and the hash the vessel already recorded for it — copied, never
computed. Units the vessel records no hash for are stamped unstamped with a
reason, because the set of unverifiable passages is a finding a reader is owed.
Full mapping table, the ep311 worked fixture, and the explicit list of what a
machine cannot produce: rispecs/episode-to-passages.contract.md.
validate — hold a book to its vessel
passages validate --episode 311 --twee book.tweeRe-reads the vessel, compares every stamped hash against the vessel's current
records, and walks every internal link from the declared start. Exits 1 on
drift, on a stamped source that has left the vessel, or on a link with no passage
behind it; 0 when clean. Unstamped passages are reported, never failed — a
hand-authored passage is allowed to exist, it simply cannot be verified.
This is the structural half of PARITY ALWAYS. It does not replace the browser
walk: Harlowe runtime errors surface only when rendered, so validate also
lists markup candidates for a human to check.
Status
The npm distribution ships the canonical mkepisode Chronicle vessel command,
plus passages sketch and passages validate — the episode→book derivation
named in rispecs/episode-to-book.spec.md §S3 and specified in
rispecs/episode-to-passages.contract.md. Rendering .twee to a
library-visible .html stays with the Twine repo's donor injector. The Python
distribution remains a placeholder and exposes none of these.
License
MIT
