User stories
MechaHarness acceptance paths are told as user stories: short prose scenes
centered on software-engineer dragons. The same narratives live in fixture
story.json files and run as dual-mode data-driven tests under tests/stories/.
See the Cursor skill .cursor/skills/user-stories/SKILL.md when adding features:
touched functionality must be exercised by a story, and story bodies stay
readable prose (not As-a checklists).
Personas
Nubble — operations / DevOps
Nubble keeps the Spark lit and the pages quiet. He cares about lanes and profiles, CI that does not depend on LAN, and EventLog cost signals he can forward to billing or Grafana.
Fangore — application developer
Fangore builds host apps on MechaHarness Config. He cares about deny-by-default
grants, CompoundPolicy composition, DI wiring, and product rules that stay in
JudgementPolicy — never in the model’s mouth.
Taloneth — ML engineer
Taloneth owns judge contracts and adapter drift. He cares about offline fixture batches, versioned System One cassettes, and flipping the same story to live when the forge is warm.
Running the suite
Default CI runs every story offline (static fixture + cassettes) — no skips.
# CI / offline — all stories, zero skips
pytest -q tests/stories
# Full suite (unit + stories)
pytest -q
# Live OpenAI-compat (junespark) — same tests, real HTTP
MECHA_STORY_BACKEND=live MECHA_STORY_MODEL=openai_compat/qwen3-30b-thinking \
MECHA_BASE_URL=http://127.0.0.1:8000/v1 pytest -q tests/stories -k nubble_run_cost
# Live System One
MECHA_STORY_BACKEND=live MECHA_STORY_MODEL=systemone/laya \
MECHA_JUDGE_BASE_URL=http://127.0.0.1:8009 pytest -q tests/stories -k refund_verdict
# Shorthand aliases (still dual-mode stories, not separate skipif modules)
MECHA_LIVE_JUNESPARK=1 MECHA_BASE_URL=… pytest -q tests/stories -k nubble_run_cost
MECHA_LIVE_QWEN=1 MECHA_BASE_URL=… pytest -q tests/stories -k nubble_run_cost
MECHA_LIVE_JUDGE=1 pytest -q tests/stories -k 'refund_verdict or systemone_cassette'
|
Behavior |
|---|---|
|
In-process fixture + HTTP cassettes |
|
Real servers; soft expects only |
Models
Path |
Role |
|---|---|
|
Generic in-process model; model-agnostic paths |
|
Captured OpenAI-compat chat |
|
Captured System One judge wire |
Nubble’s stories
nubble_lane_load_hint — The wrong torch on the wall
Nubble is mid-shift when a paging alert fires: the refund-routing job is failing
on the Spark. He SSH’s in, expects a judge profile, and finds someone left the
reason torch lit from an earlier experiment. The harness should not shrug
with a generic failure — it should tell him, in operator language, that this
work needs the judge lane and which load command to run. When the error names
infer load decide-fast (or the host’s judge hint), Nubble flips the profile
and the page clears. He relates to this story whenever a lane mismatch wastes
minutes of incident time.
nubble_run_cost_events — Counting the sparks
Finance asked Nubble for a boring truth: can they see that a harness run
actually burned units? He kicks a one-shot pass-through (“say hello”) and opens
the EventLog. He needs core:inference and core:cost sitting there with
non-zero units — the same signals he would forward to billing or a Grafana
panel. If those events are missing, ops is flying blind; if they’re present, he
trusts the ledger enough to put an alert on it.
nubble_ci_static_smoke — Merge night without the mountain
It is late; the DGX Spark is offline for maintenance, and a PR still needs green CI. Nubble refuses to block merges on LAN reachability. He wants the OpenAI-compat path proven from a cassette — a recorded reply replayed through the real HTTP client — so the pipeline stays honest about wire shape without waking the mountain. When static CI is green, he sleeps; when someone breaks the adapter, CI fails before production.
Fangore’s stories
fangore_compound_grants — The locked scroll, then the keyring
Fangore is building a host app that must not let agents scribble the filesystem
by default. He starts with a read-only grant set — the write tool is refused,
and he can show product that deny-by-default works. Later he composes that
policy with a write layer via CompoundPolicy (one keyring, not a rewritten
list) and the same tool succeeds. He relates to this whenever an app needs
reusable permission packs instead of copy-pasted grant arrays.
fangore_refund_verdict — Billing owns the double-charge
A customer was charged twice. Fangore’s product rule is simple: only the billing
team may own that ticket — the model may suggest, but it must not grant
permission. He runs the judge over the ticket text, then JudgementPolicy
decides ALLOW only when the route signal is billing. If the model picks
technical, policy denies. He relates to this whenever business authority must
stay in code, not in prompt vibes.
fangore_config_access_policy — Wiring the nest with DI
Fangore subclasses MechaHarnessConfig for his host. He does not want a
service-locator bag of grants — he overrides get_access_policy() to return
his CompoundPolicy, configures once, and injects AccessControl. When the
injector hands back a control that already honors his layers, he knows the app
is wired the MechaHarness way. He relates to this whenever a host must stay
DI-first instead of sprinkling grants into constructors by hand.
Taloneth’s stories
taloneth_fixture_judge_batch — Lab bench without the forge
Taloneth is iterating on question sets — noul, choice, and score in one batch —
and cannot wait on a GPU forge for every tweak. He drives FixtureJudgeProvider
with known answers and checks that validation accepts clean signals and reports
errors when answers are missing. The lab bench is offline, deterministic, and
fast; he relates to this whenever contract tests must not depend on a served
model.
taloneth_systemone_cassette — Pinning Laya’s handwriting
A new Laya build changes confidence fields in the System One JSON. Taloneth
keeps a versioned cassette of the wire response under systemone/laya/v1
and replays it through SystemOneJudgeProvider. If mapping breaks, the story
fails before silent drift ships. He relates to this whenever adapter code must
stay pinned to a known model handwriting.
taloneth_live_parity — Same scroll, live ink
When the forge is warm, Taloneth flips MECHA_STORY_BACKEND=live and runs the
same request and expect scrolls against the real endpoint. He is not
rewriting tests — he is asking whether live ink still satisfies the soft
acceptance he already trusts from cassettes. Drift shows up as a failed story,
not a new skipif module. He relates to this whenever “does production still
match the lab?” must be one command away.
Live parity is a mode of the cassette-backed stories above (for example
nubble_run_cost_events, fangore_refund_verdict,
taloneth_systemone_cassette), not a separate empty fixture.