passages
v0.3.12
Published
Miadi Passages - Narrative Formulations for the Miadi Chronicle
Maintainers
Readme
Passages
Create chronicle episode folders, and turn an episode into an interactive story.
In the Miadi stack, an episode is the folder a piece of work is filed under. Passages is how an
episode is born (mkepisode) and how it later becomes a book someone can walk through: a Twine
story whose every passage is read from the episode and stamped with the source it came from
(passages sketch, passages validate). The chronicle stays the source of truth and is never
written back.
Works with:
@miadi/inquiry-weave: allocates episode directories, registers the episode with the chronicle's medicine wheel, and relates inquiries to episodes.@miadi/episodic-memory-schema: the episode manifest's shapes.
Installation
npm install -g passagesA Python distribution exists as a placeholder and exposes none of the commands below.
Usage
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.
mkepisode \
-n 133 \
-t honcho-eval-adequate-memories \
-g "Establish adequate memories for the honcho evaluation." \
-r "https://example.test/reference-one" \
-r "owner/repo#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. 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 "planning meeting" \
-g "The meeting becomes provenance the chronicle can see." \
-r "provenance/meeting-notes.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: when a wheel url is set or
--register is given (--register <url> overrides the env; the env is read as
MIADI_CHRONICLE_MW_URL first, then the legacy MW_API_URL_OVERRIDE /
MW_API_URL; 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.
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 keeping a book faithful to its episode. It does not replace
walking the book in a browser: Harlowe runtime errors surface only when rendered, so validate also
lists markup candidates for a human to check.
Status
The npm distribution ships mkepisode, passages sketch and passages validate. Rendering
.twee to an .html story is done by a separate renderer. The Python distribution remains a
placeholder and exposes none of these.
License
MIT
