How to set up, verify, troubleshoot, and clean up the Yemaya Study & Deconstruction Workspace locally. Everything here uses commands that exist today; sections gain steps as later checklist phases land (PostgreSQL stores in Section 3, media pipelines in Section 5).
Local setup#
# 1. Core infrastructure (PostgreSQL + pgvector, Redis, MinIO, Mailpit):
docker compose -f docker/docker-compose.dev.yml up -d
docker compose -f docker/docker-compose.dev.yml ps # verify health
# 2. Dependencies (pnpm workspace, hoisted linker):
pnpm install
# 3. Run the workspace service (Hono; defaults to port 4020):
cd apps/yemaya/svc-study-workspace && pnpm serve
The service reads its environment through the fail-fast schema in
apps/yemaya/svc-study-workspace/src/config.ts (YSD-1012): missing production
values, pretty logs in production, or inconsistent feature flags abort startup
with a readable issue list. Feature flags and kill switches (YSD-1014) are set
via STUDY_FLAGS_ENABLE / STUDY_FLAGS_DISABLE / STUDY_FLAGS_KILL using
axis:key entries, e.g. STUDY_FLAGS_KILL=live-session:live-session. Providers
are default-deny: nothing works until STUDY_FLAGS_ENABLE=provider:<id> names
it.
Seed loading and source-safe fixtures#
There is no restricted media anywhere in fixtures — object keys and text stand
in for bytes, by contract. Seed data comes from the deterministic factories in
libs/contracts/src/study/fixtures.ts:
import {
buildStudyFixture, // minimum-valid, schema-validated
buildCompleteStudyFixture, // fullest canonical variant
buildSupersededFixture, // revision-1-superseded-by-2 pair
buildRejectedFixture,
buildSoftDeletedFixture, // deletion-flow case
buildRightsExpiredGrant, // expired + revoked grant
buildContestedClaim, // dissent recorded beside the reading
} from '@oshun/contracts/study';
const project = buildStudyFixture('StudyProject');
Every factory validates through the schema registry, so a seed can never drift
from the contract it exercises. The golden corpus
(libs/contracts/schemas/study/golden/) byte-pins serialized forms.
Targeted verification#
Run only what you touched — never the whole workspace graph:
# Contracts (schemas, invariants, goldens):
cd libs/contracts && npx vitest run src/study && npx tsc --noEmit -p tsconfig.json
# Workspace core library:
cd libs/yemaya/study-workspace && npx vitest run && npx tsc --noEmit -p tsconfig.lib.json
# YSD-22054: the ten named behaviours (failure, permission, rights, expiry,
# deletion, accessibility, version, partial, offline, scale). The check reads a
# V8 coverage report and asks whether a test EXECUTED the line that produces
# each declared state - so it needs the suite run with coverage first, and a
# missing report is a failure rather than a pass. Exemptions live in
# docs/proposals/yemaya-study-workspace/requirement-scope-exemptions.json and
# are only for strings the lexicon matched that are not behaviours at all.
cd libs/yemaya/study-workspace && npx vitest run --coverage.enabled \
--coverage.provider=v8 --coverage.reporter=json \
--coverage.reportsDirectory=.coverage --coverage.include='src/**'
node tools/yemaya-study/check-requirement-scope.mjs
# Service:
cd apps/yemaya/svc-study-workspace && npx vitest run && npx tsc --noEmit -p tsconfig.app.json
# Program checkers — this block is the set CI runs in the lint job, and
# check-doc-conformance.mjs compares the two so the claim stays true. A checker
# that runs in a different workflow is listed under "Checkers CI runs elsewhere"
# below rather than here.
node tools/yemaya-study/check-capability-inventory.mjs
node tools/yemaya-study/check-reuse-ledger.mjs
# YSD-0101/0103: both registers above are validated against their own $schema
# FILE, not a hand copy of it. These are the validator's rules, including the
# refusal that stops it reporting a document valid against a schema it cannot
# fully read.
node --test tools/yemaya-study/json-schema-subset.test.mjs
node tools/yemaya-study/check-traceability.mjs
# YSD-22055: scope is read from the checklist's own EXT tags rather than a list
# written here, so an item tagged EXT tomorrow is in scope tomorrow. The
# eighteen Section 0.2 ids are an anchor against the derivation going quiet - a
# check over a set that quietly shrank passes by having nothing left to check.
node tools/yemaya-study/check-decision-log.mjs
node --test tools/yemaya-study/check-decision-log.test.mjs
# YSD-22055: the line above proves each decision artifact HAS a section headed
# "Machine-enforced outcome". This one proves the outcome is carried by
# something - each assertion is bound to a quotation from the approved artifact,
# so an expectation cannot be widened without the approver. A decision still
# awaiting approval carries a pending control instead: the mechanism that
# refuses meanwhile, quoted from its "Pending control" section, because the way
# a proposed default becomes an approval is that the subject ships while the
# decision waits and nothing says no. Open conformance questions (a decision the
# shipped system has outgrown) are printed, not failed: whether a phase has
# arrived is the decision owner's ruling.
node tools/yemaya-study/check-decision-enforcement.mjs
node --test tools/yemaya-study/check-decision-enforcement.test.mjs
node tools/yemaya-study/check-evidence-index.mjs
node tools/yemaya-study/check-architecture.mjs
node --test tools/yemaya-study/check-architecture.test.mjs
node tools/yemaya-study/check-stub-scan.mjs
# YSD-22056: the four classes the stub scan cannot see - hard-coded success,
# an in-memory store of record, an orphaned projection table, a migration with
# no rollback guidance.
node tools/yemaya-study/check-release-integrity.mjs
node --test tools/yemaya-study/check-release-integrity.test.mjs
node tools/yemaya-study/check-test-portfolio.mjs
node --test tools/yemaya-study/check-test-portfolio.test.mjs
# YSD-18107: the accessibility surface map still accounts for every study
# component, every evidence lane still cites a suite that exists, and every
# recorded run still names a place that does. The standings themselves need the
# git history to age the records against, so they run in the study-workspace
# workflow; drop --artifacts-only to read them locally, and add --record to run
# the lanes that need no staged stack and write what they found.
npx tsx tools/yemaya-study/check-accessibility-evidence.ts --artifacts-only
npx tsx --test tools/yemaya-study/check-accessibility-evidence.test.mjs
node tools/yemaya-study/check-open-item-accountability.mjs
npx tsx --test tools/yemaya-study/check-open-item-accountability.test.mjs
# YSD-19047: the register a facilitator writes an observed trial into, and the
# reader that recomputes the verdict from the rows with the shipped functions.
# It passes on the empty register it ships with - nobody has been timed - and
# fails on a row the instrument would refuse. Add --require-measured to ask
# whether the time clause is settled rather than what stands behind it.
npx tsx tools/yemaya-study/check-attention-trials.ts
npx tsx --test tools/yemaya-study/check-attention-trials.test.mjs
# YSD-19064: what this deployment draws for each lens renderer kind, and the
# accessibility evidence derived from it for the §19.4 battery. A `drawnBy` that
# does not resolve to a component naming the renderer fails; --require-drawn
# asks whether every enabled lens binds to a visualization that exists.
npx tsx tools/yemaya-study/check-lens-renderers.ts
npx tsx --test tools/yemaya-study/check-lens-renderers.test.mjs
# YSD-19108: the rule tests for the Phase 5 runner (the runner itself reads a
# deployment and is listed below, under the checkers CI cannot run).
npx tsx --test tools/yemaya-study/run-phase-five-exit.test.mjs
# YSD-19028: the rule tests for the Phase 1 runner (the runner itself reads a
# deployment and is listed below, under the checkers CI cannot run).
npx tsx --test tools/yemaya-study/run-phase-one-exit.test.mjs
# YSD-19047: the rule tests for the Phase 2 runner, local for the same reason.
# Its two withholdings are what a run without a deployment can check: a project
# it could not read, and a declared output the assembly refused to attribute.
npx tsx --test tools/yemaya-study/run-phase-two-exit.test.mjs
# YSD-19065: the rule tests for the Phase 3 runner (the runner itself reads a
# deployment and is listed below). Its two selections are what a run without one
# can check: a study whose evidence is all the other subject's is not this
# subject's journey, and one whose evidence is unstamped IS, in both.
npx tsx --test tools/yemaya-study/run-phase-three-exit.test.mjs
# YSD-19006: the Phase 0 runner, which unlike the four above needs no
# deployment — its subject is the source, so the sweeps run against this
# checkout and the test file IS the completeness gate. A module somebody adds
# that computes one of Hathor's four owned quantities, or a surface that labels
# an epistemic record, fails here rather than being quietly absent from a corpus
# that still looks whole. Run the gate itself with
# `npx tsx tools/yemaya-study/run-phase-zero-exit.ts`.
npx tsx --test tools/yemaya-study/run-phase-zero-exit.test.mjs
# YSD-18010/18011/18012/19005: the rule tests for the corpus coverage runner
# (the runner itself reads a sealed corpus and is listed below).
npx tsx --test tools/yemaya-study/run-corpus-coverage.test.mjs
# YSD-18002: the expert panel roster, its constituencies parsed out of the
# item's own sentence, and every conflict declaration put through the shipped
# `expertIsEligible`. --require-recruited asks whether the panel exists.
npx tsx tools/yemaya-study/check-expert-panel.ts
npx tsx --test tools/yemaya-study/check-expert-panel.test.mjs
# The requirement-scope check itself needs a coverage report, so the lint job
# runs only its planted-hole rule tests; the check runs in the library job.
node --test tools/yemaya-study/check-requirement-scope.test.mjs
# YSD-22050/22051: every requirement-bearing construct of the proposal is
# enumerated, and every construct whose checklist overlap is low is one somebody
# has read and recorded a cover for. Regenerate the enumeration with --write.
node tools/yemaya-study/enumerate-requirements.mjs --check
node --test tools/yemaya-study/enumerate-requirements.test.mjs
# Schema/golden/client drift (regenerate with the same scripts minus --check).
# The client is generated from STUDY_SERVICE_ROUTES, so a route added to the
# manifest without regenerating fails here.
cd libs/contracts && npx tsx scripts/generate-study-schemas.ts --check
cd libs/contracts && npx tsx scripts/generate-study-goldens.ts --check
cd libs/contracts && npx tsx scripts/generate-study-client.ts --check
Checkers outside the lint job — the first two run in the study-workspace workflow; the last two cannot run in CI at all (a pre-push git-state check reads a detached HEAD on a CI checkout, and the shadow-validation run loads the legacy app), so CI runs only their rule tests and you run these before pushing:
# YSD-22059: documented claims against the code that carries them - the shipped
# HTTP and domain-event surfaces against their published contracts, every
# Prometheus alert against its runbook section, this file's checker block
# against the lint job, and a register of quote-bound claims. Runs in the
# study-workspace workflow because it reads the service sources.
node tools/yemaya-study/check-doc-conformance.mjs
node --test tools/yemaya-study/check-doc-conformance.test.mjs
# YSD-22060: the tip carrying the evidence is on origin/main, not only on its own
# branch. Add --fetch to refresh the remote-tracking ref first.
node tools/yemaya-study/check-branch-published.mjs
# YSD-19143: the parallel/shadow validation before cutover. Loads BOTH systems in
# one process (only possible from tools/ - a type:lib may not import an app) and
# compares the six dimensions. --json regenerates
# docs/proposals/yemaya-study-workspace/shadow-validation-verdict.json, and
# --check re-derives it and fails on drift (CI runs that one, because until
# 2026-08-15 nothing re-ran the derivation and the record could have gone stale
# without anybody noticing).
npx tsx tools/yemaya-study/shadow-validate.ts
npx tsx tools/yemaya-study/shadow-validate.ts --check
# YSD-19108: the Phase 5 exit over a cohort somebody else produced. Lists a
# tenant's projects, assembles one population per project and hands them to the
# shipped gate; read-only, and it withholds the rate when any project could not
# be read. Needs a deployment, so CI runs its rule tests and you run this one.
npx tsx tools/yemaya-study/run-phase-five-exit.ts --tenant <id> --actor <userId>
# YSD-19028: the Phase 1 exit over studies somebody else completed. Describes
# every study in a tenant's projects — the medium off the source the anchors
# reach, the artefacts off a notebook and an export, the persistence off the
# store — and hands them to the shipped gate. Read-only; it withholds the
# verdict when a project could not be read, and when the notebook store is
# unwired, because the gate would otherwise blame learners for a deployment.
npx tsx tools/yemaya-study/run-phase-one-exit.ts --tenant <id> --actor <userId>
# YSD-19065: the Phase 3 exit over journeys somebody else walked. Reads every
# question in a tenant's projects, assembles one arc per (question, subject)
# with the shipped read model and hands them to the gate, which grades the FIRST
# per subject — so nothing here ranks or picks a best one. Read-only; it
# withholds when a question could not be read, because that one may be the
# journey that would have been graded.
npx tsx tools/yemaya-study/run-phase-three-exit.ts --tenant <id> --actor <userId>
# YSD-18010/18011/18012/19005: what a sealed evaluation corpus covers. Lists a
# version's cases and hands them to each vocabulary's own grader; the bars are
# required flags because `assertRelationSetMeasures` uses them to catch a
# corpus that CANNOT FAIL, and defaulting them would hide that check.
npx tsx tools/yemaya-study/run-corpus-coverage.ts --dataset <id> --version <semver>
npx tsx --test tools/yemaya-study/shadow-validate.test.mjs
Troubleshooting#
- Nx fails with duplicate projects (worktrees on this box): bypass Nx — run
npx tsc --noEmit/npx vitest runfrom the library directory directly. Never delete anything under.claude/worktrees/. - Pre-commit typecheck fans out to hundreds of projects / times out:
SKIP_TYPECHECK=1 git commit ...is the sanctioned escape; typecheck the touched projects directly instead (commands above). - Schema drift check fails after editing contracts: regenerate all three
artifacts (
generate-study-schemas.ts,--write-baseline,generate-study-goldens.ts) and re-run the study suite. Note thatlibs/contracts/schemas/is in.prettierignore— generated JSON is byte-compared and must not be reformatted. stub:legitimatescan hits:check-stub-scan.mjsfindings carry file:line and category. A legitimate case (documented approximation, fail-loud seam) is suppressed with an end-of-line// stub:legitimate <reason>— reason required.- Service refuses to boot: the config error lists every issue at once; fix
all of them. Enabling a permanently disabled capability (prohibited analyzers,
graph-suggestion:identity) is a config error by design, not a supported override.
Cleanup#
docker compose -f docker/docker-compose.dev.yml down # stop (keeps data)
docker compose -f docker/docker-compose.dev.yml down -v # stop + wipe data
Kill only the dev-server PIDs you started (track them; never
pkill -f next-server on this shared box), then verify the port is free with
ss -lnt | grep 4020.