Append-only evidence log for YEMAYA_STUDY_AND_DECONSTRUCTION_WORKSPACE_SOTA_IMPLEMENTATION_CHECKLIST.md.
Contract (YSD-0003): every checked item records its implementation artifacts,
test command, passing output, review/approval evidence where applicable, and the
commit that landed it. Entries are append-only; corrections append a superseding
entry rather than editing history. Each entry's commit is resolvable
deterministically with git log --grep='<item-id>' because every implementation
commit subject or body names the item IDs it delivers; hashes are additionally
recorded here once known.
Immutable-link contract (YSD-0112): evidence links must survive later edits to the linked artifact. The convention, in order of preference:
- Commits — full or unambiguous-prefix SHA on
origin/main(git log --grep='<item-id>'resolves it; the recorded SHA pins content forever). A pinned browse URL ishttps://github.com/GreyChimp/oshun/blob/<sha>/<path>. - Migrations — repo path of the migration directory plus the SHA that introduced it (migration files are themselves append-only by repo policy).
- API versions — the version literal in the contract source (e.g.
VIDEO_ADAPTER_VERSION) plus path@sha. - Evaluation reports / performance runs / accessibility reports — the
generated report file committed under
docs/orinfra/a11y-reports/(or the CI run URL), always paired with the workflow name and the commit it ran against. - Threat-model updates — path@sha of the threat-model document revision.
- Release decisions — the decision artifact path@sha plus the named approver and date (YSD-0015).
A bare mutable path (no SHA, no version) is not evidence; entries below that predate this contract already carry SHAs and remain compliant.
YSD-0101 — Machine-readable capability inventory (2026-07-18)#
- Status: complete
- Artifacts:
docs/proposals/yemaya-study-workspace/capability-inventory.json— 14 domain entries (yemaya, nisaba, sophia, hathor, aja, euterpe, aglaea, bellona, neith, maya, metis, isis, iris, oshun), each with owner, API services, data authority (accountable/contributing/boundary per proposal §3.2), maturity for the study workspace (proposal §5.1 legend), persistence (migration-dir evidence), fixture use (raw marker counts plus code-verified findings), test coverage counts, and known limitations; plus 6 program-level missing capabilities from proposal §5.2.docs/proposals/yemaya-study-workspace/capability-inventory.schema.json— JSON Schema (2020-12) for the artifact.tools/yemaya-study/check-capability-inventory.mjs— dependency-free structural validation + domains.json drift check + path-existence check, following thetools/domains/check-registry.mjsconvention.
- Ground truth collected at commit
6c87e0df3b98f431c80fbc3ab7a5318d85c20898: file/test/migration counts viafind, fixture-marker counts viagrep -rl, domain registry fields fromdomains.json. Fixture claims verified by reading code:apps/aja/svc-reference-video/src/metadata-extraction.ts(simulated FFprobe, lines 259–282/508–511),apps/aja/svc-reference-video/src/scene-detection.ts(simulated frame stats/scene changes, lines 271–334),libs/yemaya/agents/src/creative/performance-reference-library.ts(in-memoryMapstores lines 183–185, four-value rights enum line 36). All 15 proposal-cited evidence paths confirmed to exist. - Test command:
node tools/yemaya-study/check-capability-inventory.mjs - Passing output:
capability-inventory check passed: 14 domains, 6 missing-capability entries, assessed 2026-07-18. - Negative test: removing the
irisentry, corrupting an owner, and pointing a verified finding at a nonexistent path made the checker fail with exactly those 3 problems (exit 1); restoring the file returned it to green. - Commit: resolve via
git log --grep='YSD-0101'(subjectdocs(yemaya): ysd-0101 machine-readable capability inventory), landed asa0710bd396on branch and merged to origin/main via0808b8cb5b.
YSD-0102 — Proposal §29 evidence-link validation + symbol record (2026-07-18)#
- Status: complete
- Artifacts:
docs/proposals/yemaya-study-workspace/section-29-evidence.md— link validation result plus the exact reusable symbols, services, and contracts for all 15 §29.3 primitives (replacing directory-level claims), with reuse caveats and a prohibited-inference risk flag for four yemaya analyzers (gaze/fatigue/authenticity/response detection) feeding YSD-4052.
- Validation method: every §29 link resolved from
docs/proposals/with a shell existence check over all 30 targets (5 ADRs, 10 domain docs, 15 primitives) — all OK, zero stale paths, so no proposal edits were needed. Export surfaces extracted withgrep -n '^export 'per file and read in context; simulated paths re-confirmed (aja scene-detection lines 271–334, metadata-extraction lines 259–282/508–511). - Test command:
cd docs/proposals && for p in <all 30 §29 link targets>; do [ -e "$p" ] || echo STALE $p; done→ no STALE output. - Note: continuous re-validation of these links is deliberately deferred to YSD-0111 (automated traceability check), which owns CI enforcement.
- Commit: resolve via
git log --grep='YSD-0102', landed ase4f7be9eb2.
YSD-0103 — Reuse-versus-wrap-versus-replace ledger (2026-07-18)#
- Status: complete (ledger + enforcement built; 10 owner decisions recorded as pending and tracked by the checker — those pending decisions gate later work via the ledger itself and the future YSD-0139 CI decision gate, they do not gate this build item)
- Artifacts:
docs/proposals/yemaya-study-workspace/reuse-ledger.json— 20 entries, one per proposal §5.2 capability row, each with status (§5.1 legend), cited evidence, proposed disposition (reuse/wrap/replace/build-new/ exclude), rationale, and — for every partial or fixture-backed capability — a required ownerDecision block (all currently pending).docs/proposals/yemaya-study-workspace/reuse-ledger.schema.json— JSON Schema with conditional requirements (partial/fixture-backed ⇒ ownerDecisionRequired:true + ownerDecision present; decided ⇒ decidedBy/decidedOn/decision/rationale).tools/yemaya-study/check-reuse-ledger.mjs— parses the §5.2 markdown table straight from the proposal so ledger and proposal cannot drift silently; enforces the decision policy; validates evidence paths; reports the pending-decision count.
- Test command:
node tools/yemaya-study/check-reuse-ledger.mjs - Passing output:
reuse-ledger check passed: 20 entries covering 20 proposal §5.2 rows; 10 owner decision(s) pending. - Negative test: deleting the rights-resolver entry, un-requiring the nisaba decision, and claiming a decided state without decidedBy/decidedOn/rationale produced exactly 5 problems (exit 1); restore returned green.
- Commit: resolve via
git log --grep='YSD-0103', landed asd099eb67e1.
YSD-0104 — Existing schema inventory + identity conflicts (2026-07-18)#
- Status: complete
- Artifacts:
docs/proposals/yemaya-study-workspace/schema-inventory.md— inventory of the 12 schema families (media, annotation, notebook, evidence, rights, search, graph, learning, replay, telemetry, signing, audit) with exact file paths, key exported types/tables, owning domains, the five migration/schema directories (nisaba, sophia, yemaya, oshun prisma + metis drizzle-style), and 13 documented identity/semantics conflicts, each with the Section 2/3/4 contract obligation that must resolve it.
- Method: one sequential Explore-agent sweep (no parallel agents, per session-limit policy), then main-session spot-verification of ten load-bearing claims by reading cited files — all ten confirmed (aja dual annotation enums, sophia Notebook:935/EvidencePack:971/RightsStatus:72, CanonicalPlatformAuditEventSchema:160, metis dual MasteryLevel, dual AuditLog models sophia:1331/yemaya:1118, oshun NisabaAnnotation:2017 and NisabaNotebook:2111, bellona REMOTE_REPLAY_BUNDLE_VERSION:66, live-media PlaybackGrantClaimsSchema:44).
- Notable finds feeding later items:
libs/shared/live-mediaplayback-grant flow is the closest existing rights-decision primitive (pattern for Section 4.1); the canonical audit-event contract + hash-chain store already exist (libs/contracts/src/common/canonical-audit-event.ts,libs/shared/audit-platform) and YSD-3060 must ingest them rather than invent a new ledger; oshun already holds cross-domain Nisaba/Metis/Veritas projections, so the workspace's projection pattern has precedent. - Commit: resolve via
git log --grep='YSD-0104', landed asdbb87a0916(merged to origin/main via84c7410039).
YSD-0105 — Infrastructure inventory (2026-07-18)#
- Status: complete
- Artifacts:
docs/proposals/yemaya-study-workspace/infrastructure-inventory.md— databases (Postgres16+pgvector via PgBouncer; 18 domain DBs + oshun_dev fromPOSTGRES_MULTIPLE_DATABASES, including existingyemayaandnisabaDBs), object stores (MinIO dev / S3 prod / live-media S3 store), queues (Redis+BullMQ vialibs/shared/queue; SQS+DLQ terraform; Kafka+ Debezium profile-gated), search (Elasticsearch profile + PG FTS), vectors (pgvector default + Qdrant profile), graph (Neo4j profile; TS/Postgres graph engines today), time-series (none dedicated — matches YSD-3043 posture), audit (canonical contract + hash-chain with in-memory-only store; divergent domain tables), observability, RunPod GPU posture, and 4 named gaps feeding Sections 1/3/6.
- Method: read
docker/docker-compose.dev.yml(images, profiles, database provisioning line 28),infra/andinfra/terraform/layouts, SQS module naming/alarms,infra/runpod/endpoints, BullMQ dependency inlibs/shared/queue/package.json;.env.examplepresent, no committed root.env. - Commit: resolve via
git log --grep='YSD-0105', landed as21d024fcf2.
YSD-0106 — Oshun Studio surface inventory (2026-07-18)#
- Status: complete
- Artifacts:
docs/proposals/yemaya-study-workspace/studio-surface-inventory.md— all eight dimensions: routes (58 /studio route dirs enumerated; no /studio/study yet; 1,464 studio components with fixture caveat), shared navigation (libs/oshun/navigation + shell-core + app components), authentication (libs/oshun/auth, auth-session cookies/types, BFF), tenant context (tenant-console, tenant-scoped prisma uniqueness, TenantIdSchema), Library persistence (LibraryLibraryCollection:1594 + Nisaba projections + mounted /nisaba surfaces), design tokens (shared package + app design-system), responsive baseline (breakpoints/ mediaQueries tokens.ts:329-344), accessibility primitives (AccessibilityShell, focus/keyboard/i18n components), plus reuse-worthy adjacent capabilities (offline, clipper-extension for YSD-5011, DomainDegradation pattern for YSD-2007) and 3 named gaps.
- Method: direct ls/grep/read of apps/oshun/web/src (app routes, studio routes, components, design-system tokens, auth-session) and libs/oshun/* (auth, navigation, shell-core, design-tokens, persistence prisma).
- Commit: resolve via
git log --grep='YSD-0106', landed as596e6bbfd9.
YSD-0107 — Harness inventory mapped to release gates (2026-07-18)#
- Status: complete
- Artifacts:
docs/proposals/yemaya-study-workspace/harness-inventory.md— the eight harness families (Playwright/e2e, accessibility, contract, evaluation, performance, deletion, privacy, security) with exact paths/workflows, a gate-by-gate coverage table for all 19 mandatory release gates (YSD-18130–18148), and named gaps (no epistemic-conflation tests, no media-derivative deletion fan-out, no parser-fuzz/SSRF suites, no gold sets pending YSD-0130, gates 3/9/13–19 unharnessed).
- Method: surveyed
testing/(e2e, performance/k6+benchmarks, chaos, prompt-injection with held-out sets, boundary validator), all 102.github/workflows/names with study-relevant ones read, deletion/privacy code under libs/oshun (dsar-deletion-cascade + unit/integration tests, privacy erasers, consent stores/ledger), and Section 18.6 gate text. - Commit: resolve via
git log --grep='YSD-0107', landed as390e735509(merged to origin/main via33956eb359).
YSD-0108 — Source-format support matrix (2026-07-18)#
- Status: complete
- Artifacts:
docs/proposals/yemaya-study-workspace/source-format-matrix.md— 12-row matrix (film/video, audio, subtitles, transcripts, images, documents, storyboards, telemetry, creator-owned 3D, level scenes, commentary, paratext) with concrete formats, evidence-backed existing capability, §5.1-legend status, and target checklist items; plus cross-cutting requirements (parser sandboxing, simulated-path quarantine, link-only provider posture, rational frame-rate gap in aja types, capability discovery).
- Key ground truth established: real ffmpeg/ffprobe execution exists in
libs/shared/encoding(imf-delivery, audio-analysis, timeline-assembly) andlibs/shared/live-media(neith-ffmpeg-transcoder, ffmpeg-hls-dash-packager), with on-box/usr/bin/ffmpeg+/usr/bin/ ffprobe— these, not aja's simulated probe, are the decode foundation. Neith 3D import confirmed by readinglibs/neith/assets/crates/neith-asset-import/src/lib.rs:13(GltfImporter, UsdImporter, FbxImporter, ObjImporter, AlembicImporter). No PDF parser found in nisaba/sophia. - Commit: resolve via
git log --grep='YSD-0108', landed ased20585c24.
YSD-0109 — User-persona acceptance matrix (2026-07-18)#
- Status: complete
- Artifacts:
docs/proposals/yemaya-study-workspace/persona-acceptance-matrix.md— all six §6 personas (learner filmmaker, learner game developer, actor/animator/director, character/costume/concept artist, teacher/mentor/team lead, researcher/critic) each mapped to jobs (from proposal §6 verbatim), §7 journeys, concrete acceptance checklist items (Section 22.1 proofs + gates), domain dependencies, and one non-negotiable safety/integrity constraint per persona (no-model manual study; no instrumented external titles; no real-state emotion inference; resolution-limited crops + cultural taxonomy authorship; no taste scoring; immutable scholarly history).
- Method: proposal §6 read in full (lines 341–384) and mapped against Section 22.1/22.2 acceptance items and Section 18.6 gates.
- Commit: resolve via
git log --grep='YSD-0109', landed as5c1cd4e387.
YSD-0110 + YSD-0111 — Traceability ledger + automated check (2026-07-18)#
- Status: complete (built together — the check is the ledger's enforcement — verified individually)
- Artifacts:
docs/proposals/yemaya-study-workspace/traceability-ledger.json(YSD-0110) — GENERATED ledger with 158 entries, one per normative proposal subsection (every###under numbered sections 1–29, section-level entries for 26/27, Final Recommendation; Table of Contents excluded as non-normative), each carrying mapped checklist sections with live item ranges/counts, explicit item IDs where established, release gates (23.5 → YSD-18130..18148), and notes. Test evidence per item lives in EVIDENCE_LOG.md (linked from the ledger header).tools/yemaya-study/traceability-map.json— the curated subsection→checklist mapping (158 entries) the ledger is generated from, seeded from the checklist's own coverage index and refined at subsection level (e.g. 9.2→YSD-2072/2073, 9.3→YSD-2074..2077, 17.2→YSD-1002..1004, 29.x→YSD-0102).tools/yemaya-study/check-traceability.mjs(YSD-0111) — parses proposal headers and checklist IDs; fails on: unmapped proposal subsection, stale map entry, nonexistent checklist section/item (exact-token Set membership, never substring — YSD-1002 vs YSD-10020 prefix hazard handled), checklist references to nonexistent proposal sections (proposal Section Nscan), and ledger drift vs regeneration (--writeregenerates)..github/workflows/ci.yml— all three yemaya-study checkers added to the quality/lint job beside the domain-registry check (automation requirement of YSD-0111).
- Test command:
node tools/yemaya-study/check-traceability.mjs - Passing output:
traceability check passed: 158 proposal subsections mapped, 1305 checklist items known, 158 map entries. - Negative test: removing the 9.2 mapping, adding a bogus map key,
referencing YSD-99999 and truncated YSD-100 (prefix of real IDs — proves
word-boundary semantics), rewriting a checklist reference to
proposal Section 99, and leaving the ledger stale produced exactly 6 problems (exit 1); restore returned green. - Commit: resolve via
git log --grep='YSD-0110', landed as6fd3ff3fd9.
YSD-0112 — Implementation evidence log with immutable links (2026-07-18)#
- Status: complete
- Artifacts: this file's header now carries the immutable-link contract covering all eight classes YSD-0112 names (commits, migrations, API versions, evaluation reports, performance runs, accessibility reports, threat-model updates, release decisions), each with its pinning convention (SHA-anchored paths/URLs, version literals, workflow+commit pairs, named approver for release decisions). Existing entries already comply (every completed item records its landing SHA).
- Verification: all recorded SHAs resolve on origin/main
(
git merge-base --is-ancestor <sha> origin/mainfor a0710bd396, e4f7be9eb2, d099eb67e1, dbb87a0916, 21d024fcf2, 596e6bbfd9, 390e735509, ed20585c24, 5c1cd4e387, 6fd3ff3fd9 — see commit for the run). - Commit: resolve via
git log --grep='YSD-0112'.
YSD-0113 — Nisaba primitive generalization assessment (2026-07-18)#
- Status: complete
- Artifacts:
docs/proposals/yemaya-study-workspace/reuse-ledger.json— newnisabaPrimitiveAssessmentssection with six primitive families: annotations-w3c-model (generalizes-with-extension — selector union takes new media selector kinds; FragmentSelector/Media-Fragments is a bridge but lacks rational frame rate/edition identity), editions-critical-edition (needs-new-contract — philology-shaped; VariantUnit review pattern templates EditionAlignmentMap), comparative-structures-collation (needs-new-contract), corpora-canon-registries (generalizes-with-extension — CTS URN/ canonical-uri/versification-mapping discipline is the SourceWork identity shape; versification mapping is the alignment-map precedent), study-plans-research-projects (generalizes), notebooks-library-items (generalizes).- Schema extended (
reuse-ledger.schema.json) and checker extended (check-reuse-ledger.mjs) to require ≥5 assessments with valid outcomes, substantive rationales, and existing evidence paths.
- Ground truth read:
libs/nisaba/core/src/types/annotations.ts(W3C motivations + TextQuote/TextPosition/Fragment/XPath selector union),libs/nisaba/database/prisma/schema.prisma(CollationProject:306, VariantUnit:349 char-offset units, CriticalEdition:400),libs/nisaba/workspace/src/research-environment.ts,libs/nisaba/canon/. - Test command:
node tools/yemaya-study/check-reuse-ledger.mjs - Negative test: invalid outcome
magically-works+ nonexistent evidence path produced exactly 2 problems (exit 1); restore returned green. - Commit: resolve via
git log --grep='YSD-0113', landed asbf148f6544.
YSD-0139 — Decision log published + CI rollout gate (2026-07-18)#
- Status: complete. The 18 EXT decisions themselves (YSD-0120, YSD-0122–YSD-0138) remain unchecked and proposed — per YSD-0015 they complete only with a named approver; the drafts, harness, and enforcement are this item's deliverable.
- Artifacts:
docs/proposals/yemaya-study-workspace/decisions/ysd-0120.md…ysd-0138.md(18 files) — one decision artifact per EXT item, each with Question, Recommendation (grounded in proposal §26 and the Section 0.1 inventories), Options considered with rejection rationale, Consequences, Machine-enforced outcome, and a pending Approval block naming the decision owner (@GreyChimp).docs/proposals/yemaya-study-workspace/decisions/decision-log.json— published machine-readable log (18 entries, allproposed, allblocking: true).tools/yemaya-study/check-decision-log.mjs— validates completeness of every required decision + artifact sections; rejects approved-without- approver/date/outcome (YSD-0015), log/artifact status disagreement, and expired approvals (reviewBy in the past);--enforce-rolloutfails while any blocking decision is unresolved — the production-rollout gate the study-workspace deploy pipeline (YSD-1010) must invoke..github/workflows/ci.yml— plain-mode check added to the quality job.
- Test command:
node tools/yemaya-study/check-decision-log.mjs - Passing output:
decision-log check passed: 18 decisions tracked, 18 blocking decision(s) still unresolved (informational; rollout gate not enforced);--enforce-rolloutcorrectly fails listing all 18. - Negative test: approved-without-fields, artifact/log disagreement, expired reviewBy, and a removed required decision produced exactly 7 problems (exit 1); restore returned green.
- Commit: resolve via
git log --grep='YSD-0139', landed as27ea6e7a56.
YSD-0150–YSD-0162 + YSD-0165 — Domain responsibility contracts (2026-07-18)#
- Status: complete (14 definition items; YSD-0163 conformance tests and YSD-0164 architecture tests remain unchecked — they need real adapters and scaffolding to test, else they would be self-asserting gates)
- Artifacts:
docs/proposals/yemaya-study-workspace/responsibility-contracts.md— version 1.0.0, superseded-not-edited. YSD-0150 defines the shared convention (onelibs/<owner>/study-adapterper accountable owner,STUDY_ADAPTER_CONTRACT_VERSIONsemver, capability discovery + YSD-2007 response vocabulary, domain-native ID preservation, no direct cross-domain DB access). Each domain section (0151 Yemaya, 0152 Nisaba, 0153 Sophia, 0154 Hathor, 0155 Aja, 0156 Euterpe, 0157 Aglaea, 0158 Bellona+runtimes incl. the seven named DCC bridges and bridge-lib reuse, 0159 Metis, 0160 Isis, 0161 Oshun, 0162 Iris, 0165 Neith/Maya) carries Responsibilities (checked word-by-word against the checklist item's enumerated list — every listed responsibility present), Adapter boundary, and Must-not constraints.
- Verification: each of the 14 sections was diffed against its item's enumerated responsibility list before marking; grounded in the YSD-0101 data-authority fields and the §3.2 matrix.
- Commit: resolve via
git log --grep='YSD-0150', landed as8ccda3dc4c.
YSD-1001 — Architecture ADR (2026-07-18)#
- Status: complete (ADR created with all seven required dimensions; ADR status is Proposed, acceptance explicitly gated on YSD-0120/0122 approvals — creation, not acceptance, is this item's deliverable)
- Artifacts:
docs/adr/ADR-0074-yemaya-study-deconstruction-workspace-architecture.md— logical architecture (five planes with canonical owners), domain boundaries and data authority (Postgres-authoritative, existing yemaya/nisaba/sophia databases, single rights decision point, epistemic identity as contract property), trust boundaries (untrusted bytes/text, tenant, model, rollout gate), route placement (/studio/study per YSD-0122 draft), deployment topology (ECS Fargate + BullMQ/SQS + RunPod via Isis + MinIO/S3 + FTS/pgvector-first projections), and migration path from specialist apps (studio-web pages and svc-reference-video wrapped/retired-behind-redirect, simulated paths never migrated).
- Verification: all seven content dimensions named by YSD-1001 checked present; consistent with responsibility-contracts.md v1.0.0, the YSD-0104/0105 inventories, and the 18 decision drafts (no contradiction with any proposed recommendation).
- Commit: resolve via
git log --grep='YSD-1001', landed as398022c496.
YSD-1003 — Scaffold apps/yemaya/svc-study-workspace (2026-07-18)#
- Status: complete
- Artifacts:
apps/yemaya/svc-study-workspace/— Hono/node service with:src/config.ts— zod env schema, fail-fastConfigValidationErrorwith readable per-field report; production consistency rules (DATABASE_URL required, JSON logs enforced, shutdown grace capped at 25s under the ECS 30s stopTimeout).src/logging.ts— pino structured JSON logs with service/env base fields, credential redaction backstop, YSD-0009 hygiene note.src/request-context.ts— X-Request-Id honor-or-mint (validated token pattern) + real W3C Trace Context: traceparent parse (zero-id rejection), trace continuation with fresh span id, response header emission; request-scoped child logger carries requestId/traceId/ spanId.src/metrics.ts— prom-client registry with default process metrics + RED metrics (duration histogram, in-flight gauge, totals counter) labeled by matched route pattern (bounded cardinality).src/readiness.ts— probe framework with per-probe timeout; real postgresSELECT 1probe when DATABASE_URL configured; honestnot-configuredreporting otherwise (visible seam, not fake success).src/app.ts— /health, /ready (503 on failed probe), /metrics, structured 404, error handler that never leaks internals in production; deliberately zero placeholder business routes.src/index.ts— fail-fast config load, serve, graceful shutdown (SIGTERM/SIGINT drain with deadline, pool close, second-signal force exit).project.json(tags scope:yemaya/type:app/plane:experience, build/ serve/lint/test/typecheck targets), catalog-only deps,deploy/README.mddeployment manifest (ECS naming contract + gated registration steps + container contract), service README.
- Test command:
npx vitest run(in the app dir) — 15/15 passed;npx tsc --noEmit -p tsconfig.app.json— clean. - Runtime proof: booted live on :4321 — structured logs, /health 200 with
identity, /ready 200 with honest postgres
not-configuredprobe, inboundx-request-id: smoke-1honored + valid traceparent emitted, /metrics served 24 histogram bucket lines, SIGTERM → drain → exit 0. Real-postgres ok-path deferred to walking skeleton (dev compose not running); probe failure/timeout paths unit-tested. - Note: a NUL byte corrupted one test string during authoring (made the file
grep-binary); found via
cat -A, fixed byte-safely, suite green. - Commit: resolve via
git log --grep='YSD-1003', landed as5238d19b9a.
YSD-0120, YSD-0122–YSD-0138 — All 18 P0 EXT decisions approved (2026-07-18)#
- Status: complete — approved as recommended by the decision owner (@GreyChimp, the named approver) in this session's conversation ("approve them all as recommended"), recorded 2026-07-18, review-by 2027-07-18.
- Artifacts:
decisions/decision-log.json— every entry nowstatus: approvedwith approvedBy/approvedOn/outcome/reviewBy; all 18decisions/ysd-01xx.mdartifacts flipped to approved with the approval line filled. Each artifact carries the YSD-0015 required set: decision, named approver, date, rationale, alternatives with rejection reasons, consequences, and machine-enforced outcome. - Gate result:
node tools/yemaya-study/check-decision-log.mjs --enforce-rollout→18 decisions tracked, 0 blocking decision(s) still unresolved (rollout gate: PASS). - Downstream effects: ADR-0074 status flipped Proposed → Accepted (was gated on YSD-0120/0122); YSD-1002 (route scaffold), YSD-0121 (Nx/CODEOWNERS boundary encoding), and Section 2 permanent storage shapes (YSD-0128) are now unblocked. The 10 reuse-ledger ownerDecision slots (YSD-0103) are a separate approval set and remain pending.
- Commit: resolve via
git log --grep='YSD-0120', landed as64955a27a5.
YSD-0103 addendum — All 10 reuse-ledger owner decisions approved (2026-07-18)#
- Status: the ledger's pending owner decisions are now decided — approved as proposed by the owner (@GreyChimp) in this session's conversation ("approve the reuse-ledger decisions as proposed too"), recorded 2026-07-18. Dispositions now binding: nisaba-study-notebook-substrate → reuse; aja-reference-video-model → wrap; aja-ingestion-scene-detection → replace; performance-reference-library → replace; yemaya-creative-analyzers → wrap (prohibited-inference modules excluded); director-replay-studio-panel → replace; hathor-theory-lenses → reuse; aglaea-fashion-knowledge → reuse; animation-learning-surface → replace; dailies-review-surface → replace.
- Gate result:
node tools/yemaya-study/check-reuse-ledger.mjs→20 entries covering 20 proposal §5.2 rows; 0 owner decision(s) pending. - Commit: resolve via
git log --grep='YSD-0103 addendum', landed as7ea609cc42.
YSD-0121 — Ownership boundaries encoded and enforced (2026-07-18)#
- Status: complete (all six encoding surfaces the item names)
- Artifacts:
- Nx tags:
apps/yemaya/svc-study-workspace/project.jsoncarriesscope:yemaya,type:app,plane:experience; the adapter tag scheme (scope:<owner>+type:study-adapteronlibs/<owner>/study-adapter) is defined in responsibility-contracts.md and enforced by the architecture checker the moment an adapter directory appears. - Dependency constraints:
eslint.config.jsdepConstraints — newplane:experiencerule (may depend only on scope:shared/contracts/ oshun/yemaya, type:study-adapter, plane:experience) andtype:study-adapterrule (adapters may never import each other; cross-lens composition is experience-plane work). Service files lint clean under the new rules (eslint exit 0). - CODEOWNERS: verified generated coverage —
libs/yemaya/andapps/yemaya/owned by @GreyChimp (lines 103–104),/tools/covers tools/yemaya-study; staleness is already CI-guarded by check-registry.mjs, so no hand edits (they would break the generator drift check). - Contract namespaces:
libs/contracts/src/study/reserved as the single home for study contracts (responsibility-contracts.md YSD-0150; materialized by YSD-1005); scope:contracts depConstraint already prevents contracts from depending on domain code. - Service ownership metadata: domains.json registry entry (yemaya, owner @GreyChimp) + project.json tags; registry sync CI-guarded.
- Architecture tests:
tools/yemaya-study/check-architecture.mjs— scans experience-plane and adapter sources for (1) direct cross-domain imports bypassing study adapters, (2) cross-domain database/persistence imports (YSD-0164), (3) prohibited-inference analyzer imports (the four YSD-4052-flagged modules, tests included), and (4) missing required tags; wired into the ci.yml quality job with the other checkers.
- Nx tags:
- Test command:
node tools/yemaya-study/check-architecture.mjs - Passing output:
study-workspace architecture check passed: 1 experience-plane project(s), 0 study adapter(s) scanned (1 total). - Negative test: a planted file importing
@sophia/knowledge-graph,@hathor/database, and.../eye-contact-gaze-monitorproduced exactly 3 problems, one per rule (exit 1); removal returned green. - Commit: resolve via
git log --grep='YSD-0121', landed ase8434e418a.
YSD-1002 — Canonical Studio route /studio/study (2026-07-18)#
- Status: complete (scaffold at the approved route with the approved layout conventions; the global "Study" nav entry is Section 7 work)
- Artifacts:
apps/oshun/web/src/app/studio/study/page.tsx— follows the Studio route convention exactly (Metadata with canonical/studio/studyper approved YSD-0122, ShellLayoutactive="studio", workspace component, quickAction links to real surfaces: /studio, /nisaba/notebooks, /library).apps/oshun/web/src/components/studio/StudioStudyWorkspace.tsx— the entry surface with an explicit honesty contract: a typedSTUDY_LAUNCH_CAPABILITIESlist where every capability reports its real state; the six unshipped capabilities (projects/ingest, playback/ annotation, camera/performance/narrative/sound lenses per the approved YSD-0126 set) render "Not yet available" with zero interactive affordance; the two available entries link to genuinely shipped surfaces (Nisaba notebooks, shared Library).apps/oshun/web/vitest.config.ts— include pattern extended to.spec.{ts,tsx}(new.test.files are ratcheted; existing pattern only matched.test.).apps/oshun/web/src/app/studio/study/__tests__/study-page.spec.tsx— 4 tests: identity/posture; honesty invariant (every unavailable capability shows the explicit status and renders no link/button); available links point only to route directories that exist on disk (fs-verified, so the page can never advertise a dead destination); metadata declares the approved canonical route.
- Test command:
npx vitest run src/app/studio/study(in apps/oshun/web) — 4/4 passed. - Commit: resolve via
git log --grep='YSD-1002', landed as9eca3bec5e.
YSD-1005 + YSD-2001–YSD-2004 — Study contracts namespace + first primitives (2026-07-18)#
- Status: complete (five items, each verified against its full text)
- Artifacts:
libs/contracts/src/study/(the YSD-0121-reserved namespace), exported as@oshun/contracts/studysubpath andStudyContractsnamespace (namespace-only at root to avoid name collisions with other domains' SegmentId/Timecode):ids.ts(YSD-2001) — 35 branded identifiers covering every Section 2.2 core entity plus AnalysisRunId, RightsDecisionId, ReviewDecisionId, StudyEventId, StudyExportId, ProductionBacklinkId; domain-owned identities referenced through*Refbrands over native formats (Nisaba/Metis/Sophia/Isis — never re-minted);DomainNativeObjectRefpreserves arbitrary native IDs verbatim (YSD-3002 mapping philosophy).author.ts(YSD-2004) — strict discriminated union human/model/imported-system/collaborative-group; model identity carries modelId/modelVersion/analysisRunId and structurally cannot validate with a userId (conflation-rejection tested both directions).envelope.ts(YSD-2002) — all nine required fields (schemaVersion, tenant, project, creator, timestamps, revision, supersession, provenance, deletionState) with supersession-chain refinements and the pure append-onlysuperseding()helper (deterministic — caller supplies time; refuses already-superseded and legal-hold records).locators.ts(YSD-2003) — all nine primitives: rational frame rate (exact cross-multiplication equality, gcd canonicalization, known-rate constants), real SMPTE 12M drop-frame timecode (dropped-label validation; bidirectional conversion), monotonic time (bigint-safe string nanoseconds + clock-domain-guarded comparison), wall-clock, normalized/pixel/spatial/document coordinates with refinements, and runtime-event locators requiring at least one locating field.study.spec.ts— 21 tests including known-correct SMPTE anchors (00:00:59;29→1799, 00:01:00;02→1800, 00:10:00;00→17982, 01:00:00;00→107892), a dense 2,000-frame DF round-trip sweep plus 59.94 DF samples, envelope chain negatives, and region round-trips.
- Test command:
npx vitest run src/study(in libs/contracts) — 21/21 passed;npx tsc --noEmit -p tsconfig.json— clean. - No duplicate domain types (YSD-1005): tenant identity follows the platform's canonical-audit-event convention (opaque token, no new brand); Nisaba/Metis/Sophia records are referenced, not redefined.
- Commit: resolve via
git log --grep='YSD-1005', landed as1a4a675bd9(SKIP_TYPECHECK=1 used per the documented nx-typecheck fan-out trap — 289-project hook typecheck surfaced pre-existing common/citation.ts errors in untouched files; libs/contracts itself tsc-clean, verified directly).
YSD-2005–YSD-2007 — Review states, confidence, response contracts (2026-07-18)#
- Status: complete (three items, each verified against its full text)
- Artifacts (in
libs/contracts/src/study/):review.ts(YSD-2005) — the six states as a closed enum; the legal transition table (suggested→accepted/corrected/contested/rejected; accepted|corrected→contested/superseded; contested→accepted/corrected/ rejected; rejected→superseded; superseded terminal) withcanTransitionReviewState/assertReviewTransitionand a typedIllegalReviewTransitionError; immutableReviewEvent(subject id + pinned revision, reviewer, reason, occurredAt) with refinements: models cannot accept/correct/reject (suggest-and-contest only), corrected transitions must carry corrections;replayReviewEventsderives state by replay and rejects broken chains.confidence.ts(YSD-2006) — calibrated confidence with required calibration lineage (method, set id+version, validatedAt), bracketing uncertainty interval with coverage, and applicability boundary; explicituncalibrated(raw score + native scale) andmissing(typed reason) variants;isDisplayableProbabilityallows only calibrated values — bare numbers do not parse.responses.ts(YSD-2007) —CapabilityDescriptor(dotted capability key, supported/unsupported/unavailable with mandatory reason when not supported, adapter contract version) andmakeStudyResponseSchemaproducing the full outcome union: ok, ok-degraded (with mode+reason), partial-success (≥1 completion, exact failure manifest), unsupported, unavailable (retryable/retryAfterMs coherence), forbidden (policy), and insufficient-rights (denied action + inspectable RightsDecisionId).
- Test command:
npx vitest run src/study(libs/contracts) — 32/32 passed (11 new: exhaustive 36-pair transition matrix, model-reviewer rejection, replay chain breaks, interval bracketing, partial-success coherence);tsc --noEmitclean. - Commit: resolve via
git log --grep='YSD-2005', landed as78135b41a5.
YSD-2008 — Schema publication, examples, compatibility tests (2026-07-18)#
- Status: complete. Component-by-component against the item text: JSON
Schema ✓ (17 draft-2020-12 schemas published from the registry); OpenAPI ✓
(
openapi.components.json3.1 components document); runtime validation ✓ (zod layer, declared authoritative in the manifest); generated client — satisfied by the typed package (zod-inferred types are the generated client surface for TS consumers) plus the published schema set proven independently consumable via ajv in the spec suite; HTTP-API client generation explicitly belongs to YSD-1009 where service APIs exist — recorded here so the 22.3 audit can contest the interpretation; canonical examples ✓; negative examples ✓; backward-compatibility tests ✓. - Artifacts:
libs/contracts/src/study/schema-registry.ts— the single publication registry (17 contracts); a contract cannot exist without entering the pipeline (manifest-vs-registry equality is tested).libs/contracts/scripts/generate-study-schemas.ts— emits per-contract JSON Schema with versioned$id, the OpenAPI components doc, and a manifest carrying the runtime-vs-structural caveat;--checkfails on drift or breaking changes;--write-baselinefreezes the per-major snapshot (schemas/study/baseline/v0/); exportsfindBreakingChanges(removed contract/property, type change, enum narrowing, newly-required property, additionalProperties tightening, union narrowing) — the YSD-2010 detector foundation.libs/contracts/src/study/examples.ts— canonical + structural-negative sets for all 17 contracts, pluszodOnlyNegativespinning exactly where the runtime layer is stricter (cross-field refinements, formats).libs/contracts/src/study/schema-publication.spec.ts— 7 tests: no-drift, example coverage for every contract, canonical examples pass BOTH zod and ajv-against-published-schema, structural negatives fail BOTH layers, runtime-only negatives fail zod, no breaking changes vs the committed baseline, and detector unit tests on synthetic diffs.- CI:
--checkadded to the quality job; package scriptsgenerate:study-schemas/check:study-schemas.
- Test command:
npx vitest run src/study(libs/contracts) — 39/39 passed;npx tsx scripts/generate-study-schemas.ts --check→17 contracts published, no drift, no breaking changes vs baseline v0. - Commit: resolve via
git log --grep='YSD-2008', landed as0ec646e9cf(merge822f3c900b; generated schemas added to .prettierignore after the pre-commit prettier reformat broke byte-exact drift comparison — schemas and baseline regenerated to generator format).
YSD-2009 + YSD-2010 — Property round-trips + compatibility policy (2026-07-18)#
- Status: complete (both items verified against their full text)
- Artifacts:
libs/contracts/src/study/compat.ts(YSD-2009) — the version-tolerant boundary:SUPPORTED_STUDY_SCHEMA_VERSIONS,assertSupportedSchemaVersion(explicit refusal of unknown versions), andparseWithCompat— strips ONLY pure unknown-key failures (a newer writer's optional fields), reports every stripped path, never mutates the caller's object, and refuses to rescue any real validation failure (mixed failures stay failures).libs/contracts/src/study/roundtrip.property.spec.ts(YSD-2009) — fast-check property suites (fast-check added to the pnpm catalog): constructively-valid arbitraries for envelope/author/region/monotonic/ locator/confidence; per-contract properties that (a) JSON wire round-trips are identity AND both serializer layers (zod runtime, ajv-compiled published schema) agree on 200 random instances each, and (b) unknown optional fields from a newer writer fail strict parsing but pass the compat boundary with knowns preserved and the injected key reported; 700-run drop-frame timecode round-trip property (29.97/59.94 DF + 24 non-drop); arbitrary-length superseding-chain lineage property; prior-supported-version suite (every supported version's canonical examples parse under the current runtime; unknown versions refused explicitly; compat never masks real failures).docs/proposals/yemaya-study-workspace/compatibility-policy.md(YSD-2010) — semver rules (breaking=major with new baseline, additive=minor via the compat boundary, append-only history), the per-class table for contracts/events/taxonomies/saved-views/portable- exports with class-specific rules, the registry-as-chokepoint mechanism (a class entering the registry automatically enters CI breaking-change detection — no per-class opt-in to forget), support windows, and the no-self-approval escalation rule for claimed-safe breaking changes. Automated detection is live in CI (--checkin the quality job) for everything published; events/taxonomies/views/exports enter it by construction when their schemas land.
- Test command:
npx vitest run src/study(libs/contracts) — 56/56 passed (17 new). - Commit: resolve via
git log --grep='YSD-2009', landed as500ec3a395.
YSD-2020–YSD-2023 — StudyProject, StudyQuestion, SourceWork, SourceEdition (2026-07-18)#
- Status: complete (four items, each verified against its enumerated field list)
- Artifacts (in
libs/contracts/src/study/entities/):study-project.ts(YSD-2020) — all ten required facets: members (role enum, ≥1 owner enforced, unique userIds), purpose, curriculum (MetisConceptRefs), policies (RightsGrantIds), sources, notebooks (NisabaNotebookRefs), collections, target projects (yemaya-production/instrumented-game refs), archive state, and append-only membership history withrosterFromHistoryreplay (remove-wins, joinedAt stable across role changes — tested).study-question.ts(YSD-2021) — question text, the exact four-state lifecycle (open/under-investigation/answered/abandoned) with a legal transition table including evidence-driven reopening, scope, originating context (description + optional anchor), linked evidence/ comparisons/collections/exercises, and resolution history whose replay must equal the stored lifecycle (drift rejected); answers must cite ≥1 evidence anchor (YSD-2112 alignment).source-work.ts(YSD-2022 + YSD-2023) — SourceWork with copy- independent identity, ambiguity-preserving identity candidates (closed certainty vocabulary — numeric pseudo-confidence rejected), and human-reviewed append-only merge events; SourceEdition with the full enumerated identity (cut/build/patch/platform/region/language/ frame-rate/aspect/color/audio/subtitle), rational-only frame rates (floats rejected), sha256 content-hash immutable technical revision, game-builds-require-platform and frames-require-rate refinements.- All four registered in the publication registry (now 21 contracts) with canonical/structural-negative/zodOnly example sets — automatically inside the dual-layer validation and baseline compat pipeline.
- Test command:
npx vitest run src/study(libs/contracts) — 66/66 passed (10 entity tests + expanded publication coverage); tsc clean. - Commit: resolve via
git log --grep='YSD-2020', landed as5498a37c73.
YSD-2024–YSD-2027 — Alignment, SourceAsset, RightsGrant, MediaTrack (2026-07-18)#
- Status: complete (four items, each verified against its enumerated field list)
- Artifacts (in
libs/contracts/src/study/entities/):alignment.ts(YSD-2024) — EditionAlignmentMap pinning exact endpoint technical revisions (sha256), five construction methods, correspondence kinds exact/approximate/reordered/added/removed/rescored/regraded/ unmapped with side-presence rules (added has no source range, removed no target range — enforced), per-correspondence confidence + review state, EXPLICIT gaps (surfaced, never interpolated — YSD-2135), and anchor-migration PROPOSALS with index-validated correspondence links (proposals never self-apply — YSD-2133/2134).source-asset.ts(YSD-2025 + YSD-2027) — SourceAsset covering all ten kinds with a storage discriminated union: owned bytes (object-store + content hash) XOR link-only provider references (stream references cannot store bytes and files cannot be links — YSD-4009 enforced both directions); rights-bound from birth (≥1 grant); engine assets/scenes must keep native identity; sidecar metadata objects. MediaTrack with all ten stream kinds, per-kind clock requirements (video⇒rational frame rate, audio⇒sample rate, custom⇒label).rights-grant.ts(YSD-2026) — all fourteen enumerated facets; the twelve YSD-4002 actions as a closed deny-by-default vocabulary; model-training gated on unrevoked per-participant consent (YSD-4036 — revoked consent tested to not count); user-declared references quarantined from sharing/model/export actions (YSD-4010); attribution text required when flagged; validity ordering; ISO territory codes.
- Test command:
npx vitest run src/study(libs/contracts) — 76/76 passed (10 new); 25 contracts published; tsc clean. - Commit: resolve via
git log --grep='YSD-2024', landed asd4ef0debfd.
YSD-2028–YSD-2032 — Segment, EvidenceAnchor, StudyEntity, Detection, Observation (2026-07-18)#
- Status: complete (five items, each verified against its enumerated field list)
- Artifacts (in
libs/contracts/src/study/entities/):anchor.ts(YSD-2029) — the six-kind SourceLocator discriminated union (frame-range, frame-region, transcript-span with pinned exactText for tokenization-drift detection, document, runtime-event-range, spatial with nodePath-or-position rule) and EvidenceAnchor pinning exact edition + sha256 technical revision, policy-permitted representations (YSD-2102 vocabulary), acquisition provenance, ≥1 rights-grant dependency, review state, envelope-immutable versioning, and the deep-link restore path.segment-entity.ts(YSD-2028 + YSD-2030) — StudySegment with the film+game kind vocabulary plus labeled custom kinds (YSD-2071 — domain-native terms never coerced), hierarchy with self-parent rejection, pinned source revision, segmentation version, author, confidence, append-only corrections, and the born-accepted wall (model-authored segments must start suggested — YSD-0019); StudyEntity with the ten core kinds + domain-owned extensibility requiring owning domain + native kind, and the work-scope note (cross-work identity is a reviewed cross-reference, never implicit — YSD-2082).epistemic.ts(YSD-2031 + YSD-2032) — Detection requiring processor code identity, exact configuration, analysisRunId (no orphan outputs — YSD-2113), ≥1 anchor (YSD-2112), confidence, review state, and applicable limitations; Observation as human-authored-by-type (model authorship structurally rejected — the epistemic wall of YSD-2108 at the contract layer), with modality, explicit scope, and ≥1 anchor.
- Test command:
npx vitest run src/study(libs/contracts) — 84/84 passed (8 new); 31 contracts published; tsc clean. - Commit: resolve via
git log --grep='YSD-2028', landed asff941438b2.
YSD-2033–YSD-2036 — InterpretationClaim, CraftHypothesis, TaxonomyTerm, ComparisonSet (2026-07-18)#
- Status: complete (four items, each verified against its enumerated field list)
- Artifacts (in
libs/contracts/src/study/entities/):claims.ts(YSD-2033 + YSD-2034) — InterpretationClaim with the exact eight-status YSD-2109 epistemic vocabulary, mandatory support anchors (YSD-2112), first-class counterevidence, coexisting dissents (YSD-2111 — the original reading survives beside them, tested), corpus boundary, limitations, review history, and two walls: model authors are forced to model-suggested status (YSD-2108) and contested status must actually show dissent or counterevidence. CraftHypothesis with derivation requirement (claims or anchors), the four-value named causal-strength classification (numeric strength rejected), counterexamples, intended practice, contextual applicability, and the no-universal-laws rule (experimentally-tested requires limitations or counterexamples).taxonomy-comparison.ts(YSD-2035 + YSD-2036) — TaxonomyTerm with owner domain, semver vocabulary version, preferred label, aliases, BCP-47-validated translations, provenance (YSD-4072 authorship), broader/narrower/related with self-relation rejection, applicability, and a lifecycle union where deprecation REQUIRES a migration rule. ComparisonSet with research question, stated inclusion logic, exclusions, example/counterexample members, index-validated pairwise alignments with frame offsets, five layouts with the 2–8 focused / matrix-beyond-8 rule (YSD-22011 at the contract layer), corpus boundary, collaborators, and revision-pinning frozen snapshots.
- Test command:
npx vitest run src/study(libs/contracts) — 95/95 passed (11 new); 35 contracts published; tsc clean. - Commit: resolve via
git log --grep='YSD-2033', landed asd94b08d387.
YSD-2037–YSD-2040 — Inspiration, CrossReference, CreativePrinciple (2026-07-18)#
- Status: complete (four items, each verified against its enumerated field list)
- Artifacts (in
libs/contracts/src/study/entities/):inspiration.ts(YSD-2037 + YSD-2038) — InspirationCollection with creative question, target projects/concepts, intended audience effect, scope, exclusions, saved views (presentation-only state — spatial proximity never creates evidence relations, YSD-2154), collaborators, and a mandatory diversity posture; InspirationItem with all twelve facets — authorized anchor + restated rights binding, relevant aspect, intended effect/problem, principle/question links, a MANDATORY do-not-copy boundary (the YSD-4033/4035 transfer-safeguard seed), six-role vocabulary, mandatory attribution, cultural context, confidence, review, envelope versioning.cross-reference.ts(YSD-2039 + YSD-2040) — CrossReference with the approved fifteen-relation vocabulary (YSD-0133, shared with YSD-2151), symmetric-relations-cannot-be-directed enforcement (YSD-2152), revision-pinned endpoints (YSD-2156), rationale/evidence/ counterevidence/context-bounds/rights/validity, self-edge rejection, and the identity rules of YSD-2155 in the type system: identity edges cannot be born accepted and models cannot assert identity at all (similarity only). CreativePrinciple with abstracted wording, supporting AND contradicting sources, mandatory transformation rationale (the distance-from-source argument), limitations, review, outcome-typed track record, and project applicability.
- Test command:
npx vitest run src/study(libs/contracts) — 106/106 passed (11 new); 39 contracts published; tsc clean. - Commit: resolve via
git log --grep='YSD-2037', landed as7966c5a747.
YSD-2041–YSD-2043 — OriginalConcept, CreativeDecision, OutcomeEvidence (2026-07-18)#
- Status: complete (three items, each verified against its enumerated field list)
- Artifacts (
libs/contracts/src/study/entities/creative.ts):- OriginalConcept (YSD-2041) — project-owned identity, seven concept kinds, typed self-rejecting relationships, rationale, envelope version history, and the two structural prohibitions: authorship is source-independent (humans/groups only — model and imported-system authors rejected) and the masquerade ban is architectural — the schema has NO locator/edition/anchor fields and .strict() rejects any attempt to add them (tested with editionId/locator/anchorId injections); source connection exists only through principle derivations and inspiration items.
- CreativeDecision (YSD-2042) — all eleven facets; committed decisions must carry ≥1 principle/source path (the YSD-22013 source-to-decision trail) and ≥1 named approval, while proposed decisions may still be forming; immutable revisions via the envelope.
- OutcomeEvidence (YSD-2043) — intended effect, four source kinds, consent evidence REQUIRED for playtests/screenings (YSD-4056) but not releases, ≥1 anchor, five-value result vocabulary, human interpretation separate from the result value, mandatory uncertainty statement, linked decision, and epistemic identity fixed by literal to 'practice-result' (cannot claim source-fact status — YSD-2107).
- Test command:
npx vitest run src/study(libs/contracts) — 115/115 passed (9 new); 42 contracts published; tsc clean. - Commit: resolve via
git log --grep='YSD-2041', landed asc47f8033ab.
YSD-2044–YSD-2048 — Notebook/StudyCard + learning-loop contracts (2026-07-18)#
- Status: complete (five items, each verified against its enumerated field list)
- Artifacts (in
libs/contracts/src/study/entities/):notebook.ts(YSD-2044) — StudyNotebook keyed by NISABA canonical identity (no workspace UUID field exists; injecting one is rejected — tested), source-grounded narrative with marker-deduplicated anchored citations, typed rights-aware embeds (authored representation from the YSD-2102 vocabulary; rights re-checked at view time), card refs, three-state publication where publishing requires ≥1 citation, and envelope versioning. StudyCard as the atomic cited insight (≥1 anchor mandatory) with optional claim/comparison distillation.learning.ts(YSD-2045–2048) — PracticeExercise with objective, Metis concept, ≥1 constraint, behavior-anchored rubric (≥1 criterion), anchored examples requiring rights grants, expected evidence, accommodations, self-paced/due union, self-study/assigned union; PracticeAttempt learner-owned with ≥1 artifact, revisions, evidence-linked feedback with retained learner challenges (YSD-4077), reflection, structured scoped sharing consent with revocation, and the learner-deletion-request field feeding the YSD-3063 saga; MasteryRecord referencing the Metis canonical record with named-level+uncertainty estimate (numeric scores do not parse), system/human assessor union, history, and the taste wall as a literal (measures: 'concept-mastery-only'— taste/originality scoring unrepresentable, injection tested); SpacedReviewItem with the four source kinds (card/claim/term/drill), FSRS schedule state, explicit scheduler version, anchors, mandatory discrimination target, rating-typed review history, and rights-permitted derivative references.
- Test command:
npx vitest run src/study(libs/contracts) — 126/126 passed (11 new); 48 contracts published; tsc clean. - Commit: resolve via
git log --grep='YSD-2044', landed asa6e34e44b0.
YSD-2049–YSD-2052 — Transfer, AnalysisRun, ReviewDecision, fixtures capstone (2026-07-18)#
- Status: complete — Section 2.2 is now 33/33
- Artifacts:
entities/transfer-run-review.ts— ProjectTransfer (YSD-2049) whose payload union contains ONLY abstract kinds (principle/constraint/ question/task — a media payload is unrepresentable, YSD-4035), with destination, produced artifact, sanctioning decisions (≥1), rights review state, inspectable advisory originality warnings (YSD-4038), recorded-never-silent blocked content with typed blocked classes, backlink, and downstream impact refs. AnalysisRun (YSD-2050) with all eleven facets and four lifecycle coherence rules (cancelled⇒who+why, failed⇒failures, succeeded⇒outputs — success without outputs is not success, queued/running⇒no actual cost). ReviewDecision (YSD-2051) with reviewer authority vocabulary, five actions, correct⇒corrections, supersede⇒higher version, prior/new versions, conflict context, timestamp, and canonical audit event identity.fixtures.ts+scripts/generate-study-goldens.ts+fixtures-golden.spec.ts(YSD-2052) — deterministic builders (no randomness, no clock, no restricted media, schema-validated construction) covering every published contract with minimal + complete cases, and ALL seVEN required case kinds: prior-version (oldest supported schema version), rejected (every reviewable contract), rights-expired (grant with ended validity + revocation), contested (claim with retained dissent), superseded (built through the real superseding() helper for every enveloped contract), plus soft-deleted deletion cases; 213 golden fixture cases committed and byte-compared, with the golden check wired into CI beside the schema check.
- Test command:
npx vitest run src/study(libs/contracts) — 131/131 passed; goldens--checkgreen; 51 contracts published; tsc clean. - Commit: resolve via
git log --grep='YSD-2049', landed asfe41f98ed4.
YSD-2070–YSD-2077 — Hierarchy + film/game anchors + sync contract (2026-07-18)#
- Status: complete (eight items, each verified against its enumerated vocabulary)
- Artifacts (in
libs/contracts/src/study/anchors/):hierarchy.ts(YSD-2070/2071) — the ten ordered levels verbatim from the item text; depth/legality helpers (child strictly finer, skipping allowed, inversion rejected); the native-term map carrying film AND game vocabulary onto shared levels without coercion (round ≡ shot in level, not in name; unknown terms return null — never guessed); HierarchyNodeSemantics with parent/order/overlap/provenance.film.ts(YSD-2072/2073) — FilmStructureAnchor with the full thirteen-kind structural vocabulary (reel…word, frame, region, language-track), edition + sha256 revision pinning, rational frame rate + optional SMPTE timecode, per-kind data rules (region⇒region, word/line/track⇒language track); FilmEntityAnchor with the twelve entity kinds and audio-side kinds requiring their audio track.game.ts(YSD-2074–2077) — GameIdentityAnchor with all nine identity facets (explicit empty-mods assertion, capture configuration, content-manifest hash); GameSessionAnchor with the twelve session kinds, locator-required rule, save-states requiring engine-native refs; GameEntityAnchor with the twelve entity kinds carrying engine-native identity; SessionSyncContract as a TWO-variant union — deterministic replay (seed + input-stream hash) or video-clock mapping with MANDATORY uncertaintyMs — no third option exists, so unstated sync error is unrepresentable.- All seven anchor contracts registered (58 published contracts; 227 golden cases).
- Test command:
npx vitest run src/study(libs/contracts) — 142/142 passed (11 new); tsc clean. - Commit: resolve via
git log --grep='YSD-2070', landed asefd67632c2.
YSD-2078–YSD-2084 — Document/still/3D/level anchors + identity, canon, deep links (2026-07-18)#
- Status: complete — Section 2.3 is now 15/15
- Artifacts (in
libs/contracts/src/study/anchors/):document-still.ts(YSD-2078/2079) — DocumentAnchor addressing document/edition/page/panel/block/line/word with rectangular OR polygonal regions and page/ordinal/char-span rules; StillImageAnchor pinning image revision + canvas, optional layer (never fabricated for flat formats), pixel/normalized region union with out-of-canvas rejection, review-gated detected entities, palette regions with sRGB-validated dominance-ordered colors, and user guides (line/curve/silhouette-trace/grid).three-d.ts(YSD-2080/2081) — Asset3DAnchor with all fifteen kinds and per-kind data obligations (vertex/face sets enumerate indices, uv-island/material-slot carry ordinals, texture regions carry regions, clips/time-samples carry names+times), engine-native paths verbatim; LevelSceneAnchor with the ten level kinds, native refs, and gameplay references naming their system.identity-canon.ts(YSD-2082/2083) — CrossViewIdentityCandidate with five evidence bases, MANDATORY uncertainty, and two walls: appearance-only identity can never be accepted (corroborating bases required) and model proposals stay suggested/contested. Canonicalization/equality: exact cross-rate frame-time equality by rational cross-multiplication (unreduced 23.976 forms equal; same frame number at different rates correctly unequal; 24@24fps == 25@25fps == 1s), transcript tokenization drift fails loudly via pinned exactText, pixel↔normalized equivalence with explicit one-pixel tolerance, and telemetry-clock cross-domain comparison returning null — never a silent boolean.deep-link.ts(YSD-2084) — loss-free URL encoding (canonical key-sorted JSON in base64url) restoring exact edition + revision + locator + entity + permitted view; deterministic; malformed links throw typed errors; round-trip verified for every locator kind including a unicode transcript span, plus a 100-run property test.
- Test command:
npx vitest run src/study(libs/contracts) — 155/155 passed (24 anchors tests); tsc clean. One test-authoring error caught and fixed: the unreduced-rate equality case had doubled the frame count (1001s ≠ 2002s) — corrected to same-count-same-time. - Commit: resolve via
git log --grep='YSD-2078', landed asc95d73e57e.
YSD-2101/2103/2109–2113 — Epistemic integrity contracts + invariant suites (2026-07-18)#
- Status: complete (seven items). YSD-2100/2102/2104/2105/2106/2107/ 2108 remain deliberately unchecked: their contract halves exist (pinned identity, representation vocabulary, rights binding, display predicate, authorship/review models, distinct epistemic types) but the items demand runtime/UI/persistence ENFORCEMENT that lands with Sections 3/5/7 — no under-verified marking.
- Artifacts:
entities/compound-locator.ts(YSD-2101) — conjunction of 2–10 distinct component locators (frame range + region + transcript span + entity + runtime events + document location all combinable), duplicate components rejected, mandatory conjunction rationale; registered + published.- EvidenceAnchor
derivation(YSD-2103, additive) — parent anchors (≥1)- processor/codeVersion/model/configuration, completing acquisition method + source revision + processor + model + code + configuration + derivation lineage on evidence.
- InterpretationClaim additive fields (YSD-2110) — named supportStrength (single-source→corpus-wide), reviewerAgreement tally, culturalContextBounds; joins existing counterevidence/corpus-boundary/ limitations to complete the item's stored set.
- YSD-2111 verified complete on existing artifacts: the Dissent model
(coexisting readings tested — original survives beside dissent) plus the
contradictionrelation type for contradictory claims. epistemic-invariants.spec.ts(YSD-2112/2113 + 2109 closure) — the six unanchored-rejection invariants (analysis, observation, claim, comparison member, inspiration item, feedback item) and the identity invariants (no machine output without run + processor code version; no interpretation without epistemic status + author); closed-vocabulary assertion for the eight governed statuses.- Pre-release tightening, documented: YSD-2112 exposed that feedback items could be unanchored (evidenceAnchorIds had no minimum) — real gap, fixed to min(1). Per the compatibility policy this is breaking; handled as a documented pre-release tightening: STUDY_CONTRACTS_VERSION 0.1.0→0.2.0, 0.1.0 retained in the supported list, baseline v0 regenerated same-day (nothing consumes v0 externally yet). Recorded here rather than silently.
- Test command:
npx vitest run src/study(libs/contracts) — 168/168 passed (13 new invariants); 59 published contracts; 229 golden cases; tsc clean. - Commit: resolve via
git log --grep='YSD-2101'.
2026-07-18 — YSD-2132, YSD-2133, YSD-2134, YSD-2135, YSD-2137 (Section 2.5: alignment provenance + anchor migration)#
- Scope: the migration-logic half of Section 2.5. Deliberately left unchecked: YSD-2130 (manual authoring/review UI — Studio surface, Section 7), YSD-2131 (real audio-fingerprint/shot-structure/subtitle/telemetry proposal pipelines — media plane, Section 5), YSD-2136 (build/patch map persistence + revalidation task queue — Section 3/5; the contract SHAPE is exercised here but live-service revalidation scheduling is not built yet).
- Artifacts (libs/contracts/src/study/):
- Correspondence
processor(YSD-2132, additive) — optional codeVersion/modelId/modelVersion object on each correspondence, completing the stored set: kind + method + model/code version + confidence + source/target ranges + review state (the rest already existed onEditionAlignmentMap/Correspondencefrom YSD-2064). entities/anchor-migration.ts(YSD-2133/2134/2135) — pure deterministic logic.proposeAnchorMigrations(map, anchors): throws unless the map is accepted (proposals never come from suggested/contested/rejected maps) and unless every anchor sits on the map's exact source edition + technical revision; output is proposals + gap notices only — nothing self-applies (YSD-2133). Anchors overlapping explicit gaps or removed/added/reordered/ rescored/regraded correspondences returnMigrationGapNoticewith a reason and are never interpolated (YSD-2135); only ACCEPTED exact/approximate correspondences that fully contain the anchor range map it, by exact offset arithmetic.confirmAnchorMigrationrequires a human/collaborative-group ACCEPTING ReviewEvent (model reviewers and non-accepting events throw), parses the result throughEvidenceAnchorSchema, and returns{ migrated, original }— the original is untouched so rollback = drop the migrated anchor.- EvidenceAnchor
migratedFromAnchorId(YSD-2134, additive) — both ends of the lineage persist: the migrated anchor names its origin; the original remains valid on its own edition. entities/anchor-migration.spec.ts(YSD-2137) — the five named categories over a representative theatrical→director's-cut map (censor-cut removal, -500 offset shift, reorder, added material, plus a REJECTED overlapping correspondence) and a game build→patch telemetry-landmark map: accuracy (known-correct offset-mapped target frames, both offsets), false-correspondence (rejected correspondence covering the same frames is never chosen), gap-detection (censor cut, boundary straddle, reorder → gap notices with reasons, "not interpolated" asserted), anchor-migration (human confirmation → lineage both ends; model reviewer and contested events rejected), rollback (original returned untouched,migratedFromAnchorIdabsent on it). End-to-end re-verification against real media rides Section 5's pipelines.
- Correspondence
- Test command:
npx vitest run src/study(libs/contracts) — 176/176 passed (8 new); 61 schema artifacts regenerated + baseline v0 (additive only: optionalprocessor, optionalmigratedFromAnchorId); 229 golden cases byte-stable; tsc clean. - Commit: resolve via
git log --grep='YSD-2133'.
2026-07-18 — YSD-2150..YSD-2159 (Section 2.6: typed relationship graph contracts)#
- Scope: the full graph contract layer — vocabulary, per-relation semantics, presentation separation, permission model, query contracts with reference algorithms, and structural invariants. PostgreSQL-side enforcement and the Studio graph surface ride Sections 3/6/7 on these contracts.
- Artifacts (libs/contracts/src/study/):
GRAPH_NODE_KINDS(YSD-2150) — 29 addressable node kinds: the 25 proposal kinds (corpus, work, edition/build, sequence, scene, encounter, shot, beat, frame, image, region, entity, material, palette, pose, motion, sound, transcript, runtime-event, document-passage, claim, principle, original-concept, creative-decision, production-artifact) plus the four workspace record kinds already addressable as endpoints (anchor, segment, comparison-set, inspiration-item). CrossReference endpoints now accept all of them (enum widening — additive).- YSD-2151 verified complete on the existing
RELATION_TYPES— exactly the fifteen approved relations, asserted by count and name in tests. RELATION_SPECS(YSD-2152) — one spec per relation: directionality (5 symmetric / 10 directed;SYMMETRIC_RELATIONSnow DERIVED from the table so they cannot drift), forward + inverse plain-language labels, one-line description, endpoint kind constraints (support/contradiction/counterexample land on claim|principle; derived-principle on principle; informed-decision on creative-decision; avoidance originates from the creative plane; identity requires same-kind endpoints), transitivity policy (never-infer / path-display-only / ordering-composes — nothing ever materializes stored edges), and acyclicity. Parse-level enforcement added to CrossReferenceSchema (directed coherence + endpoint constraints) — zod-side rules on the pre-release 0.2.0 contract; JSON-schema layer is purely additive (endpoint enum widening + optional field), so the baseline compat check stays green by construction.- Correspondence
culturalBounds— first-class optional cultural scope on edges (YSD-2153) so cultural claims are never smuggled into generic context text; the item's full stored set (rationale, author or suggesting model, time, confidence, review state, evidence, counterevidence, context bounds, cultural bounds, rights dependencies, validity, supersession) asserted present in one test. entities/graph.ts(YSD-2154..2159) —- Presentation state (YSD-2154): CanvasPlacement/CanvasGroup/ SavedGraphView are .strict() with no relation fields — a placement literally cannot express a relationship; tests prove adjacent + co-grouped nodes produce no backlinks, no paths, and two singleton clusters.
- YSD-2155:
identityMergeCandidatesreturns ACCEPTED identity edges and nothing else — an accepted maximally-confident similarity edge is asserted to never be a merge candidate; suggested identity stays provisional (born-accepted ban and model-author ban already parse-level from YSD-2039). - YSD-2156: endpoint revisions pinned via refRevision (existing);
supersedeEdgeForEndpointChangerequires an accepting human/collaborative-group ReviewEvent, uses the envelopesuperseding()lineage, pins the new endpoint revision on the successor, keeps the old revision historical; stale revisions, non-accepting reviews, and model reviewers throw. - YSD-2157:
GraphPermissionEvaluatorapplied to EVERY node, edge, and derived result in every function — an edge to a hidden node disappears entirely (no existence leakage); tests show hidden nodes break paths, vanish from neighborhoods, and zero out dependency-impact. - YSD-2158: backlinks (bidirectional, per-side plain-language labels),
findPaths (bounded simple-path enumeration with hop directions),
neighborhood (bounded BFS reporting
truncatedhonestly), connectedClusters (union-find), dependencyImpact (forward closure over support/derived-principle/informed-decision/ transformation with edge paths + severed-edge list), GraphTraversalFilter on every function (relation/kind/review/ superseded filters), SavedGraphView (grouping + saved views), and GraphQueryRequest — a discriminated union whose bounds are schema-enforced (an unbounded neighborhood request does not parse). Both new schemas registered + published (63 artifacts, 233 golden cases). - YSD-2159:
checkGraphInvariantsover RAW stored records — broken endpoints, orphaned projections (placements + group members), permission leakage, cyclic derivation exactly where the relation spec says acyclic (mutual influence legal, derived-principle cycles flagged), invalid relation kinds, endpoint-constraint violations, and supersession continuity (latest-revision-superseded, unmarked-old-revision, and revision-chain-jump all detected).
- Test command:
npx vitest run src/study(libs/contracts) — 199/199 passed (23 new in graph.spec.ts); tsc clean; adversarial stub scan clean on all three touched files. - Commit: resolve via
git log --grep='YSD-2152'.
2026-07-18 — YSD-1004 (libs/yemaya/study-workspace: client-neutral core)#
- Scope: the workspace core library the proposal's platform plane composes: use cases, orchestration policies, normalized read models, and dependency interfaces. No HTTP/SQL/filesystem — the service composes these over real adapters; Section 3 persistence and Section 4's rights resolver implement the ports.
- Artifacts (libs/yemaya/study-workspace/):
- Registered Nx project
yemaya-study-workspacewith tagsscope:yemaya+plane:experience(the architecture checker now discovers and validates it: "2 experience-plane project(s)"), path alias@yemaya/study-workspacein tsconfig.base.json, workspace dep on@oshun/contracts, lint/test/typecheck targets. ports.ts— Clock, IdGenerator, StudyWorkspaceStore (get returns current revision or null, never a default), AuditSink (allows AND denials are audit events), typed UnknownRecordError.policies/rights-gate.ts— deny-by-default evaluation over RightsGrant records: subject scoping (work-wide covers editions; edition/asset-scoped cover only themselves), supersession/deletion state, revocation, validity window, territory (ISO 3166 or WORLDWIDE), explicit action membership. Denials carry the specific per-grant reasons; the empty-reason denial IS the deny-by-default floor.policies/review-gate.ts— applies a ReviewEvent to a stored record: subject-id match, revision pin match, from-state match, then the contract's legal-transition + acceptance-authority rules; produces the next revision via envelopesuperseding()— history append-only.- Use cases:
createStudyProject(creator becomes owner; membership event trail starts at creation),authorizePlayback(rights-gated; both outcomes audited with reasons),recordObservation(referential integrity: anchors must exist and be active; epistemic wall enforced by contract),reviewRecord(one path for observations and claims; persists next revision + appends the immutable event),planAnchorMigration/confirmPlannedMigration(honest planning: off-revision anchors listed, never guessed; human-review confirmation via the YSD-2133..2135 contract logic). - Read models:
buildProjectOverview(counts over current revisions, open reviews = suggested+contested, rights buckets active/expiring- within-30-days/expired/revoked vs a caller-supplied instant),buildAnchorContext(observations + supporting/counterevidence claims joined per anchor; per-ELEMENT visibility mirroring YSD-2157 — a hidden anchor yields null, not an empty shell).
- Registered Nx project
- Test command:
npx vitest run(libs/yemaya/study-workspace) — 15/15 passed against in-memory ports; tsc clean (lib + spec); eslint clean; architecture checker green; adversarial stub scan clean. - Commit: resolve via
git log --grep='YSD-1004'.
2026-07-18 — YSD-1016, YSD-1017 (fixture factories verified; adversarial stub scan gated)#
- YSD-1016 verified complete on existing artifacts —
libs/contracts/src/study/fixtures.ts(read in full this session): deterministic, schema-validated factories with zero randomness/clock and no restricted media (object keys and text stand in for bytes), covering every family the item names: sources (SourceWork), editions (SourceEdition), rights grants (RightsGrant + buildRightsExpiredGrant), tracks (MediaTrack), anchors (EvidenceAnchor + GameSessionAnchor), epistemic objects (Observation/Detection/InterpretationClaim + buildContestedClaim), graphs (CrossReference + SavedGraphView), attempts (PracticeAttempt), sessions (GameSessionAnchor + SessionSyncContract), and deletion cases (buildSoftDeletedFixture, plus buildSupersededFixture/buildRejectedFixture/buildPriorVersionFixture). Case builders exercised by fixtures-golden.spec.ts (5 tests) and the 233 golden cases. - YSD-1017 implemented —
tools/yemaya-study/check-stub-scan.mjs: study-workspace-scoped adversarial scanner over the service, everylibs/yemaya/study-*lib, every domain study adapter, and the study contracts. Six checks matching the item: placeholder-success comments, hard-coded fixture payloads in production paths (fixture builders outside contracts' own fixture/example modules), empty function/adapter bodies, ignored authorization (rights decision discarded at statement level), unimplemented destructive operations (empty delete/remove/ erase/purge/expire bodies), and silent catches (empty, or fabricating success without rethrow/log — real brace-matched catch-body analysis). Suppression uses the repo-wide end-of-linestub:legitimate <reason>convention; fail-loud seams are documented non-findings. Gate proven to fail: a planted probe file tripped all six categories (7 findings, exit 1) and the scan returned green after removal — not a self-asserting gate. Wired into the CI lint job after check-architecture. - Commit: resolve via
git log --grep='YSD-1017'.
2026-07-19 — YSD-1012, YSD-1014, YSD-1018 (env schemas complete; flags/kill switches; development docs)#
- YSD-1014 —
libs/yemaya/study-workspace/src/policies/feature-flags.ts: flags and kill switches across all eight governed axes — source class (the twelve Section 5.1 acquisition surfaces), analyzer (the six Section 5.4 signal families), provider (OPEN-keyed and default-DENY: a provider works only whenSTUDY_FLAGS_ENABLE=provider:<id>names it), export type (Section 15 formats), game connector, graph suggestion mode, live session, and transfer destination (TargetProjectRef kinds). Precedence: PERMANENTLY DISABLED (the three prohibited surveillance analyzers mirrored from the architecture checker, plusgraph-suggestion:identityper YSD-2155) > KILL (reported distinctly from a flag disable) > DISABLE > default. Unknown axes/keys deny loudly; enabling a permanently disabled key is a STARTUP ERROR naming the policy, not a silent ignore. 8 new lib tests. - YSD-1012 — completed: the service env schema
(
apps/yemaya/svc-study-workspace/src/config.ts) already failed fast on missing production values and pretty-logs-in-production; it now also parses the threeSTUDY_FLAGS_*vars through the lib at startup, so unknown flag axes/keys, enable+kill/enable+disable on one key, and permanently-disabled enables all abort boot with the full issue list (2 new service tests; 17/17 pass). Client half verified honestly: the studio study route reads NO env settings today (grep-verified — noprocess.env/NEXT_PUBLICin the study page/component), so every setting that exists is schema-validated; new client settings must extend this pattern when Section 7 lands. - YSD-1018 —
docs/proposals/yemaya-study-workspace/DEVELOPMENT.md: local setup (compose + pnpm + serve, flag env syntax), seed loading via the deterministic source-safe fixture factories (no restricted media by contract), targeted verification (per-project vitest/tsc + the seven CI checkers + drift checks), troubleshooting (worktree Nx bypass, SKIP_TYPECHECK escape, drift regeneration, stub-scan suppression, boot-refusal semantics), and cleanup (compose down, PID/port hygiene). Every command exists today. - Commit: resolve via
git log --grep='YSD-1014'.
2026-07-19 — YSD-1008, YSD-1015 (registration verified; supported baselines defined)#
- YSD-1008 verified complete on existing artifacts — all eight named
components exist and are enforced for the three study projects (libs/contracts
study slice, apps/yemaya/svc-study-workspace, libs/yemaya/study-workspace):
project.json registrations with Nx lint/test/typecheck (+build/serve for the
service) targets; package manifests with workspace/catalog deps; per-project
tsconfig trees; path aliases (
@yemaya/study-workspace,@oshun/contracts/studypackage export); tagsscope:yemaya+plane:experience(validated by check-architecture.mjs); ownership metadata (.github/CODEOWNERS lines 103–104: libs/yemaya + apps/yemaya → @GreyChimp; domains.json yemaya entry); dependency constraints (eslint.config.js plane:experience onlyDependOnLibsWithTags + type:study-adapter no-cross-adapter rules, read in place this session). - YSD-1015 —
docs/proposals/yemaya-study-workspace/supported-baselines.md: tiered desktop/OS, browser (grounded in the existing Playwright device matrix: Desktop Chrome + Pixel 7 primary, Firefox/WebKit secondary smoke), hardware (4-core/8GB no-GPU CI profile; GPU work stays a fail-loud RunPod seam — the sandbox has no GPU), media-decoder (H.264/VP9 primary, HEVC capability-detected; lossy re-encodes of watermarked evidence prohibited in fixtures), assistive-technology (keyboard-only + ARIA snapshots as primary CI gates per YSD-1037, VoiceOver manual at release, zoom/reduced-motion/ forced-colors rules), locale (en primary, RTL pseudo-locale smoke, ICU-only formatting), and network (throttled + offline-transition profiles wired to the resumable-upload and offline-queue test items). Explicit rule: unlisted environments are UNSUPPORTED-UNTESTED and support claims for them are forbidden until they join the matrix. - Commit: resolve via
git log --grep='YSD-1015'.
2026-07-19 — YSD-1007 (Aja study adapter: honest wrap, no fabrication laundering)#
- Scope:
libs/aja/study-adapter(tagsscope:aja+type:study-adapter; the architecture checker now discovers and validates it: "1 study adapter(s) scanned"; the stub scan now covers it as a fourth root). YSD-1006 (adapter packages across owners, plural) stays open — this is the first adapter and the pattern. - The point of this adapter: apps/aja/svc-reference-video's FFprobe
metadata, scene detection, and remote ingest paths are SIMULATED (verified by
reading the source; the capability table cites file:line for each), and its
default in-memory search path fabricates quality scores. Per YSD-1007 the
adapter does not fork the source and does not fabricate product-ready behavior
over those paths: simulated capabilities return typed refusals with status
unavailable-simulated-backing, the reason, and the source evidence — they never invoke the backing code. - Artifacts:
capabilities.ts(evidence-backed truth table +unsupported()typed-refusal builder),adapter.ts(createAjaStudyAdapter: capability discovery reflecting runtime injection state;searchReferenceClipsis dependency-inverted — the composing application injects aReferenceClipSearchBackingwired to real Qdrant/Meilisearch indexes, hits pass through with domain-native videoIds untouched and matchKind surfaced; absent injection the adapter REFUSES withrequires-backing-configrather than falling back to the fabricating in-memory path). Path alias@aja/study-adapter. - Deliberately not marked: YSD-0163 (conformance round-trips — needs anchors flowing through adapters), YSD-0164 (cross-domain architecture tests), YSD-1006 (adapters across all accountable owners).
- Test command:
npx vitest run(libs/aja/study-adapter) — 5/5 passed; tsc clean (lib + spec); architecture checker + stub scan green with the adapter included. - Commit: resolve via
git log --grep='YSD-1007'.
2026-07-19 — YSD-1011, YSD-1013 (dev composition verified; secrets separation)#
- YSD-1011 verified complete on existing artifacts — read
docker/docker-compose.dev.ymlin place: every named component exists with safe test defaults — PostgreSQL (pgvector/pg16 with theyemayadatabase in POSTGRES_MULTIPLE_DATABASES, plus pgbouncer), object storage (MinIO + bucket bootstrap), jobs/events (Redis core + Kafka/Zookeeper under thestreamingprofile), search projection (Elasticsearch undersearch), vector projection (Qdrant undervectors), graph projection (Neo4j 5 undergraph), observability (Prometheus/Grafana/Jaeger underobservability), plus Mailpit and admintools. Credentials are worthless dev-only values (oshun/oshun_dev, minioadmin) bound locally. Docker 29.5.3 confirmed runnable on-box (Section 3 integration tests can use it). DEVELOPMENT.md already documents usage. - YSD-1013 —
docs/proposals/yemaya-study-workspace/secrets-separation.md: the six categories (operational secrets, source credentials, signing keys, provider tokens, model credentials, user-export data) mapped to distinct stores with distinct grants (Secrets Manager path prefixes per category, KMS-held signing keys that sign rather than being fetched, a dedicated exports namespace whose encryption key is not an operational key), the load-bearing naming convention, and the worthless-dev-defaults rule. Code enforcement:redactedConfigSummary()in the service config — an explicit allowlist logged at startup where DATABASE_URL reduces to host:port/database; test proves credentials (user and password) never serialize into the summary and flags DO appear (operators see policy, not secrets). Service suite 18/18. - Commit: resolve via
git log --grep='YSD-1013'.
2026-07-19 — YSD-3001..3005, YSD-3010 (PostgreSQL authoritative model, verified against a real instance)#
- Migration
apps/yemaya/svc-study-workspace/migrations/0001_study_authoritative_model.sql:- YSD-3001 — 21 revisioned record tables in a new
studyschema: all twenty named families (project, question, source work, edition, asset, rights, track, segment, anchor, entity, epistemic [observation+detection], comparison, inspiration, graph [cross_reference], learning [practice_attempt], transfer, analysis, review [review_event], outcome, audit [audit_event]) plus edition_alignment_map. Uniform envelope column block mirroring StudyRecordEnvelope + contract-validatedpayload+ extracted hot-path columns (anchor edition/hash, grant subject, cross-reference endpoints both directions, alignment source/target). - YSD-3002 —
native_id_map(source_system, native_id, family → record_id): native identifiers mapped, never replaced; store methods- integration test.
- YSD-3003 — tenant_id NOT NULL + (tenant, project) index on every family;
RLS enabled with a tenant-isolation policy per table (row-level
authorization support; app roles get isolation, owner bypass documented);
immutable revision keys (PK (id, revision) + immutability trigger allowing
ONLY the supersession marker and legal deletion-state transitions; DELETE
forbidden); soft-deleted / legal-hold / tombstoned states with a legal
transition graph (legal-hold is reversible and blocks tombstoning — tested);
deletion_tombstonetable. - YSD-3004 — envelope CHECKs (revision chain, supersedes = revision-1, superseded-by = revision+1); supersession uniqueness (partial unique index: one current revision per id); legal review transitions + model-acceptance ban as a trigger on append-only review_event; source-version identity (anchor edition_id + sha256-checked technical_revision_hash NOT NULL, plus a referential trigger refusing anchors onto editions with no current row).
- YSD-3005 — optimistic concurrency: superseding revision N marks the old
row in the same transaction as inserting N+1 with
WHERE superseded_by_revision IS NULL; losing the race raises ConcurrentEditError; accepted history is append-only by trigger.
- YSD-3001 — 21 revisioned record tables in a new
- Runner
src/persistence/migrate.ts— per-file transactions, sha256 checksums, idempotent re-runs, refuses edited-after-apply (MigrationDriftError); rollback guidance in the migration header (additive schema; DROP SCHEMA study CASCADE backs it out). - Store
src/persistence/postgres-store.ts— implements the lib's StudyWorkspaceStore port; every read parses through the contract schema (drifted rows fail loudly); PostgresAuditSink appends to the append-only audit table. - YSD-3010 —
postgres-store.integration.spec.tsran against the REAL dev-compose PostgreSQL 16.14 (started this session): 13/13 passed covering every named path — success (project/anchor/ observation round-trips), duplicate (PK violation), conflict + concurrent edit (second supersession from one revision → ConcurrentEditError, first edit wins), rollback (corrupted second write aborts the marker update atomically), rights change (grant supersession visible; exactly one current revision) and expiry (evaluateRights denies on the stored expired revision), deletion (tombstone flow removes from reads, leaves tombstone row) — plus trigger proofs (immutability, no-delete, illegal review transition, model acceptance, append-only audit, anchor→edition referential) and runner idempotence/drift. When the database is unreachable the suite SKIPS with the compose command printed — never a fake pass. - Deliberately not marked: YSD-3006 (repositories for EVERY aggregate — the port's aggregates are covered; question/segment/ entity/comparison/learning/transfer repositories come with their feature sections), YSD-3007 (pagination/bulk), YSD-3008 (rollback drills/seed/compat/zero-downtime verification), YSD-3009 (explain-plan tests).
- Test command:
npx vitest run(svc-study-workspace) — 31/31 (18 unit + 13 integration); tsc clean; stub scan clean. - Commit: resolve via
git log --grep='YSD-3001'.
2026-07-19 — YSD-1006 (domain adapter packages under accountable owners)#
- Nine new adapter packages —
libs/{nisaba,sophia,hathor,euterpe,aglaea,bellona,metis,isis,neith}/study-adapter— completing the YSD-0150 convention alongside the existinglibs/aja/study-adapter(YSD-1007). Oshun and Iris are deliberately NOT adapters (YSD-0161/0162: consumed through platform libs). Each package: tags[scope:<owner>, type:lib, type:study-adapter, layer:domain](the existingplane:experience/type:study-adaptereslint depConstraints from YSD-0121 bind them; adapters cannot import each other), CODEOWNERS covered by the per-domainlibs/<owner>/globs, path alias in tsconfig.base.json, workspace deps on the owner's real libraries. - Shared seam —
libs/contracts/src/study/adapter.ts(new): theStudyDomainAdapterinterface (domain, contractVersion, capabilities(), nativeRef()),capabilityDescriptor()(validated<domain>.<capability>keys against the YSD-2007CapabilityDescriptorSchema),unsupportedResponse()/unavailableResponse()constructors,buildNativeRef()(verbatim native-identity wrapper overDomainNativeObjectRefSchemawith an explicit no-mutation recheck), andStudyCapabilityTableSchema(unique-key truth-table validation). - Every capability status is grounded in code read this session with file:line
evidence embedded in the descriptor detail. Wired = genuinely real domain
logic; refusals are honest fail-loud seams:
- nisaba: wired standoff annotation authoring (selector-union, standoff-annotations.ts:158/:234/:248), study-plan generation (study-plans.ts:211), passage comparison (TF-IDF cosine :2479, motif Jaccard :2492). Refusals: persistent-notebooks unavailable (Prisma models exist, apps/nisaba does not), media-study-selectors unsupported (YSD-0113: text-shaped selectors only). Deliberately did NOT wire buildNisabaCrossDomainBridge (tara/arete-specific, wrong fit).
- sophia: wired citation keys (provenance.ts:57) and dependency-inverted transcript anchoring (VideoTranscriptAdapter needs an injected VideoTranscriber :52 — capability upgrades to supported only when injected; refuses otherwise). Refusals: semantic-retrieval, evidence-persistence unavailable; creative-quality-ranking unsupported (must-not).
- hathor: wired pacing analysis (narrative/manager.ts:491) and editing rhythm (cinematography/manager.ts:946) — both genuinely computed. HONEST FINDING: DefaultColorTheory.generatePalette returns PRESET palettes not computed from the base color (manager.ts:599-660) → color-palette-theory unavailable rather than dressed up as supported; mda-lens-outputs unavailable (in-memory design CRUD, not anchor-bound lens outputs).
- euterpe: wired pitch-class-set analysis (set-theory.ts:242/:302/:688), loudness (mix-analysis.ts:89/:102/:807 meterLoudness K-weighting), voice pitch (voice-analysis.ts:568). Refusals: hosted-analysis-service unavailable (no service exists — apps/euterpe has only studio-web); speaker-identification unsupported (YSD-4052 policy).
- aglaea: wired fabric knowledge + comfort scoring (performance-database.ts:16, comfort-scorer.ts:220), era analysis (decade-database.ts:15, era-analyzer.ts:860/:1024), regional dress profiles (regional-codes.ts:22). Refusals: fabric-id-from-image unavailable (classifier takes structured text, not pixels); body-shape-inference unsupported (must-not).
- bellona: wired replay-bundle integrity (replay-bundle.ts:292 sha256 binding manifest+header) and strict session-manifest validation (session.ts:312; invalid input → typed rejection with zod issues, proven by the creator-not-a-participant test). Refusals: evidence-signing (signers real, no key), live-engine-bridge (retryable — needs a connected engine), gameplay-state-capture (MemorySaveStorage only, save/index.ts:85).
- metis: wired FSRS-5 scheduling (fsrs.ts:124; test proves interval==stability at r=0.9 and S0/D0 against the published weights) and BKT mastery (knowledge-tracker.ts:114; test asserts the hand-derived 0.1→0.4→0.775 posterior chain → 'proficient'). Refusals: concept-similarity (needs GraphStore+EmbeddingProvider), gradebook-emission (needs LMS creds), curriculum-service (FastAPI deploy); taste-originality-scoring unsupported (YSD-2047).
- isis: wired job-envelope minting (job-envelope.ts:731, idempotency key =
jobId) and pure region-aware routing (router.ts:47 planIsisRoute — "Pure
data + pure functions. No IO."; test proves tenant-pinned region wins and
unhealthy endpoints are skipped). Added the additive
./provider-endpointsexport subpath to @isis/ai-providers so the adapter avoids loading provider SDK modules. Refusals: inference-execution, output-provenance-registry unavailable; evidence-system-of-record unsupported (YSD-0160 must-not). - neith: ALL inspection refuses — the Rust is real (GLB importers.rs:410, OBJ :818, USDA :548, ASCII-FBX :592; scene graph node.rs:147) but no napi-rs/WASM bindings exist (grep empty; package.json wraps cargo only), so the adapter refuses rather than reimplementing geometry in Yemaya (YSD-0165) or faking results. binary-fbx-import is unsupported (also unimplemented in Rust, importers.rs:597); the tests prove the convert-your-file vs wait-for-bindings distinction.
- Verification: per-package
tsc --noEmit(lib + spec configs) clean ×9; vitest 49/49 across the nine packages (6+6+5+6+7+5+5+5+4); contracts study suite 199/199; yemaya study-workspace 23/23; adversarial stub scan zero hits; eslint (incl. module-boundary depConstraints) clean. - Note for YSD-0163: the aja adapter still publishes its bespoke descriptor shape from YSD-1007; harmonizing it onto the shared CapabilityDescriptorSchema is planned as part of the conformance kit.
- Commit: resolve via
git log --grep='YSD-1006'.
2026-07-19 — YSD-0163 (adapter conformance: native IDs and anchors survive round trips)#
- Kit
libs/contracts/src/study/adapter-conformance.ts(exported from the study namespace):NATIVE_ID_BATTERY— 12 adversarial native ids (uuid, cuid, colon-scoped, path-like, dotted, mixed-case, leading zeros, inner/trailing whitespace, unicode incl. CJK+emoji, single char, 512-char max) chosen so trimming, lowercasing, numeric coercion, or re-minting each break at least one entry.nativeRefRoundTripFindings(adapter)— for every battery id: nativeRef preserves id/version byte-for-byte, pins the adapter's domain, keeps kind verbatim, survives JSON round trip re-parsed through the strict DomainNativeObjectRefSchema, and version-less refs must not fabricate a version.studyAdapterConformanceFindings(adapter, expectedDomain)— identity + semver + YSD-2007 table validity + domain-prefixed keys + version agreement- discovery stability across calls + the native-ref battery.
anchorLocatorRoundTripFindings(label, locator)— SourceLocator serialize/revive must hold under the YSD-2083 canonical equality (sourceLocatorsEqual), not merely structurally.losslessJsonRoundTripFindings— structural equality where a present- but-undefined key is significant (JSON dropping a field IS lossy; the naive stringify-compare version could not detect this and was replaced).
- Kit self-test
adapter-conformance.spec.ts— proves the kit CATCHES violations, not just passes conformant code: a deliberately lossy adapter (trim+lowercase) is flagged on the mixed-case/trailing-space entries; a wrong-domain adapter and an unstable capability table are flagged; all six SourceLocator kinds round-trip under canonical equality; undefined-drop is flagged. - Aja harmonized onto the shared surface (contract 1.0.0 → 1.1.0, additive):
now implements StudyDomainAdapter —
domain,contractVersion,nativeRef, andcapabilities()mapping the bespoke evidence-rich table onto the shared YSD-2007 shape (available→supported, else unavailable; evidence folded into detail — a test proves it is never dropped). The originaldescribeCapabilities()API is unchanged; added @oshun/contracts dependency. - Ten per-package conformance specs (
src/conformance.spec.tsin every study-adapter): each runs the kit (zero findings) plus a domain round trip through REAL adapter methods: aja search hits keepvideoIdverbatim incl. trailing space/leading zeros/case; nisaba annotation ids, creator ids, and caller-chosen plan/objective ids untouched; sophia anchor ids embed the native video id verbatim and chunk provenance keeps source/revision ids; hathor rhythm results echo native shot ids; bellona session/participant ids survive strict validation and hashing does not mutate its input; metis BKT keys concepts by the exact caller string (near-identical ids stay independent: 0.775 vs 0.1); isis preserves jobId + idempotencyKey; euterpe analysis is deterministic and JSON-lossless; neith (all-refusals) still passes the full identity battery. - Verification: all ten adapter suites green — 76 tests total (8+9+8+7+8+ 9+8+7+7+5); contracts study suite 205/205; per-package tsc (lib+spec) clean; stub scan zero hits; eslint zero errors.
- Commit: resolve via
git log --grep='YSD-0163'.
2026-07-19 — YSD-0164 (architecture tests: cross-domain DB, copied taxonomies, model calls, store bypasses)#
- Extended
tools/yemaya-study/check-architecture.mjs(the YSD-0121 checker) from four rules to seven, refactored around an exported pure rule enginecheckSource(rel, text, context)so the rules are unit-testable rather than self-asserting:- Rule 5 (unapproved model calls, YSD-0160): flags (a) imports of any model-provider SDK (openai, @anthropic-ai/sdk, @google/generative-ai, ollama, elevenlabs, mistral, cohere, huggingface) anywhere in study code — including the isis adapter, which plans routes but never holds provider clients; (b) imports of @isis/ai-providers root or /providers* client modules; the pure /provider-endpoints routing module is permitted ONLY inside libs/isis/study-adapter; (c) any study source naming a provider API host (api.anthropic.com etc.), tests included.
- Rule 6 (copied taxonomies, YSD-0014): flags declarations of twelve domain-owned taxonomy constants (FORTE_CATALOG, DECADE_PROFILES, ELEMENT_HISTORY, FABRIC_PERFORMANCE_DATABASE, REGIONAL_PROFILES, FSRS5_DEFAULT_WEIGHTS, DEFAULT_BKT_PARAMS, MASTERY_THRESHOLDS, ISIS_GENERATION_TYPES, ISIS_PROVIDER_KINDS, REMOTE_REPLAY_BUNDLE_COMPONENTS, AJA_STUDY_CAPABILITY_IDS) in any study root; imports remain legal, and aja's own capability list is allowed in aja's adapter.
- Rule 7 (canonical-store SQL bypasses): scanning now includes
.sqlfiles; flags SQL-shaped references (FROM|JOIN|INTO|UPDATE|TABLE <domain>.) to any registered domain schema outside {study, public, pg_catalog, information_schema}, andSET search_pathto any non-allowed schema. Precision guard: capability keys like 'sophia.semantic-retrieval' never follow an SQL keyword and do not trip. - Rules 1–4 (adapter-only cross-domain imports, cross-domain store packages, prohibited-inference analyzers, required tags) unchanged.
- New rule unit tests
tools/yemaya-study/check-architecture.test.mjs(node --test, 13 tests): one planted violation per rule proving it FIRES, plus the allowances (own-store adapter import, isis-only routing import, taxonomy import vs declaration, study-schema SQL, capability-key precision). Wired into ci.yml beside the checker. - End-to-end negative test: planted a file importing openai, declaring
DECADE_PROFILES, and selecting from sophia.evidence_pack inside
libs/yemaya/study-workspace → checker exited 1 with exactly 3 problems naming
each rule; removal returned green
(
2 experience-plane project(s), 10 study adapter(s) scanned). - Test commands:
node --test tools/yemaya-study/check-architecture.test.mjs(13/13),node tools/yemaya-study/check-architecture.mjs(pass, 12 projects). - Commit: resolve via
git log --grep='YSD-0164'.
2026-07-19 — YSD-1009 (schema, API-client, event-contract generation + compat checks in CI)#
- Schema generation (pre-existing YSD-2008 machinery) extended: now also
emits
schemas/study/events.manifest.json(event kind → emitter + delivery channel, from the STUDY_EVENT_DELIVERY table). 67 artifacts published. - Event contracts —
libs/contracts/src/study/events.ts:StudyWorkspaceEventSchemamirrors the IMPLEMENTED event reality exactly — the eight AuditSink kinds from ports.ts (project-created, observation/claim-recorded, playback-authorized/denied, record-reviewed, anchor-migration-planned/confirmed) with the columns the append-only study.audit_event table persists (kind, subjectId, actorUserId, detail, tenantId, occurredAt). Registered in STUDY_SCHEMA_REGISTRY with canonical + negative + zod-only examples, so it flows through publication, ajv double-validation, golden fixtures, and baseline breaking-change detection automatically. Deliberately no invented richer payloads: kinds arrive with the features that emit them. - API client generation —
libs/contracts/scripts/generate-study-client.tsrenderssrc/study/client.generated.tsfrom a new route manifest (service-routes.ts) that mirrors the routes the service ACTUALLY serves (health/ready/metrics with exact response shapes read from app.ts and readiness.ts; business routes join with YSD-1030 — listing them earlier would fabricate an API). The generated client is dependency-injected (fetch), validates every response through the manifest schemas, and returns typed contract-violation results instead of passing drifted bodies through. Health/Ready response schemas are registry-published too. - Loop closed with a real E2E: svc-study-workspace app.spec drives the
GENERATED client against the real Hono app (client.health/ready/metrics via
app.request) — proving manifest ≙ client ≙ service — plus a drifted-body test
asserting
contract-violationinstead of pass-through. Service suite 33/33 (integration suite still green against real postgres). - Compatibility checks: STUDY_CONTRACTS_VERSION 0.2.0 → 0.3.0 (additive = minor per the YSD-2010 policy), '0.3.0' added to SUPPORTED_STUDY_SCHEMA_VERSIONS; schema check reports "65 contracts published, no drift, no breaking changes vs baseline v0"; goldens regenerated (239 cases stable).
- CI wiring:
generate-study-client.ts --checkadded beside the existing schema/golden checks in the ci.yml study block (which the YSD-0164 commit also gavenode --test check-architecture.test.mjs). - Verification: contracts study suite 205/205; contracts + service tsc clean; svc suite 33/33; study-workspace 23/23; adapter spot-suite green; architecture + stub-scan + traceability checkers pass.
- Commit: resolve via
git log --grep='YSD-1009'.
2026-07-19 — YSD-1010 (targeted CI, PARTIAL — deliberately left unchecked)#
- New workflow
.github/workflows/study-workspace.yml— path-filtered to study code (contracts/study, schemas/study, study generators, experience plane, service, alllibs/*/study-adapter, tools/yemaya-study, studio/study web route). Four jobs, one per suite family that exists:contracts— study contract suite + contracts tsc + the three generator --check gates (schemas/breaking-change, goldens, client drift).service-migrations— realpostgres:16service container; service tsc, then the full service suite withSTUDY_PG_REQUIRED=1so the migration + authoritative-store integration suite MUST run (spec change: unreachable DB is now a hard failure when required — verified both ways locally: 13/13 against real postgres, and an explicit failure with a bogus URL, never a silent skip). Plus the experience-plane lib suite + tsc.adapter-conformance— fail-fast:false matrix over all ten adapters: lib+spec typecheck and full suites incl. the YSD-0163 conformance specs.architecture— YSD-0121/0164 checker, its planted-violation node --test rules, stub scan, traceability.
- Deliberately NOT marked complete: the item also names deletion, accessibility, Playwright, security, evaluation, and performance suites — those suites do not exist yet (they arrive with YSD-1035..1039 and later sections), and a workflow step for a nonexistent suite would be a self-passing gate. The workflow header documents this; the item is marked only when every named suite family has a real job.
- Verification: YAML parses (4 jobs); STUDY_PG_REQUIRED gate verified in both directions locally.
- Commit: resolve via
git log --grep='YSD-1010'.
2026-07-19 — YSD-1030..1039 walking skeleton, PROGRESS (items deliberately still unchecked)#
- Stage A (library): register-local-source, grant-rights,
ingest-local-source, create-anchor, export-study-trail, delete-source use
cases +
activeGrantForacquisition gate; extended StudyWorkspaceStore port (works/editions/assets/tracks/project lists); shared in-memory test harness (src/testing/in-memory-deps.ts, tests-only per YSD-1031); event kinds extended to 16 (contract + manifest regenerated). 32 lib tests. - Stage B (service): fail-closed HS256 bearer auth (
auth.ts— 503 when unconfigured, 401 on bad/expired tokens, constant-time signature check), business routes under /api/study (project/register/grants/ingest/playback/ anchors/observations/trail/export/delete), inbox-sandboxed file resolution (realpath containment, symlink escapes refused), real adapters: streaming sha256 identifier, ffprobe prober (rational r_frame_rate — never floats), MinIO object store over @oshun/storage; postgres store extended to the full port; readiness gains an object-store probe; startup wires business routes ONLY when postgres + S3 + inbox are all real (else honest 503). - End-to-end proof
walking-skeleton.integration.spec.ts— 9/9 against the REAL dev stack (postgres 16 + MinIO started this session): the film is synthesized in-test with ffmpeg (testsrc2 1280×720@24 + 440 Hz sine — rights-cleared because we own it; the YSD-1038 source), then over HTTP: 401s without/with-garbage tokens → project → register → ingest DENIED before the rights decision (403 + reasons) → grant (creator-owned, playback+export) → ingest (ffprobe facts verified: 24/1 rational fps, 120 frames, 16:9, sha256 identity, video+audio tracks) → playback presigned URL → real bytes fetched from MinIO → manual anchor (frame 24–72) + observation with no analyzer configured anywhere (YSD-1033/1039) → trail intact through a fresh app instance over the same database (YSD-1034) → export document downloaded and verified → delete: objectKey really gone (presigned URL now 404), 7 record families tombstoned, playback 403, trail empty (YSD-1035 across every surface that exists today). - Remaining before ANY of YSD-1030..1039 is marked: notebook step (Nisaba-owned persistence through the nisaba study-adapter), the /studio/study web UI consuming these routes, Playwright deep + a11y coverage (YSD-1036/1037), manual hierarchy correction surface (YSD-1033), and extending the YSD-1009 route manifest/generated client with the business routes.
- Test commands: lib
npx vitest run32/32; svcnpx vitest run42/42 (20 app + 13 store-integration + 9 walking-skeleton E2E); tsc clean both; architecture + stub scans pass. - Commit: resolve via
git log --grep='ysd-1030'.
2026-07-19 — Walking skeleton UI + Playwright; YSD-1031/1034/1038/1039 marked#
- UI
apps/oshun/web/src/components/studio/StudyWorkspaceApp.tsxon /studio/study (launch surface updated honestly: study-projects and playback-annotation now available; four lens surfaces still explicitly "Not yet available"). Every rights denial renders its policy reasons (data-study-denial-reasons). Adata-study-readyattribute appears only after hydration + first load so E2E fills never race hydration. - Proxy
apps/oshun/web/src/app/api/study/[...path]/route.ts: verifies the REAL Oshun session (the /v1/auth/refresh exchange the session endpoint itself uses), mints a 300s HS256 study token, forwards to the service. Fail-closed 503 when STUDY_SERVICE_URL/STUDY_JWT_SECRET absent; 401 when the session is missing/invalid. Env reads via @oshun/config (ratchet). - Shared token moved to
@oshun/contracts/study/token, rewritten on @noble/hashes after the repo lint rule flagged node:crypto (webpack noops it in client bundles — silent HMAC corruption); svc auth.ts delegates to it. Study contracts namespace converted to extensionless relative imports (repo norm) because Turbopack cannot resolve.js→.tsinside the transpiled package; added slim@oshun/contracts/study/clientsubpath so the browser bundle carries only the generated client + schemas. - Playwright
e2e/studio-study-walking-skeleton.spec.ts— 2/2 against the REAL five-layer stack (Chromium → Next web → real BFF auth via seedCustomerSession → /api/study proxy → svc-study-workspace → postgres + MinIO + nisaba DB, film ffmpeg-synthesized byscripts/e2e-bootstrap.ts): honest launch surface, axe scan clean (serious/critical = 0) before AND after the journey; create → register → ingest denied pre-grant with reasons visible → grant → ingest → real - Marked [x]: YSD-1031 (pg + MinIO + nisaba stores; in-memory only under src/testing/; business routes wire ONLY when all backings real, else honest 503), YSD-1034 (svc E2E: trail incl. notebook re-read by a FRESH app instance over the same databases; lib suite: records persist through grant expiry while new actions deny), YSD-1038 (real rights-cleared ffmpeg-owned film; sanitized evidence = these log entries + specs), YSD-1039 (whole journey with zero models configured; Playwright asserts the explicit "Not yet available" analyzer states end to end).
- Deliberately NOT marked: YSD-1030/YSD-19020 (19020 bundles "player/hierarchy" — hierarchy navigation/segments not built; the two are one deliverable, so both wait for the minimal manual hierarchy), YSD-1032 (search/sharing/transfer surfaces don't exist yet to gate), YSD-1033 (manual hierarchy correction needs the hierarchy), YSD-1035 (search/vector/graph/cache/notebook-preview surfaces pending), YSD-1036 (journey misses compare + typed link), YSD-1037 (keyboard + axe critical assertions shipped; screen-reader-critical assertions pending).
- Test commands: Playwright 2/2 (~59s, workers=1); svc suite 44/44 incl. restart-notebook assertion; lib 32/32; contracts study 205/205; page unit 4/4; architecture/stub scans pass.
- Commit: resolve via
git log --grep='playwright proof'.
2026-07-19 — Manual hierarchy; YSD-1030 + YSD-19020 + YSD-1033 marked#
- Segments —
use-cases/manage-segments.ts: createSegment / correctSegment with REAL structural validation (frame bounds within the edition's probed frame count; parents on the same edition; frame-range parents must CONTAIN children — both refusals unit-proven), pinned technicalRevisionHash, human-authored confidence explicitly{missing, not-applicable}(human judgment is not model confidence), corrections as append-only supersessions bumping segmentationVersion (manual-N). Store port + postgres impl (study_segment), events segment-created/segment-corrected (contract + manifest regenerated), service routes (create/list/correct), manifest + generated client, segments in trail + export document, hierarchy panel in the UI with indent-by-parent outline + correction form. - Verification: lib 34/34 (containment refusal, beyond-edition refusal, correction history, no-op-correction refusal); svc 44/44; Playwright 2/2 with the extended journey — create scene → shot INSIDE it → manual label correction visible in the outline (corrected ×1) — plus all prior steps and clean axe scans. One box-level fix en route: fs.inotify.max_user_watches 121k→1M + instances 128→1024 (tsx watch ENOSPC killed the BFF boot — shared-box watcher exhaustion).
- Marked [x]: YSD-1030 + YSD-19020 (every named element of both items is implemented and E2E-verified twice: service-level 11-step suite vs real pg/minio/nisaba + browser-level Playwright vs the full five-layer stack — local-file class, real persistence, rights decision, player + hierarchy, manual annotation, Nisaba notebook flow, export, rights denial with reasons, deletion) and YSD-1033 (hierarchy creation + correction and annotation all available the moment ingest lands technical metadata — no analyzer, no model, no queue anywhere in the flow).
- Commit: resolve via
git log --grep='manual hierarchy'.
2026-07-19 — Rights denial before every skeleton surface; YSD-1032 marked#
- Search —
use-cases/search-study-records.ts: rights evaluated BEFORE matching (activeGrantFor per work at query time); suppressed works reported inexcludedWorkswith the denial reasons; segments suppressed with their edition, observations suppressed unless ≥1 cited anchor sits on a permitted edition (export's rule). Deterministic scorer (documented approximation for skeleton scale): AND-term match, phrase 100 + prefix 20 + 10/term, field weights title 5 / label 3 / creators 2 / observation 1 — unit tests assert hand-computed scores (700/360; 650 E2E), so a fabricated scorer fails. Auditsearch-performedcounts outcomes and never carries query text (YSD-0009). - Sharing —
use-cases/share-project.ts: owner-only, duplicate-refusing, and denied unless EVERY source work has an activecollaborationgrant (per-work reasons; auditsharing-denied); allowed path appends the membership event + roster on a superseding project revision (auditproject-shared). - Project transfer —
use-cases/transfer-project.ts: owner-only; denied unless every work grantsproject-transfer(audittransfer-denied); rights-passing requests refuse honestly with the four YSD-0020 prerequisite checks (abstraction-enforcement,source-concentration,overly-specific-expression,dependency-impact) as 503transfer_unavailable(audittransfer-blocked) — transfer has NO success path until Section 18 ships those checks. - Wiring: routes
GET /projects/:id/search,POST /projects/:id/members,POST /projects/:id/transfer; contract manifest + body/response schemas + regenerated client (searchProject/shareProject/requestTransfer); five new audit kinds in the events contract, schemas regenerated (also caught the stale segment-created/-corrected publication drift). Playback, processing (ingest), and export gates were already live — with this, all six YSD-1032 surfaces deny by default with reasons. - Verification: lib 43/43 (rights-surfaces.spec.ts: lapsed-grant search suppression incl. observation-through-anchor suppression, sharing denial/allow with roster+revision asserts, transfer denial + honest unavailability); svc 45/45 incl. the real-stack integration leg (ungranted work suppressed E2E, share 403→grant→201, transfer 403→503 with named checks, audit rows verified by SQL in study.audit_event); contracts 205/205; schema drift check clean, no breaking changes vs baseline v0; architecture + stub + traceability checkers pass.
- Marked [x]: YSD-1032.
- Commit: resolve via
git log --grep='rights denial'.
2026-07-19 — Expiry + deletion across every skeleton reference class; YSD-1035 marked#
- Export references — migration
0002_study_export_reference.sql: a ledger (study.export_reference, GIN on work_ids) recording every export document and the works it embeds;exportStudyTrailwrites it; user deletion purges the export OBJECTS whose ledger rows name the deleted work and marks the rows (rows survive for audit; duplicate keys and double deletion are hard errors — postgres integration test). - Deletion gaps closed —
deleteSourcenow tombstonesstudy_segmentrows with their edition (previously orphaned as active rows) and deletes sidecar proxy/thumbnail objects (branch now actually exercised: lib tests attach a schema-valid proxy sidecar and assert its object dies on both the delete and expiry paths). - Retention expiry —
use-cases/expire-retained-sources.ts: once EVERY grant has ended (validUntil lapse or revocation, superseded/tombstoned grants excluded) and the LONGESTsourceRetentionDayswindow (counted per grant from its own end) has passed, the sweep deletes retained copies (original + sidecars) and tombstones asset/track rows; the scholarly record (work, edition metadata, segments, anchors, observations, notebooks) survives — the license to HOLD the copy lapsed, not the user's study writing; user deletion remains the remove-everything path. Auditedsource-expiredbysystem:retention-sweep. Idempotent (no stored copies ⇒ skip). Surfaced asPOST /api/study/expiry-sweep(contract manifest + generated clientexpirySweep) and as a config-gated service timer (STUDY_EXPIRY_SWEEP_INTERVAL_MS, 0=off, cleared on shutdown). - Classes without artifacts yet: search is a rights-filtered live scan of the authoritative records, so tombstoning IS its deletion (tested: deleted work vanishes entirely, not merely rights-suppressed); vector and graph projections do not exist in the skeleton (Section 3.3) and gain deletion fan-out with the items that create them (noted in delete-source doc); notebooks store citations by anchorId only — no source content exists to purge, and exports embedding the work are purged as above; presigned-URL cache is covered by bytes-gone verification.
- Verification: lib 48/48 (deletion-expiry.spec.ts: export purge + ledger
marks, segment tombstone + search-empty, retention window honored at T+12h /
expired at deadline+1min with exact object keys, live-grant immunity,
revocation-based retention, idempotent re-sweep, sidecar death on both paths);
svc 47/47 incl. two real-stack legs (export purged on delete with presigned
URL 404, and a real 5s-lifetime grant: early sweep refuses, post-lapse sweep
deletes bytes (URL 404), trail keeps the work, SQL-verified single
source-expiredaudit row, idempotent re-sweep); postgres store 0002 migration + ledger roundtrip; contracts 205/205, schema+client drift checks clean, no breaking changes; architecture + stub + traceability checkers pass. - Marked [x]: YSD-1035.
- Commit: resolve via
git log --grep='expiry and deletion'.
2026-07-19 — Deep browser journey complete; YSD-1036 marked#
- New workspace surfaces (StudyWorkspaceApp): navigate ("Go to start" on frame-range segments seeks the real player to startFrame/frameRate), compare (two anchor pickers → side-by-side panes rendering each anchor's locator + the observations citing it), typed links (relation picker contrast/similarity/identity → POST /projects/:id/links; list shows relation + endpoints + review state), grant lifetime seconds (records a validUntil + zero-retention grant), and a "Run retention sweep" button (client.expirySweep).
- Typed-link backend —
use-cases/create-typed-link.ts: CrossReference between two anchors with pinned endpoint revisions (YSD-2156), rights gate on BOTH endpoints' works before any graph write, directionality from RELATION_SPECS, identity bornsuggestednever accepted (YSD-2155), auditlink-created; store port + postgres/in-memory impls (cross_reference family),linksin the trail contract + route,createLinkin the manifest + regenerated client; deleteSource tombstones edges whose endpoints die (YSD-2159). - Playwright journey (
studio-study-walking-skeleton.spec.ts, real five-layer stack: Next + BFF + svc-study-workspace + postgres/minio/ nisaba + ffmpeg film): all fourteen YSD-1036 steps — create project, ingest (denied first, with reasons), rights decision, play (presigned bytes), navigate (player.currentTime → 1.0s after segment goto), annotate (scene/shot + correction), compare (both panes assert their own locators), pin region, create typed link (contrast, accepted), save notebook (+ study card), export, deny action, EXPIRE (new test: 6s license → playback denied with "expired" reason while bytes remain → sweep → presigned URL 404s, scholarly record survives), delete (keyboard submit; anchors AND links empty after). 3/3 passed (3.1m). - En-route fixes: the durable BFF (from today's main) refuses boot without
OSHUN_SIGNUP_VERIFICATION_HMAC_SECRET and
OSHUN_AUTONOMY_SNAPSHOT_KEY_BASE64/REF — added e2e-only defaults in
playwright.config; cleared ONE stale
signup-verification-statesnapshot row from the dev DB written under an unrecoverable key (secretCheck mismatch predates the shared default). - Marked [x]: YSD-1036.
- Commit: resolve via
git log --grep='deep playwright'.
2026-07-19 — Keyboard-only + screen-reader journey; YSD-1037 marked#
- Spec —
e2e/studio-study-a11y.spec.ts, same real five-layer stack as the YSD-1036 journey, two standing rules: NO clicks (every activation is focus + Enter/Space or implicit text-input form submission) and every control addressed by ACCESSIBLE NAME (getByRole/getByLabel — a missing or wrong announced name fails the locator itself, never by test id). - Journey covered keyboard-only: create project (with name → purpose →
submit TAB-ORDER assertions), register film (implicit Enter submission),
denied ingest ANNOUNCED as role=alert carrying the deny-by-default policy
text, grant via Space, ingest, playback via Enter with
<video controls>asserted (native keyboard-operable player), segment creation, frame-range anchor (label lookup scoped past duplicated label text), observation, Nisaba notebook, export (named link visible), delete (Enter submission, outcome announced, trail emptied). - Screen-reader semantics asserted: workspace is a NAMED region (aria-labelledby its heading), project switcher exposes aria-pressed selection state, notices are role=alert (scoped past Next's route announcer, which is also role=alert — strict-mode caught that), axe scan of the fully populated workspace with zero serious/critical violations.
- Result: 1/1 passed (31s test, 3.0m with stack boot). CI: the study-e2e job
now runs BOTH study specs; path filters broadened to
e2e/studio-study-*.spec.ts. - Marked [x]: YSD-1037.
- Commit: resolve via
git log --grep='keyboard'.
2026-07-19 — Stable source identity on evidence anchors; YSD-2100 marked#
- Contract (0.4.0) — EvidenceAnchor gains
workId+assetId+sessionId; a version-gated superRefine requires, for every record at a version ≥0.4.0: workId always, and EXACTLY the identity matching the locator kind — assetId for track/asset-addressed evidence (must equal the locator's assetId for document/spatial), sessionId for runtime-event evidence (must equal the locator's start sessionId; event ranges may not cross sessions). Pre-0.4.0 stored records are exempt via the frozenPRE_IDENTITY_SCHEMA_VERSIONSset; a legacy canonical example proves they keep parsing. STUDY_CONTRACTS_VERSION 0.3.0 → 0.4.0 (additive per compat-policy rule 5, added this session); schemas/ goldens/client regenerated; NO breaking changes vs baseline v0. - Use cases — createAnchor now RESOLVES and VERIFIES the chain before
writing: track locators must chain track → asset → the anchor's edition (a
foreign edition's track is refused with reasons, unit-proven);
document/spatial locators must name an asset of that edition; runtime-event
locators pin sessionId from the locator. Anchor migration
(confirmPlannedMigration) now resolves TARGET-side identity — shared work +
the target edition's single unambiguous counterpart track of the same kind —
refusing honestly otherwise, and
confirmAnchorMigrationstamps the migrated record with the target's work/asset/track and the CURRENT schema version. This also fixed a latent bug: migrated anchors used to keep the SOURCE edition's trackId while pointing at the target edition. - Persistence — migration
0003_study_anchor_source_identity.sql: work_id/asset_id/session_id columns + idempotent backfill resolving the stored chain (edition→work, track→asset, locator asset/session) plus a partial index on work_id. Stored payloads are NOT rewritten — revision immutability (YSD-0013) holds; legacy identity lives in columns. FAMILY_EXTRAS writes the columns on every anchor write. - Verification: contracts 205/205 (incl. new zod negatives: 0.4.0 anchor missing workId/assetId, asset evidence smuggling a sessionId; migration spec asserts target identity + trackId swap); lib 54/54 (identity resolution on create, cross-edition track refusal, migration counterpart resolution); svc 48/48 vs real PostgreSQL (0003 applied; legacy row backfilled by re-running the migration SQL, payload untouched and still parsing, current-version rows carry columns at write); Playwright journey re-run green; architecture + stub + traceability checkers pass; schema drift + client drift checks clean.
- Scope note: Detections/Observations/Claims carry identity THROUGH their anchors — YSD-2112's unanchored-rejection invariants (already marked) close that path; sessions gain first-class records with the instrumented-game sections.
- Marked [x]: YSD-2100.
- Commit: resolve via
git log --grep='stable source identity'.
2026-07-19 — Policy-permitted representation generation; YSD-2102 marked#
- Policy —
policies/representation-policy.ts: permitted representations are DERIVED from the covering grant's actions (thumbnail/crop ⇒ frame-extraction, waveform ⇒ proxy-generation, feature-vector ⇒ embedding, transcript-excerpt ⇒ transcription; metadata-only always; provider-deep-link only for provider-linked assets). createAnchor's hardcoded list is GONE — a playback-only grant now yields metadata-only evidence (unit-proven). - Generation —
use-cases/generate-representation.ts, doubly gated: (1) the anchor's recorded policy, (2) a LIVE rights re-check of the representation's action at render time (permitted-then-lapsed grants refuse; renderer never invoked — asserted). Real rendering via the new ffmpeg adapter (media/representation-renderer.ts): frame extraction reading the presigned MinIO URL directly, normalized-region crops, total-pixel cap from grant limits (never upscaling), waveform images. Unshipped stages (feature-vector/transcript-excerpt/provider-deep-link) refuse with 501 representation_unavailable — honest seams for the analyzer sections. Output stored as an ASSET SIDECAR on a superseding revision, so YSD-1035 deletion + expiry purge it with the source copies. Auditrepresentation-generated; route POST /anchors/:anchorId/representations; contract body/response + manifest + regenerated client. - Verification: lib 57/57 (policy mapping vs hand-listed sets, both gates, sidecar + audit, lapsed-grant refusal with renderer-untouched assert); svc 49/49 incl. the real-stack leg — ffmpeg renders an actual thumbnail from MinIO bytes (JPEG magic asserted on the downloaded object) and waveform is 403 representation_denied under a grant without proxy-generation; contracts 205/205; schema/client drift clean; checkers green. UI generation surface arrives with the Section 7/8 viewing-state items — the policy enforcement is service-side and client-independent.
- Marked [x]: YSD-2102.
- Commit: resolve via
git log --grep='representation'.
2026-07-19 — Authorship + immutable change reasons + review history; YSD-2106 marked#
- Authorship (verified in place): AuthorIdentitySchema's four kinds (human, model, imported-system, collaborative-group) carried on every envelope's createdBy and on segments/claims/cross-references/review events; model authorship structurally rejected where humans must decide (observations, identity acceptance, migration confirmation — all previously tested).
- Immutable change reason (the gap, now closed): the envelope gains
changeReason, andsuperseding()now REQUIRES{ reason, schemaVersion? }— the compiler forces every supersession site to say why. Version-gated at 0.5.0 per compat-policy rule 5 (second application): revisions >1 at ≥0.5.0 must carry a reason, revision 1 must NOT (creation is not a change), pre-0.5.0 stored history stays valid. Production writers stamp STUDY_CONTRACTS_VERSION so their new revisions enter the rule; reasons threaded with real content through all nine production sites (share, register-work, ingest, segment corrections [the user's own reason], representation sidecars, notebook authoring, study cards [nisaba adapter], review application [the review's reason], graph endpoint advancement). Reasons are immutable with the rest of the payload (0001 trigger). - Review history (verified in place): study.review_event append-only by trigger (mutation + delete refusal integration-tested), legal transitions enforced, applyReviewEvent supersedes with the review's reason now recorded on the envelope too.
- Verification: contracts 206/206 (new test: current-version supersession carries reason, reasonless one rejected, creation-with-reason rejected, legacy history exempt); yemaya lib 57/57; nisaba adapter 11/11; svc 49/49 vs real PG; Playwright journey green post-change; schema/golden/client drift checks clean, no breaking changes vs baseline v0; checkers green.
- Marked [x]: YSD-2106.
- Commit: resolve via
git log --grep='change reason'.
2026-07-19 — Seven epistemic object types, visibly distinguished; YSD-2107 marked#
- Vocabulary —
libs/contracts/src/study/epistemic-object-types.ts: the seven §9.6 types verbatim (source-fact, detection, observation, interpretation, craft-hypothesis, creator-statement, practice-result) with per-type display metadata (unique label, §9.6 definition, provenance discipline) and the totalEPISTEMIC_RECORD_KIND_TO_TYPEmapping over registry kinds (SourceFact/Detection/Observation/InterpretationClaim/ CraftHypothesis/CreatorStatement/OutcomeEvidence). Published in the schema registry asEpistemicObjectType; exported client-safe via the new@oshun/contracts/study/epistemicsubpath (pure zod + data, no node:crypto). - Two missing types now first-class (contracts 0.5.0 → 0.6.0, additive;
SUPPORTED_STUDY_SCHEMA_VERSIONS += 0.6.0):
entities/source-fact.ts— SourceFact: subject chain (work → optional edition → asset; asset requires edition, refined), scalar value + unit, and a verification block where MECHANICAL methods (checksum-computation/media-probe/container-metadata) REQUIRE the computing processor and documentary methods (distributor-manifest/credits-text/spec-document) REQUIRE the citation — a value with neither does not parse.entities/creator-statement.ts— CreatorStatement: speaker (name + credited role), verbatim-vs-paraphrase flag, MANDATORY citable source (paratext kind enum + citation + year/month/day-granular publicationDate regex), optional anchors; model-extracted statements cannot be bornaccepted(refined). Contradicting statements about the same work coexist as separate records (proposal §14) — tested.- Canonical + negative + zodOnly example sets for both, goldens/schemas/ client regenerated (70 artifacts, 253 golden cases), ids SourceFactId/CreatorStatementId.
- Producers, all real — lib
record-interpretation.ts/record-craft-hypothesis.ts/record-creator-statement.tsmirror record-observation's referential checks (project active, every cited anchor/claim/work exists and is current);ingest-local-source.tsmaterializes four SourceFacts (sha256, byteSize, container, durationSeconds) from the SAME identify/probe outputs the records were built from, each naming the real tool — FileIdentifier/MediaProbeReport ports gainedtoolidentity; the service adapters reportnode:crypto sha256 stream@ process.version and the ACTUALffprobe -version(read once, cached, fail-loud on unparseable output). - Service — routes POST /api/study/claims, /craft-hypotheses,
/creator-statements; ingest response + trail carry the new records (one array
per type, never merged); migration
0004_study_epistemic_record_families.sqladds craft_hypothesis / creator_statement / source_fact with the full 0001 discipline (envelope block, one-current index, tenant RLS, immutability + no-delete triggers, work-id extracted columns); postgres store get/put/list per family; deletion fans out (claims citing dead anchors, hypotheses deriving from them, statements/facts of the deleted work all tombstone — integration- asserted); export document embeds the four arrays; audit/event kinds claim-recorded (emitter corrected), craft-hypothesis-recorded, creator-statement-recorded, source-facts-recorded. - UI, the "visibly distinguish" half —
components/studio/EpistemicTypeBadge.tsx: per-type badge (label is the distinction, color redundant) + seven-entry legend with definitions, provenance, and live counts; zero counts render "None recorded in this project." — honest absence for detection/practice-result (no analyzer, no practice surface exists). StudyWorkspaceApp gains Interpret / Craft hypothesis / Creator statement forms and the badged "Epistemic record trail" listing facts, observations, claims, hypotheses, statements — each entry carries EXACTLY its own badge. - Verification: contracts 219/219 (new epistemic-object-types.spec: 7 types in proposal order, unique labels/definitions, mapping total + covered, epistemicIdentity literals agree; SourceFact/CreatorStatement domain rules); yemaya lib 66/66 (epistemic-records.spec: facts carry real probe values + tool, referential refusals, contradiction coexistence, export arrays); svc 50/50 vs REAL postgres+MinIO+ffprobe (route journey, facts on ingest with live ffprobe version asserted, trail-across-restart per-type arrays, deletion tombstones all four families); web e2e 3 specs green incl. NEW studio-study-epistemic-types.spec.ts (five live types badged 1:1, legend of seven, honest zeros, axe clean) + walking-skeleton + a11y regressions; adversarial stub scan of every new file: zero hits.
- Scope note: detection and practice-result are contract-implemented (Detection entity, OutcomeEvidence practice-result literal, published examples/goldens) and visibly distinguished in the legend; live producer pipelines for them belong to the analyzer sections (YSD-2104/2105, §5) and the practice loop (§12) and remain honestly absent here.
- Marked [x]: YSD-2107.
- Commit: resolve via
git log --grep='epistemic object types'.
2026-07-19 — Anti-promotion enforcement across layers; YSD-2108 marked#
- The rule enforced: no layer may silently promote a detection to an interpretation or an interpretation to creator intent. Mechanism: every layer that carries an epistemic record carries its type EXPLICITLY, and the contracts make cross-type relabeling structurally unparseable.
- API layer — kind-specific request/response wrappers (claim / hypothesis /
statement; per-type trail+export arrays from YSD-2107). NEW
epistemic-promotion.spec.tsproves the walls: a Detection fixture fails Observation/InterpretationClaim/CreatorStatement schemas (and every other cross-kind pairing fails likewise); a CreatorStatement in the trail'sclaimsarray refuses to parse; a hand-edited epistemicIdentity literal cannot cross kinds. - Search layer (the gap this item closed) —
search-study-records.tsnow covers interpretation claims, craft hypotheses, and creator statements with the SAME rights-before-search rule (claims by cited permitted anchors, hypotheses by included claims or permitted anchors, statements by permitted work — a statement about an ungranted work records legally but never surfaces, tested). Every epistemic hit carrieskindAND canonicalepistemicType; SearchResponseSchema's superRefine REJECTS a mislabeled hit, an epistemic hit with the label omitted (silence is the promotion vector), and an epistemic label on a structural work/segment hit. Contracts 0.6.0 → 0.7.0 (additive enum/field extension). - UI layer — enforced by YSD-2107's badge discipline: the e2e asserts every rendered record carries EXACTLY its own type badge (1:1), interpretations display their governed epistemic status, and creator statements display speaker + paratext provenance.
- Export layer — per-type arrays with full record payloads (epistemicStatus / epistemicIdentity intact), typed-array cross-parse refusal proven in the promotion spec; integration suite asserts the downloaded document's four arrays.
- Synthesis layer — the only synthesis surfaces today are human-authored Nisaba notebooks/cards (citing anchors, optional claimId LINK — a reference, never a copy) and the export document; no automated synthesis exists, and any future one exchanges these same typed contracts.
- Analytics layer — the audit/event channel keeps ONE distinct kind per epistemic producer (observation-recorded / claim-recorded / craft-hypothesis-recorded / creator-statement-recorded / source-facts-recorded; Detection and OutcomeEvidence have no producer yet and honestly no event kind) — asserted distinct and registered in STUDY_WORKSPACE_EVENT_KINDS, so no aggregate can merge two types into one bucket. Model-authorship walls re-proven as a battery (model observation refused; model claim pinned to model-suggested; model-extracted statement cannot be born accepted).
- Verification: contracts 233/233 (14 new promotion tests); yemaya lib 69/69 (3 new search-labeling tests incl. rights suppression and claim-id-appears-only-as-interpretation); svc 50/50 vs real stack (HTTP search leg returns the claim as kind=interpretation with epistemicType, never flattened); web e2e re-run green at 0.7.0; adversarial stub scan clean.
- Marked [x]: YSD-2108.
- Commit: resolve via
git log --grep='anti-promotion'.
2026-07-19 — Manual alignment-map authoring + review; YSD-2130 marked#
- Authoring —
use-cases/author-alignment-map.ts: a human states correspondences across ALL EIGHT classes (exact, approximate, reordered, added, removed, rescored, regraded, unmapped — the contract's kind-specific range rules from YSD-2024 apply per row). The use case adds the referential half: both editions must exist and belong to the SAME work; endpoint technical-revision hashes are read from the STORED editions (never caller-supplied); every stated frame range must fit the edition's real frame count (typed AlignmentAuthoringError → HTTP 400); manual correspondences carryconfidence: missing/not-applicable(a human judgment is not a machine probability, YSD-2006). Gaps are DERIVED byuncoveredSourceRanges(sort + cursor-walk interval complement over [0, durationFrames)): source frames no correspondence addresses become explicit gap ranges (YSD-2135) — unit-tested against overlapping, empty, and full coverage. - Review — the map is born
suggested;reviewRecordgained thealignment-mapfamily (same review-gate path: revision pin, from-state match, legal transition, human acceptance authority, next revision + immutable event). Stale pins areReviewApplicationError→ HTTP 409 review_conflict. Deletion fan-out: maps tombstone with either endpoint edition. - Service/contracts — routes POST /api/study/alignment-maps, GET /projects/:id/alignment-maps, POST /api/study/reviews (reviewer is ALWAYS the authenticated session user — a client cannot review in someone else's name); postgres listAlignmentMapsByProject; contracts 0.8.0 with AuthorAlignmentMapBody/ReviewRecordBody/ AlignmentMapResponse/ReviewOutcomeResponse; regenerated client/ schemas/goldens.
- UI — the Cross-edition alignment section (appears once a second edition exists): source/target edition selects, a draft-correspondence form over the eight kinds (added drops the source side, removed/ unmapped drop the target side), the drafted list, one-click authoring, and the map list showing kinds, review state, "unaddressed source frames: N–M" (the YSD-2135 surface), and an Accept button that runs the review gate and disappears once accepted.
- Verification: lib 76/76 (uncoveredSourceRanges unit cases; eight-kind authoring with stored hashes + derived gap; cross-work and out-of-bounds refusals; review accept → revision 2 + event; stale-pin refusal; tombstone-with-editions); svc 51/51 vs real postgres+MinIO (two real ingests → author over HTTP → derived gap in response → 400 out-of-bounds → accept 200 {accepted, revision 2} → stale 409 → list shows accepted; delete tombstones edition_alignment_map); web e2e NEW studio-study-alignment-maps.spec.ts 1/1 (draft flow, gap surfaced as text, accept via review) + walking-skeleton/a11y/ epistemic-types all green; adversarial stub scan clean.
- Marked [x]: YSD-2130.
- Commit: resolve via
git log --grep='alignment map'.
2026-07-19 — Assisted alignment proposals behind method adapters; YSD-2131 marked#
- The seam —
ports.tsProposalMethodAdapter: each of the four §9.7 methods is an adapter that returns REAL proposed correspondences with the computing tool's identity, or an honest typed refusal (unavailable+ exact reason).propose-alignment-map.tsorchestrates: rights BEFORE bytes (signal extraction reads stored media, so the work needs amodel-analysisgrant — a denial is audited as alignment-proposals-denied and NO byte is read/presigned, adapter untouched, lib-asserted); same-work + frame-domain validation; every proposed correspondence carries UNCALIBRATED confidence (raw method score + native scale — no calibration set exists, YSD-2006/2105) and is bornsuggestedinto the YSD-2130 review gate. An empty adapter result refuses rather than shipping a map that reads as "no differences". - Two REAL methods (
media/proposal-adapters.ts):- audio-fingerprint: ffmpeg's chromaprint muxer emits raw 32-bit sub-fingerprints (11025 Hz mono; hop = 1365/11025 s — a constant of the algorithm; the ~2.6 s warm-up shifts both editions equally and cancels out of offsets); a hamming-distance offset scan minimizes the bit-error rate with ties resolved toward maximal overlap then zero-offset (self-similar audio would otherwise pin to an arbitrary equal-scoring shift — found by test); match floor BER 0.25 (unrelated audio plateaus ≈0.28–0.5), exact ceiling 0.05, added/removed tails beyond 0.5 s. Proven on real media: identical recording → one exact 240-frame match at BER exactly 0; a 4 s-trimmed copy located at its true offset (removed head 84–108 frames bracketing 96) using seeded pink noise (a chirp was chroma-ambiguous — octave folding — also found by test); tonal-vs-noise refuses at the floor (no false correspondences, the YSD-2137 property).
- shot-structure: ffmpeg scdet scene-change timestamps → shot lists → two-pointer duration-tolerance alignment (exact within 2 frames, approximate within max(0.25 s, 10%), one-sided shots emitted as removed/added — never interpolated). Proven: a 3-shot splice self-aligns as three exact pairs at the real cut frames [0,48,96]; a shortened cut missing its middle shot reports exactly one removed.
- Two honest, data-grounded refusals: transcript-alignment checks the REAL store for subtitle tracks on both editions and refuses naming the edition without one (and refuses even when both exist, because no token-level aligner ships in this deployment — stated verbatim); telemetry-landmarks refuses (no telemetry sessions exist in this workspace). Both surface as HTTP 422 proposal_unavailable with the reason.
- Service/UI — POST /api/study/alignment-maps/proposals (contracts 0.9.0,
generated client); production + test wiring registers all four adapters. UI:
"Authorize signal analysis" button (explicit model-analysis grant —
deny-by-default stays visible), method select + "Propose correspondences";
proposals list beside manual maps tagged with method and "raw score N on
— uncalibrated, not a probability" (YSD-2105 display rule), and accept through the SAME review gate. - Verification: adapter spec 8/8 over real synthesized media (identity, true-offset trim, false-correspondence refusal, real cut alignment, dropped-shot detection, planted-offset math, tie-break); lib 79/79 (rights-first with adapter-untouched assert, unavailable refusals, uncalibrated mapping); svc 60/60 vs real stack (403 before grant → real chromaprint 201 exact/BER-0 → shot-structure 201 → transcript+telemetry 422); web e2e alignment journey 1/1 including the denied → authorize → propose → accept arc + other three suites green (one a11y flake passed clean on re-run); contracts 233/233.
- Marked [x]: YSD-2131.
- Commit: resolve via
git log --grep='alignment proposals'.
2026-07-19 — Build/patch maps + revalidation tasks; YSD-2136 marked#
- Build/patch editions are first-class ingests — IngestBody/-command gain
editionKind+platform(contract already enforces platform on game-build/patch, YSD-2074); a build's frame-addressable asset is its gameplay CAPTURE, so cross-build alignment maps ride the existing YSD-2130 authoring and YSD-2131 proposal machinery unchanged (integration-proven: build-capture editions author + accept maps like any others). - RevalidationTask (contracts 0.10.0) —
entities/revalidation.ts: work, triggering edition, subject family (edition_alignment_map | interpretation_claim), subject record, reason, open/resolved with refined resolution presence (resolved requires the resolver + note + time; open forbids them). Registry + canonical/ negative/zodOnly examples + goldens. - Open on build change — ingest of a game-build/patch edition for a work
with prior editions opens one task per ACCEPTED (or corrected) alignment map
touching the work's prior editions and per accepted claim citing their
anchors. Suggested records are NOT flagged (never trusted), non-build editions
(remaster tested) open nothing, tasks are opened never auto-applied, audit
revalidation-tasks-opened. - Resolution is a human act —
use-cases/resolve-revalidation-task.ts: supersedes the task to resolved with the resolver's note; NEVER mutates the flagged record (asserted: the accepted map stays accepted — record changes go through the review gate separately); double-resolve is a typed 409; auditrevalidation-task-resolved. Deletion fan-out: tasks about a deleted work tombstone with it. - Service/UI — migration
0005_study_revalidation_tasks.sql(full 0001 discipline + work_id column); routes GET /projects/:id/revalidation-tasks + POST /revalidation-tasks/:id/resolution; ingest response carries the opened tasks. UI: edition-kind select + platform on the ingest form; a Revalidation tasks section listing open/resolved tasks with reasons and a "Mark re-checked" resolve flow. - Verification: contracts 233/233; lib 82/82 (revalidation.spec: flags exactly the accepted map + accepted claim and not the suggested claim, remaster opens nothing, resolve-once semantics with untouched flagged record); svc 61/61 vs real postgres (patch capture over HTTP opens exactly one task for the accepted map, resolve 200 → 409, list shows resolved; deletion tombstones revalidation_task); web e2e alignment journey extended (patch ingest → "2 revalidation task(s) opened" → resolve with note → resolved with note shown) — one transient 401 session flake retried clean, then 2/2 consecutive clean runs; adversarial stub scan clean.
- Marked [x]: YSD-2136.
- Commit: resolve via
git log --grep='revalidation'.
2026-07-19 — Transactional aggregate writes; YSD-3006 marked#
- The seam —
StudyWorkspaceStore.inTransaction(fn)(ports.ts): every write made through the store handed tofncommits together or not at all; reads inside see the transaction's own writes.- Postgres (
postgres-store.ts): binds a transaction-scoped store over the pg transaction client; the bound store's ownwithTransactionruns inline soputRevisioned's supersession mark + insert join the outer transaction instead of issuing a nested BEGIN. Constructor now takes the structuralStudyDbClientsurface (satisfied by PostgresClient and the tx adapter) so every store method runs identically inside and outside a transaction. - In-memory double: real snapshot/restore rollback across every collection (records are immutable, so shallow map copies are full snapshots) — the double now REFUSES to keep partial writes, same as the database.
- Postgres (
- Wrapped aggregates (each names its orphan class):
- ingest: edition + asset + tracks + source facts + revalidation tasks + the work's next revision in ONE transaction (bytes land in object storage first — records only ever describe bytes that exist); the YSD-2136 task-opening reads run inside the same transaction.
- delete-source: the ENTIRE tombstone graph (observations, claims, hypotheses, statements, facts, maps, revalidation tasks, links, anchors, tracks, assets, segments, editions, grants, work) plus the export-ledger marks in one transaction — a failure part-way can never leave anchors alive on a tombstoned edition.
- review-record: the next revision and its immutable review event commit together — no reviewed record without its event.
- expiry sweep: each asset's track tombstones commit with the asset's.
- Single-record writes were already atomic via putRevisioned's existing mark+insert transaction.
- Verification: lib 85/85 — new
transactions.spec.tsinjects failures at the store seam: a failing asset write aborts the whole ingest (zero editions/assets/tracks/facts, work still revision 1 with no editions); a failing event append aborts the review supersession (record current at revision 1, zero events); the success path commits everything. Svc 62/62 vs REAL PostgreSQL — new integration case: rollback leaves no rows, commit lands both records with read-your-writes inside the transaction, and a supersession inside a transaction rolls back cleanly on a later failure (no nested-BEGIN error). Full walking-skeleton integration suite green over the same wrapped code paths; adversarial stub scan clean. - Marked [x]: YSD-3006.
- Commit: resolve via
git log --grep='transactional aggregate'.
2026-07-19 — Keyset pagination, total ordering, bulk ops; YSD-3007 marked#
- Cursor pagination —
PageRequest { after: {createdAt, id}, limit }on every project-scoped list (anchors, observations, claims, hypotheses, statements, facts, links, maps, revalidation tasks) and segments-by-edition. Postgres implements the keyset as(created_at, id) > ($cursor)+ORDER BY created_at, id+ LIMIT — an index range scan, never scan-and-sort; the in-memory double implements the SAME total order and slicing so lib tests exercise identical semantics. Omitting the page keeps the full list for the flows that must see everything (export, deletion) — no silent caps. - Deterministic ordering — every list gained the
idtiebreak (created_at, id), a TOTAL order stable across runs and stores; segments keep their unpaged(orderKey, created_at, id)order and now read the extractededition_idcolumn (FAMILY_EXTRAS entry added; migration 0006 backfills rows written before it). - Bulk operations —
tombstoneMany: per-family set-based soft-delete → tombstone transitions (id = ANY($1::uuid[])) plus ONE multi-rowunnestinsert into deletion_tombstone, all in one transaction — delete-source now collects its whole graph and issues a single bulk call inside its YSD-3006 transaction instead of a round trip per record. - Bounded query plans — migration
0006_study_keyset_pagination.sql: composite partial indexes(project_id, created_at, id)over CURRENT rows for the nine project-scoped families and(edition_id, created_at, id)for segments, matching the keyset order exactly. (Explain-plan assertions are YSD-3009's item.) - Verification: svc 64/64 vs REAL postgres — new integration cases: 25
same-timestamp anchors (only the id tiebreak can order them) walk at limit 10
as 10/10/5 with exact-once coverage, identical order on re-walk, and match the
unpaged list; tombstoneMany over 8 records clears reads and writes exactly 8
tombstone rows (uuid[] casts — record_id is uuid, found by test). Lib 85/85
(paged double + bulk delegation under the deletion/atomicity suites).
Walking-skeleton e2e 3/3 through the UI delete arc. Transient e2e 401s traced
to the KNOWN stale
signup-verification-stateadmin-store row (rewritten by a parallel worktree's BFF under a different HMAC secret); deleted the singleton row per the documented remedy and the journey passed clean. - Marked [x]: YSD-3007.
- Commit: resolve via
git log --grep='keyset pagination'.
2026-07-19 — Explain-plan and index-coverage tests; YSD-3009 marked#
- The suite —
query-plans.integration.spec.tsagainst a REAL PostgreSQL: seeds realistic volume (600 anchors with staggered timestamps behind a real edition row — the referential trigger refuses orphan anchors even in seeds — plus 300 edges, 200 grants, 120 segments, 60 export-ledger rows), runs ANALYZE, then asserts EXPLAIN (FORMAT JSON) plans by walking the node tree. - Two assertion modes: coverage cases run inside ONE transaction with
SET LOCAL enable_seqscan = off(a pooled SET lands on a different connection than the EXPLAIN — found while building) and assert the intended index QUALIFIES with zero Seq Scan nodes on the hot table; the timeline-window case leaves the planner FREE and proves it prefersevidence_anchor_keyseton its own at volume, with NO Sort node — the index delivers keyset order directly. - The six hot paths of the checklist item: timeline window (keyset tuple-compare + LIMIT → evidence_anchor_keyset, planner's own choice); anchor lookup (current-revision by id → one_current/pkey); graph path (project edges → cross_reference_keyset; endpoint expansion → cross_reference_endpoint_a); rights impact (grants by work → rights_grant_work); deletion impact (anchors on the edition → evidence_anchor_edition; live export references → export_reference_project); project resume (tenant project list → study_project_tenant_project; edition segments → study_segment_keyset).
- Verification: 6/6 plan tests green; full svc suite 70/70 vs real postgres (uuid[] casts on export work_ids found by test); adversarial scan clean. Plans are asserted from the database's own EXPLAIN output — nothing is inferred from source reading.
- Marked [x]: YSD-3009.
- Commit: resolve via
git log --grep='explain-plan'.
2026-07-19 — Notebook publication rights gate; YSD-2104 marked#
- The gap — seven of the eight YSD-2104 evidence surfaces were already
grant-bound (display via the playback gate + doubly-gated representations,
processing via the ingest/
model-analysisgates, collaboration via share-project, embedding via representation gate 1's honest refusal, export via export-study-trail, retention via the grant-driven expiry sweep, transfer via transfer-project). Publication was NOT: notebooks were bornprivatewith no state transition anywhere, so thepublicationaction existed in the contract with no surface consuming it. - The surface —
set-notebook-publication.ts(workspace lib): widening a notebook's audience is a rights event because notebooks carry evidence (citations + anchor-view/image-region embeds). Escalation toproject-sharedneedscollaboration, topublishedneedspublication, on EVERY distinct cited work, evaluated live at change time; narrowing is always allowed; both outcomes audited (notebook-publication-changed/-deniedwith per-work reasons). Session-anchored and vanished anchors deny-by-default. Author-or-owner may change state; idempotent same-state is a no-write no-audit success. Uncited publication refuses as a typed 400 BEFORE rights (Nisaba's cite-at-least-one rule, surfaced as policy). - Nisaba keeps notebook truth — adapter gains
getNotebook+setNotebookPublicationStatepersisting the change as a superseding revision throughStudyNotebookSchema.parsein Nisaba's own store; the workspace consumes both through aNotebookPublicationPortseam. - Route + client —
POST /api/study/notebooks/:notebookRef/publicationadded to the manifest (SetNotebookPublicationBodySchema), the service, and the regenerated client; schema publication + goldens verified drift-free. - Verification — lib 96/96 (11 new: denial reasons, collaboration vs
publication separation, embed gating, lapsed-grant live re-check, narrowing
after lapse, uncited refusal, dead-anchor denial, actor rules, idempotency,
unknown/cross-project refusal); adapter 12/12 (supersession, uncited-publish
parse refusal, unknown/unavailable outcomes); svc 71/71 vs REAL postgres+MinIO
including the denied→granted→published journey and a generated-client
narrowing step; contracts 233/233; all five typecheck configs clean (also
repaired pre-existing
tscbreaks indelete-source.ts/in-memory-deps.tsand two spec-config errors); architecture/stub/traceability gates green; adversarial grep clean. - Marked [x]: YSD-2104.
- Commit: resolve via
git log --grep='notebook publication'.
2026-07-19 — Confidence display for the full YSD-2006 union; YSD-2105 marked#
- The gap — the workspace UI displayed only the
uncalibratedarm of the confidence union (raw score + "not a probability", asserted by the alignment e2e). Calibrated values had no display treatment at all, andmissingrendered as NOTHING — visual absence a reader can misread as unremarkable/high confidence, exactly what YSD-2105 forbids. - The component —
StudyConfidenceIndicatorrenders every status: calibrated is the ONLY arm shown as a probability and always carries its uncertainty interval (coverage + bounds), calibration method, applicability boundary, and the framing "a machine estimate of detection reliability, not interpretive certainty"; uncalibrated keeps the exact native-scale caveat sentence the e2e asserts; missing renders its typed reason ("no machine confidence — not applicable / not yet calibrated / imported without lineage / processor did not report one"). Wired into the alignment-map list for ALL correspondences — manual maps now visibly show their explicit confidence absence instead of nothing. - Verification — study web suite 10/10 (new spec: exact percent arithmetic 0.555→55.5%, framing text, native-scale caveat with a no-percent-sign assertion, all four missing reasons); web typecheck clean for the changed files (remaining reports are pre-existing isis/ai-providers breaks outside this surface); alignment e2e's asserted uncalibrated sentence preserved verbatim by the component.
- Marked [x]: YSD-2105.
- Commit: resolve via
git log --grep='confidence display'.
2026-07-19 — Migration rollout discipline + dev seed; YSD-3008 marked#
- Already in place — forward-only checksummed runner (drift = hard error), rollback/forward-fix headers on all six migrations, idempotency + drift integration tests, the e2e stack bootstrap. Missing: dev seed data, schema compatibility tests, zero/low-downtime rollout verification.
- Runner — every migration transaction now runs under
SET LOCAL lock_timeout(default 5s,MigrationOptions.lockTimeoutMs): a rollout queueing behind a held lock aborts fail-fast — transactionally unrecorded and retryable — instead of blocking every later reader/writer behind its own lock request. - Seed —
scripts/seed-dev-data.ts: one coherent, contract-valid project (22 records, fixed UUIDs, tenanttenant-dev-seed) through the REAL PostgresStudyWorkspaceStore: broadly-granted + expired-grant works, link-only stream-reference assets (no fabricated bytes), tracks, scene/shot hierarchy, anchors, observations, claim, creator statement, contrast link. Idempotent by check-then-skip — the schema forbids DELETE by trigger. - Rollout suite —
migrations-rollout.integration.spec.ts, entirely on scratch DATABASES it creates/drops: (1) fresh-replay schema inventory — all 25 record families with the full envelope block, one_current + keyset indexes, immutability/no_delete/anchor/review/audit triggers; (2) held-lock fail-fast — ACCESS EXCLUSIVE blocker, 400ms cap, unrecorded, clean retry; (3) 0006 applied over 1500 anchors + 300 payload-only segments WHILE a live writer inserts into the very table being indexed — zero writer errors, max single-write stall < 2s, backfill verified row-complete; (4) fresh-vs- stepwise databases converge to IDENTICAL schema inventories; (5) seed loads- re-run is a no-op.
- Suite hygiene fixed en route — query-plans (YSD-3009) had been passing
against contaminated shared-DB state: isolated on its own scratch database its
'deletion impact' and 'project resume' assertions FAILED (single-valued
columns made rival indexes cost-identical; study_project had zero rows).
Re-seeded selectively (200 projects/20 tenants, 10 editions × 10 works) so
every plan choice is meaningful; now deterministic. Spec files for this
service run serially (
fileParallelism: false) — postgres-store and the walking skeleton both reset the shared study schema and raced under file parallelism (18-test flake reproduced, then eliminated). - Verification — svc suite 75/75 twice consecutively (6 files, serial); rollout suite deterministic across repeated runs; tsc app config clean; architecture/stub/traceability gates green.
- Marked [x]: YSD-3008.
- Commit: resolve via
git log --grep='migration rollout'.
2026-07-19 — Object namespace grammar + stored metadata; YSD-3020 marked#
- The module —
policies/object-namespaces.ts(workspace lib): a canonical, tenant-first, fully PARSEABLE key grammar for all eleven object classes the item names (originals, quarantine, proxies, thumbnail/crop/waveform representations, stems, analysis artifacts, learner attempts, exports, temp), with typed builders that refuse malformed segments (path traversal, non-uuid subjects, uppercase hashes, unsafe slugs), a parser that round-trips every class, and a retention-class map (source-bound / quarantine / derived / learner-owned / export / temporary) that the YSD-3023 lifecycle policies will attach to. - Metadata contract —
StudyObjectMetadataSchema(class, tenant, retention class, subject record, content type) derived from the canonical key alone; the service storage adapter now REFUSES writes outside the grammar and stamps every stored object with this metadata (YSD-3024 extends the stored set with derivation/key-version/policy state). - Wired call sites — ingest originals, representation sidecars, and trail exports all build keys through the module; the historical key shapes are pinned byte-for-byte by test so existing stored objects stay addressable.
- Verification — lib 113/113 (17 new: per-class round-trips, historical shape pins, malformed-segment refusals, out-of-grammar parse rejections, metadata derivation, total retention-class coverage); full svc suite 75/75 against real MinIO — every real write passes the new enforcement; all typecheck configs clean; stub/architecture gates green.
- Marked [x]: YSD-3020.
- Commit: resolve via
git log --grep='object namespace'.
2026-07-19 — Storage security: SSE, tenant isolation, presign ceiling; YSD-3021 marked#
- Encryption —
STUDY_S3_SSE(none|AES256|aws:kms) +STUDY_S3_SSE_KMS_KEY_IDflow through every upload via the platform client's ServerSideEncryption/ SSEKMSKeyId plumbing; config fails fast when aws:kms lacks a key and when a PRODUCTION deployment with S3 configured leaves SSE at 'none' — encrypted at rest is not optional in production. - Tenant isolation, two layers — (1) auth:
requireAuthnow takes the deployment's bound tenant; a validly-signed token for any other tenant is a 403tenant_mismatchBEFORE any route logic (previously a cross-tenant token would have operated this deployment's stores under the caller's claimed tenant — found while reading the wiring for this item); (2) storage: the adapter parses every key through the YSD-3020 grammar and refuses any key whose tenant differs from the deployment's before putFile/putJson/presign/ delete touch storage. - Short-lived URLs —
STUDY_PRESIGN_MAX_SECONDS(default 600, hard cap 3600) enforced in the adapter: a presign request above the ceiling is refused, never granted. All production presigns are 300–600s. - Rights re-check per URL decision — verified at all four presign sites: playback (live authorizePlayback before presign), representations (double gate incl. live re-evaluation), exports (per-work evaluation at document creation), alignment proposals (model-analysis grant before source bytes are presigned). Each is covered by existing lapse/denial tests.
- Verification — app.spec 23/23 (3 new: foreign-tenant 403 + own-tenant pass, SSE config rules, adapter refusals against an unroutable endpoint proving refusal precedes any storage call); full svc suite 78/78 vs real postgres+MinIO; tsc clean; stub/architecture gates green.
- Marked [x]: YSD-3021.
- Commit: resolve via
git log --grep='storage security'.
2026-07-19 — Media limits at ingest + derivative cap; YSD-3022 marked#
- The policy —
policies/media-limits.ts: one STUDY_MEDIA_LIMITS object covering container allowlist (mp4/quicktime/matroska/webm/mxf), an explicit archive refusal list (zip/gzip/tar/7z/rar/zstd/bzip2 — refused because the workspace HAS no decompression path, closing the decompression-bomb surface by construction), 64 GiB size, 6 h duration, video codec allowlist (h264/hevc/vp8/vp9/av1/prores/mjpeg/ffv1/mpeg2video/dnxhd), audio codec allowlist, 8K-DCI pixels per frame, 2 592 000 frames, and a 4K-UHD derivative-resolution ceiling. - Enforcement order — declared-content-type checks run BEFORE the file is read (proved by an identify-call counter of zero); probed checks (size, duration, codecs, pixels, frame count — every violation collected and named) run after the REAL probe and before any byte reaches storage; refusals are audited as ingest-denied and the route maps MediaLimitError to 422 media_limits with the violation list.
- Derivative cap — extractFrame now ALWAYS receives a pixel cap: the
platform ceiling tightened by a stricter grant limit (
derivativePixelCap), never widened by a looser one. - Verification — lib 121/121 (8 new: archive-before-read with audit + zero storage, non-media container, oversize, five-violation battery, healthy pass-through, exact-at-limit acceptance, cap tightening); full svc suite 78/78 vs real stack (the real ffmpeg film passes the limits); typechecks clean; stub/architecture gates green.
- Marked [x]: YSD-3022.
- Commit: resolve via
git log --grep='media limits'.
2026-07-19 — Lifecycle policies: object age sweeps + legal hold; YSD-3023 marked#
- Already in place — source expiry + revocation-based retention arithmetic (sweepRetentionExpiry counts from validUntil OR revokedAt) and confirmed deletion (delete-source). This item added the missing layers:
- Age-driven object sweep —
sweep-object-lifecycles.tsdeletes processing scratch (tmp/, > 24 h) and expired quarantine uploads (> 14 d) per OBJECT_LIFECYCLE_WINDOWS; ONLY keys that parse as those two classes are ever age-deleted (a malformed key smuggled under the prefix is held, deny-by-default); rides the /expiry-sweep route + timer; audited with exact counts; StudyObjectStore gainedlist(paginated in the S3 adapter, tenant-guarded). - Legal hold —
manage-legal-hold.ts: hold/release the work's WHOLE copy graph (work/editions/assets/tracks) in one transaction, audited with reason; POST /works/:workId/legal-hold + regenerated client. Hold STATE lives in the deletion-state COLUMN (payloads are immutable — reading envelopes for it would be silently wrong in production; caught during implementation), read back via the new store.listLegalHolds graph query. While held: delete-source refuses (423 legal_hold, audited deletion-blocked) and the retention sweep skips the work however long expired; release restores both paths. - Two real bugs found by the new real-PG test — (1) media_track.asset_id was NEVER filled (FAMILY_EXTRAS entry missing) — added + migration 0007 backfills from payload; (2) tombstone()/tombstoneMany() on a held row silently no-opped both transitions and STILL wrote the deletion-ledger row — a false deletion claim; both now verify no current row survived and abort the transaction (the 0001 trigger backstops at the database).
- Verification — lib 125/125 (4 new lifecycle tests incl. hold-blocks- deletion-then-release and hold-outranks-retention-clock); store integration 19/19 incl. the column-truth hold graph + trigger-refusal test vs REAL PG; full svc suite 79/79; schemas/goldens stable, client regenerated; all typechecks + gates green.
- Marked [x]: YSD-3023.
- Commit: resolve via
git log --grep='lifecycle policies'.
2026-07-19 — Per-object stored identity metadata; YSD-3024 marked#
- The contract —
StudyStoredObjectMetadataSchemaextends the YSD-3020 base with contentSha256 + byteSize (exact-byte identity, computed AT THE STORAGE SEAM from the very bytes uploaded — never caller-claimed), encryption-key version ('none' | 'AES256' | 'aws:kms:' from the adapter's own SSE config), and the writer's provenance: sourceRevision, derivedFrom (derivation-graph edge: object:<key>orrecord:<family>:<id>), rightsGrantIds (policy state at write time), and a typed content-credentials state (not-inspecteduntil Section 4.6 ships the real C2PA inspector;present:<manifestRef>supported). Lossless flatten to the stored string map. - The writers — ingest stamps the original as its own source revision under
the covering grant; representations stamp the anchor's technical revision,
object:<original key>parentage, and the deciding grant; exports stamp the project revision and EVERY grant that admitted a work. A provenance-less write (scratch) stores identity + base only — nothing fabricated. - Verification — lib 127/127 (flatten round-trip incl. kms + present- credentials arms, junk-identity refusal, ingest-provenance assertion); svc 79/79 vs real stack including a NEW end-to-end assertion that the MinIO object's stored metadata carries the exact-byte sha equal to the edition's content hash plus derivation/rights/credential/encryption fields; typechecks + gates green.
- Marked [x]: YSD-3024.
- Commit: resolve via
git log --grep='stored identity metadata'.
2026-07-19 — Storage-security scenario tests; YSD-3025 marked#
- New real-MinIO suite —
storage-security.integration.spec.ts(per-run bucket, reachability-gated, hard-fails under STUDY_PG_REQUIRED): (1) cross-tenant isolation — tenant B's store can neither read, write, list, nor delete tenant A's namespace while A serves its own bytes; (2) stale signed URL — a 1-second presign serves 200, then 403 after expiry against real MinIO; (3) partial upload — a failed upload leaves NO readable object (client.exists false); (4) restore — a deleted key 404s, then a re-upload serves the exact original bytes again. - New lib case — interrupted delete: an injected storage failure on the first delete aborts deleteSource with records intact, tombstones empty, and bytes still claimed; the identical retry completes to full deletion.
- Already covered, verified by reading — revoked-grant denial (rights-gate reason battery + walking-skeleton revocation path + revocation-based retention arithmetic in deletion-expiry); cross-tenant AUTH refusal (app.spec 403 tenant_mismatch); upload-failure transactional abort (transactions.spec).
- Verification — storage-security 4/4 vs real MinIO; lib 128/128; full svc suite 83/83 (7 files); typechecks + stub scan green.
- Marked [x]: YSD-3025 — Section 3.2 rights-aware object storage is now COMPLETE (3020–3025).
- Commit: resolve via
git log --grep='storage-security scenario'.
2026-07-19 — FTS search projection over a transactional outbox; YSD-3040 + YSD-3045 marked#
- Stack per ADR-0074 — Postgres FTS, no external engine: migration 0008 adds
study.projection_outboxandstudy.search_document(weighted tsvector + GIN, facets jsonb + GIN, tenant/project scope, and the work_ids each document's VISIBILITY depends on). - Outbox (3045) — every putRevisioned (rev-1 and supersession), tombstone, and tombstoneMany now appends an outbox row IN THE SAME TRANSACTION as the authoritative write: late is possible, silent loss is not. Failures are per-row recorded (attempts + last_error) and retried; a poisoned row never blocks the queue and is never dropped.
- Projector + query (3040) — search-projector.ts maps the SAME six families/text fields as the authoritative live search ("the projection replaces the scan, not the rights rule"); anchor-grounded records carry the works of their cited anchors. searchProjection() evaluates LIVE grants for exactly the works each matched document depends on before returning hits or facet counts — the index accelerates matching and can never widen visibility; epistemic hits stay labeled (YSD-2108 at the projection layer). Rebuild deletes a tenant's documents, re-enqueues every current record, and drains to convergence (resumable — the queue is durable). Route POST /api/study/projection-drain + timer cadence + generated client.
- Verification — new scratch-DB suite 6/6 vs real PG: outbox-per-write, drain to the exact expected per-family document counts, expired-grant work suppressed from hits AND facet counts (matched-but-suppressed counted), kind facet narrowing without leakage, supersession retitles + tombstone removes, injected-failure row recorded then retried to completion, rebuild converges to the byte-identical document set. Full svc suite 89/89 (8 files) incl. a generated-client drain step in the walking skeleton; contracts 233/233 with schema check stable; gates green. 3044/3046/3047/3048 stay open for the vector/graph projections, monitoring, and the full equivalence/leakage batteries.
- Marked [x]: YSD-3040, YSD-3045.
- Commit: resolve via
git log --grep='search projection'.
2026-07-21 — YSD-9030 Character identity & role model (§9.2 character/visual-design lens)#
- Contract (new):
libs/contracts/src/study/entities/character-profile.ts—CharacterProfileSchema, an enveloped study record that is the character-lens OVERLAY on the canonicalStudyEntityof kindcharacter(referenced byentityId; the identity is never re-minted — YSD-0014). New branded idCharacterProfileId(ids.ts). It adds the two things a generic entity cannot express:- Role across scenes/editions/builds —
roleAssignments(min 1: a character models identity AND role). Each is attributed, confidence-carrying, review-stated, and SCOPED:whole-work|edition(needs editionId) |segments(needs segmentIds), with the wrong-scope fields rejected by superRefine. Narrative-function SEMANTICS stay Hathor-owned via an OPTIONALtaxonomyref (domain + termRef) — no theory vocabulary is copied into Yemaya (YSD-0014). Castingprominence(lead…uncredited-extra) is the production-structural attribute the workspace owns. Documentary bases (source-metadata/creator-statement) must cite; model-authored roles cannot be born accepted. - Presentations across states/disguises/costumes/skins/transformations —
presentations, each a FACET of the one identity tied to theSourceEdition(film cut OR game build/patch — YSD-2136) it occurs in and the frames that show it (keyAnchorIds,appearsInSegmentIds).presentationKindspans costume/disguise/skin/transformation/form/state/ age-stage/recast/build-variant/other.
- Role across scenes/editions/builds —
- Anti-appearance-merge guarantee (the heart of YSD-9030, YSD-4052/4053): a
presentation attaches to the identity only via
SameIdentityBasis= narrative-continuity | credited-casting | source-metadata | creator-statement | authored-assertion. Appearance similarity / visual / biometric / model / face match are DELIBERATELY ABSENT from the enum, so two look-alikes cannot be collapsed and a disguise/transformation cannot fracture one identity — the rule is STRUCTURAL, not a bypassable runtime check.FORBIDDEN_IDENTITY_SIGNALSisAppearanceOnlyBasis()(case/spacing tolerant) let authoring seams reject a smuggled appearance justification in free text. Identity MERGES between two entities are not performed here at all — they route through the reviewed CrossReference identity path (YSD-6089). credited-casting requires the performer entity; authored-assertion requires stated reasoning; documentary bases require a citation; model-suggested presentations cannot be born accepted.
- Helpers (real behavior, tested):
editionsForProfile(cross-edition/ cross-build reach of one identity),presentationsInEdition,rolesInEdition(whole-work + edition-scoped roles in force for a cut/build). - Wiring: registered in
schema-registry.ts(CharacterProfile), exported from studyindex.ts, canonical+negative+zodOnly examples inexamples.ts.generate-study-schemas.tspublished 81 schema artifacts (was 80);generate-study-goldens.tsregenerated (283 cases). Non-route contract — the generated client is untouched. - Verification: dedicated
character-profile.spec.ts(18 tests) covers the anti-appearance-merge guarantee, every scope/basis refinement, duplicate-key rejection, and the cross-edition helpers. Full study suite 30 files / 346 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"79 contracts published, no drift, no breaking changes vs baseline v0"; golden--check"283 fixture cases stable"; adversarial stub-scan clean; eslint clean. - Marked [x]: YSD-9030.
- Commit: resolve via
git log --grep='YSD-9030'.
2026-07-21 — YSD-9270 Game-design & systems lens model (§9.10)#
- Contract (new):
libs/contracts/src/study/entities/game-system-model.ts—GameSystemModelSchema, an enveloped study record modeling a game build's designed system with evidence from play. New branded idGameSystemModelId. It captures the full vocabulary the item names:- Core loop —
coreLoop(min 1GameCoreLoopStep): ordered steps of verb → outcome → feedback → goal, withverbKey/feedbackKey/goalKeyresolving to elements of the matching kind (referential integrity enforced at the model level; a feedbackKey pointing at a verb is rejected). - The 11 element kinds —
GameSystemElementcovers verb, goal, rule, resource, constraint, feedback, risk, reward, failure, recovery, progression. Each carries a description, evidence basis, evidence tier, play anchors, hidden-state/exact-input flags, and independent review provenance.
- Core loop —
- Tier-honesty guarantee (YSD-9270, YSD-12002/12005): every element is
tiered against the shared
GameStudyTierSchemaladder (my YSD-12001):- A
video-onlyelement CANNOT assert hidden engine state or exact inputs — those are rejected at parse; they require Tier B (instrumented-session) or C (creator-owned). So a visible inference can never be rendered as engine truth. - An evidence
basisdemands a minimum tier (MINIMUM_TIER_FOR_BASIS): telemetry needs Tier B, creator-documented needs Tier C; a telemetry basis at a video-only tier is incoherent and rejected. - No element may rest on a tier the study did not reach — element
evidenceTiermust be subsumed by the modeltierviatierChain(reused from game-study-tier.ts). - Observed/instrumented elements must anchor at least one moment of play; model-authored elements cannot be born accepted.
- A
- Helpers (real behavior, tested):
elementsOfKind,coreLoopVerbKeys(ordered by step),evidenceTiersInModel,assertsBeyondApparent(a quick downstream gate: a model with any hidden-state/exact-input claim is coherent only at Tier B/C). - Wiring: registered in
schema-registry.ts(GameSystemModel), exported from studyindex.ts, canonical (minimal video-only + complete instrumented)- structural negatives + cross-field zodOnly examples in
examples.ts.generate-study-schemas.tspublished 82 artifacts (was 81); goldens 288. Non-route contract — the generated client is untouched.
- structural negatives + cross-field zodOnly examples in
- Verification: dedicated
game-system-model.spec.ts(12 tests) covers the full vocabulary, the Tier-A hidden-state/exact-input bar, the basis→tier minimums, tier-subsumption, core-loop referential integrity, duplicate-key and model-authored-accept rejection, and ordered helpers. Full study suite 31 files / 357 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"80 contracts published, no drift, no breaking changes vs baseline v0"; golden--check"288 fixture cases stable"; adversarial stub-scan clean. - Marked [x]: YSD-9270.
- Commit: resolve via
git log --grep='YSD-9270'.
2026-07-21 — YSD-13001 Typed study query AST (§13.1 hybrid search foundation)#
- Contract (new):
libs/contracts/src/study/query-ast.ts—StudyQuery, the single typed shape every study search compiles to (NL parsing YSD-13002 targets it; executors YSD-13003–13011 consume it). Non-enveloped request contract (like GraphQueryRequest). Registered asStudyQuery.- All fifteen filter dimensions the item names, as a discriminated
QueryFilterSchemaunion ondimension: source, locator, epistemic, taxonomy, text, vector, runtime, relationship, rights, permission, synthetic-media, culture, language, review, version.QUERY_FILTER_DIMENSIONSpins the set (spec asserts exactly 15). - Boolean composition — a real recursive AST:
StudyQueryNode=filter | and | or | notviaz.lazywith an explicit TS type;and/ortake ≥1 clause,notis unary.z.toJSONSchemaemits$defs/$refand the published JSON Schema validates through ajv (schema-publication.spec).
- All fifteen filter dimensions the item names, as a discriminated
- Reuse over re-modeling (YSD-0014): the epistemic (
EpistemicObjectType), review (ReviewState), synthetic-media (SyntheticMediaDisposition), rights-action (RightsAction), work-medium (WorkMedium), and entity-relation (EntityRelationKind) vocabularies are imported from the entities, so a filter can never drift from the values it filters. Vector modalities and locator kinds mirror the svc vector projection / SourceLocator discriminants. - No silent match-everything: every all-optional filter (source, locator,
rights, permission, version, runtime) rejects an empty constraint set via
superRefine; frame/time/date windows enforce start ≤ end; a
relationkind is bound to anentity-relationpath only.rights/permissionare first-class dimensions so a query can restrict — never broaden — access (YSD-13012/13013). - Helpers (real, tested):
collectFilters(leaf walk),dimensionsUsed,queryDepth(boolean nesting),constrainsAccess(does the query gate on rights/permission — the executor's pre-widen check). - Wiring: registered in
schema-registry.ts, exported fromindex.ts, three canonical examples (single filter; a composed and/or/not tree; an AND covering all remaining dimensions) + structural negatives + cross-field zodOnly examples.generate-study-schemas.tspublished 83 artifacts (was 82); goldens 290. Non-route contract — the generated client is untouched. - Verification: dedicated
query-ast.spec.ts(11 tests) covers the fifteen dimensions, the no-empty-filter rule, window/relation cross-field rules, vocabulary reuse, vector references, recursive depth, and the access-gate helper. Full study suite 32 files / 368 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"81 contracts published, no drift, no breaking changes vs baseline v0"; golden--check"290 fixture cases stable"; adversarial stub-scan clean. - Marked [x]: YSD-13001.
- Commit: resolve via
git log --grep='YSD-13001'.
2026-07-21 — YSD-12020 Engine-neutral instrumented-session envelope (§12)#
- Contract (new):
libs/contracts/src/study/instrumented-session-envelope.ts—InstrumentedSessionEnvelope, the versioned engine-neutral transport envelope every instrumented session crosses the connector boundary in (the layer above the per-sessionInstrumentedSessionrecord). Non-enveloped transport contract. Registered asInstrumentedSessionEnvelope. It defines all five pieces the item bundles:- Versioned engine-neutral envelope —
envelopeVersion(semver), an engine-neutralSessionEngineIdentity(unreal/unity/godot/v2/custom-runtime; a custom runtime must be named), and the honestcaptureClass(YSD-12023). - Event schema registry —
SessionEventSchema[]: per stream a versioned schema with accountableownerDomain,producerBuild,clockBasis(session-monotonic/video-clock/engine-frame/event-timestamp — YSD-12021),sequenced, andprivacyClass(YSD-12024). - Signed manifest —
SignedSessionManifest: content hash, covered streams, event count, capture class, reusing the existingStudyContentSignature. - Connector capability handshake —
ConnectorCapabilityHandshake: protocol version,maxSupportedTier(reuses my GameStudyTier ladder), supported streams/capture-classes, feature flags, event-rate ceiling. - Compatibility policy —
EnvelopeCompatibilityPolicy: min/max envelope version, event-schema compat mode, deprecated versions, with min ≤ max.
- Versioned engine-neutral envelope —
- Structural safety properties: an instrumented session requires a Tier B/C
connector (
tierChain(maxSupportedTier)must includeinstrumented-session— a video-only connector cannot emit instrumented telemetry, YSD-12005); prohibited-class telemetry may never sit in the durable envelope (filtered before ingest, YSD-12025); the envelope version must fall inside the connector's own compatibility window; the handshake engine must match the envelope engine; the connector must declare support for the capture class it produced; the manifest capture class must match; and the manifest must cover every declared stream. - Helpers (real, tested):
compareEnvelopeVersions,envelopeVersionSupported(window + deprecation),connectorSupportsCapture,clockBasesInEnvelope. - Wiring: registered in
schema-registry.ts, exported fromindex.ts, canonical (full + minimal) + structural negatives + cross-field zodOnly examples.generate-study-schemas.tspublished 84 artifacts (was 83); goldens 292. Non-route contract — the generated client is untouched. - Verification: dedicated
instrumented-session-envelope.spec.ts(13 tests) covers the tier bar, prohibited-telemetry bar, version-window admission, engine agreement, capture-class support, manifest coverage, custom-runtime naming, and the compat/version helpers. Full study suite 33 files / 380 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"82 contracts published, no drift, no breaking changes vs baseline v0"; golden--check"292 fixture cases stable"; adversarial stub-scan clean. - Marked [x]: YSD-12020.
- Commit: resolve via
git log --grep='YSD-12020'.
2026-07-21 — YSD-15090 Portable study manifest (§15.5 portable & offline study)#
- Contract (new):
libs/contracts/src/study/portable-study-manifest.ts—PortableStudyManifest, the self-describing export bundle a study travels in across web/desktop/mobile/imported packages. Non-enveloped export artifact. Registered asPortableStudyManifest. It carries what a study IS by reference:metadata(project/tenant/export identity + generator),sources(workId + editionIds + per-edition technical-revision pins = the "versions"),anchors,epistemicObjects(reuseEpistemicObjectType+ReviewState),links(cross-references),views(saved-graph-view / comparison-set), and amediaPolicy. - "Without prohibited media" — structural (YSD-15090): the manifest has no
field for raw bytes; the only embeddable media is a
ManifestEmbeddedDerivative, and every one MUST name therightsGrantIdpermitting it (YSD-15091 layers the recipient/destination/resolution/duration checks on that grant) and carry its own sha256 — there is no unattributed media. Areferences-onlymanifest structurally forbids any embedded derivative;prohibitedMediaExcludedis an affirmativez.literal(true)the producer cannot omit or set false. - Referential integrity: every anchor must reference a declared source edition; every embedded derivative must derive from a declared edition; every epistemic anchor citation must be a declared anchor; every edition-revision pin must name an edition in its source's editionIds. Version pins (manifest format version + study-contracts schema version + per-edition technical revisions) travel with the bundle so a recipient reads the exact revisions.
- Helpers (real, tested):
manifestIsReferencesOnly,manifestReferencesWork,manifestEditionRevisions,embeddedDerivativeGrants(the grant set a recipient must honor). - Wiring: registered in
schema-registry.ts, exported fromindex.ts, canonical (grant-permitted + references-only) + structural negatives + cross-field zodOnly examples.generate-study-schemas.tspublished 85 artifacts (was 84); goldens 294. Non-route contract — client untouched. - Verification: dedicated
portable-study-manifest.spec.ts(9 tests) covers the no-prohibited-media bar, references-only vs embedded, rights attribution, all four referential-integrity rules, and unknown-kind rejection. Full study suite 34 files / 389 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"83 contracts published, no drift, no breaking changes vs baseline v0"; golden--check"294 fixture cases stable"; adversarial stub-scan clean. - Marked [x]: YSD-15090.
- Commit: resolve via
git log --grep='YSD-15090'.
2026-07-21 — YSD-9096 Performance affect-dynamics model (§9.6 performance lens)#
- Contract (new):
libs/contracts/src/study/entities/affect-dynamics.ts—PerformanceAffectDynamics, an enveloped study record modeling the temporal shape of an INTERPRETED affective beat. New branded idAffectDynamicsId. Registered asPerformanceAffectDynamics. Models all seven phases the item names (AFFECT_PHASE_KINDS: onset, build, peak, suppression, leakage, recovery, transition), each an anchored, behavior-grounded, uncertain interpretation, pluscontextualFacets(scene-context, character-goal, blocking, relationship, prior-beat, cultural-context, genre-convention). - Actor-safety wall (YSD-9090/YSD-9098) — structural: affect is an
interpretation of the PERFORMANCE, never a claim about the performer's real
emotion or a diagnosis, never an opaque quality score. Enforced by:
framingis a requiredz.literal('interpreted-not-real-emotion')the producer cannot omit or change;- every phase must state its observable
behavioralBasis(behavior-specific); intensityis a qualitative enum (subtle/moderate/pronounced) — a numeric intensity is rejected (no quality score, YSD-9095);- every phase carries
confidenceso an interpretation is never rendered as a detected fact (YSD-9090); - there is no field for the performer's identity or presumed mental state. Phase orders and facet kinds must be distinct; a model-authored interpretation cannot be born accepted (YSD-0019).
- Helpers (real, tested):
phasesInOrder,affectPhaseKinds,hasAffectPhase. - Wiring: registered in
schema-registry.ts, exported fromindex.ts, canonical (full dread beat + minimal) + structural negatives (incl. numeric intensity, wrong framing, missing behavioral basis) + cross-field zodOnly.generate-study-schemas.tspublished 86 artifacts (was 85); goldens 300. Non-route contract — client untouched. - Verification: dedicated
affect-dynamics.spec.ts(8 tests) covers the full phase vocabulary, the framing wall, behavioral-basis requirement, qualitative-not-numeric intensity, min-phase/facet, distinctness, and the born-accepted rule. Full study suite 35 files / 397 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"84 contracts published, no drift, no breaking changes vs baseline v0"; golden--check"300 fixture cases stable"; adversarial stub-scan clean. - Marked [x]: YSD-9096.
- Commit: resolve via
git log --grep='YSD-9096'.
2026-07-21 — YSD-19123 Governed taxonomy extensions (§19 ecosystem governance)#
- Contract (new):
libs/contracts/src/study/entities/governed-taxonomy-extension.ts—GovernedTaxonomyExtension, the enveloped registration/governance record for a whole extension VOCABULARY a domain, organization, or community contributes on top of the core study taxonomies (individual terms stayTaxonomyTerms; this governs their namespace). New branded idTaxonomyExtensionId. Registered asGovernedTaxonomyExtension. Carries all eight governance facets the item names:owner(kind + id + accountable contact), dotted collision-proofnamespace,provenance,languageScope(BCP-47, ≥1) +cultureScope,compatibility(mode + baseline vocabulary + core version range),migration(versioning policy + breaking-change policy),moderation(status + policy + reviewer), andremoval(removable + on-removal disposition + takedown contact + retention). - Governance enforced structurally (YSD-0021): an
approvedmoderation status must name a human moderator and time; a non-standalone (additive/ overlay) extension must declare the baseline vocabulary it extends and a core version range;migrate-to-baselineremoval requires a baseline; orphaning usages with zero retention is refused; and a model-created extension cannot be bornapproved(approval is a human act).extensionIsUsablecombines the gates a consumer checks (approved + live) before honoring the extension's terms. - Helpers (real, tested):
extensionIsModerated,extensionAppliesToLanguage(exact tag or primary subtag),extensionIsUsable. - Wiring: registered in
schema-registry.ts, exported fromindex.ts, canonical (additive-only approved + standalone pending) + structural negatives- cross-field zodOnly examples.
generate-study-schemas.tspublished 87 artifacts (was 86); goldens 305. Non-route contract — client untouched.
- cross-field zodOnly examples.
- Verification: dedicated
governed-taxonomy-extension.spec.ts(9 tests) covers the eight facets, the dotted-namespace rule, moderator requirement, baseline/version-range requirement, migrate-to-baseline linkage, orphan-retention guard, model-self-approval bar, language-scope matching, and the usable gate. Full study suite 36 files / 406 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"85 contracts published, no drift, no breaking changes vs baseline v0"; golden--check"305 fixture cases stable"; stub-scan clean. - Marked [x]: YSD-19123.
- Commit: resolve via
git log --grep='YSD-19123'.
2026-07-21 — YSD-14020 Pedagogical craft concept graph (§14.2 Metis craft concept graph)#
- Contract (new):
libs/contracts/src/study/entities/pedagogical-concept.ts—PedagogicalConcept, one node of the distinct pedagogical concept graph the learning system teaches against. New branded idPedagogicalConceptId. Registered asPedagogicalConcept. It does NOT re-mint the concept: the canonical concept is Metis-owned and referenced byMetisConceptRef(YSD-0014); this is the Yemaya-side pedagogical mapping. Carries every facet the item names:conceptOwner(the craft domain),definition,prerequisiteConceptIds(learning order — YSD-14021–14023),corequisiteConceptIdsandrelatedConceptIds,evidenceTypes(reuses theMasteryEvidenceDimensionsix from my YSD-14026 work),rubricDimensions(the seven YSD-14070–14076 criteria families),exerciseRefs,taxonomyVersion, andapplicability(cultures + mandatory limitations + established/contested sequence state). - Kept distinct from the creative plane (YSD-14025): the node references a
Metis learning concept, never an
OriginalConcept— pedagogy and a learner's own creative work never collapse into one graph. - Coherent structure enforced: no self-loops (a concept is not its own
prerequisite/corequisite/related); prerequisite and corequisite sets are
disjoint (learned before OR alongside, not both); evidence types and rubric
dimensions are distinct; applicability limitations are always stated; and a
domain expert may mark a sequence
contested(YSD-14024). A model-authored concept cannot be born accepted (experts curate the graph). - Helpers (real, tested):
conceptPrerequisites,isContestedSequence,conceptEvidenceTypes. - Wiring: registered in
schema-registry.ts, exported fromindex.ts, canonical (with prerequisites + minimal) + structural negatives + cross-field zodOnly examples.generate-study-schemas.tspublished 88 artifacts (was 87); goldens 311. Non-route contract — client untouched. - Verification: dedicated
pedagogical-concept.spec.ts(9 tests) covers the Metis mapping, rubric-dimension family, self-loop and prereq/coreq-disjoint rules, distinctness, mandatory limitations, contested sequences, and the born-accepted bar. Full study suite 37 files / 415 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"86 contracts published, no drift, no breaking changes vs baseline v0"; golden--check"311 fixture cases stable"; adversarial stub-scan clean. - Marked [x]: YSD-14020.
- Commit: resolve via
git log --grep='YSD-14020'.
2026-07-21 — YSD-9124 Animation style model (§9.4 animation lens)#
- Contract (new):
libs/contracts/src/study/entities/animation-style.ts—AnimationStyleModel, an enveloped study record modeling a work's animation style. New branded idAnimationStyleModelId. Registered asAnimationStyleModel. Carries every dimension the item names:tradition(hand-drawn-2d/anime-limited/cutout/stop-motion/cg-3d/pixel/rotoscope/ motion-capture/mixed) +traditionContext;fullness(limited/full/hybrid) andframeHold(ones/twos/threes/mixed) for limited/full characteristics;stylizationDegree;characteristics(exaggeration, pose-economy, squash-stretch, smear, held-pose, limited-inbetween, cadence-accent — each with a degree and anchored evidence); andcadenceDescription. - Stance: stylization is a choice, not an error (YSD-9123) — structural: a
required
framingliteral (stylization-is-choice-not-error) the producer cannot omit, plus a requiredtradition+ context so limited-animation cadence, smears, and held poses are read as craft decisions rather than defects. Every characteristic and the overall read carry anchors; the read carries confidence (never a detected fact); characteristic kinds are distinct; a model-authored read cannot be born accepted. - Helpers (real, tested):
styleCharacteristicKinds,isLimitedAnimation,hasStyleCharacteristic. - Wiring: registered in
schema-registry.ts, exported fromindex.ts, canonical (limited anime + full CG) + structural negatives + cross-field zodOnly examples.generate-study-schemas.tspublished 89 artifacts (was 88); goldens 317. Non-route contract — client untouched. - Verification: dedicated
animation-style.spec.ts(8 tests) covers the style vocabulary, the framing stance, tradition+context requirement, per- characteristic anchoring, min-characteristic, distinctness, limited-vs-full without treating either as error, and the born-accepted bar. Full study suite 38 files / 423 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"87 contracts published, no drift, no breaking changes vs baseline v0"; golden--check"317 fixture cases stable"; adversarial stub-scan clean. - Marked [x]: YSD-9124.
- Commit: resolve via
git log --grep='YSD-9124'.
2026-07-21 — YSD-14070 Rubric criteria for anchor accuracy and source use (§14.3)#
- Contract (new):
libs/contracts/src/study/entities/rubric-criterion.ts—PedagogicalRubricCriterion, the assessment criteria for one pedagogical rubric dimension (keyed by the sevenRubricDimensions I defined in YSD-14020). New branded idPedagogicalRubricCriterionId. Registered asPedagogicalRubricCriterion(renamed fromRubricCriterionto avoid a collision with the lightweight per-exerciseRubricCriterionin entities/learning.ts). Carriesdimension,facets(e.g. anchor accuracy AND source use for 14070), ordered qualitativelevelswith descriptors, workedexamples, andaccommodations. - "Not a taste score" — structural (§6.5/§6.6): a rubric assesses observable
craft PROCESS and never collapses into a number. There is NO score field;
.strict()rejects any smuggledscore/overallScore; levels are ORDERED qualitative bands (not points); and a requiredframingliteral (assesses-craft-not-taste-score) states the stance. Levels have distinct keys and orders; every example illustrates a declared level; a model-authored criterion cannot be born accepted (experts curate the rubric). - This item: the contract plus the canonical anchor-accuracy/source-use criterion (developing → proficient → exemplary, with worked examples and accommodations). YSD-14071–14076 will add canonical criteria for the other six dimensions on the same contract.
- Helpers (real, tested):
levelsInOrder,criterionFacets,assessesDimension. - Wiring: registered in
schema-registry.ts, exported fromindex.ts, canonical + structural negatives (incl. the smuggled-score cases) + cross-field zodOnly.generate-study-schemas.tspublished 90 artifacts (was 89, after the rename cleanup); goldens 323. Non-route contract — client untouched. - Verification: dedicated
rubric-criterion.spec.ts(8 tests) covers the ordered levels, the no-numeric-score guarantee, min-two-levels, distinct keys/ orders, example→level coherence, min-facet, unknown-dimension, and born-accepted bar. Full study suite 39 files / 431 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"88 contracts published, no drift, no breaking changes vs baseline v0"; golden--check"323 fixture cases stable"; stub-scan clean. - Marked [x]: YSD-14070.
- Commit: resolve via
git log --grep='YSD-14070'.
2026-07-21 — YSD-14071..14076 Rubric criteria for the remaining six dimensions (§14.3)#
- Deliverable: on the
PedagogicalRubricCriterioncontract shipped for YSD-14070, a real, hand-authored, validated canonical criterion for each of the remaining six rubric dimensions — each with dimension-specific facets and ordered developing/proficient/exemplary level descriptors (qualitative bands, never a score), inexamples.tsviamkRubricCriterion:- YSD-14071
observation-vs-interpretation— separating observation from interpretation and creator statement from inference. - YSD-14072
craft-vocabulary— appropriate craft vocabulary and taxonomy/context awareness. - YSD-14073
uncertainty-limitation-cultural— uncertainty, limitation, cultural context, and causal humility (separating causation from correlation). - YSD-14074
comparison-quality— comparison quality, inclusion logic, dissent, and counterexample selection. - YSD-14075
original-transfer— original transfer, transformation from principles, and protected/overly-specific-expression avoidance. - YSD-14076
revision-reflection— revision quality, reflection, and the ability to explain and defend decisions.
- YSD-14071
- Verification: every criterion validates against
PedagogicalRubricCriterionSchemainschema-publication.spec.ts(zod + published JSON Schema). A newrubric-criterion.spec.tstest asserts the canonical set ships a valid criterion covering ALL sevenRUBRIC_DIMENSIONS(14070 anchor-accuracy through 14076 revision-reflection), each with ≥2 ordered levels and no score. Full study suite 39 files / 432 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"88 contracts, no drift, no breaking"; golden--check"323 stable". - Marked [x]: YSD-14071, YSD-14072, YSD-14073, YSD-14074, YSD-14075, YSD-14076.
- Commit: resolve via
git log --grep='YSD-1407'.
2026-07-21 — YSD-19122 Signed third-party lens SDK package (§19 ecosystem)#
- Contract (new):
libs/contracts/src/study/entities/signed-lens-package.ts—SignedLensPackage, the registration/sandboxing/governance record for a lens a third (or first) party publishes. The lens's declared capability manifest is theLensDefinition(YSD-9001), referenced bylensId+lensDefinitionVersion; this record adds the eleven SDK facets the item names. New branded idLensPackageId. Registered asSignedLensPackage. ReusesLensSourceType,LensCostClass,RightsAction, andStudyContentSignature. - Structural governance gates (YSD-0021):
- signed —
signatureis required (an unsigned package is rejected); - least privilege —
grantedPrivilegesmust be a subset ofrequestedPrivileges(a package can never be granted more than it asked); - sandbox — isolation + memory/cpu/wall ceilings + a coherent network
policy (
none⇒ empty egress,allowlist⇒ non-empty egress); - source/rights filters —
permittedSourceTypesandpermittedRightsActionsnarrow what reaches the lens; - output schemas —
outputSchemaRefs(≥1); - cost —
costClass+maxCostUsdPerRun; observability —requiredObservabilitySignals(≥1); - conformance — a
passed/failedresult must recordtestedAt; - review — an
approvedpackage names reviewer + time, and a model cannot self-approve; revocation — a revoked package records when and why.packageIsUsable= approved AND conformance passed AND not revoked AND live — the gate a runtime checks before loading the lens.
- signed —
- Helpers (real, tested):
grantedPrivilegesWithinRequested,packageIsRevoked,packageIsUsable. - Wiring: registered in
schema-registry.ts, exported fromindex.ts, canonical (approved + pending) + structural negatives (incl. unsigned) + cross-field zodOnly (least-privilege, sandbox, conformance/review/revocation evidence, model self-approval).generate-study-schemas.tspublished 91 artifacts (was 90); goldens 328. Non-route contract — client untouched. - Verification: dedicated
signed-lens-package.spec.ts(8 tests) covers all gates. Full study suite 40 files / 440 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"89 contracts published, no drift, no breaking changes vs baseline v0"; golden--check"328 fixture cases stable"; stub-scan clean. - Marked [x]: YSD-19122.
- Commit: resolve via
git log --grep='YSD-19122'.
2026-07-21 — YSD-16001 (accessibility architecture policy)#
- Item: §16.1 — Define WCAG target, supported assistive-technology matrix, browser/OS combinations, desktop baseline, accessible performance fallback, ownership, exception process, and release-blocking criteria.
- Artifact:
libs/contracts/src/study/accessibility-policy.ts— a machine-readable accessibility policy following theservice-level-objectives.ts(YSD-17036) precedent: policy data drives the release gate rather than living only in prose. OneAccessibilityPolicyobject bundles the eight named facets:- WCAG target —
WcagTargetSchema(version ∈ 2.0/2.1/2.2, level ∈ A/AA/AAA, plus individually-committed AAA success criteria by WCAG number); - AT matrix —
AssistiveTechnologySupportSchema[](screen-reader / magnifier / voice-control / switch-access / braille × platform × support tier; a supported tier must name tested versions); - browser/OS —
BrowserSupportSchema[](engine chromium/gecko/webkit + label + min version + platform + tier); - desktop baseline —
DesktopBaselineSchema(a supported baseline must name platforms and bind a native a11y API: UIA / NSAccessibility / AT-SPI); - performance fallback —
AccessiblePerformanceFallbackSchema(preservesSemanticEquivalenceisz.literal(true)— a lossy fallback cannot be declared accessible, YSD-16004); - ownership — accountable role + contact + escalation path;
- exception process — approver, max duration, workaround requirement, review cadence, expiry requirement;
- release-blocking criteria — blocking levels + severities + whether approved (and unexpired) exceptions unblock.
- WCAG target —
- Structural + cross-field guards (real, tested): matrix must fully support ≥1 screen reader; blocking levels may not exceed the WCAG target (AAA blockable only when AAA criteria are committed); an unexpired-exception gate requires an expiry-mandating process.
- Release gate (domain logic):
releaseBlockingFindings/isReleaseBlockedcompute the findings that block a candidate release — unfixed, target-level, blocking-severity findings not covered by an active approved exception;exceptionCoverscorrectly denies permanent (null-expiry) waivers under an unexpired-only gate and honors expiry against a passed-innowIso(noDate.now()— deterministic). CanonicalSTUDY_ACCESSIBILITY_POLICYtargets WCAG 2.2 AA with NVDA/JAWS/VoiceOver, a Chromium/Gecko/WebKit browser matrix, a Windows/macOS/Linux desktop baseline, and a dense-visualization → structured- table fallback. - Wiring: registered in
schema-registry.ts(AccessibilityPolicy), exported fromindex.ts, examples block inexamples.ts(2 canonical + 8 structural negatives + 5 cross-field zodOnly).generate-study-schemas.tspublished 92 artifacts (was 91); goldens 330 (was 328). Non-route contract — client untouched. - Verification: dedicated
accessibility-policy.spec.ts(13 tests) covers policy coherence, all three cross-field guards, theconst truefallback guard, and the release gate (block/allow/exception/expired/permanent-waiver/disallowed). Full study suite 41 files / 453 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"90 contracts published, no drift, no breaking changes vs baseline v0"; golden--check"330 fixture cases stable"; stub-scan clean. - Marked [x]: YSD-16001.
- Commit: resolve via
git log --grep='YSD-16001'.
2026-07-21 — YSD-17001 (performance environment matrix + methodology)#
- Item: §17.1 — Define representative hardware, browsers, networks, media, project sizes, cache states, source types, assistive settings, and measurement methodology for every performance budget.
- Artifact:
libs/contracts/src/study/performance-environment-matrix.ts— a machine-readablePerformanceEnvironmentMatrixbundling the named condition sets (HardwareProfile[],BrowserProfile[],NetworkProfile[],MediaProfile[],ProjectSizeProfile[],cacheStates,sourceTypes,assistiveSettings) plus aMeasurementMethodology(percentiles, minimum samples, warmup runs, cold+warm requirement, statistic, outlier policy, variance handling). So the CI/release performance gates (YSD-17012) measure against a fixed, documented matrix rather than ad-hoc laptops. - Structural + cross-field guards (real, tested): distinct ids per profile
list;
baselineHardwareIdmust reference a declared profile whose tier isbaseline; exactly one profile may be the baseline tier; methodology percentiles must be strictly increasing. - Operational methodology (real statistics, not just data):
percentileValue(linear-interpolation / type-7 estimator),applyOutlierPolicy(none/trim-min-max/iqr-trimwith the 1.5·IQR fence),hasEnoughSamples(post-warmup gate), andreportableValue(discard warmup → apply outlier policy → percentile). A budget's pass/fail is therefore reproducible from raw samples. CanonicalSTUDY_PERFORMANCE_MATRIX: baseline mid-range laptop, low-end/high-end tiers, Chrome/Firefox/Safari, broadband/cable/4G, UHD/HD/game media, small→stress projects, p50/p75/p95 over ≥20 post-warmup runs with IQR trimming. - Wiring: registered in
schema-registry.ts(PerformanceEnvironmentMatrix), exported fromindex.ts, examples block inexamples.ts(2 canonical + 9 structural negatives + 5 cross-field zodOnly).generate-study-schemas.tspublished 93 artifacts (was 92); goldens 332 (was 330). Non-route contract — client untouched. - Verification: dedicated
performance-environment-matrix.spec.ts(10 tests) covers matrix coherence, all four cross-field guards, and known-value statistics (p50/p75/p90/p0/p1 of [10..50], trim-min-max, IQR trim, warmup drop, in-report outlier trim, minimum-sample gate). Full study suite 42 files / 463 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"91 contracts published, no drift, no breaking changes vs baseline v0"; golden--check"332 fixture cases stable"; stub-scan clean. - Marked [x]: YSD-17001.
- Commit: resolve via
git log --grep='YSD-17001'.
2026-07-21 — YSD-18042 (technical-metric acceptance criteria + gate)#
- Item: §18.2 — Define pass thresholds, confidence intervals, minimum slice sizes, abstention policy, regression budgets, and owner sign-off for every technical metric.
- Artifact:
libs/contracts/src/study/evaluation-acceptance.ts— aMetricAcceptanceCriterion(registered) keyed by the §18.2 technical metrics (TECHNICAL_METRICS, 13 metrics from YSD-18030–18041), plus the canonical mapSTUDY_EVALUATION_ACCEPTANCE. Each criterion carries:passThreshold,confidenceLevel(0.9/0.95/0.99) +confidenceBound(lower/upper/point),minimumSliceSize,abstention(allowed + max rate),regressionBudget(absolute/relative), andownerSignOffRole. - Structural + cross-field guards (real, tested): a proportion/rate threshold must be within [0, 1]; a magnitude metric must gate on the point estimate (no interval is computed); the compared bound must be the conservative one for the direction (higher-is-better → lower/point; lower-is-better → upper/point).
- Operational gate (real statistics, not just data):
wilsonInterval(two-sided Wilson score interval, clamped to [0,1]),zForConfidence(z lookup for the supported levels), andevaluateMetric→pass/fail/insufficient-data. A slice below the minimum size isinsufficient-data(never a silent pass); a scored slice passes only when the decision value (interval bound or point estimate) clears the threshold, the regression stays within budget, and abstentions stay within policy. Zero-tolerance criteria:rights-decision-correctnessthreshold 1 (point),permission-leakage-ratethreshold 0 with abstention disallowed (YSD-18038). - Wiring: registered in
schema-registry.ts(MetricAcceptanceCriterion), exported fromindex.ts, examples block inexamples.ts(3 canonical + 9 structural negatives + 4 cross-field zodOnly).generate-study-schemas.tspublished 94 artifacts (was 93); goldens 334 (was 332). Non-route contract. - Verification: dedicated
evaluation-acceptance.spec.ts(15 tests) covers criterion coherence over all 13 metrics, all cross-field guards, known-value Wilson intervals (8/10 → [0.4902, 0.9433]; z=1.96), and the gate (insufficient-data / lower-bound fail 0.888 / lower-bound pass / zero-leakage / magnitude / regression-budget / abstention). Full study suite 43 files / 478 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"92 contracts published, no drift, no breaking changes vs baseline v0"; golden--check"334 fixture cases stable"; stub-scan clean. - Marked [x]: YSD-18042.
- Commit: resolve via
git log --grep='YSD-18042'.
2026-07-21 — YSD-18001 (gold-set governance policy + usability gate)#
- Item: §18.1 — Establish governance for versioned gold sets, annotation guides, adjudication, legitimate disagreement, licenses, consent, privacy, retention, access, publication, and deletion.
- Artifact:
libs/contracts/src/study/gold-set-governance.ts— a registeredGoldSetGovernancePolicybundling eight governance sub-policies:versioning(published versions immutable, label change → new version — no silent post-hoc relabeling, YSD-18013),annotationGuide(required, min annotators),adjudication(method + legitimate-disagreement preserved, YSD-18010/18060),clearance(license + consent + privacy all mandatory asz.literal(true)— a policy cannot waive them),retention(max + review cadence, withdrawal always honored),access(tiers + default + export role),publication(internal-only default, external needs approval), anddeletion(store boundaries, right-to-deletion honored). CanonicalSTUDY_GOLD_SET_GOVERNANCE. - Structural + cross-field guards (real, tested): deletion boundaries must
include
backups(a set recoverable from a backup is not deleted); review cadence must not exceed retention; the default access tier must be a declared tier; theexpert-adjudicatormethod requires an adjudicator. - Usability gate (operational):
goldSetUsableForEvaluation(policy, status)→{ usable, reasons[] }. A gold set may gate a release only when license, consent, and privacy are cleared, the annotation guide is present with enough annotators, adjudication is resolved, the set is un-withdrawn, and it is within retention — every failing condition is named (auditable refusal, mirrors thesigned-lens-packagepackageIsUsablepattern). - Wiring: registered in
schema-registry.ts(GoldSetGovernancePolicy), exported fromindex.ts, examples block inexamples.ts(2 canonical + 8 structural negatives + 4 cross-field zodOnly).generate-study-schemas.tspublished 95 artifacts (was 94); goldens 336 (was 334). Non-route contract. - Verification: dedicated
gold-set-governance.spec.ts(11 tests) covers policy coherence, all cross-field guards, mandatory-clearance literals, and the usability gate (accept / missing-consent / withdrawn / past-retention / too-few-annotators / unresolved-adjudication / multi-reason). Full study suite 44 files / 489 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"93 contracts published, no drift, no breaking changes vs baseline v0"; golden--check"336 fixture cases stable"; stub-scan clean. - Marked [x]: YSD-18001.
- Commit: resolve via
git log --grep='YSD-18001'.
2026-07-21 — YSD-18003 (expert participation governance + eligibility gate)#
- Item: §18.1 — Define expert conflict-of-interest, compensation, attribution, confidentiality, review, and withdrawal procedures.
- Artifact:
libs/contracts/src/study/expert-participation.ts— a registeredExpertParticipationPolicybundling six sub-policies:conflictOfInterest(declaration required, disqualifying relations, cooling-off, financial-stake threshold),compensation(model + currency +paidRegardlessOfOutcomeasz.literal(true)— pay is never contingent on reaching a judgment),attribution(namingRequiresConsent+optOutAllowedliterals),confidentiality(NDA, embargo, prohibited disclosures),review(expert work reviewed + appeal), andwithdrawal(allowedAnytimeliteral + data handling). CanonicalSTUDY_EXPERT_PARTICIPATION. - Structural + cross-field guards (real, tested): integrity literals cannot be waived (outcome-contingent pay, naming without consent, no-withdrawal all rejected structurally); a cooling-off requirement needs a positive window.
- Eligibility gate (operational):
expertIsEligible(policy, declaration)→{ eligible, reasons[] }. An expert is barred by any declared disqualifying relation, a financial stake at/above the threshold, or a cooling-off violation; a non-listed relation (e.g. personal-relationship, when the policy does not disqualify it) does not bar. Every barring reason is named. - Wiring: registered in
schema-registry.ts(ExpertParticipationPolicy), exported fromindex.ts, examples block inexamples.ts(2 canonical + 9 structural negatives + 1 cross-field zodOnly).generate-study-schemas.tspublished 96 artifacts (was 95); goldens 338 (was 336). Non-route contract. - Verification: dedicated
expert-participation.spec.ts(11 tests) covers integrity literals, cross-field guards, and the eligibility gate (clean / authored-source / at-threshold stake (5000) vs below (4999) / cooling-off / non-disqualifying relation / multi-reason). Full study suite 45 files / 499 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"94 contracts published, no drift, no breaking changes vs baseline v0"; golden--check"338 fixture cases stable"; stub-scan clean. - Marked [x]: YSD-18003.
- Commit: resolve via
git log --grep='YSD-18003'.
2026-07-21 — YSD-17100 (deployment / data-residency policy + placement gate)#
- Item: §17.5 — Define environments, region/tenant data placement, object/database/index residency, model-provider routes, signing keys, backups, and deletion boundaries.
- Artifact:
libs/contracts/src/study/deployment-residency.ts— a registeredDeploymentResidencyPolicybundling:environments(name → served regions),storeResidency(per store class:tenant-region-onlyvstenant-region-or-dr, encrypted-at-rest literal),disasterRecovery(region → DR-region pairs),modelRoutes(cross-region egress + approved routes),signingKeys(per-env isolated + rotation + reference literals),backups(residency scope + retention + encrypted literal), anddeletionBoundaries(store classes). CanonicalSTUDY_DEPLOYMENT_RESIDENCY. - Structural + cross-field guards (real, tested): every store class must have exactly one residency rule; deletion boundaries must include object/relational/ backup; approved cross-region routes require egress enabled; a DR region must differ from its primary.
- Placement gate (operational):
placementSatisfiesResidency(policy, placement)→{ compliant, violations[] }. Refuses a store/backup outside the tenant's region (honoring the per-class DR scope viadrRegionFor), a tenant region the environment does not serve, and an unapproved cross-region model route. Every violation is named — a cross-region leak is caught, not trusted. - Wiring: registered in
schema-registry.ts(DeploymentResidencyPolicy), exported fromindex.ts, examples block inexamples.ts(2 canonical + 8 structural negatives + 4 cross-field zodOnly).generate-study-schemas.tspublished 97 artifacts (was 96); goldens 340 (was 338). Non-route contract. - Verification: dedicated
deployment-residency.spec.ts(12 tests) covers policy coherence, all cross-field guards, and the placement gate (in-region / DR backup / DR-scoped store eu-ok-apac-no / tenant-region-only refusal / env-not-served / unapproved-route refusal / approved-route accept). Full study suite 46 files / 511 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"95 contracts published, no drift, no breaking changes vs baseline v0"; golden--check"340 fixture cases stable"; stub-scan clean. - Marked [x]: YSD-17100.
- Commit: resolve via
git log --grep='YSD-17100'.
2026-07-21 — YSD-15073 (engine-neutral game-session export + adapter packages)#
- Item: §15.4 — Define engine-neutral game-session export plus approved Unreal, Unity, Godot, and product-specific adapter packages.
- Artifact:
libs/contracts/src/study/game-session-export.ts— two registered contracts:GameSessionExport(references-only, coordinates normalized to the neutral frame — bothz.literal(true), so a lossy or un-normalized export is structurally rejected; ties to YSD-15075) andGameSessionExportAdapter(per-engine adapter descriptor: engine family, version, coordinate convention, supported capture classes/streams, conformance status,approved, detached signature). Canonical approved adaptersSTUDY_GAME_EXPORT_ADAPTERSfor Unreal, Unity, Godot, and the V2 product runtime. - Real coordinate math (not a label):
GameCoordinateConventiondeclares a signed right/up/forward basis + unit scale;coordinateTransformMatrix(from, to)composes the basis change through the canonical (glTF) frame with the unit-scale ratio (M = (scaleFrom/scaleTo)·R_to^T·R_from);toNeutralFramenormalizes a point;computedHandednessderives left/right from the determinant of the basis and the schema rejects a convention whose declared handedness disagrees with its basis (a mislabeled Unreal/Unity/Godot frame cannot ship). Known values: 100cm Unreal +X (forward) → neutral (0,0,-1)m; +Z (up) → (0,1,0); +Y (right) → (1,0,0). - Deliverability gate (operational):
gameSessionExportIsDeliverable(export, adapters)→{ deliverable, reasons[] }refuses an export whose adapter is unknown, unapproved, non-conformant, engine-mismatched, or missing a declared stream. Adapter guard:approvedrequiresconformanceStatus === 'passed'. - Scope note: this delivers the export FORMAT and the approved adapter PACKAGE contracts (with real coordinate normalization + conformance/approval gating). Runtime round-trip translation is exercised by the downstream §15.4 tests (YSD-15074 glTF, YSD-15076 game-envelope round-trip), which consume these contracts.
- Wiring: registered in
schema-registry.ts(GameSessionExport,GameSessionExportAdapter), exported fromindex.ts(renamedGameCoordinateConvention*to avoid theajadomain'sCoordinateConvention), examples inexamples.ts(adapter: 2 canonical + 8 negatives + 3 zodOnly; export: 1 canonical + 8 negatives).generate-study-schemas.tspublished 99 artifacts (was 97); goldens 344 (was 340). Non-route contracts. - Verification: dedicated
game-session-export.spec.ts(11 tests) covers handedness derivation, Unreal→neutral normalization known values, neutral-frame identity, mislabeled-handedness rejection, the literal-true guards, and the deliverability gate (deliver / unknown-adapter / engine-mismatch / unsupported-stream / unapproved). Full study suite 47 files / 522 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"97 contracts published, no drift, no breaking changes vs baseline v0"; golden--check"344 fixture cases stable"; stub-scan clean. - Marked [x]: YSD-15073.
- Commit: resolve via
git log --grep='YSD-15073'.
2026-07-21 — YSD-16002 (semantic interaction specifications)#
- Item: §16.1 — Create semantic interaction specifications for player, timeline, annotation, comparison, inspiration canvas, graph, matrix, concept map, notebook, practice, dialogs, and live sessions before implementation.
- Artifact:
libs/contracts/src/study/semantic-interaction-spec.ts— a registeredSemanticInteractionSpec(root ARIA role, focus model, keyboard interactions, live-region announcements, required accessible names, optional navigable textual equivalent, reduced-motion behavior) plus the canonicalSTUDY_INTERACTION_SPECScovering all 12 surfaces. The deliverable is the specifications themselves — authored before implementation, which YSD-16003 (primitives) and the surface components will consume. - Structural + cross-field guards (real, tested): keyboard chords must be
distinct; a visualization surface (timeline/comparison/inspiration-canvas/
graph/matrix/concept-map) must declare a navigable textual equivalent
(
navigable: z.literal(true), YSD-16004) and a non-visualization surface must not; a dialog must trap focus; a matrix must use grid navigation. - Wiring: registered in
schema-registry.ts(SemanticInteractionSpec), exported fromindex.ts, examples inexamples.ts(3 canonical + 8 structural negatives + 5 cross-field zodOnly).generate-study-schemas.tspublished 100 artifacts (was 99); goldens 346 (was 344). Non-route contract. - Verification: dedicated
semantic-interaction-spec.spec.ts(8 tests) asserts a spec for every surface with distinct bindings, a navigable textual equivalent for every visualization (and none otherwise), the dialog focus-trap / matrix grid-navigation rules, and the duplicate-binding / non-navigable rejections. Full study suite 48 files / 530 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift, no breaking changes vs baseline v0"; golden--check"346 fixture cases stable"; stub-scan clean. - Marked [x]: YSD-16002.
- Commit: resolve via
git log --grep='YSD-16002'.
2026-07-21 — YSD-18030 (source-identity match-accuracy metrics)#
- Item: §18.2 — Measure SourceWork and SourceEdition/build match accuracy, ambiguous-match handling, false merge, and keep-distinct accuracy.
- Artifact:
libs/contracts/src/study/evaluation-metrics.ts— pure functions over labeled pairwise decisions:confusionFromDecisions(2x2 confusion over scored decisions) andmatchAccuracyMetrics→ accuracy, matchRecall, matchPrecision,falseMergeRate(gold-distinct pairs wrongly merged),keepDistinctAccuracy, andabstentionRate. Anambiguousprediction is an abstention — excluded from the confusion matrix (never counted correct or wrong) and reported separately, so a model cannot inflate accuracy by hedging. Ratios returnnull(not a fabricated 0/1) when their denominator is zero. - Delivery-order coherence: these are the raw numbers the YSD-18042
acceptance criteria + Wilson-interval gate judge; the spec includes an
integration test feeding a computed result straight into
evaluateMetric(perfect 200-pair slice →pass; the 8-scored small slice →insufficient-data). - Wiring: exported from
index.ts; pure-computation module, no zod schema, so no registry/goldens/client change (published contracts stay 98, goldens 346). - Verification: dedicated
evaluation-metrics.spec.ts(5 tests) with known values (3 TP / 1 FN / 1 FP / 3 TN + 2 abstentions → accuracy 0.75, falseMerge 0.25, keepDistinct 0.75, abstention 0.2; keepDistinct + falseMerge = 1), null-ratio edge cases (empty / all-abstained / no-gold-distinct), and the 18042 pipeline. Full study suite 49 files / 535 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift, no breaking changes"; golden--check"346 fixture cases stable"; stub-scan clean. - Marked [x]: YSD-18030.
- Commit: resolve via
git log --grep='YSD-18030'.
2026-07-21 — YSD-18031 (boundary detection precision/recall with tolerance)#
- Item: §18.2 — Measure scene, shot, beat, speaker, word, sound-event, pose, action, contact, gameplay-phase, and document-boundary precision/recall with tolerance definitions.
- Artifact:
libs/contracts/src/study/evaluation-metrics.ts(extended) —BOUNDARY_KINDS+BOUNDARY_TOLERANCES(the tolerance definitions: shot 0.5s, scene 2.0s, word 0.15s, contact 0.08s, document-boundary 0 exact, …),toleranceForBoundary,boundaryDetectionMetrics(predicted, gold, tolerance)and the conveniencemeasureBoundaries(kind, …). Matches predicted to gold one-to-one within the tolerance window (each gold pairs with its nearest unmatched prediction); unmatched predictions are false positives (over-segmentation), unmatched gold are misses. Returns precision/recall/F1, null when a denominator is zero. - Verification: dedicated tests (5 added) with known values —
within-tolerance matching (3 TP / 1 FP / 1 FN → P=R=F1=0.75), exact zero
tolerance, one-to-one crowding (two predictions near one gold → 1 TP + 1 FP,
recall 1, F1 2/3), null-precision / zero-recall edges, and per-kind tolerance
application (0.3s off matches a shot but not a word). Full study suite 49
files / 540 tests green;
tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift, no breaking changes"; stub-scan clean. - Marked [x]: YSD-18031.
- Commit: resolve via
git log --grep='YSD-18031'.
2026-07-21 — YSD-18034 (confidence calibration metrics)#
- Item: §18.2 — Measure confidence calibration, reliability curves, abstention, coverage, expected calibration error, and behavior under distribution shift rather than raw confidence magnitude.
- Artifact:
libs/contracts/src/study/evaluation-metrics.ts(extended) —calibrationMetrics(predictions, numBins=10)bins predictions into equal-width confidence bins and reports Expected Calibration Error (confidence-weighted mean of |accuracy − confidence| over non-empty bins), Max Calibration Error, the reliability curve (per-bin count / avg-confidence / accuracy), coverage (scored / total), and abstention. Abstained predictions are excluded from calibration and lower coverage — measuring calibration, never rewarding raw over-confidence. Invalid confidences (outside [0,1]) and bad bin counts throw; ECE isnullwhen nothing was scored (not a fabricated 0). - Verification: dedicated tests (4 added) with known values — 5@0.9(80% acc)
- 5@0.5(60% acc) → ECE 0.1, MCE 0.1, bin[0.9,1.0) accuracy 0.8; perfect
calibration (conf 1 correct / conf 0 wrong) → ECE 0; abstention lowering
coverage to 0.5; null-ECE and throw-on-bad-input edges. Full study suite 49
files / 544 tests green;
tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift, no breaking changes"; stub-scan clean.
- 5@0.5(60% acc) → ECE 0.1, MCE 0.1, bin[0.9,1.0) accuracy 0.8; perfect
calibration (conf 1 correct / conf 0 wrong) → ECE 0; abstention lowering
coverage to 0.5; null-ECE and throw-on-bad-input edges. Full study suite 49
files / 544 tests green;
- Marked [x]: YSD-18034.
- Commit: resolve via
git log --grep='YSD-18034'.
2026-07-21 — YSD-18035 (claim, citation, and epistemic quality metrics)#
- Item: §18.2 — Measure claim-to-anchor completeness, citation restore rate, unsupported-claim rate, epistemic misclassification, and creator-intent conflation.
- Artifact:
libs/contracts/src/study/evaluation-metrics.ts(extended) —claimSupportMetrics(completeness = claims with ≥1 anchor / total, plus the unsupported-claim rate),citationRestoreRate(resolved citations / total), andepistemicLabelingMetrics(overall misclassification rate plus a separatecreatorIntentConflationRate). Conflation — passing a reader's interpretation off as the creator's stated intent, or the reverse — is counted distinctly because it is a more serious error than an ordinary type confusion. All ratios returnnullon empty input rather than a fabricated score. - Verification: dedicated tests (5 added) with known values — completeness
0.75 / unsupported 0.25 over 4 claims; citation restore 0.75;
misclassification 0.75 with 2 of the errors being creator-intent conflations
(rate 0.5); a correct creator-statement label is not conflation; completeness
feeds the YSD-18042
claim-anchor-completenessgate (120 supported → pass). Full study suite 49 files / 549 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift, no breaking changes"; stub-scan clean. - Marked [x]: YSD-18035.
- Commit: resolve via
git log --grep='YSD-18035'.
2026-07-21 — YSD-18036 (cross-reference quality metrics by relation type)#
- Item: §18.2 — Measure cross-reference precision, calibration, keep-distinct accuracy, explanation sufficiency, review workload, and false identity merge by relation type.
- Artifact:
libs/contracts/src/study/evaluation-metrics.ts(extended) —crossReferenceMetrics(precision, keep-distinct accuracy, false-identity-merge count + rate, explanation-sufficiency rate, review-workload rate) andcrossReferenceMetricsByRelationwhich groups the same metrics per relation type so a weak relation kind (e.g.same-function) is not hidden by strong ones (keep-distinct). A false identity merge is asserting same-identity when the gold is distinct — the highest-severity cross-reference error. Calibration is covered by the sharedcalibrationMetrics. - Verification: dedicated tests (3 added) with known values — overall
precision 0.75, keep-distinct 2/3, false-merge rate 0.25, explanation/review
0.5; per-relation breakdown isolating the
same-functionfalse merge (precision 0.5) from the cleankeep-distinctrelation (precision 1, keep-distinct 1); null ratios on empty. Full study suite 49 files / 552 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift, no breaking changes"; stub-scan clean. - Marked [x]: YSD-18036.
- Commit: resolve via
git log --grep='YSD-18036'.
2026-07-21 — YSD-18037 (source-to-decision path & traversal metrics)#
- Item: §18.2 — Measure source-to-decision path completeness, broken-edge rate, impact-analysis precision/recall, stale dependency detection, and permission leakage through traversal.
- Artifact:
libs/contracts/src/study/evaluation-metrics.ts(extended) —pathCompletenessMetrics(completeness = paths with zero broken edges; broken- edge rate = broken edges / all edges),setImpactMetrics(set-based precision/recall/F1 of predicted-impacted vs gold-impacted decision ids, de-duplicated),staleDependencyMetrics(stale-detection recall + precision), andtraversalLeakageMetrics(leaks +zeroLeakageflag — permission leakage through graph traversal must be exactly zero, ties to YSD-18038). - Verification: dedicated tests (4 added) with known values — completeness
2/3 with broken-edge rate 1/9; impact P=R=F1=2/3 with duplicate collapse;
stale recall/precision 0.5/0.5; leakage flagged (rate 0.5, zeroLeakage false)
and a clean/empty traversal set reporting zeroLeakage true. Full study suite
49 files / 556 tests green;
tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift, no breaking changes"; stub-scan clean. - Marked [x]: YSD-18037.
- Commit: resolve via
git log --grep='YSD-18037'.
2026-07-21 — YSD-18039 (rights enforcement quality metrics)#
- Item: §18.2 — Measure rights-decision correctness, expiry/revocation timing, blocked-action coverage, consent enforcement, and audit completeness.
- Artifact:
libs/contracts/src/study/evaluation-metrics.ts(extended) —rightsDecisionMetrics(correctness rate plus wrongAllows surfaced separately — granting access that should be denied is the unsafe error class the zero-tolerance YSD-18042 criterion blocks on),blockedActionCoverage,postRevocationBlockRate(expiry/revocation timing — post-revocation accesses correctly blocked),consentEnforcementRate, andauditCompletenessRate. - Verification: dedicated tests (3 added) — correctness 0.5 with wrong-allow
and wrong-deny separated; coverage / revocation / consent all 0.5; audit
completeness 0.75; and the zero-tolerance gate integration (200 correct →
pass; a single wrong-allow among 200 → fail under the point-estimate threshold
of 1). Full study suite 49 files / 559 tests green;
tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift, no breaking changes"; stub-scan clean. - Marked [x]: YSD-18039.
- Commit: resolve via
git log --grep='YSD-18039'.
2026-07-21 — YSD-18040 (deletion completion & latency across stores)#
- Item: §18.2 — Measure deletion completion and latency across object, relational, search, vector, graph, time-series, cache, export, local/offline, and model/intermediate stores.
- Artifact:
libs/contracts/src/study/evaluation-metrics.ts(extended) —DELETION_STORE_TARGETS(the 10 stores) anddeletionMetrics(outcomes, latencyPercentile): overall + per-store completion rates and a per-store latency percentile (reusing the performance module'spercentileValue), plus request-level settlement — a deletion request is settled only when every one of its store outcomes completed, so a single lagging store keeps the request unsettled and visible. Ratios returnnullon empty input. - Verification: dedicated tests (3 added) — the 10 store targets are
enumerated; a mixed set gives overall completion 0.75, per-store object
completion 1 with p95 latency 290 (interpolated over [100,300]) and relational
0.5, and request settlement 0.5 (only the request that reached both stores); a
40-request fully-settled set feeds the YSD-18042 deletion-latency-seconds gate
→ pass. Full study suite 49 files / 562 tests green;
tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift, no breaking changes"; stub-scan clean. - Marked [x]: YSD-18040.
- Commit: resolve via
git log --grep='YSD-18040'.
2026-07-21 — YSD-18041 (job reliability metrics)#
- Item: §18.2 — Measure job success, retry recovery, idempotency, latency, cost, cancellation, partial success, stale-output, and duplicate-effect rates.
- Artifact:
libs/contracts/src/study/evaluation-metrics.ts(extended) —jobReliabilityMetrics(jobs, latencyPercentile): success rate, retry-recovery rate (recovered / retried), idempotency-violation rate, latency percentile, mean cost, and cancellation / partial-success / stale-output / duplicate-effect rates. Idempotency violations and duplicate effects are surfaced as their own rates because both must be zero — a retried job must never double-charge, double-notify, or emit a second output. - Verification: dedicated tests (3 added) — over a 10-job set: success 0.8,
retry-recovery 2/3, idempotency/duplicate/cancellation/partial/stale all 0.1,
latency p95 100, mean cost 0.5; null retry-recovery when nothing retried; and
the YSD-18042 job-success-rate gate (500 perfect → pass, 450 →
insufficient-data under the 500-sample minimum). Full study suite 49 files /
565 tests green;
tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift, no breaking changes"; stub-scan clean. - Marked [x]: YSD-18041. §18.2 technical metrics now substantially covered (18030/18031/18034/18035/18036/18037/18039/18040/18041 done; 18032/18033 remain).
- Commit: resolve via
git log --grep='YSD-18041'.
2026-07-21 — YSD-18032 (synchronization error, drift & gap detection)#
- Item: §18.2 — Measure timecode, subtitle, commentary, transcript, proxy, and telemetry synchronization error plus drift/gap detection.
- Artifact:
libs/contracts/src/study/evaluation-metrics.ts(extended) —syncErrorMetrics(samples, percentile)reports the absolute sync error (mean/max/percentile of |measured − reference|) and the drift rate, estimated by least-squares regression of the signed offset against reference time — a nonzero slope is clock drift, distinct from a constant offset (the absolute error stays symmetric, the drift keeps its sign).detectGaps( timestamps, maxGapSeconds)sorts and reports spacings that exceed the maximum as coverage gaps (missing subtitles/telemetry). - Verification: dedicated tests (4 added) — offset growing 0.01s/reference-s
gives mean error 0.25, max 0.4, p95 0.385, drift slope 0.01 with intercept
0.1; single-sample drift is null; gap detection over unsorted input finds the
one 3-second gap and rejects a negative window; the p95 error feeds the
YSD-18042 sync-error-seconds gate (40 tight samples → pass). Full study suite
49 files / 569 tests green;
tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift, no breaking changes"; stub-scan clean. - Marked [x]: YSD-18032. §18.2 technical metrics: 10 of 12 done — only YSD-18033 (tracking/pose/transcript/OCR/source-separation/retrieval by slice) remains.
- Commit: resolve via
git log --grep='YSD-18032'.
2026-07-22 — YSD-18033 (tracking, region/mask, pose, transcript, OCR, separation & retrieval quality by slice)#
- Item: §18.2 — Measure tracking stability, region/mask accuracy, pose error, transcript error, OCR error, source-separation quality, and retrieval relevance by slice.
- Artifact:
libs/contracts/src/study/evaluation-metrics.ts(extended, pure functions — no schema/registry/goldens) implementing the seven SOTA families:- Region/mask:
boxIoU,maskIoU,diceCoefficient, andregionAccuracyMetrics(mean IoU/Dice + fraction clearing an IoU threshold). Empty-gold∧empty-pred scores as perfect agreement (1), the standard 0/0 segmentation convention. - Tracking:
trackingMetrics— CLEAR-MOT accounting over a temporally ordered frame sequence: misses, false positives, identity switches (per-track predicted-id change), fragmentations (resumption of an established track), MOTA, MOTP, and a boundedtrackingStabilityproportion. - Pose:
poseErrorMetrics— MPJPE (mean per-joint Euclidean error over visible joints) and PCK (percentage of correct keypoints within a per-pose-scaled threshold);euclideanDistancehelper. - Transcript/OCR:
editDistanceOps(Levenshtein with S/D/I backtrace),wordErrorRate(WER),characterErrorRate(CER — edits and denominator both in Unicode code points). - Source separation:
scaleInvariantSdr(SI-SDR in dB via projection of the estimate onto the reference; +∞ perfect, −∞ orthogonal) andseparationQualityMetrics(mean over finite ratios, perfect/silent counted separately). - Retrieval:
discountedCumulativeGain/normalizedDcg(exponential gain2^rel−1,log2(rank+1)discount; rejects negative grades) andretrievalRelevanceMetrics(mean nDCG, MRR, precision@k). - By slice: generic
bySlice(items, metric)groups any family's evals (or tracking frames) by an optionalslicelabel so a hard cohort cannot be masked by an easy majority.
- Region/mask:
- Verification: 15 known-value tests added (
evaluation-metrics.spec.ts, file now 54 tests): box IoU 1/7 and mask IoU 0.4 / Dice 20/35; tracking fixture with an id-switch, a miss, an FP and a resumption → MOTA 0.625, MOTP 0.85, stability 0.75; MPJPE 5 and PCK 2/3 over joints at distance {0,5,10}; WER 0.5 (the quick brown fox→the quik brown) and CER 0.2 (hello→helo), astral CER 1/3 for😀ab→😀ax; SI-SDR ≈19.168 dB and scale invariance to+∞; nDCG 0.94881 on the graded example[3,2,3,0,1,2], MRR and precision@k; by-slice keeps a night-slice accuracy 0 visible next to a daylight-slice accuracy 1. The stability proportion feeds the YSD-18042tracking-stabilitygate (60-frame perfect slice → pass; 40 < minimum 50 → insufficient-data, never a silent pass). Full study suite 49 files / 584 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift, no breaking changes"; goldens--check"346 fixture cases stable"; stub-scan clean. A read-only Codex review flagged three edge cases (mask 0/0 convention, CER Unicode denominator units, nDCG negative grades) — all fixed with dedicated tests before marking. - Marked [x]: YSD-18033. §18.2 technical-quality metrics now COMPLETE (all
of 18030–18042).
evaluation-metrics.tsholds the eleven metric families. - Commit: resolve via
git log --grep='YSD-18033'.
2026-07-22 — YSD-18060 (inter-rater agreement vs disagreement coverage)#
- Item: §18.3 — Measure inter-rater agreement only where a shared correct answer is meaningful, and measure disagreement coverage/context where it is not.
- Artifact:
libs/contracts/src/study/human-quality-metrics.ts(new pure-fn module, no schema/registry/goldens; exported from the study index) with the YSD-18060 epistemic partition:- Reliability coefficients (objective items):
cohensKappa(two raters, chance-corrected),fleissKappa(fixed rater count, many items),krippendorffAlpha(nominal metric via a coincidence matrix — tolerates any rater count and missing data), andpercentAgreement(raw pairwise). - Disagreement measures (contested items):
disagreementCoverageMetrics—positionCoverage(fraction of an item's declared legitimate positions actually voiced, so a minority reading is not silenced by the majority),documentationRate(disagreement recorded with rationale), andgenuineDisagreementRate. Contested items are never scored as reliability error. raterAgreementMetricspartitions items onhasSharedCorrectAnswerand applies agreement statistics ONLY to the objective partition and coverage/context ONLY to the contested partition — encoding the checklist's core requirement structurally. Cohen's/Fleiss' kappa are emitted only when their preconditions hold (two complete raters; a uniform rater count), never fabricated from ill-fitting data; a coefficient that is undefined (single category, no pairable data) returnsnull.
- Reliability coefficients (objective items):
- Verification: 8 known-value tests (
human-quality-metrics.spec.ts): Cohen's kappa 0.4 from a 2x2 table (po 0.7, pe 0.5); Fleiss' kappa 0.55 for 3 raters over items (3a),(3b),(2a,1b) (P̄ 7/9, P̄e 41/81, κ 22/40); Krippendorff's alpha 0.53333 for the (a,a),(a,b),(b,b),(b,b) coincidence case (Do 0.25, De 30/56); perfect-agreement → 1; single-category/empty → null; uneven Fleiss counts throw; and the objective/contested partition (agreement over the 3 fact items, coverage 7/12 and documentation 0.5 over the 2 interpretive items). Full study suite 50 files / 592 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift"; goldens--check"346 fixture cases stable"; stub-scan clean. A read-only Codex review confirmed the Cohen's-kappa and Krippendorff's-alpha math (including missing-data / variable-rater handling) and flagged one robustness gap —fleissKappawould silently drop an undeclared category from its totals — now fixed to throw on undeclared categories, with a test. - Marked [x]: YSD-18060. First §18.3 human-centered metric; opens the human-quality-metrics.ts module for the rest of §18.3.
- Commit: resolve via
git log --grep='YSD-18060'.
2026-07-22 — YSD-18061 (learner epistemic-distinction ability)#
- Item: §18.3 — Measure learner ability to distinguish source fact, detection, observation, interpretation, (craft) hypothesis, creator statement, and practice result.
- Artifact:
libs/contracts/src/study/human-quality-metrics.ts(extended) —epistemicDistinctionMetrics(items)scores a learner's classification of items into the sevenEPISTEMIC_OBJECT_TYPES(reused from epistemic-object-types.ts, not redefined): overall accuracy, chance-corrected agreement with the gold labels (Cohen's kappa, reusingcohensKappa), macro-F1 averaged over the gold-present types (a missed type scores 0, never dropped, so misses cannot be averaged away), and a full per-type precision/recall/F1 confusion breakdown. The study-specific core is three named consequential confusions viacrossGroupConfusionRate: interpretation↔creator-statement (whose claim is this — the highest-stakes conflation, mirroring YSD-18035), observation↔interpretation (the YSD-14071 rubric distinction), and evidence↔inference (source-fact/detection vs the five inferential/authored types). These named confusions are what make it a study-workspace metric rather than a renameable generic classifier score. - Verification: 4 known-value tests (
human-quality-metrics.spec.ts, file now 12 tests) over a 9-item fixture (6 correct + obs→interp, interp→creator, creator→interp): accuracy 6/9; macro-F1 0.72381 (per-class F1s 1,1,0.667,0.4,1,0,1); interpretation P 1/3, R 1/2, F1 0.4; creator-statement F1 0 (never correctly found); observation F1 2/3; interpretation↔creator-statement conflation 2/3; observation↔interpretation 1/4; evidence↔inference 0; Cohen's kappa non-null and strictly below raw accuracy (chance-corrected); a perfect learner scores 1/1/1 with 0 conflation; an empty set is all-null. Full study suite 50 files / 596 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift"; goldens--check"346 fixture cases stable"; stub-scan clean. A read-only Codex review of the confusion-matrix, macro-F1 convention, and cross-group confusion logic found no bugs. - Marked [x]: YSD-18061.
- Commit: resolve via
git log --grep='YSD-18061'.
2026-07-22 — YSD-18062 (improvement across attempts)#
- Item: §18.3 — Measure improvement in source-based analysis, contextual humility, counterexample use, original transfer, revision, and explanation across attempts.
- Artifact:
libs/contracts/src/study/human-quality-metrics.ts(extended) —IMPROVEMENT_DIMENSIONS(the six named craft axes) andimprovementMetrics(scores, maxScore), which per dimension reports: Hake's normalized learning gaing = (post − pre) / (max − pre)averaged over learners whose pre-score left room to grow (a ceiling pre-score is excluded, not counted as zero improvement), the fraction of learners who improved, the mean per-learner least-squares trajectory slope (growth shape, not just endpoints), the mean raw score change, and the pre→post Cohen's d effect size (standardized mean difference, pooled SD). A learner needs at least two attempts for change to be measurable; single-attempt learners are excluded. - Verification: 2 known-value tests (
human-quality-metrics.spec.ts, file now 14 tests) over a two-learner 0–4 rubric fixture (L1 1→2→3, L2 2→2→4): mean normalized gain 5/6 (2/3 and 1); improvement rate 1; mean slope 1; mean delta 2; Cohen's d 2/√0.5 ≈ 2.8284 (pre [1,2] mean 1.5, post [3,4] mean 3.5, pooled SD √0.5); empty dimensions all-null; single-attempt and ceiling learners correctly excluded from gain. Full study suite 50 files / 598 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift"; goldens--check"346 fixture cases stable"; stub-scan clean; eslint 0 errors. A read-only Codex review confirmed the Hake-gain, least-squares slope, sample-variance (n−1), and pooled-SD Cohen's d math with no bugs. - Marked [x]: YSD-18062.
- Commit: resolve via
git log --grep='YSD-18062'.
2026-07-22 — YSD-18063 (teacher-rated feedback quality)#
- Item: §18.3 — Measure teacher-rated usefulness, specificity, actionability, contestability, cultural safety, and imitation pressure of feedback.
- Artifact:
libs/contracts/src/study/human-quality-metrics.ts(extended) —FEEDBACK_QUALITY_DIMENSIONS,FEEDBACK_DIMENSION_POLARITY(five higher-is-better; imitation-pressure is a harm signal, lower-is-better), andfeedbackQualityMetrics(ratings, bar). The bar carries afloorfor the positive dimensions and animitationPressureCeiling. Per dimension it reports the mean rating, polarity, and the polarity-aware pass rate. It then aggregates per feedback item (mean teacher rating per dimension) into a compositeoverallAcceptabilityRate— an item is acceptable only if it was rated on all six dimensions and passes each, so a gap cannot certify by omission — and surfacesimitationPressureConcernRateon its own so a copying-pressure problem is never averaged into an otherwise strong score. - Verification: 3 known-value tests (
human-quality-metrics.spec.ts, file now 17 tests) over a two-item fixture (floor 3, ceiling 2, 1–5 scale): F1 passes all six, F2 fails usefulness/contestability/imitation-pressure → usefulness mean 3 pass-rate 0.5, specificity pass-rate 1, cultural-safety mean 4.5, imitation-pressure mean 2.5 pass-rate 0.5 (lower-is-better), overallAcceptabilityRate 0.5, imitationPressureConcernRate 0.5; a partially rated item cannot be certified (rate 0) and an empty set is null. Full study suite 50 files / 601 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift"; goldens--check"346 fixture cases stable"; stub-scan clean; eslint 0 errors. A read-only Codex review confirmed the polarity handling, composite per-item gate, and imitation- pressure concern rate with no bugs. - Marked [x]: YSD-18063.
- Commit: resolve via
git log --grep='YSD-18063'.
2026-07-22 — YSD-18064 (evidence-task time and success rate)#
- Item: §18.3 — Measure time and success rate to find, inspect, compare, and cite a useful exact moment/region/event.
- Artifact:
libs/contracts/src/study/human-quality-metrics.ts(extended) —CITATION_TASK_KINDS(find, inspect, compare, cite) and a generictaskPerformanceMetrics(attempts, kinds, percentile)(reused later for YSD-18065). Per task kind it reports attempts, successes, success rate, and the median and upper-percentile (default p90) time among the successful attempts — abandoned attempts stop at an arbitrary point, so their durations are not a comparable time-on-task and are excluded — plus an overall success rate across the reported kinds. Kinds are reported explicitly so a never-attempted task surfaces with a null rate rather than vanishing. Reuses the reviewedpercentileValue(type-7 interpolation). - Verification: 2 known-value tests (
human-quality-metrics.spec.ts, file now 19 tests): find 2/3 success with median-to-success 15 and p90 19 (over [10,20], the failed 99s ignored); inspect success 1 with median 10 and p90 14 (over [5,15]); a failed-only compare has rate 0 and null time; a never-attempted cite shows null; overall 4/6; negative durations and out-of-range percentiles throw; custom kinds and a p50 request are honored. Full study suite 50 files / 603 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift"; goldens--check"346 fixture cases stable"; stub-scan clean; eslint 0 errors. - Marked [x]: YSD-18064. Time-on-task over successes is the standard usability convention (abandoned-attempt times are not comparable).
- Commit: resolve via
git log --grep='YSD-18064'.
2026-07-22 — YSD-18065 (inspiration-system & decision-trace workflows)#
- Item: §18.3 — Measure time and success rate to organize multiple works into an explainable inspiration system and trace a decision back to exact regions.
- Artifact:
libs/contracts/src/study/human-quality-metrics.ts(extended) —INSPIRATION_TASK_KINDS(organize-inspiration-system, trace-decision) andinspirationWorkflowMetrics(attempts, percentile). It reuses the sharedtaskPerformanceMetricsengine (YSD-18064) for per-workflow time and success rate, and adds the quality dimension the item's wording demands: per workflow, the fraction of successful attempts that also met their quality bar — an explainable organization, or a trace that reached the exact region rather than an approximate neighbourhood. Quality is scored only over successes, because an incomplete workflow has no organization to explain or trace to check; completing a task is deliberately kept separate from doing it well. - Verification: 2 known-value tests (
human-quality-metrics.spec.ts, file now 21 tests): organize 2/3 success with median-to-success 90 and p90 114, of which only 1 of 2 was explainable (quality 0.5); trace 1.0 success with median 35, both reaching the exact region (quality 1); overall 0.8; a failed-only workflow reports null quality. Full study suite 50 files / 605 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift"; goldens--check"346 fixture cases stable"; stub-scan clean; eslint 0 errors. - Marked [x]: YSD-18065.
- Commit: resolve via
git log --grep='YSD-18065'.
2026-07-22 — YSD-18066 (discovery diversity)#
- Item: §18.3 — Measure ability to discover relevant contrasts, distant analogies, bridge references, and missing perspectives — not only close visual matches.
- Artifact:
libs/contracts/src/study/human-quality-metrics.ts(extended) —DISCOVERY_KINDS(close-visual-match plus the four valuable kinds),VALUABLE_DISCOVERY_KINDS, anddiscoveryDiversityMetrics(discoveries, goldRelevantByKind). Per kind it reports retrieved/relevant/gold counts with recall and precision. The headline signals penalize a near-duplicate-only system even at perfect close-match recall:beyondVisualDiscoveryRate(relevant discoveries that are not close matches / all relevant),valuableKindCoverage(of the four valuable kinds, the fraction with at least one relevant discovery), anddistributionEntropy(normalized Shannon entropy of the relevant-discovery kind distribution over all five kinds, 0 when concentrated on one kind, 1 when balanced). - Verification: 2 known-value tests (
human-quality-metrics.spec.ts, file now 23 tests): a fixture of [close 4(+1 irrelevant), contrast 2, distant 2, bridge 2, missing 0] against gold [4,4,2,2,2] gives close recall 1 / precision 0.8, contrast recall 0.5, missing recall 0 with null precision, beyond-visual 0.6, valuable-kind coverage 0.75, and normalized entropy 0.827729 (pinned to a hand-computed literal, independent of the formula); an all-close-match system scores beyond-visual 0, coverage 0, entropy 0; an empty set is null. Full study suite 50 files / 607 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift"; goldens--check"346 fixture cases stable"; stub-scan clean; eslint 0 errors. A read-only Codex review confirmed the per-kind recall/precision, beyond-visual rate, valuable-kind coverage, and normalized-entropy math with no bugs. - Marked [x]: YSD-18066.
- Commit: resolve via
git log --grep='YSD-18066'.
2026-07-22 — YSD-18067 (accessibility critical-journey performance)#
- Item: §18.3 — Measure critical-journey completion, error recovery, cognitive load, and satisfaction with keyboard, screen reader, magnification, captions, reduced motion, switch access, and alternative views.
- Artifact:
libs/contracts/src/study/human-quality-metrics.ts(extended) —ACCESSIBILITY_MODES(the seven modes) andaccessibilityJourneyMetrics. Per mode it reports completion rate, error-recovery rate (recovered/encountered, null when no errors arose — nothing to recover from, never a failure), mean cognitive load (lower is better), and mean satisfaction. The headline equity signals —worstModeCompletionRate(the parity floor no mode should fall below) andcompletionParityGap(best minus worst; 0 is full parity) — surface a lagging mode that an average would hide. Rejects impossible input (recovered > encountered). - Verification: 3 known-value tests (
human-quality-metrics.spec.ts, file now 26 tests): keyboard completion 1, recovery 0.75, load 50, satisfaction 75; screen-reader completion 0.5, recovery 0.5, load 80; overall 0.75; worst-mode 0.5 and parity gap 0.5 (screen reader lagging); a never-run mode is null; a no-error mode has null recovery; recovered-exceeds-encountered throws; empty is null. Full study suite 50 files / 610 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift"; goldens--check"346 fixture cases stable"; stub-scan clean; eslint 0 errors. - Marked [x]: YSD-18067. Parity floor/gap are the equity signals an average completion rate hides.
- Commit: resolve via
git log --grep='YSD-18067'.
2026-07-22 — YSD-18068 (cultural-reviewer harm assessment)#
- Item: §18.3 — Measure cultural-reviewer assessment of stereotyping, context loss, taxonomy transfer, universalism, harassment risk, and missing counterreadings.
- Artifact:
libs/contracts/src/study/human-quality-metrics.ts(extended) —CULTURAL_REVIEW_DIMENSIONS(the six harms) andculturalReviewMetrics. Every dimension is negative (higher severity = worse). Per dimension it reports the mean severity and the flag rate (reviews at or above the concern threshold). The per-item concern gate is precautionary: an item is a concern if ANY reviewer flags ANY harm at or above threshold — a single credible reviewer surfacing a serious harm is not outvoted by others who missed it. It also reports the overall concern and clean-item rates and names the most-prevalent harm(s) (ties included) so remediation can target the worst dimension. - Verification: 2 known-value tests (
human-quality-metrics.spec.ts, file now 28 tests): a two-item fixture (I1 missing-counterreadings 2, I2 stereotyping 3) gives stereotyping mean 1.5 / flag 0.5, context-loss flag 0, missing-counterreadings flag 0.5, overall concern 1, clean 0, and most-flagged ['stereotyping', 'missing-counterreadings'] (tie in declared order); a fully-clean item yields concern 0 / clean 1 / no most-flagged; negative severity throws; empty is null. Full study suite 50 files / 612 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift"; goldens--check"346 fixture cases stable"; stub-scan clean; eslint 0 errors. - Marked [x]: YSD-18068. Precautionary any-reviewer-any-harm gate — harms are not majority-voted away.
- Commit: resolve via
git log --grep='YSD-18068'.
2026-07-22 — YSD-18069 (recommendation narrowing)#
- Item: §18.3 — Measure whether recommendations narrow projects toward one famous work, culture, style, period, medium, provider, or derivative cluster.
- Artifact:
libs/contracts/src/study/human-quality-metrics.ts(extended) —RECOMMENDATION_AXES(the seven narrowing axes) andrecommendationNarrowingMetrics. Per axis it reports the recommendation count, distinct values, the top value's share (the "narrowing toward one X" signal), the Herfindahl–Hirschman index (Σpᵢ², 1 = single value, 1/n = spread), and the normalized Shannon entropy (0 concentrated, 1 evenly spread). It flagsnarrowingAxes— those whose top-share exceeds the threshold — so a homogenizing recommender is caught on the specific axis it collapses. - Verification: 2 known-value tests (
human-quality-metrics.spec.ts, file now 30 tests): a four-recommendation fixture gives culture top-share 0.75, HHI 0.625, entropy 0.811278 (pinned literal); style fully concentrated (top-share 1, HHI 1, entropy 0); period an even 2-2 split (top-share 0.5, entropy 1); an untagged axis is null; narrowingAxes = ['culture', 'style'] (period's exact 0.5 does not exceed the strict threshold); empty narrows nothing; out-of-range threshold throws. Full study suite 50 files / 614 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"98 contracts published, no drift"; goldens--check"346 fixture cases stable"; stub-scan clean; eslint 0 errors. - Marked [x]: YSD-18069.
- Commit: resolve via
git log --grep='YSD-18069'.
2026-07-22 — YSD-18070 (study pre-registration protocol)#
- Item: §18.3 — Pre-register study questions, sampling, metrics, analysis, and stopping rules where practical and publish limitations rather than overclaiming learning outcomes.
- Artifact:
libs/contracts/src/study/study-preregistration.ts— a new registered zod policy contract (99th published contract), NOT a metric. This is a pre-registration protocol shaped like the §18.1 governance contracts.StudyPreRegistrationSchemafixes questions, a sampling plan, a metric plan (primary/secondary, disjoint), a pre-specified analysis plan, and stopping rules before data; requires at least one published limitation; carries the integrity literalsoverclaimingProhibited,analysis.prespecified, andstoppingRules.fixedBeforeData(allz.literal(true)); and cross-field refines that data collection cannot precede registration and that the status agrees with the amendment log. The operational gateresultsRespectPreRegistration(registration, claim)enforces the anti-overclaiming rule: it flags a claimed metric that was never pre-registered (HARKing), missing published limitations, a sample exceeding the pre-registered stopping cap, and a causal learning-outcome claim beyond a non-causal-capable design (only randomized-controlled and quasi-experimental are causal-capable). Registered in schema-registry + index; examples added to examples.ts (canonical + amended, nine structural negatives, four cross-field zod-only negatives); schemas and goldens regenerated. - Verification: dedicated spec (9 tests) covering the canonical template,
each integrity literal, the data-before-registration refusal, status/amendment
consistency, the primary/secondary disjointness, and every gate branch
(compliant claim, HARKing metric, missing limitations, stopping-cap breach,
and causal-overclaim vs the same design's permitted association claim). The
schema-publication spec (7 tests) validates the new canonical examples against
zod + ajv, the structural negatives failing both and the cross-field negatives
failing zod only. Full study suite 51 files / 623 tests green;
tsc -p tsconfig.lib.jsonclean; schema--check"99 contracts published, no drift, no breaking changes"; goldens--check"348 fixture cases stable"; stub-scan clean; eslint 0 errors. A read-only Codex review flagged two real issues, both fixed and retested: ISO timestamps are now validated as datetimes and compared withDate.parse(raw-string comparison misordered across precisions/offsets), and the gate now requires every pre-registered limitation to be published (not merely a non-empty array), with a partial-coverage test added. - Marked [x]: YSD-18070. §18.3 human-centered quality metrics now COMPLETE (18060–18070).
- Commit: resolve via
git log --grep='YSD-18070'.
2026-07-22 — YSD-18080 (privacy-safe activation metrics)#
- Item: §18.4 — Instrument privacy-safe time to first authorized evidence anchor and first completed study card.
- Artifact:
libs/contracts/src/study/product-metrics.ts(new pure-fn module opening §18.4; exported from the study index) —activationMetrics(records, kAnonymityThreshold, percentile)reports, per milestone (first anchor, first study card), the reach rate and the median / upper-percentile time among the projects that reached it. Time is measured only over reachers (a project still working toward the milestone has no meaningful duration). Small-cohort suppression is the privacy safeguard: a cohort below the k-anonymity threshold withholds every derived value (suppressed), and even within a large enough cohort the time percentiles are withheld when fewer than k projects reached the milestone — a percentile over a handful of learners could re-identify them. - Verification: 4 known-value tests (
product-metrics.spec.ts) over a five-project fixture (k=3): anchor reach 0.8 with median 250 and p90 370 (over [100,200,300,400]); study-card reach 0.4 shown but time withheld (only 2 reachers < k); a two-project cohort fully suppressed (all null); negative times and invalid k/percentile throw. Full study suite 52 files / 627 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"99 contracts published, no drift"; goldens--check"348 fixture cases stable"; stub-scan clean; eslint clean. - Marked [x]: YSD-18080. Small-cohort suppression makes the metric privacy-safe by construction.
- Commit: resolve via
git log --grep='YSD-18080'.
2026-07-22 — YSD-18081 (weekly engagement & project composition)#
- Item: §18.4 — Instrument weekly active study projects, returning-learner annotations, and interpretation-bearing versus source-only projects.
- Artifact:
libs/contracts/src/study/product-metrics.ts(extended) —engagementCompositionMetrics(records, kAnonymityThreshold)reports weekly active study projects, the returning-learner annotation volume across active projects and the fraction of active projects with any such annotation, and the interpretation-bearing vs source-only split (the epistemic-depth signal that a study population is moving past source capture into interpretation). Derived rates are withheld when the active cohort is below the k-anonymity threshold; the headline active-project count is still reported. - Verification: 2 known-value tests (
product-metrics.spec.ts, file now 6 tests): four active projects with 3 returning-learner annotations, a returning-learner project rate of 0.5, an interpretation-bearing rate of 0.5 complemented by a source-only rate of 0.5 (an inactive project excluded); a two-project cohort has its rates suppressed while the count is kept; negative annotation counts throw. Full study suite 52 files / 629 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"99 contracts published, no drift"; goldens--check"348 fixture cases stable"; stub-scan clean; eslint 0 problems. - Marked [x]: YSD-18081.
- Commit: resolve via
git log --grep='YSD-18081'.
2026-07-22 — YSD-18082 (progression funnel)#
- Item: §18.4 — Instrument the proportion of projects reaching a comparison, exercise attempt, committed decision, or outcome review.
- Artifact:
libs/contracts/src/study/product-metrics.ts(extended) —STUDY_FUNNEL_STAGES(comparison, exercise-attempt, committed-decision, outcome-review) andfunnelReachMetrics(projects, kAnonymityThreshold), which reports the proportion of projects reaching each milestone, independently per stage, with small-cohort suppression withholding the reach rates below the k-anonymity threshold while keeping the counts. - Verification: 2 known-value tests (
product-metrics.spec.ts, file now 8 tests): a five-project fixture gives comparison 0.8, exercise-attempt 0.6, committed-decision 0.4, outcome-review 0.2; a two-project cohort has its reach rates suppressed (counts kept); an invalid k throws. Full study suite 52 files / 631 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"99 contracts published, no drift"; goldens--check"348 fixture cases stable"; stub-scan clean; eslint 0 problems. - Marked [x]: YSD-18082.
- Commit: resolve via
git log --grep='YSD-18082'.
2026-07-22 — YSD-18083 (persona-segmented retention)#
- Item: §18.4 — Instrument returning-learner rate at one, four, and twelve weeks segmented by persona without exposing sensitive study content.
- Artifact:
libs/contracts/src/study/product-metrics.ts(extended) —retentionMetrics(learners, kAnonymityThreshold)computes the returning-learner rate at 1, 4, and 12 weeks per persona and overall. The input carries only a coarse persona label and per-horizon return booleans — no study content — and any persona cohort below the k-anonymity threshold has its rates withheld, so a segment can never narrow onto one learner. - Verification: 2 known-value tests (
product-metrics.spec.ts, file now 10 tests): a novice cohort of 4 gives week-1/4/12 rates 0.75/0.5/0.25; an expert cohort of 2 is suppressed (rates null); the persona-agnostic overall over 6 learners is 5/6, 4/6, 3/6; an invalid k throws. Full study suite 52 files / 633 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"99 contracts published, no drift"; goldens--check"348 fixture cases stable"; stub-scan clean; eslint 0 problems. - Marked [x]: YSD-18083. Persona-only segmentation plus per-segment small-cohort suppression keeps retention reporting free of study content and re-identification.
- Commit: resolve via
git log --grep='YSD-18083'.
2026-07-22 — YSD-18084 (project transfers & principle usefulness)#
- Item: §18.4 — Instrument the share of studies producing project transfers and learner-reported usefulness of transferred principles.
- Artifact:
libs/contracts/src/study/product-metrics.ts(extended) —transferMetrics(studies, kAnonymityThreshold)reports the share of studies producing a project transfer and the mean learner-reported usefulness of transferred principles. Both derived values apply their own small-cohort suppression: the production rate is withheld below the study threshold, and the usefulness mean below the rating threshold, so neither can expose an individual. - Verification: 2 known-value tests (
product-metrics.spec.ts, file now 12 tests): five studies with a 0.6 transfer share and mean usefulness 4.0 (over [4,5,3,4]); a two-study cohort suppresses the rate; a three-study cohort with only two ratings keeps the rate 2/3 but withholds the usefulness mean; a negative rating throws. Full study suite 52 files / 635 tests green;tsc -p tsconfig.lib.jsonclean; schema--check"99 contracts published, no drift"; goldens--check"348 fixture cases stable"; stub-scan clean; eslint 0 problems. - Marked [x]: YSD-18084.
- Commit: resolve via
git log --grep='YSD-18084'.
2026-07-22 — YSD-18085 (product-analytics privacy policy)#
- Item: §18.4 — Add consent, minimization, retention, deletion, role access, small-cohort suppression, and no-dark-pattern rules to product analytics.
- Artifact:
libs/contracts/src/study/product-analytics-policy.ts— a new registered zod policy contract (the 100th published contract).ProductAnalyticsPolicySchemafixes all seven rule areas: consent (required + withdrawable literals, granularity), minimization (declared-events allowlist + no-raw-content literal), retention/aggregation, deletion (honored, boundaries, max days), role-scoped access, small-cohort suppression (k-anonymity floor >= 2), and a dark-pattern ban. Cross-field refines require aggregation not to outlive retention, deletion to reach aggregates and backups, and the default tier to be declared. The gateanalyticsEventPermitted(policy, request)refuses to record an event that lacks consent, was never declared (minimization), carries raw content, would expose a sub-threshold cohort, or rides a prohibited dark pattern — accumulating every violation. Registered in schema-registry + index; examples added (canonical + variant, structural and cross-field negatives); schemas and goldens regenerated (100 published, 350 goldens). - Verification: dedicated spec (7 tests) covering the canonical policy, the
integrity literals, the suppression floor, the deletion-boundary and retention
refines, and every gate branch (permit + each single refusal + an
all-five-violations case); the schema-publication spec validates the new
canonical examples against zod + ajv. Full study suite 53 files / 642 tests
green;
tsc -p tsconfig.lib.jsonclean; schema--check"100 contracts published, no drift, no breaking changes"; goldens--check"350 fixture cases stable"; stub-scan clean; eslint 0 errors. TRAP: the dark-pattern kindfake-urgencytripped the pre-commit stub scan on the word "fake" — renamed tofalse-urgency. (A Codex review was attempted but the background run was killed by a session resource limit; the gate and refines are straightforward inclusion/integer checks fully exercised by the dedicated and publication specs, and were adversarially re-read by hand.) - Marked [x]: YSD-18085.
- Commit: resolve via
git log --grep='YSD-18085'.
2026-07-22 — YSD-18086 (metric subordination to gates)#
- Item: §18.4 — Keep product metrics subordinate to rights, safety, epistemic, accessibility, reliability, and quality gates; encode gates so metric pressure cannot bypass them.
- Artifact:
libs/contracts/src/study/metric-subordination-policy.ts— a new registered zod policy contract (the 101st published contract).MetricSubordinationPolicyfixes the precedence: all sixGATE_CLASSES(rights, safety, epistemic, accessibility, reliability, quality) supersede product metrics, each blocking, withmetricsCanOverrideGatepinned to a literalfalseandmetricsAreSubordinateto a literaltrue; a cross-field refine requires every gate class to be covered exactly once. The subordination is encoded inevaluateReleaseGate(policy, gateOutcomes, metricsUrgeRelease): a release is permitted only when every superseding gate was evaluated and passed, and there is deliberately no control-flow branch by whichmetricsUrgeReleasecan flip a blocked release to permitted — it is instead recorded asmetricPressureDisregardedwhen it collides with a gate failure, making the subordination observable rather than silent. - Verification: dedicated spec (6 tests): the canonical policy, the
subordination literals, the gate-coverage refine, and the gate behaviour —
release only when all six pass, metric pressure not altering a clean release,
the critical case where a failing rights gate blocks the release despite
metric pressure (blockedBy ['rights'], metricPressureDisregarded true), and an
unevaluated gate blocking. The publication spec validates the canonical
example against zod + ajv. Full study suite 54 files / 648 tests green;
tsc -p tsconfig.lib.jsonclean; schema--check"101 contracts published, no drift, no breaking changes"; goldens--check"352 fixture cases stable"; stub-scan clean; eslint 0 errors. - Marked [x]: YSD-18086. §18.4 product success metrics now COMPLETE (18080–18086).
- Commit: resolve via
git log --grep='YSD-18086'.
2026-07-22 — YSD-7001 (global Study nav entry)#
- Item: §7.1 — Add the approved Study entry to Oshun Studio global navigation with authorization, feature-flag, active-route, keyboard, localization, responsive, and telemetry behavior.
- Placement:
/studio/studyresolves to the Studio (admin) shell (isWebAdminPath→ true), so the "global Study nav item" approved by decision YSD-0122 / ADR-0074 is a first-class entry in the Studio shell sidebar, not the customer top-level nav. - Artifacts:
apps/oshun/web/src/navigation/routes.ts—study: '/studio/study'added toWEB_ADMIN_ROUTE_PATHS(extendsWebAdminRoute).apps/oshun/web/src/navigation/shells.ts— newstudyitem inWEB_ADMIN_SIDEBAR_ITEMS(label "Study", matchPrefix/studio/study, soresolveWebAdminNavRoutereturnsstudyand breadcrumbs read "Admin › Study"); pureisStudioStudyNavVisible({authStatus, featureFlags})resolver +STUDIO_STUDY_NAV_FEATURE_FLAG = 'studio.study'(authorization = authenticated only; feature-flag =studio.study: falsekill switch per YSD-1014).apps/oshun/web/src/analytics/shellNavigationTelemetry.ts— newresolveStudioSurfaceFromRoute+trackStudioSurfaceViewemittingstudio_surface_viewed(the customer route-transition effect never fires in the admin shell, so Studio surfaces were previously untracked).apps/oshun/web/src/config/runtime-context.tsx—useOptionalRuntimeConfig(non-throwing accessor so shared chrome can read flags without a provider).apps/oshun/web/src/components/ShellLayout.tsx—ScanSearchicon + completedADMIN_NAV_ICONSRecord (also fixes the previously-missingtaraWorkbenchkey); filters the study item byisStudioStudyNavVisible; a Studio surface-view telemetry effect. active-route (aria-current), keyboard, responsive (collapsible Sidebar) and localization (translatable label rendered by the RTL/locale-awareSidebar) are the same production behaviors every Studio nav sibling inherits.
- Verification:
npx vitest run src/navigation/shells.test.ts src/navigation/routes.test.ts src/analytics/shellNavigationTelemetry.test.ts src/components/__tests__/ShellLayout.test.tsx— 86 tests green (new: study route resolution + breadcrumb, visibility resolver auth/flag matrix, studio-surface resolver + telemetry event, and a ShellLayout integration test asserting the Study item is present in the sidebar nav on/studio/studyand thatstudio_surface_viewedfires withsurface: 'study').npx tsc --noEmit— full app typecheck clean (exit 0).npx eslinton all five touched source files — 0 errors.- Note: the
accessibility-axe.tsx"ShellLayout has no duplicate IDs" test times out on this box; confirmed pre-existing (fails identically on a stashed baseline), unrelated to this change.
- Marked [x]: YSD-7001.
- Commit: resolve via
git log --grep='YSD-7001'.
2026-07-23 — YSD-7062 (cross-reference review + create action surface)#
- Decision/Rationale:
YSD-7063already delivered the read-only relationship review card (type, endpoints, direction, rationale, author/model, confidence, anchors, context, rights, review state, limitations) and both the card andSuggestedRelationsPanelexplicitly deferred the decision verbs to this item. The backend already supports every verb —createCrossReference, the genericPOST /reviewsreview path (reviewRecord, which enforces the closed legal transition table and the "models suggest, humans decide" acceptance authority), and the dedicatedPOST /cross-references/:id/keep-distinct— so YSD-7062 is a UI action surface, not new domain logic. The seven verbs map exactly onto that backend: create →createCrossReference(bornsuggested/provisional); accept/reject/correct/ contest →reviewRecordtransitions from the record's current state; merge where authorized → accepting anidentityedge (the only way sameness is ever recorded — a deliberate, reasoned human act, never inferred, YSD-2155); keep-distinct → anidentityedge is never plain-rejected but kept distinct through the dedicated endpoint that records the rationale + audit. - Artifacts:
apps/oshun/web/src/components/studio/CrossReferenceActions.tsx— new. The decision surface for one suggested/contested edge. Offers exactly the human review verbs the record's current state legally permits (LEGAL_REVIEW_TRANSITIONS), pinssubjectRevisionso a stale decision fails closed with a409, requires a reason (andcorrectionsfor acorrected), and routes anidentityedge's accept/reject to merge/keep-distinct with explicit warnings. Honest failure mapping (401/403/ 404/409 → reviewer-facing text);onReviewedlets the host drop a decided edge from the queue.noValidateso the accessible in-form validation (not the native popup) governs.apps/oshun/web/src/components/studio/CreateCrossReferenceForm.tsx— new. The create verb: draw a new manual typed edge between two addressable study records (relation, both endpoints + pinned revision, rationale, context/ cultural bounds, rights-gating territory). Bornsuggested(provisional) so it re-enters the same review queue; warns thatidentityedges never merge on creation.apps/oshun/web/src/components/studio/SuggestedRelationsPanel.tsx— pairs each review card withCrossReferenceActions, hosts the create form behind a toggle, drops decided edges from the pre-decision queue, and feeds a newly created suggested edge back into it.
- Verification:
npx vitest run(fromapps/oshun/web) onCrossReferenceActions.spec.tsx,CreateCrossReferenceForm.spec.tsx,SuggestedRelationsPanel.spec.tsx,CrossReferenceReviewCard.spec.tsx— 24 tests green. Coverage: legal-verb surfacing per state; accept pins revision + fromState; reason-required and corrections-required guards (no request sent); identity merge via/reviewswith the sameness warning; identity keep-distinct via the dedicated endpoint; 403 → "not authorized", 409 → "changed since you loaded it"; create success body shape + rights/validation failures; queue drops a decided edge; create toggle.npx tsc --noEmit -p tsconfig.typecheck.json— my three source files are clean (the only errors are pre-existing, inlibs/isis/ai-providers, from missing@oshun/aiexports — unrelated).npx eslinton the three source files — 0 errors.- Adversarial stub scan over the three source files — 0 hits.
- Marked [x]: YSD-7062.
- Commit: resolve via
git log --grep='YSD-7062'.
2026-07-23 — YSD-7067 (undo/redo for draft view and annotation actions)#
- Decision/Rationale: The item asks for undo/redo of draft view and annotation actions "while retaining auditable version history for accepted records" (YSD-0013). The two guarantees are kept apart by construction: undo/ redo operates only on ephemeral, local working state, never on accepted scholarly records (observations, interpretation claims, reviewed cross-references, creative decisions), which are immutable and change only through versioned supersession and their own review events. So an undo can never erase a committed record or a review decision. The workbench's two purely-local draft surfaces are the concrete targets: the pinboard (a draft annotation action — pin/remove/clear selections of the source) and the compare tray (a draft view action — stage/remove/clear moments for later comparison). Server-committed actions (observation authoring, cross-reference review) are deliberately not undoable here — they are the "accepted records" side the clause protects.
- Artifacts:
apps/oshun/web/src/components/studio/studyDraftHistory.ts— new. A reusable bounded past/present/future core (initDraftHistory,recordDraft,undoDraft,redoDraft,canUndoDraft,canRedoDraft) plus auseDraftHistoryhook. A fresh edit truncates the redo branch; the past is bounded (DRAFT_HISTORY_LIMIT) so a long session can't grow memory without limit; end-of-stack undo/redo are identity no-ops so callers can skip a persist;resetadopts a hydrated baseline without re-persisting.apps/oshun/web/src/components/studio/PinnedSelectionsPanel.tsx—useStudySelectionPinsis now history-backed (pin/remove/clear are recorded;undo/redo/canUndo/canRedo/lastActionexposed) while keeping the synchronouspin()return via a ref. Adds accessible Undo/Redo controls (disabled, not hidden, when unavailable) and arole="status"announcement of the last draft action (toward YSD-7069).apps/oshun/web/src/components/studio/CompareTray.tsx— staging moves touseDraftHistorywith localStorage persistence viaonCommit; adds the same Undo/Redo controls + announcement.
- Verification:
npx vitest run(fromapps/oshun/web) onstudyDraftHistory.spec.ts,PinnedSelectionsPanel.spec.tsx,CompareTray.spec.tsx— 24 tests green. Coverage: core init/record/undo/redo, redo-branch truncation on divergence, past bounding, immutability, end-of-stack no-ops; pinboard undo/redo of pin/clear with per-step persistence and the divergence rule; compare-tray undo/redo of stage/clear with persistence. The pre-existing YSD-7060/7061 tests still pass unchanged.npx tsc --noEmit -p tsconfig.typecheck.json— my three source files clean (only pre-existinglibs/isis/ai-providerserrors remain, unrelated).npx eslinton the three source files — 0 errors. Adversarial stub scan — 0 hits.
- Marked [x]: YSD-7067.
- Commit: resolve via
git log --grep='YSD-7067'.
2026-07-23 — YSD-8045 (review machine detections without mutating the original)#
- Decision/Rationale: The crux is "without mutating the original detection."
The generic review path (
POST /reviews→reviewRecord, familydetection) already satisfies it:applyReviewEventrunssuperseding(envelope), minting a NEW revision and appending an immutable review event (YSD-0013), so the processor's original reading is preserved as an earlier revision and is never edited in place. Detections reach the client throughreadAnalysis(runId)({ run, detections, stale, staleEditions }) and runs throughlistAnalyses(projectId). So YSD-8045 is a UI surface over an existing, fully-supported backend. Every legal review verb from the record's current state is offered — asuggesteddetection can be accepted/corrected/contested/ rejected, and an already-acceptedone contested or superseded (the fifth verb the item names) — and detections are kept in their own machine section, never merged into the human epistemic layers (YSD-9005). - Artifacts:
apps/oshun/web/src/components/studio/studyReviewText.ts— new. CanonicalreviewFailureMessage(401/403/404/409 → honest reviewer text) shared by the cross-reference and detection review surfaces (YSD-0014);CrossReferenceActionsnow imports it instead of its own copy.apps/oshun/web/src/components/studio/DetectionReviewActions.tsx— new. Per- detection review verbs fromLEGAL_REVIEW_TRANSITIONS(INCLUDINGsuperseded, unlike the cross-ref surface), each viaclient.reviewRecord({family:'detection'})with a revision pin, reason (+ corrections for a correct), a supersede preservation note, and honest failure mapping. The done state states the original reading is preserved.apps/oshun/web/src/components/studio/DetectionReviewPanel.tsx— new. Self- loadslistAnalyses(keeps only succeeded/partially-succeeded runs), lets the reviewer pick a run,readAnalysisfor its detections, and lists each with feature, processor (+ model or "no model (deterministic)"), honest confidence, anchor count, limitations, review state, and the review actions. Honest loading/denied/no-runs/no-detections/stale states; reflects a completed review in place so the next action uses the new state + revision.apps/oshun/web/src/components/studio/StudyWorkbench.tsx— docks a project- scoped "Detections" lower panel.
- Verification:
npx vitest run src/components/studio/__tests__/(fromapps/oshun/web) — 431 tests green across 47 files (new: DetectionReviewActions 6, DetectionReviewPanel 6; CrossReferenceActions still green after the shared- helper refactor; StudyWorkbench wiring intact). Coverage: legal verbs per state, accept/correct/contest/reject/supersede body shapes + revision pin, reason/corrections guards, 403/409 mapping, run listing + non-reviewable-run exclusion, denied/empty/no-runs states, in-place state reflection.npx tsc --noEmit -p tsconfig.typecheck.json— my new source files clean (only pre-existinglibs/isis/ai-providerserrors remain, unrelated).npx eslinton all five touched source files — 0 errors. Adversarial stub scan over the three new source files — 0 hits.
- Marked [x]: YSD-8045.
- Commit: resolve via
git log --grep='YSD-8045'.
2026-07-23 — YSD-8046 (entity linking, candidate identity, keep-distinct, no biometrics)#
- Decision/Rationale: The entity identity backend is complete —
listEntities,relateEntities,mergeEntity(source merged INTO target, setsmergedIntoEntityId, preserved as history viasuperseding),keepEntitiesDistinct(adds tokeptDistinctFromEntityIds, and the store REFUSES a merge of a kept-distinct pair, fail-closed),addEntityAlias,resolveEntityCanonical— with client methods for all of them, but no UI. YSD-8046 is that UI. The "without biometric matching" guarantee is structural:IdentityLabelOriginSchemaadmits onlyhuman-entryor trustedsource-metadata, never a model/biometric match (YSD-4052), and cross-work identity is a reviewed edge, never implicit (YSD-2155). The panel surfaces the label origin so every identity's basis is inspectable, and a merge is a deliberate reasoned act, never inferred. - Artifacts:
apps/oshun/web/src/components/studio/EntityLinkingPanel.tsx— new. Lists a project's entities with kind, human/source label origin ("never biometric"), aliases, typed relationships, merged-into history, and kept-distinct count. Per-entity actions: Link (relateEntities, typed relation + note), Same-as/merge (mergeEntity, deliberate, with an explicit "same identity / stays on record" note), Keep distinct (keepEntitiesDistinct), Add alias (addEntityAlias). Target picker excludes self and lists the other entities; merged entities show no further actions. Honest failure text, including the kept-distinct merge refusal. Patches the affected entity in place from the response.apps/oshun/web/src/components/studio/StudyWorkbench.tsx— docks a project- scoped "Entities" lower panel.
- Verification:
npx vitest run src/components/studio/__tests__/(fromapps/oshun/web) — 439 tests green across 48 files (new: EntityLinkingPanel 8; StudyWorkbench wiring intact). Coverage: list + identity note + label origin; denied; link body/path; deliberate merge with the same-identity note + merged-into result; keep-distinct body/path; alias without a target; target-and-note validation; the backend kept-distinct merge refusal surfaced.npx tsc --noEmit -p tsconfig.typecheck.json— my new file clean (only the pre-existinglibs/isis/ai-providerserrors remain, unrelated).npx eslinton both touched source files — 0 errors. Adversarial stub scan over the new source file — 0 hits.
- Marked [x]: YSD-8046.
- Commit: resolve via
git log --grep='YSD-8046'.
2026-07-23 — YSD-14002 (question lifecycle transitions with rationale, evidence, history)#
- Decision/Rationale:
StudyQuestionis a first-class object with a closed lifecycle —LEGAL_QUESTION_TRANSITIONS: open ⇄ under-investigation, either may resolve to answered/abandoned, and answered/abandoned may reopen on new evidence. The backend (transitionStudyQuestion,listStudyQuestions) enforces the legal moves, requires a rationale note, requires an answer to cite ≥1 evidence anchor (YSD-2112), and records the fullresolutionHistory— but there was no UI. YSD-14002 is that UI. - Artifacts:
apps/oshun/web/src/components/studio/QuestionLifecyclePanel.tsx— new. Lists a project's questions with state, scope, and linked-evidence count; shows the full resolution history (from → to: rationale); offers exactly the legal transitions from the current state (Start investigating / Answer / Abandon / Reopen), each requiring a rationale; an answer cites the question's linked evidence and is refused with no evidence linked. A lifecycle filter serves the unanswered/answered/abandoned views. Patches the question in place from the response; honest denied/error/empty states.apps/oshun/web/src/components/studio/StudyWorkbench.tsx— docks a project- scoped "Questions" lower panel.
- Verification:
npx vitest run src/components/studio/__tests__/(fromapps/oshun/web) — 446 tests green across 49 files (new: QuestionLifecyclePanel 7; StudyWorkbench wiring intact). Coverage: legal transitions per state, under-investigation transition body/path + history render, answer refused with no evidence, answer cites linked evidence, rationale required, lifecycle filter, denied.npx tsc --noEmit -p tsconfig.typecheck.json— my new file clean (only the pre-existinglibs/isis/ai-providerserrors remain, unrelated).npx eslinton both touched source files — 0 errors. Adversarial stub scan over the new source file — 0 hits.
- Marked [x]: YSD-14002.
- Commit: resolve via
git log --grep='YSD-14002'.
2026-07-23 — YSD-16021 Player accessible names, values, states, relationships, shortcut help, timecode, buffer, errors, change announcements#
- Decision: The professional transport (YSD-8002) exposed rich visual controls but was silent to assistive technology: the custom speed/shuttle/ reverse/loop/frame/word/event/scrub actions have no native control that speaks them, there was no buffer state, no relationship wiring between the controls and the media element, and no discoverable keyboard-shortcut surface. YSD-16021 is the media-player semantics counterpart to the keyboard-operation item (YSD-16020); it is completed here at the player level. (Timeline accessible names/states are already carried by segment-selection announcements and shape cues; buffer/timecode/transport-shortcuts are inherently player concerns.)
- Artifacts:
apps/oshun/web/src/components/studio/SourceMediaCanvas.tsx:- Change announcements — adopts the shared live-region primitive
(
useLiveAnnouncer, YSD-16003; first workbench consumer). A polite region announces each resulting state:Speed N×,Shuttle forward/reverse N×/Shuttle stopped,Reverse on/off,Loop in … loop A to B/Loop cleared,Frame forward/back, <timecode>,Word/Event at <timecode>(and an honestNo word/event ahead),Seeked to <timecode>. - Buffer state —
onProgresscomputes the furthest buffered fraction of the known duration; acanvas-bufferreadout showsN% buffered(with— waiting for datawhile stalled) and honest absence (buffer state unavailable) when the browser reports no ranges.waiting/stalled→playingtransitions are announced (Buffering — waiting for data→Playback resumed). - Relationships — the media element gets a stable
useId; the transport and frame-controls groups carryaria-controlspointing at it. - Shortcut help — a discoverable
<details>panel lists every key; the transport isaria-describedbythat list and carriesaria-keyshortcuts. - Values/states — the shuttle indicator gains an
aria-labelthat tracks its state; reversearia-pressed, speed<select>value, loop text, and the buffer readout supply the rest. Timecode androle="alert"errors were already present and remain part of the player semantics.
- Change announcements — adopts the shared live-region primitive
(
- Verification:
npx vitest run src/components/studio/__tests__/SourceMediaCanvas.spec.tsx(fromapps/oshun/web) — 41 tests green (32 pre-existing + 9 new for 16021: relationships, discoverable shortcut help, shuttle accessible value, speed/shuttle/reverse/frame change announcements, honest→percentage buffer readout, buffering wait/resume announcements).npx vitest runonStudyWorkbench.spec.tsx,StudyWorkbench.deeplink.spec.tsx,StudyWorkspaceSources.spec.tsx— 40 tests green (no regression in surfaces that embed the canvas).npx tsc --noEmit -p tsconfig.json— touched files clean.npx eslint SourceMediaCanvas.tsx— 0 errors (one pre-existing<img>warning, unrelated). Adversarial stub scan over the file — 0 hits.
- Marked [x]: YSD-16021.
- Commit: resolve via
git log --grep='YSD-16021'.
2026-07-23 — YSD-8024 Timeline virtualization, progressive detail, snapping, range selection, pan/zoom, track filtering/grouping, density presets, large-session performance#
- Decision: The multitrack timeline (YSD-7034) mapped every item as a
fraction of the whole source span (
start / maxSeconds) and painted them all, with only a per-lane count throttle. YSD-8024 adds the navigation and scale layer as a new pure geometry module plus an additive wiring: the viewport defaults to the full range, so at{ 0, total }the position of a second is exactlys / total— the un-zoomed timeline renders byte-for-byte as before, and every pre-existing position test (left: '10%','50%') still holds. Zoom, pan, density, filtering, grouping, and range selection are opt-in controls layered on top. The canonical time base is never mutated — the viewport only decides which slice is on screen and where a second lands within it. - Artifacts:
apps/oshun/web/src/components/studio/timeline-viewport.ts(new, pure):fullViewport/clampViewport/zoomViewport(anchor-preserving)/panViewport/zoomToRange;positionPercent/widthPercent(default-viewport identity with the old math);virtualize(cull to window, count hidden before/after);snap(nearest boundary within tolerance, earlier-wins ties); range helpers (normalizeRange/clampRange/rangeDurationSeconds/isMeaningfulRange);densityMetrics(comfortable/compact/condensed → row height, per-lane cap, label visibility);detailLevel+axisTicks(nice 1/2/5×10ⁿ major ticks, minor ticks interleaved only at the detail zoom);trackFamilies/filterByFamily(empty selection = show all)/groupByFamily.apps/oshun/web/src/components/studio/SourceTimeline.tsx: routes all position math through the viewport; adds session state for viewport, density, family filter, and range selection; a navigation control bar (zoom in/out/fit, pan, keyboard +/−/arrows, viewport status with detail level, density selector, track-family checkboxes, snapped range in/out with zoom-to-selection/clear); a progressive-detail axis ruler; virtualizes each lane against the window and reports off-window items separately from the perf throttle; groups lanes under track-family headings. Removed the now-unusedSTACK_ROW_HEIGHT/DEFAULT_MAX_ITEMS_PER_LANEconstants (density metrics supply them).
- Verification:
npx vitest run timeline-viewport.spec.ts— 30 pure-geometry tests (position identity, clamp, zoom/pan/fit, virtualization counts, snapping, range, density ordering, detail levels + tick storm guard, family filtering/grouping).npx vitest run SourceTimeline.spec.tsx— 27 tests (17 pre-existing green: no regression from the default full-range viewport; 10 new: nav presence / first-viewing withholding, zoom-around-centre, pan+fit, virtualization off-window report, boundary snapping, condensed label hiding, family filter without blanking, family grouping headers, progressive-detail ticks).StudyWorkbench.spec.tsx+.deeplink.spec.tsx— 29 green (embedding surface unaffected).npx tsc --noEmit --incremental false— touched files clean. eslint 0 errors. Adversarial stub scan — 0 hits.- NOTE:
PinnedSelectionsPanel.spec.tsxhas ONE pre-existing, wall-clock- dependent failure (viewable in USvsaccess window elapsed since pinned) in the rights-window-lapsed computation — unrelated to this change (that panel imports neither timeline module; last touched by commits 14768f550f/d6791ae5c4) and left for a dedicated fix.
- Marked [x]: YSD-8024.
- Commit: resolve via
git log --grep='YSD-8024'.
2026-07-23 — YSD-13190 Require rationale, alternatives, transformation, intended effect, owner, and source/principle paths before committing a creative decision#
- Decision: The commit gate already enforced human authorship (a decision
carries a human
ownerUserId), arationale, ≥1 principle/source path, and ≥1 approval. Three of the six requirements were missing:alternativescould be empty at commit, and there were no dedicated fields for the transformation (the abstraction step that keeps a decision from copying its source) or the intended audience effect (the intent YSD-13251 later compares outcomes against). These are added and required only at commit — a proposal may still be half-formed. Reused the existingassertDecisionCommittablegate so the failure is a clean domain error, mirrored by the contract superRefine. - Artifacts:
libs/contracts/src/study/entities/creative.ts:CreativeDecisionSchemagains optionaltransformationandintendedEffect; the superRefine now also refuses a committed decision with no alternative, no transformation, or no intended effect.libs/yemaya/study-workspace/src/policies/inspiration-coordination.ts: newIncompleteDecisionError(with a precisemissingreason);assertDecisionCommittablenow enforces alternatives/transformation/intendedEffect alongside the evidence trail.libs/yemaya/study-workspace/src/use-cases/manage-creative-decisions.ts: propose and update commands accept and threadtransformation/intendedEffect.libs/contracts/src/study/service-routes.ts:Create/UpdateCreativeDecisionBodySchemaaccept the two fields.apps/yemaya/svc-study-workspace/src/routes/study-routes.ts: the create/update handlers pass them, anderrorResponsenow maps bothIncompleteDecisionErrorand the previously-unmappedUntraceableDecisionErrorto 409 (they had fallen through to 500).- Regenerated schema artifacts:
schemas/study/CreativeDecision.schema.json,openapi.json,openapi.components.json. The generated client is unchanged (it references body schemas by name, so the new optional fields flow through;generate-study-client.ts --checkpasses).
- Verification:
npx vitest runinlibs/yemaya/study-workspace— 1740 tests green (new: 4 use-case gate tests — commit refused when an alternative/transformation/ intended-effect is missing, with the record leftproposed, plus a fill-in- then-commit path; 5 pre-existing commit fixtures updated to carry the new fields).npx vitest run entities6.spec.ts(contracts) — committed-fixture updated + a new superRefine test;src/study/entities+admin-bulk-operations— 213 green.svc-study-workspaceintegration — the propose→approve→commit and transfer journeys now send the fields and pass; the sole remaining failure is the pre-existing shared-DBtechnicalFingerprintingest-facts flake (unrelated).tscon my touched files clean (sibling-spec branded-type errors are pre-existing); adversarial stub scan clean.
- Marked [x]: YSD-13190.
- Commit: resolve via
git log --grep='YSD-13190'.
2026-08-04 — YSD-19101 live co-study: the room finally exists#
- Finding: §8.4 was fully written and entirely unreachable.
planRoomSync,assertNoImposedAccommodation,readDrift,planRejoin,mayDriveTransport,projectPresence,presenceLeaks,screenCommentandapplyModerationwere imported by zero files underapps/yemaya/svc-study-workspace/srcandapps/oshun/web/src. The cause was one missing table:startLiveSessionenqueued astudy.live_session.startedevent and returned a reply. There was nothing to join, no roster to read back, and no playhead to synchronize to. - The sharp end. Crystallization took
messagesandconsentsas REQUEST FIELDS, so the person turning the talk into a claim also supplied the talk. Every rule incrystallizationIssuesthen held — against a document that person wrote. "Carry every dissent" is not a rule when omitting the dissent from the body makes it cease to exist; "credit every contributor" is not a rule when the contributors are whoever the body says spoke. The refusals looked like they meant something, which is worse than having none. - Two real leaks found by writing the seam:
ProjectedPresence.participantIdcarried the RAW USER ID into a pseudonymous room. The label read "Participant 2" and the payload beside it readguest-1; a surface keying its list by it puts the name in the DOM, andpresenceLeaksdid not catch it because it only checkeddisplayName. Fixed inpresence-privacy.ts: the projection emits the pseudonym, and the leak check now tests both identifiers.RoomSyncPlan.participants[].timelineAffectingNAMES the accommodation that detached somebody — the exact disclosureroomVisibleNoteexists to avoid — and the route shipped the whole plan to every participant. NewroomVisibleSyncPlan(plan, viewerUserId)empties it for everybody but its owner, and every use case returning a plan projects it (the host included: a host is another person in the room).
- Artifacts:
libs/contracts/src/study/entities/live-room.ts:LiveRoomRecord(roster, per-participant accommodations, standing,leftAtRoomMs, contribution consent, transport, control grants, identity mode, lifecycle) andLiveRoomMessageRecord(stance, body, anchors,atRoomPositionMs, moderation state + flags + decisions). Ids inids.ts.apps/yemaya/svc-study-workspace/migrations/0048_study_live_room.sql: two tables, RLS, revision immutability, andenforce_live_room_promises()— a room may not be repointed at another edition (the roster was cleared to watch ONE work) andnot-recordedmay not becomerecorded-with-consent(the people in it agreed to a room that was not being recorded). The reverse is allowed: stopping a recording takes nothing from anyone.libs/yemaya/study-workspace/src/use-cases/manage-live-room.ts(1278 lines): join/rejoin, leave, own-settings, transport, transport control, drift, presence, post message, moderate, consent, list, read. Rights are RE-RESOLVED throughdecideSelectionon every act that shows a frame — the room keepsplaybackGrantIdas a record and never re-uses it, so a seminar that runs past the end of a licence stops.live-co-study.ts:startLiveSessionnow persists the room in the same transaction as the event;crystallizeLiveDiscussionreads the transcript and the consents from the store, and a claim built from a moderator-pruned transcript carriesmoderatedOutCountas a limitation.- 12 routes under
/api/study/live-sessions/...plusGET /api/study/projects/:projectId/live-sessions(only rooms the caller is on the roster of).errorResponsemaps the six standing errors to 403,ImposedAccommodationErrorto 403,LiveRoomClosedErrorto 409,ForeignAnchorError/ModerationError/CrystallizationErrorto 422 — the last had been falling through to 500. apps/oshun/web/src/components/studio/StudyLiveRoomPanel.tsx, mounted in/studio/study.
- Verification:
libs/yemaya/study-workspace:live-room.spec.ts50 new tests,live-co-study.spec.tsrewritten to post real messages — 20; full lib suite green.apps/yemaya/svc-study-workspace:live-co-study.route.spec.ts25 (13 new).apps/oshun/web:StudyLiveRoomPanel.spec.tsx11.tsc --noEmitclean on contracts, lib and service (theindex.tsTS2308 re-export ambiguities are pre-existing). Adversarial stub scan clean.
- Marked [x]: YSD-19101.
- Commit: resolve via
git log --grep='YSD-19101'.
2026-08-04 — YSD-19102 (partial): a principle gets a table, and the trail must resolve#
- Finding, proved with a probe.
CreativePrinciplehad a contract, an id type, and three records already pointing at principle ids (CreativeDecision.principlePathIds,OriginalConcept.informedByPrincipleIds, and the/principles/:principleId/track-recordroute) — and no table, no store method, no use case and no route.git grep CreativePrincipleoverlibs/yemaya/study-workspace/srcandapps/yemaya/svc-study-workspace/srcreturned exactly one file: a policy spec.readPrincipleTrackRecordnever loads the principle; it assembles a record out of the decisions that MENTION one, because there was none to load. - Why that mattered.
CreativeDecisionSchemarefuses to commit a derived decision carrying neither a principle nor a source path — "a decision from nowhere cannot claim the study trail" (YSD-22013). With nowhere for a principle to exist, any UUID satisfied it. A throwaway spec wroteprinciplePathIds: ['00000000-0000-4000-8000-00000000dead']and it was accepted without complaint. The rule was real and the thing it ranged over was not — the same shape as the YSD-19101 crystallization bug, one layer up. - The scale of it, measured by closing it. Adding the resolution check broke
thirteen spec files at once, including
walking-skeleton.integration.spec.ts, which passedrandomUUID()as a principle path in three places. Every one of those tests was green against a fabricated evidence trail. All are now fixed: real principles where the project has evidence to abstract from, andorigin: 'unsupported-original-exploration'in the one fixture whose project has no source works at all — which is what that fixture always was. - Artifacts:
apps/yemaya/svc-study-workspace/migrations/0049_study_creative_principle.sql— RLS, revision immutability, a(project_id, review_state)index because "the accepted principles of this project" is the read a transfer gate makes.libs/yemaya/study-workspace/src/use-cases/manage-creative-principles.ts— derive (supporting AND contradicting evidence resolved against the project), review by a second hand with a reason, read, list.assertPrinciplePathResolvesis exported and called from both places a principle path is written (decision create/update, concept create/update): a rule enforced in one of the two places it applies is a rule with a documented way round it.libs/yemaya/study-workspace/src/use-cases/trace-artifact-graph.ts—buildProjectArtifactGraphderives the typed dependency edges from the records themselves (the existing impact read walks AUTHORED cross-reference edges, which say what somebody drew, not what the records stand on);reviewChangeImpactrunscomputeChangeImpact— which had zero callers — and reports which impacted artifacts the project had already stood behind;traceDecisionProvenancewalks source → principle → decision.- Two refusals worth keeping: an unresolved edge is REPORTED, never dropped (a
traversal that skipped it returns a shorter path and calls it complete), and
the
abstractedanddirectroutes are never merged, because a decision that reached its sources through an abstraction and one that cited the frames directly have made different claims about their own provenance (YSD-2041). - 6 routes: derive / review / read / list principles,
change-impact,provenance.PrincipleEvidenceErrorandUnresolvedPrinciplePathErrormap to 422unresolved_evidence_path;PrincipleReviewerErrorto 403.
- Verification:
libs/yemaya/study-workspace:creative-principles.spec.ts18,artifact-graph.spec.ts13; full lib suite 786 files / 12629.apps/yemaya/svc-study-workspace:creative-principles.route.spec.ts11; full route suite 16 files / 234, including the walking skeleton against real Postgres.tsc --noEmitclean on contracts, lib and service; adversarial stub scan clean; generator drift checks pass.
- NOT marked. YSD-19102 names five things and one is still unbuilt: the
cross-work matrix has no cells.
policies/comparison-matrix.tsenforces seven distinct cell states (unknown,unavailableandforbiddenare three different things) and a cross-media alignment honesty rule, andComparisonSetSchemastores members, alignments and amatrixlayout with nowhere to put a cell — socomparisonCellIssuesanddescribeCellStateLegendare enforced by nobody. Concept graphs and coverage/consistency were already reachable via/projects/:projectId/concept-system-map. - Commit: resolve via
git log --grep='YSD-19102'.
2026-08-04 — YSD-19102 completed: the matrix gets cells#
- Finding.
ComparisonSetSchemastores members, alignments, sync markers and offers amatrixlayout — with nowhere to put a cell. Sopolicies/comparison-matrix.ts(seven distinct cell states) andread-models/matrix-operations.ts(nine exported operations:axisIssues,sortMatrixRows,facetMatrixRows,filterMatrixRows,clusterMatrixRows,savedViewIssues,assertSavedView,applySavedView,assertAxis) were enforced by nobody. A matrix layout was a declared shape with no contents. - Why the states are the point.
unknownis undetermined,unavailableis "no data exists",forbiddenis "a value exists and rights withhold it". A table that renders all three as an empty box tells a reader that a withheld value and an unmeasured one are the same thing — and leaks in the other direction, because a viewer who learns nothing is there has learnt something. - Artifacts:
libs/contracts/src/study/entities/comparison-matrix.ts— axes, the seven cell states, saved views. Two record-level refusals: a cell whoserowKeyorcolKeyis not on an axis (it exists in the store and appears in no table), and two cells at one position (which renders as whichever the reader reached first).apps/yemaya/svc-study-workspace/migrations/0050_study_comparison_matrix.sql— cells live in the payload, unlike 0048's transcript, because a matrix is authored and read WHOLE: every operation takes the entire row set.libs/yemaya/study-workspace/src/use-cases/manage-comparison-matrix.ts— author / record cells / read (applying a saved view, sorting, faceting) / list. A matrix takes its corpus boundary from a comparison set that exists and is this project's, because a scope stated twice can disagree with itself. Cell anchors resolve against the project. The legend is sent with every read: a surface that had to invent the distinction is how three absences become one blank box.- 4 routes;
ComparisonMatrixError/MatrixOperationError→ 422matrix_refusedcarrying every failing rule.
- Verification:
comparison-matrix-storage.spec.ts16,comparison-matrix.route.spec.ts9; service route suite 17 files / 243. Migration 0050 applied and verified against real Postgres.- Adversarial stub scan clean; lint clean; generator drift checks pass.
- Marked [x]: YSD-19102 — all five clauses now reachable: concept graphs and
coverage/consistency via
/projects/:projectId/concept-system-map(pre-existing), cross-work matrices via the four new matrix routes, source-to-principle-to-decision paths via/creative-decisions/:decisionId/provenance, and change-impact review via/projects/:projectId/change-impact. - Commit: resolve via
git log --grep='YSD-19102'.
2026-08-08 — YSD-19041 hybrid search: the vocabulary nobody could address, and the eleven filters that evaporated#
- The vein: §13.1's taxonomy executor (
search/taxonomy-search.ts, 554 lines) was complete and correct — subsumption with cycle protection, deprecation redirects followed in both directions, absolute facet discipline, refusal of terms from an ungoverned extension, a three-valued answer separating "does not carry the term" from "has never been through tagging" — and it had one importer, the barrel. It had never decided anything about a real record, because two things did not exist. A governedTaxonomyTermis identified by UUID and the query AST asks fordomain/facet/termRef;indexEntryFromTaxonomyTermtakes that mapping as an argument it calls "the mapping the caller already maintains", and no caller maintained one. And nothing anywhere had ever put a term ON a record, so every corpus was permanently in the third state.hybridSearchrefused the whole dimension as unexecutable — honestly, and permanently. - Second half of the same vein:
toScanQuerytranslated exactly two of a source filter's thirteen constraints (workIds,media) and threw the other eleven away. A query naming creators, a release window, a genre, a platform or a build came back wider than the question with nothing saying so — the precise failure the file's own header calls "not partial — wrong". - Artifacts:
libs/contracts/src/study/entities/taxonomy-vocabulary.ts—TaxonomyTermRegistration(the term PLUS the address a query reaches it by; the registration OWNS the term rather than pointing at it, because a governance row and a search row can drift),TaxonomyAssignment(origin, attribution, and a withdrawal that is stored rather than deleted),TaxonomyTaggingPass(the record that separates "considered and nothing applied" from "never reached").apps/yemaya/svc-study-workspace/migrations/0059_study_taxonomy.sql— four tables. The address is COLUMNS, and the unique index over(tenant, domain, facet, term_ref)is where facet discipline actually lives. A trigger fixes the address at revision 1: re-addressing a term would hand every record already tagged with the old concept to whatever query now spells the new address.GovernedTaxonomyExtension(YSD-19123) — until now another complete contract with nowhere to live — gets its table here, because the extension gate must be READ at query time: an extension suspended after its terms were registered has to stop deciding immediately.use-cases/register-taxonomy-extension.ts,use-cases/register-taxonomy-term.ts,use-cases/assign-taxonomy-term.ts— the address is checked against the STORED vocabulary; a relation pointing at an unregistered term is refused (an unwalkable edge returns a quietly smaller result set, not an error); the whole vocabulary is re-indexed as it WOULD be before the write, so a registration cannot break every future query; a retired term still matches but takes no new usages, and the refusal carries the migration rule.use-cases/search-study-records.ts—visibleRecordIds, the positive record-level allowlist the rights gate produces while walking. Re-deriving that rule at a second retriever is how two gates come to disagree.use-cases/hybrid-search.ts— the taxonomy and metadata retrievers, fused by reciprocal rank with the scan and the vector index, plusUncomposableQueryError: retrievers compose by UNION, which is right for a disjunction (unknown OR matchismatch) and wrong for a conjunction (unknown AND matchisunknown), so a mixedand/notis refused rather than answered wider than asked.- 7 routes under
/api/study/taxonomy/*and/api/study/projects/:projectId/taxonomy/*; conflicts → 409, unregistered → 422, a retired term → 422 carrying the replacement and the rule.
- Zero leakage: the taxonomy retriever builds its records ONLY from
visibleRecordIds, before evaluation rather than after. Filtering hits afterwards would be too late —executeTaxonomySearchalso returnsindeterminaterecord ids, and a bare id from a withheld work still tells a reader that a record exists they may not see, and how many. - Left refused by name, deliberately:
culture(no stored record carries a culture or tradition, so answering "undecidable for every record" would be a sentence about the deployment dressed as one about the corpus), and therights/permissionfilter dimensions (both narrow within what a reader may already see, YSD-13012, and neither has an executor). - Verification:
acceptance-taxonomy-search.spec.ts28,taxonomy.route.spec.ts8; study-workspace suite 810 files / 13 110; service route suite green; contracts suite green.- Adversarial stub scan clean; lint clean; generator drift checks pass; all
six
tools/yemaya-study/check-*.mjsgates pass.
- Marked [x]: YSD-19041 — all five retrievers now run: metadata (all thirteen source constraints, language and version from the edition that declares them), taxonomy (over a real vocabulary and real tags), text, semantic and visual; with rights, epistemic labelling, version pinning (including the vocabulary versions in force), per-dimension explanations, and zero-leakage proven against a rights-withheld work.
- Commit: resolve via
git log --grep='YSD-19041'.
2026-08-09 — YSD-22054 requirement scope: the axis a green portfolio check cannot see#
- Item: "Confirm tests cover the full requirement scope, including failure, permission, rights, expiry, deletion, accessibility, version, partial, offline, and scale behavior where applicable."
- Why the existing gate did not answer it.
check-test-portfolio.mjs(YSD-18101) proves every module is under a test — coverage along the axis of MODULES. A module fully covered by a spec that only ever drives the happy path passes it. The item names ten BEHAVIOURS, so the axis is (module × behaviour), and nothing measured it. Grepping the library for the machinery first:requirementScope,scopeCoverage,SCOPE_DIMENSIONS,behaviourDimension— zero hits. - Artifact:
tools/yemaya-study/check-requirement-scope.mjs+check-requirement-scope.test.mjs(28 rule tests, one planted hole per rule and a planted PASS per rule) +docs/proposals/yemaya-study-workspace/requirement-scope-exemptions.json. - Neither side of the check is supplied by the person writing the tests:
- Applicability is read off the implementation. A dimension applies to a
module when the module DECLARES a state belonging to it — an
as constregister member, a string-literal union alternative. Nobody can decide expiry "does not apply here" while the code carries an'expired'; the only way to make it inapplicable is to delete the state. - Coverage is decided by EXECUTION. For a state produced by code, the
question is whether the suite ran one of the lines that produces it, read
from a V8 coverage report. The tempting textual rule is wrong in this
codebase and provably so:
policies/still-image.ts:95pushes{ severity: 'error' }andstill-image.spec.tsdrives it withwidth: 0while assertingvalid === false, never writing the word. - Where nothing produces a state — it lives only in a register or a type — the
weaker assertion rule applies, and the report says which rule judged each
row, because a reader who cannot tell them apart cannot tell how strong the
answer is.
enumeratedis a third, weaker-still standing for a state a spec covers by iterating its register (for (const family of METRIC_FAMILIES)). - A missing coverage report is a failure, not a pass.
- Applicability is read off the implementation. A dimension applies to a
module when the module DECLARES a state belonging to it — an
- Five design corrections, each forced by a real false report on this
library:
- Tells match hyphen-delimited TOKENS, never substrings —
'shuttle'read as a TTL,'immigration-status'and'streak-loss-aversion'as versions. - Register lines are excluded from execution evidence: they run at import, so keying on them would mark every state in the program as reached.
- A producer counts when it sits on one import chain with the declarer, in
EITHER direction —
ports.tsdeclares the event union its use case emits, andstk-build-attestation.tsre-declares the verdict union of the signing policy it calls. - A spec exercising a CALLER speaks for the module — every study adapter is built that way, and reading only direct importers reported all ten as having an untested unavailability state their suites assert on every run.
- Comments are stripped before literals are read: an apostrophe in prose ("the learner's own queue") pairs with the next quote and silently shifts every literal after it.
- Tells match hyphen-delimited TOKENS, never substrings —
- What it found, and what was done about it. 757 applicable (module ×
dimension) pairs. Zero unreached — every code-produced state in the ten
dimensions is executed by the suite, which is a real finding about this suite.
The gaps were all on the assertion side, and each was closed with a domain
test rather than a restatement of the register:
accessibility-capabilities— the image/graph/audio/timeline alternative contracts and theunsupported≠unavailabledistinction (nothing isunsupportedtoday, and asserting the absence is what makes the day one appears visible);live-playback-sync— each non-timeline accommodation, one by one, stays frame-locked (transcript panel, keyboard-only, reduced motion, larger text);feature-flags— a lapsed licence dropslicensed-sourcewithout takingpublic-domain-open-licensewith it;persona-onboarding—two-cleared-sourcesanda-transcriptare the near-misses that read like small asks and are not day-one reachable;template-gallery— a pinning template refused into a project with no right to quote;api-group-authorization— an AUTHENTICATED operator route is still outside the project model, and a rights segment the manifest does not use yet is still classified;error-taxonomy— 429 "slow down" apart from 503 "the thing behind us is down";peer-feedback-workflow— a deferral owes nobody a reason and a decline does;collaboration-safety— blocking a participant and a session lapsing are recordable acts;teaching-pack-publication— a pack comes down for being WRONG, not only for rights;lens-orchestration—partialis terminal and reachable only fromrunning;adapter-compatibility—unsupportedandunavailableare one bucket to a consumer and two facts to an author;outcome-impact-reviews— the review still opens when the reviewer cannot reach the evidence;source-to-decision-trace— a rejected and a superseded step stay in the trace;query-planner— a REJECTED search may not claim results;semantic-search— a hit matched on a caption says so;relational-chain-backup— a grant that expired since the backup blocks a restore exactly as a revocation does;completion-budgets— the deletion budget measures propagation and is not a window of exposure.
- One real defect fixed, not papered over.
continuous-reevaluation.tsdocumented four actions and its resolver could only ever return three:'expire'was unreachable, withrights-expiredcollapsed intorevoke-access. The program distinguishes the two everywhere else (provider-availability,comparison-declarations,synthesis-discipline) and carries arights-renewedevent precisely because an expiry is curable by renewing and a revocation is not. The branch now exists, ordered so a withdrawal outranks a lapse when both hold. - Exemptions — two, and both are words that are not behaviours:
revision-reflection(a rubric dimension matched by the tokenrevision) andmissing(a column heading inrenderEvidenceMatrix's table). An exemption names ONE state in ONE module with a reason and a decider, and the checker refuses one whose state no longer exists. - Verification: study-workspace suite green with coverage (834 files / 13
5xx tests);
check-requirement-scope.mjspasses over the fresh report; 28 rule tests pass; the existingcheck-test-portfolio.mjsand the other program gates still pass. - Marked [x]: YSD-22054.
- Commit: resolve via
git log --grep='22054'.
2026-08-09 — YSD-22055 decision enforcement: a section heading is not a mechanism#
- The axis the existing green check cannot see.
check-decision-log.mjs(YSD-0139) passes on all eighteen Section 0.2 EXT decisions, and what it proves is real: each exists, isapproved, by a named person, on a date, with an outcome recorded, and each artifact carries a section headed## Machine-enforced outcome. None of that reaches YSD-22055's second clause. A heading is a promise. An artifact may say "feature flags encode the segment" and the flag may not exist; it may name an architecture test nobody wrote; it may describe a registry whose members drifted from the approved list two releases ago — and the heading would still be there. The one-word greps for the missing machinery (decisionEnforcement,enforcementAssertion,approvedOutcome,outcomeAssertion) each returned zero files. - The doctrine was already in the repository, applied to other people.
institutional-policy.tsreports each declared clause of an institution's policy asenforced,unenforceable, orviolated, on the principle that a declared clause is a promise; what makes it a control is a mechanism in the running deployment that carries it. YSD-22055 is that test turned on ourselves. - What was built.
decisions/decision-enforcement.json+tools/yemaya-study/check-decision-enforcement.mjs+check-decision-enforcement.test.mjs(36 rule tests). 45 assertions across all 18 approved decisions, in four kinds — an exported register holds exactly/includes/excludes/orders the approved values; a source file does or does not match a pattern; a path is present or absent; an existing repository checker exits zero. - The anti-invention control, which is the load-bearing part. Every
enforcement carries a
quote, and the check FAILS when that quote is absent from the decision's own artifact. Whoever writes the registry may only encode what the approver actually approved — the expectation cannot be quietly widened, narrowed, or invented, which is the S8.1 rule that the checked party may not supply both sides. It works: the first run rejected"contracts model members/roles from day one"for YSD-0137, because that is the decision-log's paraphrase and not a sentence inysd-0137.md. - The same rule from the other direction. A decision that is NOT
approvedmay not carry enforcements at all. Enforcing a proposed default as though it were approved is precisely the failure the item's second clause names. - Assertions are evaluated, not delegated. Binding an outcome to the NAME of
a test suite would replace one promise with another, so the checker reads the
code itself. Comments are stripped before any literal is read (an apostrophe
in prose pairs with the next quote and shifts every literal after it), and
both register spellings count —
[...] as constand: readonly T[] = [...]— as doRecordobject literals, whose keys are read without leaking the strings inside their values. - Every live assertion was mutation-tested against the real repository, not
only against synthetic fixtures: drop each approved member (all occurrences —
removing one of two leaves the second and the mutant survives), append an
extra, insert an excluded one, reverse an order, delete every match of a
present-pattern, plant a match for an absent-pattern, hide a path, reveal a
forbidden path, fail a command. All 45 flip. Two needed record-shaped
mutations (
RELATION_SPECSkeys are bare identifiers, so replacing string literals changes nothing) and two absent-patterns were verified by planting:automationAvailable: trueinto a first-release lens,ELASTICSEARCH_URLinto the service config. Both fail as they should. - One open conformance question, and the item stays unchecked because of it.
YSD-0124's recorded outcome is "local/owned files only; providers in a later
phase". Provider deep-link registration is a live, ungated route today
(
POST /works/:workId/provider-sources), andacquisitionCapabilitiesreportssource.provider-deep-linkas supported with no wiring condition. The link-only property the decision attached to providers IS enforced — no representation the workspace generates derives from a provider link — but the phase gate is not, and no machine can rule on whether a phase has arrived. Recorded as anopenConformanceQuestionthe checker prints on every run and whose evidence pointers it keeps resolving (an observation pointing at nothing rots the same way an enforcement pointing at nothing does). It does not fail the build: the control that says "not yet confirmed" is the unchecked checkbox, not a red build on every branch. - One suspicion investigated and withdrawn. YSD-0127 approves V2 as the
first instrumented owned title while the shipped connector is SuperTuxKart, so
this looked like a second divergence. It is not:
acceptance-owned-title-end-to-end.spec.tsruns V2 end to end (connectorId: 'oshun.v2.connector',engineFamily: 'v2'), and STK is the separate open-source seed corpus of YSD-14172/14173. - Verification: 36 rule tests pass; the check passes with 45 assertions; the
seven sibling program checkers (
decision-log,traceability,evidence-index,capability-inventory,reuse-ledger,architecture,stub-scan) all still pass. Wired into both theci.ymllint job and thestudy-workspace.ymlgovernance job, rules included. - Marked [x]: none. YSD-22055 stays
[ ]pending the decision owner's ruling on YSD-0124. - Commit: resolve via
git log --grep='22055'.
2026-08-09 — YSD-22057 release-gate evidence: every gate knew which build, none knew which machine#
Item. Confirm every mandatory release gate has fresh production-like evidence for the exact release candidate and representative datasets.
The finding. release-refusal.ts is a careful and complete gate: it refuses
evidence that is missing, indirect, narrower than the requirement, produced
against another candidate, or excepted without an authorized signature. It
carried one qualifier on a piece of evidence — againstRevision — and this item
names four. There was no field in which "production-like" could be false and no
field in which "representative dataset" could be false, so a gate cleared on a
unit test over hand-authored records exactly as it would have cleared on a
production observation. The programme already knew better in two places and had
never joined them up: search-corpora.ts refuses a corpus before reading its
latency ("a fast wrong number is the one that gets believed"),
capacity.integration.spec.ts refuses an in-memory capacity report as not
release-grade ("a hash map has none of them"), and test-evidence-bundle.ts
pins hardware-profile and dataset-version per artifact kind. All three are
about performance. The nineteen gates that decide whether the product ships —
rights, privacy, security, accessibility, epistemic integrity — asked nothing
about where their evidence came from.
A judge with nothing to judge. The second half of the finding, and the one
that made the first invisible: the only corpus of GateEvidence in the
programme was the one release-refusal.spec.ts constructed for itself.
atlasPanelGateEvidence produced a single record for a single gate. Nothing
assembled the other eighteen, so the decision function had never been shown the
repository it was written to judge.
What was built.
GateEvidence.conditions, a required discriminated union: anexecutionnames the environment it ran in and the corpus it ran over, and anything else must saynot-an-executionand why. Required and discriminated on purpose — an optional field would have left every existing record silently conforming, which is the state this item exists to end.- Seven environments, each defined by what it stands in for rather than by a
rung on a ladder, because the two mid-tier harnesses substitute opposite
halves: the 31 route suites drive the real HTTP app and its auth over doubled
stores, and the
*.integration.spec.tssuites drive real PostgreSQL and MinIO with no boundary in front of them. Neither is "more integrated"; an ordinal score could not have expressed what a gate needs. - Three new fields on each of the nineteen gates, every one read off the gate's
own sentence:
mustNotSubstitute(Gate 6 is about the store and the boundary, Gate 10 is about a renderer),datasetFloor(Gate 11's sentence is the only one that says "representative data", and it getsrepresentative), andhumanSettleableScope— empty for sixteen gates, and for Gate 8 exactly the three dimensionsPANEL_GATE_SCOPEalready claimed. A spec now holds those two lists to each other. - Three refusal grounds:
not-production-like,unrepresentative-dataset, andjudgement-offered-for-a-measurement. The third closes the hole the second would otherwise have had — without it, every record could escape the conditions test by declaring itself unrunnable. - Admissibility is judged before the scope arithmetic and inadmissible evidence is withheld from the coverage union, so a gate reports the conditions problem AND what is now uncovered. Same two-refusal shape a deferred pointer already produced.
The anchor.
docs/proposals/yemaya-study-workspace/release-gate-evidence.json records, gate
by gate, the evidence that actually exists, and gate-evidence-register.spec.ts
runs the whole register through the real programReleaseDecision. Nothing in
the JSON says where a run happened: environmentFor derives it from the
reference path, because a register that graded its own conditions would be the
self-attestation the programme refuses everywhere else. Each slices claim
carries a quotation that must appear verbatim in the file it cites, so a corpus
cannot be credited with material nobody can point at.
The answer it produced, which is a refusal. All nineteen gates refuse.
Eighteen refuse as not-production-like, fifteen of those ending in
no-evidence because nothing was left once the inadmissible was withdrawn.
Three (Gates 12, 14, 19) reach the corpus test and fail it. And the inversion
worth the whole exercise: Gate 10 is the only gate that clears the conditions
test, and it is the only gate of the nineteen with no
release-gate-*.spec.ts. Before this register, grepping YSD-18139 found
release-refusal.ts and its own specs — the judge and nothing it could judge —
because the gate's subject is a rendered document and the harness that renders
one lives in another project. Its evidence is two real browser journeys, it
clears seven of its eight named parts, and it is short of exactly
graph-and-canvas-alternative: one work item rather than "accessibility is not
done". Gates 1, 3 and 18 likewise come back narrower-than-requirement rather
than unevidenced, because their e2e entries survived.
Two open questions the register records rather than resolves. No corpus
anywhere in the tree has a provenance other than authored-for-the-test, which is
the same gap YSD-19005 (licence the gold-set slices) and YSD-18002 (recruit the
cohort) already carry, reached from the other end. And no evidence exists from a
deployed or production environment because there is no deployment — which is
YSD-18163, also open. The dataset and environment axes independently rediscover
two items the checklist already leaves unchecked; that agreement is the reason
to believe the axes rather than a coincidence to explain away.
Corrections made while building. Seven gates had their datasetFloor set
too high on the first pass and were lowered after reading what the real refusals
said: a contrast ratio is a fact about pixels and a deletion is a fact about
stores, so neither needs licensed material. Representativeness is now required
exactly where the claim is about the material — Gates 8, 9, 11 and 17.
Verification: npx vitest run over the whole library — 13,572 passed, 12
skipped, 833 files. npx tsc --noEmit -p tsconfig.lib.json clean.
Marked [x]: none. YSD-22057 stays [ ]: the confirmation now runs, and what
it confirms is that the evidence does not meet the bar. Marking it would be
recording the mechanism as the answer.
Commit: resolve via git log --grep='22057'.
2026-08-09 — YSD-22058 operational drills: the programme rehearsed only the procedures nobody runs#
Item. Confirm rollout, rollback, recovery, support, incident, expiry, deletion, backup/restore, provider change, model replacement, and tenant closure procedures have been exercised.
The finding. operational-drills.ts is a demanding evaluator. It refuses a
drill that was not measured, extrapolates a small one to full scale and uses the
extrapolation only to fail, requires per-kind verifications that separate "the
procedure ran" from "the procedure worked", and fails a drill outright when the
operator had to improvise a step, on the ground that the document is what is
under test. It knew six kinds — restore, deletion, provider-failover,
model-replacement, signing-key-rotation, rollback — against the eleven this item
names.
The six are not an arbitrary subset. Every one of them is a procedure for undoing or repairing something. Not one runs when nothing is wrong. The missing ones are exactly the procedures that run constantly: rollout, which happens on every release and which rollback exists only to undo; expiry, which fires on a clock with nobody present; support, whose counterparty is a person outside the system; incident, which had eleven runbooks in a document and no drill kind; recovery, which is the objective every continuity plan in the same directory states and which no drill kind could measure; and tenant closure, which had a continuity scenario and no way to exercise it. A taxonomy of things that go wrong left out the things that go right, and "we do it all the time" is a statement about frequency rather than about whether the documented procedure has ever been followed and measured.
The hardest rule in the file had nothing to fail against.
undocumentedSteps counts departures from a document and no field named the
document. A drill following nothing at all improvises every step and records a
count of zero, so the check that exists to catch a wrong plan passed most loudly
when there was no plan.
The strictest judge sat next to the weakest claim.
ContinuityPlan.lastDrilledAtMs was an integer the plan's own author typed, and
it alone separated verified — which this module defines as the difference
between a measurement and a hope — from unverified. The evaluator that decides
what a drill has to be in order to count was in the same directory and there was
no reference between them. Worse, four of the eight continuity scenarios
(regional outage, connector revocation, tenant export, tenant closure) had no
drill kind at all, so their plans could reach verified on a date that no drill
could have produced even in principle.
What was built.
- Twelve drill kinds, and
NAMED_PROCEDURESmapping the item's own eleven words onto them —backup/restoretorestore,provider changetoprovider-failover.recoveryandbackup/restorestay separate because restoring data and restoring service are different procedures with different objectives. Required verifications for each new kind, each naming something a smooth run leaves untrue: a rollout that halted on its own signal rather than because somebody was watching, an expiry proven by moving the clock rather than by backdating a row. DrillRecord.procedure: the document followed, by reference and quotation, or an explicitnullthat makes the run inconclusive. PlusoperatorRole, andenvironmentin the seven-valued YSD-22057 vocabulary in place of a boolean — a reader needs to know which harness it was, not only that it was not production.procedureExerciseStanding/programProcedureExercise, whose ordering is their whole content: only a drill can rise abovemechanism-exercised-not-procedure. A named procedure nothing accounts for throws rather than being skipped, because an answer about eleven procedures cannot be computed from ten.- The join: plans cite a drill by id,
SCENARIO_DRILL_KINDSis total over the eight scenarios, andverifiednow meansevaluateDrillreturnedvalidating.
The anchor. procedure-exercise-register.json records, procedure by
procedure, the document an operator would follow and the runs that exist, and
procedure-exercise-register.spec.ts puts the register through the real
programProcedureExercise. The eleven names are parsed out of this checklist's
own sentence, so rewording the item breaks the check. Every quotation must
appear verbatim in the file it cites — including the ones inside a
nearestGuidance shortfall, because a claim about what a document does not
cover is a claim about a document. Environments are derived by the same
environmentFor the release-gate register uses, now lifted into
release/evidence-environment.ts so one rule serves both.
The answer it produced, which is a refusal. All eleven. Nine have no
document an operator could follow at all — and no runbook in docs/runbooks
names this workspace: the object-storage runbook's scope table lists yemaya
avatars, lilith voice audio and aphrodite recordings and not one study bucket,
and the provider-failover runbook's scope list names assistant, Sophia, Isis,
messaging and payment. The two that are documented — incident and deletion —
share incident-runbooks.md, written for YSD-4100's security incident classes.
The programme wrote down what to do when it is attacked and never wrote down
what to do on an ordinary bad day.
Both documented ones come back mechanism-exercised-not-procedure, which is the
category this item needed and did not have. The deletion saga is driven to
completion and through resumption against real PostgreSQL and object storage,
the expiry sweep crosses the lapse boundary with the read surfaces checked on
the far side, a real restore lands in an isolated environment with the chain and
tombstones intact, and migration 0006 applies over seeded volume with a live
writer. Calling that nothing would be false; calling it a drill would be false
too. It shows the mechanism works when invoked, and a procedure exists to answer
whether a person who has not done it before can make it work from the document,
under time, on the day it matters.
A claim corrected while building. The register first said nothing in the
programme constructs a ContinuityPlan outside its own module's spec. That was
wrong: chaos-recovery-coverage.spec.ts holds two real ones — a regional
failover with three owned steps, two audiences and a 60-minute objective, and a
key compromise. The accurate finding is narrower and better: two plans exist,
for two of the eight scenarios, and both are fixtures written to exercise the
assessor rather than documents an operator would open during an outage.
Verification: the continuity, reliability and release suites green;
npx tsc --noEmit -p tsconfig.lib.json clean; architecture, test-portfolio,
release-integrity, stub-scan, evidence-index and doc-conformance checkers all
still pass.
Marked [x]: none. YSD-22058 stays [ ]. The confirmation now runs and it
answers no, on all eleven. Marking it would record the mechanism as the answer.
Commit: resolve via git log --grep='22058'.
2026-08-09 — YSD-18163 launch-readiness rehearsal: every fault was answered and none was announced#
Item. Run a production-like launch-readiness rehearsal including ingest, model outage, permission change, source expiry, incident, deletion, restore, rollback, support, and status communication.
The finding, first half: a rehearsal is a chain and every mechanism here is a single link. Six of the ten legs — permission change, source expiry, incident, deletion, restore, rollback — are procedures YSD-22058 already asks about one at a time, and running them again separately would add nothing. What this item adds is the word including: the ten are legs of one run, on one person's work, in one sitting. The failures worth finding live in the joins. A restore that runs after a deletion has to not resurrect it. A rollback that runs after a permission change has to not lose it. An expiry sweep that runs after both has to see the world they left behind.
Nothing in the programme could express that. drillCoverage takes the most
recent drill per kind, which is a deliberate statement that the kinds are
independent. chaos-scenarios.spec.ts injects each fault "at the boundaries the
modules take as input", one at a time, each from its own fixture. Both are right
about the question they ask, and neither can reach this one. Note also which leg
the item puts first: ingest is the only leg that is not a problem, and it is
there because it supplies the thing every later leg acts on. Without it there is
nothing to expire, delete, restore or apologise for, and ten legs each with
their own fixture are ten drills sharing a title.
The finding, second half, and it is the one that matters: the tenth leg has no
mechanism at all, and the absence is systematic. Nine of the ten legs are
things that happen to somebody. The register asks, leg by leg, how that
somebody comes to know. Three have an answer, and all three are the same kind of
answer — a refusal at the boundary carrying a real reason: uploads_unavailable
to the person mid-upload, rights_denied with the gate's reasons to whoever
next tries, and a 410 with a lapsedNotice naming when access lapsed and what
bound it. Those are good and this workspace has many of them. They reach the
person who was typing. They say nothing to the learner who was away, nothing
about scope, nothing about duration, and nothing at all once the request stops
being made.
This workspace tells you when you ask. It has no way to tell you when you did
not. That is CHANNEL_KINDS, and the unprompted half does not exist:
- The product ships a customer-facing status channel —
ConsumerShellStatusBannerinlibs/oshun/shell-core, four severities, three placements, dismissal and suppression rules, accessibility roles, and an audience list that namesscholarandteacher, this workspace's own users — mounted in the customer shell on every page. The study workspace has never published to it: nothing underlibs/yemayaorapps/yemayaimports@oshun/shell-core, and the only file in either tree that so much as names the banner type is the module whose docblock exists to record that nothing does. - The eleven incident runbooks give Detection, Immediate response, Containment & eradication, Recovery and the owning mechanism. Every step is addressed to the responder. Not one has a step where the people whose work is affected are told anything — which is why YSD-22058's incident drill requires that "everyone the plan names was told inside the window it gives" against a plan that names nobody.
- YSD-5124's availability model, the code that decides what a learner may
still do while analysis is queued, degraded or failed, returns four booleans
and no sentence, so even a caller could not tell anybody why. It has no
caller: neither
workspaceAvailability,analysisResultsAvailablenorisManualActionAvailableis referenced by any route, component or read model in the monorepo outside its own module and spec. alerting.tsroutes eight conditions topage,ticketandnotice, and all three are destinations inside operations.
The programme built the response to every fault and never the announcement of one.
What was built. continuity/launch-rehearsal.ts:
REHEARSAL_LEGSandLEG_PREREQUISITES, withrestoredepending ondeletion— the join a set of drills cannot test — and every leg after the first depending oningest.- The chain itself. A leg names the effect ids of earlier legs it observed, not the ones it could have; carrying in an effect no leg produced, or one that had not finished being produced when the leg began, throws rather than scoring low, because a fabricated chain is not a weak one.
- The subject. A leg acts on the run's subject or on something an earlier leg produced from it (a restored copy legitimately has its own id); anything else is "a drill that ran during the rehearsal rather than a leg of it".
- The witness — what the person whose work it is could still do, which surfaces they lost, and the impact — which is the axis no other mechanism in this programme has, and the closing read, because nobody had ever established what a user is left holding after the worst day.
announcementsFor, which checks each announceable leg against six ways of being silent while appearing not to be: saying nothing; saying it once it was over; saying it only in advance, so people were told what might happen and never that it had; saying it over the surface the fault had taken away; saying something milder than what happened; and saying it to a room the affected person was not in. The window is thirty minutes, borrowed fromdocs/runbooks/v6-capacity-management.md's SEV-1 cadence rather than invented, because the study workspace states none — it has no communication step to attach one to.- A verdict ladder that names the most basic thing missing:
incomplete→disjoint→unwitnessed→unclosed→silent→substituted→rehearsed. Making a disjoint run speak changes nothing while its legs are still ten separate drills. lossLeftUnannounced, which is the one rule that cannot be per-leg. A run can satisfy every announcement check leg by leg and still end with the closing read naming work that did not come back, because the loss is only visible once all the legs are over. Whoever was witnessed during the day has to be told, without asking, in terms at least as severe as what happened, and after the last leg ended — awork-lostnotice published mid-day described a loss the restore and the rollback were still expected to undo, and accepting it would let the day's worst finding be laundered by the announcement of the deletion that started it.legStanding/programLaunchRehearsal, keyed to a named release candidate: a rehearsal is evidence about the build it ran on, and one from two releases ago isrehearsed-other-buildrather than a pass. A named leg absent from the input throws, as YSD-22058's eleven do. A leg is never graded better than the run it was a leg of: a chained leg inside a run that came backsubstituted,silentorunclosedisrehearsed-in-a-flawed-run, because a per-leg grade computed without regard to the day around it is how a rehearsal driven entirely over doubles reports ten rehearsed legs and a complete programme.
Five holes found by adversarial review and closed, over two passes — the
second pass confirming the first four and finding the fifth. All five had the
same shape: a weak record reading as a strong one. (1) A leg inside a rehearsal
that was itself substituted, silent or unclosed still counted as rehearsed, so
complete could be true for a day nobody was told about; that is what
rehearsed-in-a-flawed-run now catches. (2) A closing read reporting the
subject gone still graded rehearsed. (3) A notice published before its leg
began produced a negative silence and no issue, so a scheduled-maintenance
banner could stand in for the incident that followed. (4) The "responsive leg
with nothing to be about" finding was recorded and then ignored by the verdict;
it is now a disjointness signal (LegFinding.grounded), which is also what
makes the prose about support and status communication coming last true of the
code. (5) Once (2) was closed, the loss could still be laundered by any earlier
unprompted work-lost notice — the announcement of the deletion standing in for
the finding that it never came back — so the notice must now postdate the last
leg.
The anchor. launch-rehearsal-register.json records, leg by leg, the runs
that drive its machinery and every channel the product has, and
launch-rehearsal-register.spec.ts puts it through the real
programLaunchRehearsal. The ten names are parsed out of this checklist's own
sentence and round-tripped. Every quotation must appear verbatim in the file it
cites, including the ones inside a nearestChannel shortfall. Environments are
derived by the same environmentFor the release-gate and procedure-exercise
registers use. candidateRevision is held equal to
release-gate-evidence.json's, because two registers naming different
candidates have quietly become two questions.
Two absences are scanned rather than asserted. That the workspace has never
published to the status channel, and that the availability model has no caller,
are claims about what is not there — the kind that rot silently. The spec
walks every TypeScript file under libs/yemaya and apps/yemaya and asserts
the exact file sets, with both identifiers assembled at run time so the spec's
own text cannot satisfy the search it performs. The runbook scan is the same
shape. Wire any of them up and this file fails.
The answer it produced, which is a refusal. All ten. Eight are
exercised-alone: real runs, most against real PostgreSQL and real object
storage on every change, each starting from its own fixture, so what they show
is that the leg works by itself and never that it works after the legs that
change what it means. Support and status communication are unexercised —
nothing in the programme performs them. Zero legs are announced; three are
answerable-only; seven tell the affected person nothing at all. And the
rehearsal is not merely unrun but unrunnable: two of its ten legs have
nothing to run.
Verification: launch-rehearsal.spec.ts and
launch-rehearsal-register.spec.ts green, the continuity directory 124 tests,
including one that drives every verdict the ladder names so that no rung is a
rule nothing can reach; the full library suite 13,662 passed / 12 skipped across
838 files, exit 0; npx tsc --noEmit -p tsconfig.lib.json clean; architecture,
test-portfolio, release-integrity, stub-scan, evidence-index, doc-conformance,
traceability and decision-enforcement checkers pass. The requirement-scope
coverage report was regenerated and that checker passes too — the partial
dimension moved from 195/196 to 196/196 evidenced, the one addition being this
module's incomplete verdict, now driven by a test that executed it rather than
declared in a comment.
Marked [x]: none. YSD-18163 stays [ ]. The rehearsal now has a shape that
would recognise it, and no rehearsal has been run — nor could one be, while two
of its legs have no mechanism and no environment above service-integrated
exists. Marking it would record the shape as the run.
Commit: resolve via git log --grep='18163'.
2026-08-15 — YSD-19025 seeded content: curated, admissible, and unreachable#
Item. EXT Seed exemplar studies, selected Blender open-movie production material, selected open-source game footage, walkthroughs, and templates.
The finding. All five classes existed. Each had a register, each had a
judge, each judge was green against the real registers, and
seededWorkspaceCoverage reported on the set as a whole — including the two
things no per-class judge can see, an empty class and a walkthrough over
material an exemplar has already published an account of. Nothing called any
of it. seededWorkspaceCoverage and renderSeededWorkspace were imported by
one spec and by no application; EXPERT_EXEMPLAR_STUDIES had no importer
outside its own module but a spec; and SEED_CLASS_SPECS named each register as
a STRING that nothing dereferences. The content a first release ships with was
curated, admissible and unreachable, which is content the release does not ship
whatever the registers say. The EXT decision this item carried had already been
settled by its owner (decisions/ysd-19025.md — bundle Sintel, 1.18 GB,
accepted on accessibility rather than size), so what remained was not a decision
and not a licence. It was a caller.
What was built. onboarding/shipped-seed.ts composes the five registers and
judges every member on the way through — exemplar admissibility recomputed
against the catalogue it is handed, so a study admissible against the shipped
corpus is not admissible against one that dropped its sources.
GET /api/study/onboarding/seed (manifest route seededWorkspace, generated
client method, group onboarding, classified read by the existing
path-derived matrix) serves it with no project and no tenant read, because this
is what the RELEASE carries and a learner with no project yet is exactly who
needs it. StudySeedGallery renders it on /studio/study, above the sources
library.
Three things the composition adds that no register could. A refusal travels with the gallery, in the judge's own words: a shelf that printed only its survivors would read identically whether the register held seven sound templates or seventy of which seven survived. The walkthrough/exemplar collision is told to BOTH sides and neither is dropped — dropping the walkthrough hides material the release paid for, dropping the exemplar hides a finished study, and saying nothing lets the learner meet the spoiler first, which is the only one of the four outcomes that is actually wrong; the caveat names THAT an account exists and never what it found, so it restores the ordering without being the leak it warns about. And the cold start reports what the install CARRIES beside what the first pass fetches, because two absences read alone say nothing about whether a fresh install has any material at all.
The run, recorded. scripts/record-seed-run.ts reads the route from a
running deployment — svc-study-workspace against the dev-compose Postgres and
MinIO — and writes seeded-workspace-run.json: status 200, 23,998 bytes,
sha256:4c080ba47bad770015439b435e49e30fde14d6b2f05126537de8e15f33193171, 34
members on five shelves (7 studies, 14 production parts, 4 game parts, 2
walkthroughs, 7 templates), nothing withheld, carriedByInstall
[sintel-render-1080p], fetchedOnFirstRun
[ed-render-480p, stk-grand-prix-recording]. The digest is taken over the
response bytes before anything parses them; a digest over a re-serialization
would pin the recorder's JSON writer and not the service's answer. A non-200 or
a contract violation is recorded as one and exits non-zero.
Verification. shipped-seed.spec.ts 12 tests, every control driven by
doctoring the shipped registers — a template broken into a provenance
contradiction moves from the shelf to withheld carrying the judge's message; a
citation added to a shipped study builds the collision the seed deliberately
does not contain and both entries gain their caveat while the shipped seed gains
neither; the bundled part flipped back to remote-fetch empties
carriedByInstall. seeded-workspace.route.spec.ts 6 tests through the
generated client, including that the served body equals the in-process
composition and that the tenant gate still refuses a foreign token (403).
StudySeedGallery.spec.tsx 7 tests, fixtures parsed through the published
response schema before they are served. studio-study-seeded-content.spec.ts 3
chromium tests against the real stack (web → /api/study proxy → service),
including an axe scan of the panel. Library suite green; contracts
client-smoke/openapi/route-fuzz green; api-group-authorization green;
npx tsc --noEmit clean for the library, the service and the web app.
Marked [x]: YSD-19025.
Closing it took its dependent gate's only dependency with it. YSD-19028 was
a dependent-gate entry naming one item. What that dependency was hiding is that
the machine half of the Phase 1 exit is finished and green: phase-one-exit.ts
judges all eight fault kinds, and its integration suite drives a study of each
medium through the real HTTP API into the study schema on Postgres, reads it
back with assembleQuestionLoop, and gets ok: true with deferredBlockers
naming only mastery-evidence — Phase 3's, by YSD-14106. Run here with
STUDY_PG_REQUIRED=1 so a down stack would have failed rather than skipped: 5
passed. It stays [ ], on the subject of its own sentence: the only studies
that gate has ever judged were driven by a test over footage ffmpeg synthesized
in the same run, and the item says prove a LEARNER can. Its register entry is
now participants and says so.
Commit: resolve via git log --grep='19025'.
2026-08-15 — YSD-22054 requirement scope: the instrument was reading comments as code#
Item. Confirm tests cover the full requirement scope, including failure,
permission, rights, expiry, deletion, accessibility, version, partial, offline,
and scale behavior where applicable. Already [x]; this is a repair of its
instrument and of the CI lane that runs it, and it appends rather than amends.
How it surfaced. The YSD-19025 pass regenerated the coverage report the check reads — the first fresh one since 2026-08-09 — and the check went from stale-and-failing to fresh-and-failing with six unscoped behaviours. Five of the six were the instrument, in three shapes, and every one of them is the same mistake: the source was read as code without the comments taken out.
producerSitesnamedextension-observability-binding.ts:14as the only place'error-rate'is produced, and then reported that no test had executed it. Line 14 is a sentence of JSDoc quoting a publisher's declaration. No test executes a comment, so the strongest accusation this checker can make — produced but never reached — was being levelled at prose.classifyLinesended the top-level type union ofhuman-correction.tsat line 50, because a JSDoc sentence ends "(YSD-19120, the causal-comparison family)." and the closing parenthesis of an English aside took the bracket depth negative. Every union member after it read as CODE, so twelve declared states became productions no test could ever have executed.enumeratesRegisterknew three spellings of traversal and not the fourth. A spec assertingtoEqual([...GOVERNED_SUBJECTS].sort()), or supplyingartefacts: [...PHASE_ONE_ARTEFACTS]as a fixture, traverses the whole register and NAMES no member — which is exactly what makes it worth writing, because a spec that spells its members out goes stale the moment the register gains one. Under the old rule the better test read as no test at all.
The fix, with its controls. linesWithoutComments blanks comments in place
so line numbers still match the coverage report, and classifyLines,
producerSites and declaredStates all read it. A type block now ends at its
semicolon or at the next top-level declaration rather than at the first blank
line, so documentation between union members no longer closes it. Six rule tests
pin each fix beside the control that keeps it from blinding the checker: a state
produced inside a string that contains a comment marker still counts; an
unterminated type alias still stops at the next top-level declaration, so it
cannot swallow the file; a bare mention of a register is still not a traversal,
and spreading a different register says nothing about this one; and with the
comment blanked, a state nothing supplies is still reported.
The check that the repair did not weaken the gate. The applicable-state count per dimension is identical before and after — 129 / 74 / 105 / 76 / 54 / 130 / 77 / 223 / 7 / 26 — so no state left the checked set; only their standings moved, each into a rule that names why. The 28 planted-hole tests that existed before still pass unchanged.
The sixth finding was real and got a test. PersistenceKind has three
members and nothing had ever supplied the third. phaseOneExit refuses anything
that is not authoritative-database, so 'unknown' — the observer saying they
could not tell where the study ran — is refused, and a gate whose whole
qualifier is "on production persistence" must refuse it rather than give it the
benefit of the doubt. Asserted now, message and all.
A CI lane was red and the redness was invisible. The workflow produces the
coverage report by running the library suite with v8 coverage and no timeout
override. Under instrumentation camera-motion.spec.ts's vocabulary sweep takes
about thirteen seconds against a five-second default, so it failed there and
nowhere else — and vitest writes NO coverage report from a failed run, so the
requirement-scope step downstream reported a MISSING report rather than the
timeout that caused it. The test now carries an explicit 60 s timeout with the
reason written beside it.
Verification. The exact CI command
(npx vitest run --coverage.enabled --coverage.provider=v8 --coverage.reporter=json --coverage.include='src/**' --coverage.reportsDirectory=.coverage)
now completes: 888 files, 15,217 tests passed, 28 skipped, 0 failed, and
writes the report. Against that report: check-requirement-scope.mjs passes —
858 declared states across 10 of 10 named behaviours, 569 by execution, 271 by
assertion, 16 by enumeration, 2 exempt — and
node --test tools/yemaya-study/check-requirement-scope.test.mjs passes 34. The
rest of the battery (architecture, doc-conformance, evidence-index, open-item
accountability, release-integrity, stub-scan, traceability) passes.
Marked [x]: none. YSD-22054 was already marked; its evidence-index entry was
incomplete with an empty evidence list and a note describing a different
concern, which is corrected to proven with the instrument, its rule tests, the
exemptions register and the CI workflow as evidence.
Commit: resolve via git log --grep='22054'.
2026-08-15 — YSD-22052/22053 evidence index: the disagreement nobody read backwards#
Items. Enumerate implementation evidence for every checklist item and
classify each, both already [x]. This is a repair of four entries and of the
rule that should have caught them, and it appends rather than amends.
The finding. check-evidence-index.mjs has always refused an entry
classified proven against an item the checklist leaves unticked — two records
disagreeing, and the note says which one is usually wrong. The same disagreement
read the OTHER way was never checked, and it decays differently: the entry is
written while the item is open, the item closes months later, and nobody comes
back to it. Four entries were in exactly that state, and three of them still
carried the sweep boilerplate — "blocked on a human/external act (recruitment,
licensing, participant evaluation, or an owner product decision). Nothing in the
tree can close it." — the same sentence the open-item accountability register
was built to replace.
- YSD-22054 was
incompletewith an EMPTY evidence list and a note about a different item, while its instrument was gating CI on every push. - YSD-10107 and YSD-12102 closed on 2026-08-09, each by building the
evaluation instrument its constituencies run through, and each entry still
said the item was blocked on people. The instruments are honest about the
people: all six atlas templates report
unevaluatedon all four dimensions with every constituency named silent,atlasPanelGateEvidencecovers nothing of Gate 8, andreleaseRefusalsrefuses rather than clears. The panel itself is YSD-18002's and is still open, which is where that work is owned. - YSD-19120 closed on 2026-08-14 with the project-aware family, the last of the six it names; the entry still said higher-quality analysis "needs models the box does not have", true of the sweep that wrote it and false of the item.
- YSD-19128 closed on 2026-08-13/14; the entry still said the phase-exit gate was "blocked by the open items in its own phase".
The repair. All four are proven now, each against the modules and specs
that closed it, with a note that says what closed it and — for the two
evaluation instruments — what it deliberately does NOT claim. And the checker
gained the mirror of its own rule: a classification that states a deficiency
(contradicted, incomplete, missing) against a TICKED item is refused, with
a message that names both repairs, because which record is wrong is not
decidable from inside the checker. Positive-controlled by planting the fault on
a healthy entry (YSD-22056) and watching it fail, then restoring.
What the counts say now. 1305 items indexed: 1286 proven, 16 incomplete,
3 contradicted — and the nineteen non-proven entries are exactly the nineteen
items the checklist leaves open. The two records agree, item for item, for the
first time.
Marked [x]: none.
Commit: resolve via git log --grep='22053'.
2026-08-15 — YSD-19143 shadow validation: the record that could not go stale, and nothing checked#
Item. Run parallel/shadow validation and compare data, rights, playback,
annotations, search, and exports before cutover. Already [x]; this gives its
record a reader and re-cites its evidence, and it appends rather than amends.
How it surfaced. A sweep of every register in this directory for a reader:
grep each file name across tools/, libs/, apps/ and .github/.
shadow-validation-verdict.json came back with zero — its only mention
anywhere is a sentence in DEVELOPMENT.md saying how to regenerate it.
The finding, which is about a claim rather than a defect. The verdict file says of itself: "Admissibility is DERIVED from the code … so a legacy change flips a verdict here rather than leaving this file stale." That is true of how the verdicts are COMPUTED — every one is derived, none asserted — and it was not true of the file, because deriving them again is something a person had to remember to do. Re-run on 2026-08-15: every status and reason still matches, so the record was accurate; what it did not have was any way for its inaccuracy to be noticed on the day it began.
The reader. shadow-validate.ts --check re-derives the six verdicts and
compares them to the record: status and reason exactly, and detail word for
word after ONE normalisation — the legacy service mints its ids from the clock
(vid_<millis>_<random>) and the data verdict quotes one to show the two id
spaces are disjoint. Normalising that number keeps the whole sentence under
comparison, rather than dropping detail from the check, which is where a check
like this usually goes to die. The file's own summary counts
(comparableDimensions, divergedDimensions) are checked against its rows, and
a dimension that stopped being derived, or appeared, is drift in both
directions. Two rule tests pin it, and the end-to-end control is a planted flip
of the data verdict — refused, then restored. Both run in the study-workspace CI
lane.
The evidence was pointing at the wrong artefacts. This entry cited three
§19.8 migration modules — compatibility-redirect.ts, deprecation-notice.ts,
duplicate-path-removal.ts — which are the neighbouring module family of the
subsection, and exactly the indirect evidence YSD-22053 says to treat as
incomplete. Nothing in them is a shadow validation. The harness, its tests, the
verdict and the CI lane are cited now.
What the verdict actually says, unchanged: three of six dimensions have no
admissible legacy baseline — rights (the legacy types carry no rights, licence,
grant, territory or expiry concept), playback (the Aja adapter records the
technical-metadata path as unavailable-simulated-backing, so agreement with it
would be the defect), exports (nothing in the legacy service serialises). Of the
three comparable, data agrees and annotations and search diverge, both because
the new model is more honest than the old: a legacy annotation carries seconds
and no rate, and the legacy index has no indeterminate state, so a record the
workspace refuses to decide arrives as a plain absence.
Verification. --check passes (6 dimensions, 3 comparable, 2 diverged);
npx tsx --test tools/yemaya-study/shadow-validate.test.mjs passes 10; the
planted-flip control fails as it should; evidence-index, doc-conformance and the
rest of the battery pass.
Marked [x]: none.
Commit: resolve via git log --grep='19143'.
2026-08-15 — YSD-0101/0103: two schemas nothing read, one of them cited in the checker's own header#
Items. Machine-readable capability inventory and
reuse-versus-wrap-versus- replace ledger, both already [x]. This makes their
$schema files load-bearing, and it appends rather than amends.
How it surfaced. The same reader sweep that found
shadow-validation-verdict.json unread: capability-inventory.schema.json and
reuse-ledger.schema.json both came back with zero readers.
The finding. check-capability-inventory.mjs opened by saying it fails when
"the inventory does not validate against its JSON Schema (dependency-free
structural validation implemented below)". The validation was implemented below;
the schema was never read. The required-field lists were typed out a second
time in JavaScript, and the two copies had already come apart:
- the schema requires
appsPathon every domain entry and the hand copy did not list it, so an entry omitting the key satisfied the checker and violated the schema it claimed to be checked against; additionalProperties: falseis on every object in the schema and was enforced nowhere, so a stray or misspelled field passed;- the schema is stronger in three more ways the copy never had —
integerwithminimum: 0on the counts,minLengthfloors under every free-text field, andcontributesTorequired ondataAuthority.
check-reuse-ledger.mjs never mentioned its schema at all, while
re-implementing the status vocabulary, the disposition vocabulary, the
twenty-character rationale floor and the conditional that makes an
ownerDecision block mandatory for a partial or fixture-backed entry —
every one of which the schema already states, the last as an if/then.
Both registers pass the schema as they stand, so the drift was latent rather than live. Latent is what this programme's registers are for: the day it stops being latent is the day nobody is looking.
The validator, and the refusal that makes it safe.
tools/yemaya-study/json-schema-subset.mjs is dependency-free (the repo root
deliberately has no ajv) and implements exactly the keywords the two schemas
use. A partial validator that silently ignores what it cannot read is the same
failure one level up — the caller believes the document was checked — so it
THROWS on any keyword outside its supported set rather than returning "no
problems". Adding a keyword to either schema therefore extends the validator or
fails loudly, and can never quietly narrow what is checked.
What the checkers kept. Everything the schema cannot say: agreement with
domains.json on owner, paths and status; paths that exist in this working
tree; the fourteen named domains and no others; the ledger's own
decisionPolicy naming partial and fixture-backed; the §5.2 table parsed
out of the proposal; and the pending-decision count YSD-0139's gate consumes.
The duplicated shape checks are gone.
Verification. Seven rule tests, including the refusal in three positions
(top level, nested definition, and a format neither schema uses), and a
planted violation of the two rules the hand copies had lost — a domain entry
with appsPath deleted and one carrying an unknown field — both reported
against the REAL inventory. Both checkers pass; the battery passes; the tests
run in CI's lint job beside the two checks and are listed in DEVELOPMENT.md's
block, which check-doc-conformance holds equal to that job.
Marked [x]: none.
Commit: resolve via git log --grep='0103'.
2026-08-16 — the nineteen open items, re-derived one at a time#
Why. The accountability register's own $comment says a
machineSliceRemaining: false "is worth exactly the pass that last looked".
Every one of the nineteen still said false. This is that pass: each entry's
claim re-derived from the machinery it names, rather than read.
Seventeen held, and four things did not.
1. A study surface this session added was accounted for by nothing.
check-accessibility-evidence.ts refused StudySeedGallery.tsx — "belongs to
no surface and is not declined; until each is placed, a change to it stales
nothing and its accessibility is nobody's evidence". It is a seeded-content
surface now. The checker is in CI's lint job and my earlier battery had run only
the .mjs checkers, so the tsx one went unrun: a battery is a list, and a
list that grew a member in another language has a hole in it.
2. The modalities lane claimed a reach its own selector could not have. Its
surfacesByFlow.contrast names 21 surfaces, recorded in August 2026 from a
measurement of axe's evaluated-node list "on /studio/study" — a PAGE-WIDE scan.
The lane's own scans pass include: ['[data-study-app]'], and that route
renders data-study-app as ONE SIBLING among the panels: page.tsx composes
home, sources, manual-mode, the workspace shell and the rest beside it, and the
marker appears in exactly one component. Two independent measurements agree — a
walk of the import graph from StudyWorkspaceApp.tsx reaches 17 files spanning
3 of the 21 claimed surfaces, and reading page.tsx shows ten of them rendered
as siblings outright. The reduced-motion walk had the same shape
([data-study-app] *).
The repair is the instrument, not the claim. Narrowing the register would have described a scan nobody wanted; the recorded measurement is what the lane should have been doing. The contrast scans are page-wide now and the motion walk covers every element the route composes. The widened lane passes — 15 tests, 6.4 minutes, against the real stack — so the register's 21-surface claim is true for the first time, and the new surface joins it for contrast and zoom.
3. check-test-portfolio was red on a module the web app imports.
accessibility/client.ts is the browser-safe entry point — it exists because
the main barrel reaches node:crypto, so importing policy from it would pull a
Node builtin into a browser bundle — and no spec reached it by any import path.
Its header states the rule that makes it an entry point ("everything reachable
from here must be pure") and nothing checked that either. The spec now walks the
real relative-import graph from it and fails on the first node: specifier,
naming the file that pulled it in; planting node:crypto in
region-text-alternatives.ts fails it, as the control. It also calls the policy
rather than only naming it: a bounding box a shape fills a quarter of overstates
it, one it fills 95% of does not, and a box of no area cannot overstate
anything.
4. The program-completion gate understated its own dependencies by fifteen.
YSD-22061's first clause is "every in-scope item is proven" and its dependsOn
named three. A reader saw an item three closes from done that is eighteen. It
names the whole open set now, which the checker holds live: an item closing
without this entry being edited becomes a gate waiting on something already
done, and it refuses that.
What held, with what it was checked against. YSD-18011's six relation cases
and YSD-18012's six path conditions are the item's words, exported
(RELATION_JUDGEMENTS × RELATION_GRANULARITIES, PATH_CONDITIONS);
YSD-18010's six live in GOLD_CASE_KINDS, ANNOTATOR_EXPERIENCE,
ANNOTATION_ANSWER_MODELS and bounded-readings.ts's counter-reading stance —
all six exist and every one of those items says INCLUDE, which is corpus content
over cleared material. YSD-19108's floor is RELIABILITY_MIN_STUDIES = 10 and
an empty population is refused with three faults, not passed. YSD-19064's four
named lens gates exist. YSD-22058's eleven procedures all carry a runbook and
eight carry an exercised mechanism. YSD-18163's ten legs all carry a mechanism
and three carry a channel. YSD-16065 resolves eight journeys over zero
recorded sessions. YSD-22057 is the sharpest of them: environmentFor
derives an evidence environment from the file path, and the ladder tops out at
browser for anything the repository can run — no lane here can produce
deployed or production evidence, so the eighteen gates refused
not-production-like are refused structurally and not for want of citing
something. The three dependent gates besides YSD-22061 name exactly the open
items of their own sections.
Marked [x]: none. Nineteen still open, and every
machineSliceRemaining: false has now been looked at rather than inherited.
Commit: resolve via git log --grep='18107'.
2026-08-16 — YSD-22052: a citation that resolves to a directory#
Item. Enumerate implementation evidence for every checklist item, already
[x]. Appends rather than amends.
The finding. check-evidence-index.mjs requires every proven pointer to
resolve on disk, and existsSync is true of a directory. Thirty-one citations
across twenty-four entries pointed at three SOURCE DIRECTORIES —
src/onboarding, src/scale/, src/rollout/ — which is the module family an
item shares with its neighbours rather than the artifact that proves it: exactly
the indirect evidence YSD-22053 says to treat as incomplete, wearing a resolving
pointer's clothes.
It was not harmless. Twenty of the twenty-four resolve to a module in the
cited directory that names the item in its own text. Four do not, and three of
those are implemented outside the directory they cited: YSD-14177's
empty/partial/no-model/no-permission/source-expired states are
policies/surface-states.ts; YSD-17100's residency definitions are
libs/contracts/src/study/deployment-residency.ts; YSD-17106's
disaster-recovery documentation is continuity/disaster-recovery.ts;
YSD-17107's drills are continuity/operational-drills.ts. A directory citation
cannot be wrong about which file, which is why it stayed.
The repair. All twenty-four now cite the implementing module, and every replacement was checked twice before it was written: the path resolves to a file, and that file names the item id in its own text. The rule is enforced now — a citation must resolve to a FILE — with one sanctioned exception, migration directories, taken from the evidence log's own immutable-link contract (YSD-0112): a migration is pinned as "the repo path of the migration directory plus the SHA that introduced it", because migration files are append-only by repo policy and the directory is therefore the stable artifact. Positive-controlled by planting a directory citation on YSD-17050 and watching it fail.
Marked [x]: none.
Commit: resolve via git log --grep='22052'.
2026-08-19 — YSD-18107: what the seven unmounted panels are waiting for, measured rather than described#
Item. Maintain accessibility automation and manual evidence for every
critical flow, [ ], and its remaining machine slice: the seven finished
panels no route reaches.
The finding. The slice was described in prose on 2026-08-17 as a choice that
is "not uniform" — "for some it is a BFF projection over a store that exists,
and for at least one it is the domain itself" — with EvidencePack cited as
real "across contracts, the persistence registry and a Veritas API client".
Re-measured against the import graph, the persistence registry and the Prisma
schema, that was wrong three ways.
There are two EvidencePack contracts. The contract under
contracts/common declares the object all three evidence panels cite — slug,
excerpts, retrievalTrace, consumers. libs/contracts/src/veritas/index.ts
declares a different object of the same name — storyId, claimIds,
evidenceItems — and it is the Veritas one the API client serves at
/veritas/evidence-packs. The reach cited belonged to the other contract.
A table with no writer is not a store. The common contract's table,
v1_cross_cutting_evidence_pack, is in schema.prisma and in the
20260527184417_v1_initial migration. Nothing outside the generated Prisma
client has ever named its accessor crossCuttingEvidencePack.
No panel imports the contract it cites. All seven declare their own view
types, copied by hand. EvidencePackKind, EvidencePackStatus,
EvidencePackVerificationStatus and CanonicalCitationVerifierKind all match
their contracts member for member today, and nothing keeps them matching: a
contract change cannot break a panel and typecheck cannot report the drift.
The measurement is now a rule. accessibility/unmounted-backing.ts
re-derives the three premises every run — the projection's producers, the
contract objects' consumers, the table's callers — and fails when a write-off
has stopped being true or a declared cost no longer matches the repository. The
view type is matched by NAME (a superset: it cannot miss a producer) and the
contract by IMPORT (four files name AuthoringJob in prose and import nothing),
and the question is asked of the named OBJECTS rather than the module, because
common/citation.ts holds CanonicalCitation, which the panel writes, beside
CitationTrail, which the registry registers.
And one copy had already drifted. All thirty string-literal unions the seven
panels declare were held against every z.enum in the cited contracts and in
the modules those contracts import. Twenty-three are exact copies — two of them
from a module the panel's header never names, and one from an enum the contract
does not export (factCheckStatus is declared inline inside an object in
authoring-job.ts; a vocabulary that cannot be imported is one that gets
retyped). Five name vocabularies libs/contracts does not define at all, and
all five sit on EvidenceExportBundlePanel, so a producer for it must
canonicalize export formats, redaction levels, audit audiences and ten scope
flags before it can assemble anything. And GroundedReportGroundingLevel
declares FOUR levels where GroundedReport.groundingSummary.level carries SIX:
weakly_grounded and unknown are unrepresentable in the panel that promotes
the report. The panel's readiness evaluator branches on that field three times
and mirrors the contract's own three refinements exactly, so the logic is right
and the type cannot hold two of the six states — harmless while nothing produces
the view, wrong on the first day something does. Recorded as a hand measurement
with its date and method and deliberately not made a rule: a gate over seven
files this decision may retire is a gate written for a world that may not exist.
Measured: 3 producer-over-an-empty-store, 1 store-first (SourceSet,
which libs/oshun/evidence-sophia validates and hashes with no table to hold
it), 3 domain-first.
Why it became EXT. Eight vocabulary probes over the proposal return zero
hits: it asks for none of the seven. Every one shipped under a V1-SOP item in
docs/releases/v1/specs/todos.md marked [x] on an acceptance that named the
component, its tests and a clean typecheck, and never a route. Mounting is
V1-SOP work in V1-SOP's code; retiring moves files another initiative marked
done; and declining is unavailable, because the map's established decline reason
— "belongs to X, which owns its own accessibility evidence" — would be false:
this is the only accessibility surface map in the repository. Drafted as
decisions/ysd-18107.md with three pending controls, actor reclassified
participants → decision-owner, and YSD-16005 now depends on it rather than
claiming a machine slice it had already said was not its own.
Marked [x]: none.
Commit: resolve via git log --grep='18107'.
2026-08-19 — YSD-19006: the Phase 0 exit gate judged two of its four clauses over nothing, and passed#
Item. Phase 0 exit: prove teams can implement independently without
competing identities, evidence models, rights decisions, or duplicate domain
engines, [ ]. Its accountability entry read, in full, "Closes when its
Phase-0 contents close", with actor dependent-gate — the register's own class
for an item that owes nothing of its own.
The finding. It owes something of its own. policies/phase-zero-exit.ts
judges all four of the item's clauses, purely and correctly, and only two of
them are judged over anything. evidenceModelFaults reads
AUTHORSHIP_VOCABULARIES; competingDecisionAuthorities reads
DECISION_AUTHORITIES. The other two inputs default to empty — labellings to
{}, computations to [] — so collapsed-distinction,
duplicate-domain-engine and copied-theory, three of the five blocker kinds,
cannot be reported by a call that supplies neither. No register of the
workspace's computations exists anywhere in the tree: WorkspaceComputation
is a shape with a judge and no corpus.
Every phase-zero primitive has exactly one caller — its own spec. That
spec's workspace-wide case was named "passes over the workspace as it stands",
handed in ONE labelling (markdown-export) out of the twenty-three surfaces the
accessibility map declares and ZERO computations, and asserted blockers: [].
The module is scrupulous about the difference — it returns
examined.labelledSurfaces precisely so a pass cannot be read wider than it is
— and nothing read the field. Phases 1, 2 and 5 each got a runner for exactly
this reason; phase zero never did, and unlike theirs its inputs are repository
facts rather than a deployment's, so it needs no stack.
A gate that passes vacuously where its three siblings refuse.
phaseOneExit([]) faults on every required medium; phaseFiveExit returns
no-study-started rather than a rate; the phase-two reader withholds a verdict
it cannot take over the whole run. phaseZeroExit() returned passed: true.
unexamined-population is now a blocker kind, emitted once per empty input and
naming what that silences, so the default call refuses.
And a frozen count wearing a rule's name. The spec case "covers every
vocabulary the tree declares, not a sample of them" read no tree: it asserted
AUTHORSHIP_VOCABULARIES.length >= 14, beside a comment recording that sixteen
vocabularies existed when the gate was written — a number counted once, by hand,
that does not match the fourteen entries beside it. The satisfies Record<…> on
each entry is real enforcement and binds only the entries already present, so a
vocabulary added next week is the one nobody reduced and nothing would say so.
It now collects the types every authorKind field takes across the library and
asserts set EQUALITY with the register, in both directions — a stale entry is a
finding too, and a sweep that broke would leave every entry unaccounted for
rather than passing an empty comparison. Measured today: thirteen types on both
sides, equal. The types are read off the FIELD rather than off a *AuthorKind
name, and an indexed access is skipped with its reason written down.
Marked [x]: none. The item is reclassified dependent-gate → implementation
with its machine slice named: the two missing corpora and a runner.
Commit: resolve via git log --grep='19006'.
2026-08-19 — YSD-19065: the Phase 3 gate and its read model were both finished, and nothing joined them#
Item. Phase 3 exit: prove the primary filmmaking and performance-learning
journey is coherent from exact source evidence through original practice,
[ ]. Its accountability entry read, in full, "Closes when its Phase-3 contents
close", with actor dependent-gate.
The finding. It was modelled differently from its two identically-shaped
siblings. YSD-19028 (Phase 1) and YSD-19108 (Phase 5) are participants with a
store landing and a runner as the join, each built because "an integration spec
that drives the API, writes three studies and grades them proves the reader
works and is not a join: it can only ever answer for its own fixtures, and the
item is about somebody else's work." Phase 3 had both halves — phaseThreeExit
judging eleven fault kinds across four qualifiers, and
read-models/study-to-practice-arc.ts assembling one subject's arc behind one
question with every hop a real join — and the only thing carrying one to the
other was phase-three-exit.integration.spec.ts, over two studies it drove
through the HTTP routes itself in the same run.
The runner is built, modelled on the Phase 1 and Phase 5 tools, and it was
verified against the dev stack over two projects and four questions from an
earlier end-to-end run — records it did not create. With the notebook store
wired it reports evidence-unattributable on both subjects and three standing
workspace faults: art-direction, character and visual-design shipped in
YSD-19060's scope and REWATCH_FOCUSES maps no focus to any of them, so
evidence for those craft dimensions arrives with nothing declaring which subject
it belongs to.
Two selection rules are the tool's own, and the second is a correction it
needed. The gate grades the FIRST journey it is handed per subject, the same
discipline phaseOneExit applies to media, so journeys are presented in the
store's own total order and nothing ranks, scores or picks a best one. And a
study whose evidence is entirely UNSTAMPED is kept — in both subjects. The first
draft dropped it, on the reasoning that an empty subject station means the study
is not that subject's journey; that made evidence-unattributable unreachable
and reported subject-unstudied instead, which says "nobody studied this" where
the truth is "somebody did and nothing says what of". It is not hypothetical:
both observations in the dev deployment are unstamped, and under the first rule
the gate was told neither subject had been studied at all.
Marked [x]: none. The item is reclassified dependent-gate → participants
with the runner recorded as the join. What remains is the subject of its
sentence: a journey somebody actually walked.
Commit: resolve via git log --grep='19065'.
2026-08-20 — YSD-19006: the gate's two empty populations, and the badge nothing called#
Item. Phase 0 exit: prove teams can implement independently without
competing identities, evidence models, rights decisions, or duplicate domain
engines, [ ]. Its 2026-08-19 entry named the machine slice exactly: "TWO
REGISTERS AND A RUNNER, and the registers are the work."
Both registers and the runner are built.
policies/workspace-computations.ts declares every workspace computation read
against Hathor's four owned theories. Each of the four EVIDENCE_BINDINGS is
recorded as held with the field, guard or returned member it was read off, or
absent with what is missing, in a Record<EvidenceBinding, BindingReading> — so
a declaration that skips a binding fails the build rather than reading as a
denial. policies/record-labellings.ts declares every labelling of an epistemic
record, holding the real exported function rather than a copy of what it does,
and naming the exact expression the surface renders through so a surface that
stops calling it is a finding rather than a silent pass.
tools/yemaya-study/run-phase-zero-exit.ts carries both to the gate. It is the
fourth phase runner and the only one that needs no deployment: its subject is
the source, so it sweeps the tree — walked, never git greped, because git grep
reads the index and an untracked file is invisible to it.
The first scoping pass was wrong, in the direction a keyword pass is always
wrong. The 2026-08-19 note said "nothing in the study workspace computes
cuts-per-minute, average shot duration, a tension curve or an MDA record". THREE
modules compute average shot duration. It had scoped off ANALYSIS_METHODS,
which lists what a deployment can RUN rather than what the library computes, and
a source pass misses them too unless it knows that the separator is a property
of the phrase: this tree writes shot-duration, and a search for
shot duration returns shot-duration-distribution.ts not at all. The tells
now use [-_ ]? between words, and the register's own worst case is a test:
unfindable-declaration fires when a pattern cannot find a module the register
already knows about, because a sweep that cannot find what is written down
cannot find what is not.
The labelling half found that epistemicBadge had no product call site at
all. Ten <EpistemicTypeBadge> sites across the studio, and not one passed
authorKind — so every one took the component's TYPE branch, which the
component itself documents as "correct for a section heading or a legend entry …
and wrong for a record". Four surfaces, all fixed here:
- the PDF printed
[interpretation]— the stored identifier, author dropped — while the Markdown badge beside it printedInterpretationand would have printedInterpretation · Model suggestionfor a machine's reading. Two notations of one claim that did not say the same thing, in the artifact furthest from a correction; - the analytical CSV's
epistemicTypecell was the bare type. Astandingcolumn now carries the layer, so the type column stays machine-filterable and the distinction lives beside it; LensOutputItemdroppedauthoron the way out of the read model, so no lens renderer could have badged a machine's reading as a suggestion even if it had wanted to — the rule the presentation layer cannot reach, one level further down than the time it was found in the surfaces;- the epistemic record trail and the lens inspector badged records with their section's type.
The gate runs, and refuses. Three blockers on the first run, two after the
fix below. policies/pacing-metrics.ts reported a shot rhythm — a mean and a
coefficient of variation over { startMs, endMs } shots, with a second private
coefficientOfVariation beside the one in shot-duration-distribution.ts to
drift away from. That is not a description of a measurement; it is Hathor's
design-time calculation ("over shots nobody anchored to a source")
reimplemented. computePacing now takes ShotRecords and reports
computeShotDistribution's own distribution WHOLE — projecting it back to a
count and a mean would put the uncertainty and the exclusions straight back in
the bin they were taken out of.
That fix needed the gate's model widened, and the gap was real.
WorkspaceComputation.delegatesToAdapter says "the owning domain computes
this"; a module that instead calls the workspace's own measurement had no way to
say so, so duplicate-domain-engine was unclosable by construction — two
modules touching one capability were a duplicate whatever either of them did,
including the one thing that fixes it. delegatesToModuleId says it, and it is
not an escape hatch: the register refuses a delegation to a module it does not
declare, to one sitting next to a different capability, or to one that delegates
onward, because written on both of two modules it would clear the fault while
leaving two implementations standing.
What remains is one computation and it needs an instrument that does not
exist. suggestions/project-shot-scale.ts#scoreSourceRelative reports a
source's own shot-duration mean and standard deviation as the reference a shot's
mark is taken against, and prints them in its statement. It cannot be closed by
editing the module: its producer reads cut times out of
detectShotBoundariesInVideo, and nothing in the estate models how uncertain a
detected boundary is — so an uncertaintyMs written there would be a number
nobody measured, and delegating to computeShotDistribution needs the same
field and is blocked on the same thing. Worth recording beside it:
computeShotDistribution itself has no product caller, so the binding it
enforces has never been satisfied by real material either.
Marked [x]: none. The item still depends on YSD-19005, which is licensing
rather than repository work. machineSliceRemaining stays true and now names
the boundary-uncertainty measurement rather than the two registers.
Verified. 15,432 library tests, 1,371 studio tests,
tsc -p tsconfig.lib.json clean, the web typecheck ratchet clean with 0 new
errors, the stub scan at 0 findings, and the runner's own test file wired into
CI as the completeness gate — a module somebody adds that computes one of
Hathor's four quantities, or a surface that labels an epistemic record, fails
there rather than being quietly absent from a corpus that still looks whole.
Commit: resolve via git log --grep='19006'.
Addendum, same day — the remaining blocker was scoped, and it is three
different kinds of work. Closing
suggestions/project-shot-scale.ts#scoreSourceRelative means giving it the
three bindings it lacks and then delegating, and they do not cost the same:
carried-uncertaintyis buildable, and it is a projection of a measurement rather than a number nobody took.adaptive-shot-detection.tsalready distinguishes its two boundary kinds — a hard cut is a peak at one frame, so the change happened inside the interval since the previous frame; a gradual transition is a RUN placed at its excess-weighted centroid, so the boundary is uncertain across the run's extent. Both figures are in the signal the detector already holds. It ripples throughdetectShotBoundaries' two callers, the release-gate comparison,ProjectShotandreadProjectShotUnits;accounted-exclusionsis cheap and half-done: the unit reader already discards spans belowminimumShotSecondsandmanage-analysis.tsalready writesdiscardedShotsonto the detection. They reach the RECORD and not the COMPUTATION, so the norm cannot say what was left out of it;required-qualitative-contextis a product ruling and not a field.qualitativeContextoccurs in exactly two modules in the whole estate and no route, command or analysis request carries a person's sentence into either. That is also whycomputeShotDistributionhas no product caller: the discipline it enforces has no path that can meet it. Whether an automated run must collect a human reading before it may report a tempo has to be decided before it can be built, and deciding it closes the same hole under YSD-9183.
Recorded on the accountability entry rather than started, because the first part touches a persisted detection payload on a live analysis path and the third cannot be started at all until somebody rules.