- Status: proposed
- Priority: EXT — blocking for YSD-18107 and, through it, for YSD-16005 and the Section 22.3 completion audit. Four of the twenty-three study surfaces are reached in part or not at all, six required (flow, surface) instrument pairs are open because of it, and no accessibility work of any kind can close them: no lane can open a page the application does not compose.
- Decision owner: @GreyChimp
- Drafted: 2026-08-19 by Claude Code (Opus 5)
- Approval: pending
Question#
accessibility/surface-mounting.ts measures, from the route entry points
inward, which files the application actually renders. Seven finished panels —
6,600 lines across four surfaces — are reached by nothing in the repository but
their own component tests:
| Panel | Surface | Lines |
|---|---|---|
EvidenceReviewQueue.tsx |
lens-and-review | 494 |
SourceSetInspectionPanel.tsx |
lens-and-review | 896 |
RetrievalSetInspectionPanel.tsx |
lens-and-review | 551 |
CitationValidationPanel.tsx |
notebooks-and-reports | 676 |
GroundedReportPromotionPanel.tsx |
notebooks-and-reports | 1460 |
EvidenceExportBundlePanel.tsx |
evidence-export | 1542 |
AuthoringDraftWorkbenchPanel.tsx |
authoring-drafts | 981 |
Each is declared in accessibility-surface-map.json with a reason, and the gate
refuses both directions — an undeclared unreachable path, and a declaration for
a path a route has since started reaching. That keeps the list honest. It does
not answer the question a reader is left with, which is: mount them, or retire
them?
The question is EXT because neither answer is the accessibility lane's to give. Mounting means building producers, schemas or domains in another initiative's code; retiring means moving files that another initiative's checklist marked complete.
What was measured#
Three premises decide the cost, and all three are now re-derived on every run by
accessibility/unmounted-backing.ts rather than written down once — the same
correction this register has already made twice. The prose written on 2026-08-17
said the seven split into "a BFF projection over a store that exists" and "the
domain itself", citing EvidencePack as real "across contracts, the persistence
registry and a Veritas API client". Measured on 2026-08-19, that was wrong three
ways.
There are two EvidencePack contracts. The one under contracts/common
declares the object all three evidence panels cite — slug, excerpts,
retrievalTrace, consumers. The one under contracts/veritas 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 does have a
table: the persistence registry derives v1_cross_cutting_evidence_pack, and it
is in schema.prisma and in the 20260527184417_v1_initial migration. Nothing
outside the generated Prisma client has ever named its accessor
crossCuttingEvidencePack. Zero writers, zero readers.
No panel imports the contract it cites. Every one of the seven declares its own view type — enums, statuses, field shapes — copied by hand from a contract it never references, so a contract change cannot break a panel and typecheck cannot report a divergence.
All thirty of their string-literal unions were held against every z.enum in
the cited contracts and in the modules those contracts import, on 2026-08-19:
- 23 are exact copies, member for member and almost all in the same order.
Two of the twenty-three are copied from a module the panel's header does not
name at all —
SourceCredibilityTierfromcommon/source.ts,GroundedReportReviewPriorityfromcommon/grounded-answer.ts— and one is copied from an enum the contract does not export:AuthoringDraftFactCheckStatusreproducesfactCheckStatus: z.enum(['pending', 'partially_checked', 'checked', 'blocked']), declared inline inside an object inauthoring-job.ts. A vocabulary that cannot be imported is a vocabulary that gets retyped. - 1 has already diverged.
GroundedReportGroundingLeveldeclares four levels —grounded,partially_grounded,synthesis_only,ungrounded.GroundedReport.groundingSummary.levelisGroundedAnswerGroundingLevelSchemaand carries six:weakly_groundedandunknownare missing. The panel's readiness evaluator branches on that field three times, faithfully mirroring the three refinements the contract itself keys on the same four values — so the logic is right and the type cannot hold two of the six states a real report can be in. Nothing is wrong today, because nothing produces the view; it is wrong on the first day a producer meets aweakly_groundedreport. - 1 is a deliberate subset.
AuthoringDraftHandoffTargetoffers six of the contract's eight checkpoint kinds plus an addedrevise;outline_approvalanddraft_checkpointare not offerable as handoff targets. - 5 have no counterpart anywhere in
libs/contracts:RetrievalCandidateStatus,EvidenceExportFormat,EvidenceExportRedaction,EvidenceExportScopeFlag,EvidenceExportAudience. These are vocabularies the panels invented — export formats, redaction levels, audit audiences, the teninclude_*scope flags — and a producer would have to give each one a canonical home before it could assemble the projection at all. That is work the cost table below does not count, and all five sit on the same panel.
This half is a hand measurement made once, on the date above, and deliberately not a rule: a gate over seven files that this decision may retire is a gate written for a world that may not exist. If the ruling is to mount, it should become one.
With those corrected, the seven do split — into three groups, not two:
| Panel | Contract | Consumers outside libs/contracts |
Table | Cost |
|---|---|---|---|---|
EvidenceReviewQueue |
common/evidence-pack | persistence registry | yes | producer-over-an-empty-store |
RetrievalSetInspection |
common/evidence-pack | persistence registry | yes | producer-over-an-empty-store |
EvidenceExportBundle |
common/evidence-pack | persistence registry | yes | producer-over-an-empty-store |
SourceSetInspection |
common/source-set | libs/oshun/evidence-sophia (2) |
no | store-first |
CitationValidation |
common/citation | none | no | domain-first |
GroundedReportPromotion |
common/grounded-report | none | no | domain-first |
AuthoringDraftWorkbench |
common/authoring-job | none | no | domain-first |
SourceSet is the one with a live consumer: libs/oshun/evidence-sophia
validates it, hashes it and computes readiness over it, and no table holds one.
That is a different job from the other six and a different team to ask.
Two facts about the citation row are worth stating because getting either wrong
inverts it. common/citation.ts declares both CanonicalCitation, whose
verification field the panel writes, and CitationTrail, which the registry
registers as v1_cross_cutting_citation_trail. A table for a sibling object in
the same file is not a table for this one, and asking the question at module
granularity made the registry a consumer of both. AuthoringJob is likewise
named in prose by four contract files that import nothing; outside its own
module and spec it is imported by exactly one non-test file in the repository —
publication-release-gate.ts, itself a contract.
Where they came from#
None of the seven is a surface the Yemaya study proposal asks for. Eight
vocabulary probes over YEMAYA_STUDY_AND_DECONSTRUCTION_WORKSPACE_PROPOSAL.md —
review queue, evidence pack, citation validation, grounded report, export
bundle, authoring draft, source set, retrieval set — return zero hits.
Every one was delivered under a V1-SOP item in
docs/releases/v1/specs/todos.md, and every one of those items is marked
complete:
[x] V1-SOP-015 Add evidence review queues backed by Sophia contracts.
[x] V1-SOP-016 Add source-set inspection views for research integrity review.
[x] V1-SOP-017 Add retrieval-set inspection tooling for grounded outputs.
[x] V1-SOP-018 Add citation validation tooling for operator review.
[x] V1-SOP-019 Add grounded report promotion workflows.
[x] V1-SOP-020 Add evidence export tooling for investigations and audits.
[x] V1-SOP-036 Build operator workbench surfaces for draft inspection, …
Their verification notes are careful and specific — V1-SOP-015's runs to twenty-four lines, names every field of the view model, counts ten passing tests and records a clean adversarial stub-grep. What no note claims is a route. The acceptance was that a component exists, is tested, and typechecks, and under that acceptance all seven are genuinely done.
Six of the seven sit beside a study surface the application does reach, doing an
operator-facing version of an act the study workspace already performs:
DetectionReviewPanel beside EvidenceReviewQueue, StudyReportsWorkspace
beside GroundedReportPromotionPanel, ProjectHandoffPanel beside
EvidenceExportBundlePanel. authoring-drafts is the exception and the reason
it is a whole unrendered surface: nothing in the study workspace does
authoring-draft promotion at all.
Recommendation#
Retire all seven from the Yemaya study scope, and re-open the mounting debt against V1-SOP, where the panels and the decision both belong.
Retiring here means moving each file out of
apps/oshun/web/src/components/ studio/ into an operator directory of V1-SOP's
choosing. The study surface map's scope is that directory; a file that leaves it
stops being a study surface and the map self-corrects. Nothing is deleted and no
test is lost.
Three things make this the answer rather than mounting:
- The study workspace was never asked for them. Putting a Sophia operator queue on a learner's study route would be a product mistake made to satisfy a scope boundary that is a directory name.
- Mounting is V1-SOP work whichever panel you start with — a producer for an empty table, a persistence design, or a domain — and V1-SOP is where the requirement, the contracts and the acceptance all live.
- Six of the seven would arrive beside a study surface that already performs their act, which is a duplication a person has to rule on before any of it is worth building.
Declining them is not available, and it is the answer that would otherwise
look cheapest. The map's declined list carries twenty-six entries and the
established form of its reason is "not a Yemaya study surface: it belongs to X,
which owns its own accessibility evidence" — Isis, Aja, the AI operations
workspace, the Phase-180 bridge. Writing that for these seven would be false:
accessibility-surface-map.json is the only accessibility surface map in the
repository, and there is no Sophia or V1-SOP register to pick them up. Declining
would file a hole in the product as a scoping decision and record, in writing,
that somebody else holds evidence nobody holds.
The move must not go before the debt lands. Moving a file out of the study
scope removes the only measurement in the repository that says these panels have
no route. The map already refuses to let that happen quietly — a declaration for
a path no surface claims is a finding, so the move forces an edit to
accessibility-surface-map.json, and that edit is exactly where the debt would
be dropped. The approval should therefore carry, as one act: the destination
directory, the re-opened V1-SOP entries, and the deletion of the seven
unmounted declarations.
Options considered#
- Build the
crossCuttingEvidencePackproducer and mount the three evidence panels. Not rejected on merit — it is the single highest-yield piece of work in the table, lighting three panels at once over a schema and a migration that already exist. Rejected as this checklist's call: the producer writes Sophia evidence packs, the panels are Sophia operator tooling, and neither the requirement nor the acceptance for either lives in the Yemaya study proposal. If V1-SOP wants them mounted, this is where to start — and two of the three are cheaper than the third:EvidenceExportBundlePanelcarries all five of the invented vocabularies, so a producer for it has to canonicalize export formats, redaction levels, audit audiences and ten scope flags before it can assemble anything. - Mount
authoring-draftsbecause it is the smallest hole. Rejected. It is the smallest by file count and the largest by cost: one file, one surface, four flows, andAuthoringJobis inert — no table, no consumer, one importer and that a sibling contract. It is the only one of the seven where mounting means building the domain. - Re-file all seven as
declined. Rejected for the reason above: the reason string would be false, and the falsehood is the whole content of the entry. - Leave them declared unmounted indefinitely. Rejected as an answer and adopted as the interim state. It is what holds today, it is now measured every run rather than asserted once, and it is honest — but it leaves 6,600 lines of finished panel that nobody can reach and four surfaces that cannot be evidenced, with no owner and no date.
- Delete the seven panels. Rejected. They are complete, tested work against real contracts; the fault is in where they were filed and what was accepted as done, not in the code.
- Retire all seven to an operator directory, re-open the V1-SOP entries, and drop the declarations in the same change — recommended.
Consequences#
- Four surfaces stop being partly- or wholly-unrendered, because three of them
stop claiming paths the product does not present and
authoring-draftsceases to be a study surface at all.lens-and-review,notebooks-and-reportsandevidence-exportbecome fully mounted. - Two of the six open instrument gaps close as a consequence rather than by any
harness work: contrast and zoom on
authoring-draftswere reported as instrument debt for a page that does not exist. - The study scope drops from 137 files to 130, and 23 surfaces to 22.
- V1-SOP-015 through 020 and V1-SOP-036 re-open. Their acceptance form — a component that exists, is tested and typechecks — is the thing that produced seven route-less panels, and it will produce more until it asks for a route.
- The Yemaya register loses its measurement of these panels, which is the cost of the move and the reason the destination has to carry one before the files go.
- YSD-18107's remaining machine slice closes. What remains on the item is the manual assistive-technology pass it shares with YSD-16005, whose actor is participants nobody has recruited.
Machine-enforced outcome#
On approval, the seven paths move out of apps/oshun/web/src/components/studio/
and the seven unmounted declarations are deleted in the same change. The
surface map's scope resolver then accounts for 130 files rather than 137,
assertSurfacesAreMounted reports every claimed path reached from a route, and
assertBackingStillHolds has nothing left to measure. A move that deletes the
declarations without moving the files fails assertScopeAccountedFor — a scope
file that no surface claims and nobody declined; a move that takes the files
without deleting the declarations fails mountingFindings — a declaration aimed
at a path no surface claims. Neither half can ship alone.
If the decision instead goes to mounting, the same rule reports it: a route that reaches one of the seven turns its declaration into a finding, and the panel's surface starts answering for its flows again on the next run.
Pending control#
The seven panels are declared unmounted in
docs/proposals/yemaya-study-workspace/accessibility-surface-map.json, and
libs/yemaya/study-workspace/src/accessibility/surface-mounting.ts measures the
declaration against the import graph on every run of the accessibility gate, in
both directions. Beside it,
libs/yemaya/study-workspace/src/accessibility/unmounted-backing.ts measures
what each panel is waiting for — the projection, the contract's consumers, the
table's callers — and fails while any write-off has stopped being true or any
declared cost no longer matches the repository. Until this decision is made, the
gate reports four surfaces reached in part or not at all, six instrument pairs
with no possible instrument, and 0 of 10 accessibility flows evidenced; no
change to any harness can move those numbers, because the gap is in what the
product composes.