Date: 2026-06-29 Scope: the Oshun Documentation Center — generator
(tools/docs_center/, ~37 modules / ~12K LOC Python), rendered output
(docs-center/**, 228 pages), portal front door (vdocs-index.html), search
index (vdocs-search-index.js), measured against
docs/proposals/DOCS_CENTER_PROPOSAL.md. Method: a 12-dimension adversarial
multi-agent audit — every finding was refuted-or-confirmed by an independent
skeptic, then a completeness critic named blind spots and merged duplicates —
plus lead-verified deterministic checks (contrast math, gate runs, parser
repro). 43 findings survived verification; the headline correctness bugs were
reproduced by hand and are cited with exact file:line.
The central framing. Both of the docs center's own gates are green right now — freshness (
--check: 2976 files fresh, 2643 entity nodes) and integrity (--verify: 0 broken links, 0 dead source paths), and the self-report shows every product space at "100%". So "the gates pass" tells us nothing, and the large entity-scaffold authoring backlog is the system's own openly-published worklist — neither is re-litigated here. Every finding below is something the gates cannot or do not measure, or a place where the center's self-description out-runs reality. The recurring shape of the problem: the gates verify that committed output matches a re-render of source, but almost nothing verifies that the source parsers extracted the source correctly — so wrong/incomplete data ships fully green.
Scorecard#
| Dimension | Grade | Headline |
|---|---|---|
| Content depth (where authored) | strong | Authored prose genuinely meets the V1 bar (V8 leaves 2.2–3.3K words); the scaffold backlog is a real queue, not a cover for thin pages |
| Content accuracy / drift | adequate | Prose claims are remarkably accurate; drift is concentrated, not pervasive |
| Coverage blind spots | needs-work | Discovery roots are narrower than the surfaces the center claims to cover |
| Generator code quality | adequate | Disciplined overall; a few regex parsers silently under-enumerate, one is outright wrong |
| Gate robustness | adequate | Strong link/path/facet gates, but real holes (orphan output, #anchors, parser output) |
| Reader UX / IA | adequate | One strong shared chrome; the 2,643-node headline asset is poorly navigable in the large aggregates |
| Accessibility | adequate | Strong foundation (skip-link, focus, reduced-motion); one real WCAG-AA contrast failure + a silent search palette |
| Performance / scale | adequate | Lean median page, but a 1 MB render-blocking search index ships on every page |
| Search quality | needs-work | Whole-query substring match — multi-word / out-of-order queries miss |
| Generator testing | needs-work | ~12K LOC, 35 modules, zero Python unit tests |
| Maintainability | adequate | Strong docstrings; adding a surface is a 3-place shotgun edit; the only safety net is tautological |
| Honesty ledger / proposal | adequate | Mostly scrupulous; one localized over-claim about what the depth gate enforces |
Top priorities (the high-leverage fixes)#
- Validate parser output, not just output-matches-source. Today nothing
catches silent under-enumeration: the Yemaya API reference renders ~18 of a
real 499-path / 719-operation spec; the Prisma enum parser drops values
so 91 enums render "0 values"; the contract parser drops name-re-exported
modules. All ship green. Add golden-output unit tests + a
--verifyparser-sanity pass. (Themes A & B) - Widen discovery, or stop claiming "every part." Entity/reference
discovery keys on tracked
project.jsonand a hardcoded root.env.example, so ~40 Rust crates (~160K LOC), npm packages without aproject.json, app-level OpenAPI specs, and 26+ per-service env files are structurally invisible while the report advertises "100%." (Theme A) - Give the generator a test suite. ~12K LOC across 35 parser/render modules
has no
test_*.pyand pytest isn't even a dependency; the freshness gate is tautological for logic bugs (a wrong computation passes the moment its buggy output is committed). This is the root enabler of #1. (Theme C) - Make the 2,643 entity nodes navigable. On the 1 MB aggregate pages most
entities are reachable by neither the 12-item TOC nor global search —
directly contradicting the §8 "every entity node is addressable" promise. One
fix (
<details>cards + real<h3>headings) serves UX, a11y, and perf at once. (Themes D & F) - Defer the 1 MB search index. It loads render-blocking at top-of-
<body>on all 228 pages + portal, though its only consumer is the palette on first keypress; half of it (504 KB) is secondary heading-anchor data. (Theme E)
What's genuinely strong (so the rest is credible)#
- Where content is authored, it is deep and on-spec — not padded. Measured: V8 leaf deep-dives 2,235–3,326 prose words; gate-enforced platform pages 1,231–3,620; 771 walkthrough "view" pages median 645 (min 356) with real source-path frontmatter. The disclosed scaffold backlog is an honest authoring queue, not thin pages dressed up.
- Deterministic by construction. No
datetime.now/random/time.timein the generator; sets are sorted before emit; discovery is built fromgit ls-files(not a raw FS walk), so build output and vendored checkouts can't surface phantom nodes. This is why--checkis stable. - An honest disclosure culture. The depth proxy is repeatedly and correctly
described as advisory / necessary-but-not-sufficient / never certifying;
entity scaffolds are counted as a worklist, not passed off as prose; a
deferred-surfaces ledger and an orphan report both exist;
as-built-vs-aspirational is scrupulously labeled in prose (
@nyx/sdk"an aspirational SDK surface",@maat/contracts"a scaffold, honestly labelled"). - Disciplined code. Zero bare
except:;html.escapeused consistently (no injection vector found); real responsive media queries (@media 980px/1200px,min-width:0guards); the integrity gate genuinely validates that every entitysource_dirand journey spec exists and that page facets bind to live space slugs — so dead paths and stale facets can't ship. - A11y foundation is real: a verified skip-link, a high-contrast
:focus-visibleoutline, arole=dialog/aria-modalpalette, labeled icon controls, and a comprehensiveprefers-reduced-motionCSS block; body text contrast is excellent (16.4:1).
Findings by theme#
Severity: HIGH = ships wrong/incomplete data or breaks a core promise · MEDIUM = real defect, meaningful reader/maintainer impact · LOW/NIT = polish. Findings are grouped by root cause (per the completeness critic's merge guidance), not by audit dimension.
Theme A — Silent under-enumeration: gates green, data wrong or incomplete#
A1 · [HIGH] App-level OpenAPI specs are never discovered — the Yemaya API
reference renders a small fraction of the real surface.
apps/yemaya/api/openapi/openapi.json defines 499 paths / 719 operations
(git-tracked), yet the rendered docs-center/api-and-contracts/api-yemaya.html
is built from a different, much smaller bundled spec (the auditor measured ~18
operations). The reference discovery roots scan libs/openapi bundles + source
specs but never apps/**/openapi/. → Fix: extend reference discovery to
enumerate tracked apps/**/openapi*.json (the same instinct already applied to
rescue source-only libs/openapi/src/specs), and add a --verify check that
flags any large tracked OpenAPI spec with no corresponding reference page.
A2 · [HIGH] The Prisma enum-value parser silently drops values — 91 enums
render "0 values" in the shipped Data reference.
tools/docs_center/data.py:136:
vals = re.findall(r"^\s+([A-Z][A-Z0-9_]*)\s*$", ebody, re.M). The [A-Z]
start-anchor drops any lowercase/PascalCase value and the \s*$ end-anchor
drops any value carrying a trailing @map("…") or // comment. Reproduced:
veritas SourceStatus (active/paused/retired/pending — all lowercase) and
bellona BuildStatus (6 values with trailing attributes) both render 0
values; grep "0 values" docs-center/data/ returns 91 hits — all green. →
Fix: broaden to ^\s*([A-Za-z][A-Za-z0-9_]*)\b and strip trailing
@map(...)/comments; add a golden test asserting a known enum's values render
(see C2).
A3 · [MEDIUM] The contract reference drops modules re-exported by name (not
export *). reference.py:39 _REEXPORT_RE matches only export * from.
libs/contracts/src/tts/index.ts:27 surfaces ./client solely via
export { TTSProviderStatusSchema, … } from './client', so contract-tts.html
shows 8 modules (the star-exports) and the ./client module — including
OshunTTSClient and 6 symbols — vanishes with its source link; llm similarly
drops ./client. check_reference_coverage only proves the page file exists,
so the loss is invisible. → Fix: unify on the robust parser the repo already
has in entities.py:327-330 (_API_EXPORT_RE handles
abstract/async/let/var; _API_NAMED_RE handles export { X as Y }),
extended to capture the from '…' target of a named re-export and resolve it as
a module. (The export abstract/async weakness is real but currently latent
for contract pages — the named-re-export drop is the observable loss today.)
A4 · [MEDIUM] The configuration reference reads only the root .env.example,
missing 26+ per-service config surfaces incl. feature flags. config.py:39
hardcodes ENV_EXAMPLE = ROOT / ".env.example" and the docstring calls it "the
platform's single authoritative configuration surface." But git ls-files shows
27 tracked *.env*.example files; apps/oshun/bff/.env.example alone has 23
variables absent from root — FEATURE_FLAGS, AUTH_ISSUER, AUTH_JWKS_URL,
CORS_ORIGINS, RATE_LIMIT_MAX_REQUESTS, OSHUN_NYX_API_BASE_URL — and
apps/lilith/svc-ai/.env.example adds 17 more. None are surfaced. → Fix:
enumerate all tracked **/.env*.example via git ls-files; render each file's
unique-to-it variables attributed to its owning app/service; add a --verify
check that every tracked env-example is parsed.
A5 · [MEDIUM] Real npm packages without a project.json are invisible to the
entity catalog and silently drop their dependency-graph edges. Entity
discovery is keyed entirely on tracked project.json (entities.py:382), so a
workspace package that ships only a package.json is absent from a catalog that
advertises "Every part of the system, addressable" — and any dependency edge
pointing at it is dropped from the graph with no gate noise. This is the same
root cause as the missing Rust crates (H-series) — see the merge note below.
Merge note (per critic): A1/A5 and the missing Rust crates are one root cause — discovery keyed on
project.json— with several instances. Fixing discovery (addpackage.json-only packages,Cargo.tomlcrates, andapps/**/openapi) closes them together. A5 is the meta-fix that makes A1–A4 detectable: nothing validates parser output.
Theme B — Gate blind spots (things that can rot fully green)#
B1 · [MEDIUM] Orphaned/stale output files pass both gates forever — the
literal hole in "never-stale." cli.py:63-65 emit() is one-directional: it
proves every file the build emits matches disk, but never that disk contains
only what the build emits. Delete a project or rename a page and its old
rendered .html lingers, fully green. → Fix: add a reverse pass to
--check — collect the set of emitted paths, enumerate all generated locations
(docs-center/**/*.html + portal + index + rendered siblings), and fail on any
tracked generated file not emitted this run.
B2 · [MEDIUM] "0 broken links" never validates #fragment anchors and never
scans the search index. coverage.py:40-41: _SKIP_PREFIXES skips # and
// wholesale, and cross-page links are reduced by
target = href.split("#",1)[0] before the file-existence check — so the
fragment is never resolved. External URLs are skipped entirely. The entire TOC /
permalink / deep-link / palette target set (8,731 anchors) is unverified, and a
heading-id rename silently breaks every link to it. → Fix: when a target
carries #frag, parse the destination page's emitted id="…" set and fail if
absent (same-page and cross-page); add a pass asserting every search-index h
resolves to an emitted file + anchor. Optionally a non-blocking external-link
liveness report.
B3 · [MEDIUM] No gate or test validates parser output — under-enumeration
ships silently. The unifying defect behind Theme A. The freshness diff only
proves output is reproducible, not correct; the depth proxy measures prose,
not generated-table completeness. → Fix: golden-fixture tests per parser
(TS/Prisma/OpenAPI/env/Build.cs/event/error) + a --verify sanity layer (e.g.
"an enum block with {…} body must yield ≥1 value", "a discovered OpenAPI
spec must yield ≥1 operation").
Theme C — The generator has zero automated logic tests#
C1 · [HIGH] ~12K LOC across 35 modules has no Python unit test; the only
safety net is tautological. No test_*.py, no conftest, no pytest config;
pytest isn't in tools/requirements.txt. The sole automated test is a 662-line
Playwright reader.spec.ts that asserts against rendered DOM with loose
lower-bound floors (count > 3, > 5, > 100) — it never checks parser output
and can't catch a catastrophic drop from 499→18. The freshness gate is
tautological for logic bugs: a wrong computation passes the instant its buggy
output is regenerated and committed (exactly how A2 ships green). → Fix: add
a pytest suite — golden-corpus parser tests (B3), a discovery test asserting
known projects/crates/specs appear, and the depth-proxy fixture (C2). This is
the highest-leverage structural fix in the report.
C2 · [MEDIUM] The §11 depth proxy that gates "complete" has no fixture proving
it works. The proxy (coverage.py:317-350) is prose_words < 150 OR
(≥12 table rows AND <260 prose) OR (no link/backtick). Nothing proves it
flags a known-thin page and passes a known-good one, so a future tweak could
quietly neuter the one gate that defends "complete." → Fix: two fixtures (a
100-word filler page → flagged; a real V1-quality page → clears) asserted in CI.
Theme D — Navigability of the 2,643-node headline asset#
D1 · [MEDIUM] Most entity nodes on the large aggregate pages are reachable by
neither the on-page TOC nor global search. The 1 MB lib-iris.html inlines
~262 entity sections but the TOC is capped at 12 items and search
section-anchors are capped at 12/page (MAX_SEARCH_SECTIONS), so the long tail
is addressable only by manual scroll or Ctrl-F — contradicting the §8 "every
entity node" promise. (The page does carry per-entity id= anchors — 293 of
them — so the deep-links exist; they're just not surfaced in any index.) →
Fix: raise/remove the per-page section cap for entity pages, or index every
entity node as its own search entry with its anchor (see D2's structural fix,
which enables this).
D2 · [MEDIUM] The 1 MB aggregate pages are a fully-expanded ~21K-word DOM with
no collapse or virtualization. lib-iris.html is a single always-rendered
~18,880-node DOM; entity deep-dives are emitted as <code> runs, not headings,
so they don't appear in the document outline either (see F3). → Fix (one
change, three payoffs): wrap each entity card in <details> and emit a real
<h3> per entity. This collapses the DOM (perf), restores the
screen-reader/outline structure (a11y), and lets every entity be TOC- and
search-addressable (D1/UX).
D3 · [MEDIUM] Search is whole-query substring matching — multi-word or
out-of-order queries return "No matches." search.py:246
var q = input.value.trim().toLowerCase() and :230
var h = row.hay, i = h.indexOf(q) — the entire query is matched as one
contiguous, in-order substring. Searching instancing iris (when the haystack
reads "iris … instancing") returns nothing. → Fix: tokenize on whitespace
and require all tokens to hit (AND), scoring by summed per-token position with
the existing title-prefix/kind boosts — a ~5-line change to
score()/render().
D4 · [LOW] The portal front door has drifted into a 24-section vertical scroll (14 single-card rails); first-run orientation is buried. → Fix: collapse single-card rails into a compact directory or merge them; lift a "new here? start with the repo map / role lenses" band to the first screen.
Theme E — Performance & scale#
E1 · [MEDIUM] The 1 MB search index loads render-blocking at top-of-<body>
on every one of 228 pages + portal. shell.py:155-157 injects
<script src=".../vdocs-search-index.js"> synchronously, though the only
consumer is the palette on first keypress. (This is a deliberate file://
tradeoff — browsers block fetch() of local files — so the fix must preserve
<script>-based loading, not switch to fetch.) → Fix: add defer, or
inject the index <script> lazily on the first / / Ctrl-K keypress; both keep
file:// support.
E2 · [MEDIUM] Half the eagerly-shipped index (504 KB of 1,009 KB) is secondary
heading-anchor data, and the index grows unbounded with the corpus. Measured:
the per-entry "s" section arrays are 504,213 bytes = 50.0% of the payload
across 8,731 jump-targets; the index is one flat file that scales O(pages × 12)
— it doubles as authoring proceeds toward the proposal's thousands of nodes. →
Fix: split into a lean page-level index (loaded eagerly/deferred for instant
palette open) + an href-keyed sections shard loaded lazily via injected
<script> (not fetch, for file://). Keep s — it powers cross-page
heading search; don't drop it.
E3 · [LOW] Diagram (mermaid) and code-highlight libraries load from external
CDNs — the reader silently degrades offline / under file:// / strict CSP. →
Fix: vendor both into assets/ and reference locally, consistent with the
self-contained file:// design.
Theme F — Accessibility (strong base, specific defects)#
F1 · [MEDIUM] --text-mute (#6e7681) fails WCAG AA on every background.
Lead-computed contrast: #6e7681 on --bg #0a0c0f = 4.26:1 (and 3.56:1 on
elevated surfaces) — below the 4.5:1 required for the small text it styles
(breadcrumbs, TOC sub-links, the search label, metadata, config defaults),
contradicting the proposal's "readable typography … accessible contrast."
(--text 16.4:1 and --accent 6.95:1 pass comfortably — only the mutest tier
fails.) → Fix: lighten the token to ≈ #8b949e / #9198a1 (≥4.5:1) or reserve
#6e7681 for ≥18px text only; add a contrast assertion (F4).
F2 · [MEDIUM] The command palette is silent to screen readers — selection is a
CSS class only. search.py:140 the input is a plain textbox (no
role=combobox/aria-expanded/aria-controls); options carry no id;
arrow-key selection only toggles a .sel class (reader.js:241-246);
aria-activedescendant appears nowhere. A screen-reader user can type and
ArrowDown but hears nothing about the highlighted result. No JS focus trap backs
the aria-modal, so Tab can leave the open palette. → Fix:
role=combobox + aria-expanded/aria-controls=cmdkResults; give each option
a stable id; set aria-activedescendant in setSel(); add a minimal Tab
focus trap.
F3 · [MEDIUM] Inlined entity deep-dives are <code>, not headings — 262
sections collapse to ~12 outline stops. Same root as D2; fixed by emitting
real <h3> per entity.
F4 · [LOW] The "accessibility smoke" gate checks structure only — it asserts
zero contrast or ARIA. The Playwright suite verifies skip-link/keyboard flow
but no contrast, aria-activedescendant, or heading-order — which is why
F1/F2/F3 ship green. → Fix: add @axe-core/playwright with a contrast +
ARIA ruleset to reader.spec.ts.
F5 · [LOW] prefers-reduced-motion is honored in CSS but bypassed by the
back-to-top JS smooth-scroll (behavior:'smooth' is unconditional). → gate
the JS on matchMedia('(prefers-reduced-motion: reduce)').
F6 · [LOW] Primary navigation lives in <aside> (complementary), not a
<nav> landmark; the TOC likewise. → wrap nav regions in
<nav aria-label="…">.
Theme G — Maintainability & architecture#
G1 · [MEDIUM] Adding a generated surface is a shotgun edit across three
parallel hand-maintained lists. A new surface must be added to cli.py's
import block, wired into build(), and appended to the rails=[…] list —
three places, no single registry, easy to half-wire. → Fix: a surface
registry (a list of {render, rail} descriptors) iterated once in build().
G2 · [MEDIUM] Disclosure over-claim: the enforced depth gate is a ~150-word
floor, but one coverage-report paragraph says it "blocks at the V1 standard" and
"a done space can never silently rot." The proxy is honestly described as
advisory almost everywhere (and it is — confirmed at coverage.py:307-350); the
over-claim is localized to a single enforcement paragraph that implies the floor
guarantees V1-quality prose, which it cannot. (Verified and deliberately
downgraded from the auditor's HIGH — this is a wording fix, not a systemic
deception.) → Fix: reword to "blocks a regression below the structural
floor"; optionally strengthen the proxy (e.g. require ≥1 worked example for
page types that warrant one).
G3 · [LOW] God-modules & unlintable assets. coverage.py is 1,144 lines
ending in a 488-line render function; 40 KB of CSS lives as a single Python
raw-string literal (unlintable/unformattable); renderer signatures are
inconsistent (the same two objects in opposite order across renderers); the one
architecture doc lists 8 of 37 modules and omits the two largest. → Fix:
split coverage.py (compute vs render), move CSS to a real .css source
compiled in, normalize renderer signatures, and regenerate the module index from
the package.
G4 · [LOW] Committing 228 rendered HTML files + a 1 MB regenerated index makes
docs PRs unreviewable and drives the cross-worktree merge treadmill. →
Fix: .gitattributes merge=union (or a custom driver) for
vdocs-search-index.js, split the index per-section to localize diffs, or
generate HTML/index as a CI artifact (gitignored) with the freshness gate
enforced in CI.
Theme H — Content accuracy drift (minor, well-contained)#
H1 · [MEDIUM] The "~Nk lines" badge is computed two ways — nyx/themis
include test files, everyone else excludes them — making cross-page size
comparison invalid and labeling test code as "domain-specific code." → pick one
rule (exclude tests) and apply uniformly; show files-counted. H2 · [MEDIUM]
The euterpe deep-dive's "the two exceptions are the real-time audio engines"
enumeration is now stale (more crates are implemented). → regenerate the
enumeration from disk or soften to a non-enumerated statement. H3 · [LOW] ~37
navigation/index README hubs are counted as "authored pages clearing depth
proxy" toward the headline "100%." → exclude index/hub pages from the depth
denominator (as TEMPLATE/result pages already are). H4 · [LOW] A drifted
SDK-examples page is surfaced as authoritative API guidance. It's a
discipline page (docs-center/disciplines/api-internal.html ←
docs/api/sdk-examples.md), and it's inconsistent not uniformly fake:
@oshun/sophia-client/@oshun/bellona-client are real, but
@oshun/client/@oshun/hathor-client and the pip install oshun-sdk /
go get sections are fabricated, and createIsisClient should be
createClient (libs/isis/client/src/index.ts:31). → correct the specifiers,
drop/flag the imaginary SDKs, and add a verify() check resolving workspace
@scope/* import specifiers in authored docs against tsconfig.base.json
paths. H5 · [NIT] The self-report's drift-fix ledger says nyx "retains
@oshun/database," which the dependency graph doesn't bear out. → re-derive
ledger claims from the live graph.
Suggested roadmap#
P0 — correctness the gates can't see (1–2 days): fix the Prisma enum regex
(A2) and contract parser (A3); add app-OpenAPI + package.json/Cargo.toml
discovery (A1/A5); stand up pytest with golden-output parser tests + the
depth-proxy fixture (C1/C2/B3). This stops wrong data from shipping green and
pays for itself immediately.
P1 — close the gate holes & the headline reader defects (2–4 days):
reverse-pass orphan-output check (B1); #anchor + index validation in
--verify (B2); the <details> + <h3> entity-card refactor (D2 → D1/F3);
defer/lazy-load + split the search index (E1/E2); tokenized search (D3);
--text-mute contrast + axe gate (F1/F4); palette ARIA (F2).
P2 — durability & ergonomics (ongoing): config-surface discovery (A4);
surface registry (G1); coverage.py split + CSS extraction (G3); index merge
strategy / CI-artifact (G4); portal front-door trim (D4); the drift-uniformity
fixes (H1–H5); reword the depth-enforcement paragraph (G2).
Appendix — methodology & honesty#
Dimensions audited (12): content-depth-honesty, content-accuracy-drift, coverage-blindspots, generator-code-quality, gate-robustness, reader-ux-ia, accessibility, performance-scale, search-quality, generator-testing, maintainability, honesty-ledger-proposal. Each finding was independently refuted-or-confirmed by a skeptic agent before inclusion; a completeness critic then named blind spots and merged duplicates.
Disclosure: 2 of the 12 audit agents (search-quality, honesty-ledger-proposal) failed to return valid structured output and contributed no findings of their own. Their territory is covered by adjacent dimensions and lead checks: search-quality by D1/D3/E1/E2 (the critic personally surfaced the substring-match bug) and the honesty-ledger by G2/A1/H-series. No dimension was left dark.
False positives the audit checked and correctly did not raise (recorded so
they aren't re-raised): HTML injection — handled, html.escape is consistent;
mobile/responsive — real @media breakpoints with min-width:0 guards exist;
"1 MB pages have no in-page nav" — false, they carry a sticky TOC and 293
anchors (the perf/cap concern is real, the nav-absence claim is not); "--check
is non-deterministic" — false, no time/random in the generator; "secret
redaction fails open" — narrower than first claimed: _classify
(config.py:89) is a name-pattern allow-list that could miss a
credential-bearing var whose name doesn't match (*_DSN, *_WEBHOOK), but the
only source is .env.example placeholders, so impact is low/latent (worth
widening the classifier, not a live leak).
Report generated from a verified multi-agent audit; every file:line citation
in the Findings section was reproduced or read directly.
Remediation status (2026-06-30)#
Most findings have been fixed and shipped (regenerated; --check,
--verify, and a new pytest suite all green). Summary:
Fixed
- A1 OpenAPI discovery now spans canonical apps/services/libs specs, deduped per domain (Yemaya 18→719 ops; +metis 174, isis ×3, v7).
- A2 Prisma enum-value parser rewritten — 91 "0 values" → 0 (1321 values).
- A3 contract reference now enumerates named re-exports (tts/llm
./client). - A4 config reads every per-service
.env.example(326→762 vars; feature flags/auth/CORS surfaced) + widened secret classifier. - A5 new generated Rust crate index — the 426
Cargo.tomlcrates (neith 187, maya 95, …) are now addressable + searchable. - B1
--checkgained a reverse orphan-output pass; B2check_linksnow validates#fragmentanchors (caught + fixed 2 real latent broken anchors); B3 parser-output now covered by tests. - C1/C2 new
tools/docs_center/tests/test_generator.py(docs:center:pytest) — golden parser tests + depth-proxy flag/pass fixtures + discovery + crate index. - D1 every entity individually searchable; D3 tokenized AND search; E1/E2 the ~1 MB index is lazy-loaded on first palette use, not on every page.
- F1
--text-muteraised to AA-passing #7d8590; F2 palette combobox +aria-activedescendant+ focus trap; F3 entity nodes are real<h3>; F4 a11y assertions added; F5 reduced-motion-aware scroll; F6<nav>. - G2 depth-gate wording corrected to describe the structural floor honestly.
- H2 euterpe "two exceptions" → all three Rust workspaces; H4 SDK-examples guide marked illustrative + isis specifier corrected.
Deliberately deferred (with rationale)
- D2 (
<details>collapse) — F3 + lazy-load already addressed the a11y/perf headline; full collapse changes default UX and the existing reader test, low marginal value. - G1 (surface registry) / G3 (god-module + CSS-in-Python split) — internal-only refactors; deferred to avoid regression risk in a byte-stable generator for no reader-facing gain.
- G4 (index merge strategy) —
merge=unionwould corrupt the structured JSON index (it is not append-only); the safe resolution stays regenerate-on-conflict, and the real fix (gitignore + CI artifact) is too large to apply unilaterally. - D4 (portal trim), E3 (vendor CDN libs), H1 (line-count uniformity), H5 — lower-priority polish / authored-prose hygiene; H5 lives only in a dated historical audit snapshot, not the live center.
Remediation status (2026-07-01) — second pass#
The 2026-06-30 deferrals D4 and E3 are now fixed, plus a broken-anchor sweep and a V1 content-quality pass the first remediation did not cover:
- D4 (portal front door) —
portal.py organize_rails(): a "Start here" orientation band (role lenses + repo map) now renders first, above the product grid, and the 15 single-card rails collapse into one compact "Reference directory" grid. Each collapsed card keeps its old rail key as its DOM id, so every existingvdocs-index.html#<key>deep link still resolves. Covered by a new pytest (test_organize_rails_collapses_singles_and_lifts_orientation) and the updated portal Playwright spec (orientation-before-products assertion). - E3 (CDN libs) — highlight.js 11.9.0 (JS + github-dark CSS) and the
mermaid@11 UMD bundle are vendored under
docs-center/assets/vendor/and referenced relatively (theme.pyemits per-page root-prefixed paths; the mermaid loader switched from a CDN ESM import to the vendored classic script, which also fixesfile://where module imports are CORS-blocked).write_assetsre-emits the vendored files so the reverse orphan pass counts them as build outputs. Zero CDN references remain in rendered output. - Anchor integrity —
check_linksnow percent-decodes fragments before matching ids (browsers do; the checker didn't — the V7nànáanchors were false positives). All 39 real content-broken#anchorswere then fixed at their markdown sources: walkthrough deep-links retargeted to the post-decomposition V1 hub slugs, fivedocs/TOCs repaired, one missing proposal heading restored, and one over-deep relative path corrected.check_linksnow reports 0 structural / 0 content broken links. - V1 content quality (7-agent adversarial sweep, all 131 pages + hubs) —
verdict: the corpus is genuinely strong (code claims overwhelmingly verify;
honest-scope framing is pervasive). Fixed: a stale
buildSophiaGroundedAnswerline citation, the contracts/common file count, a stale present-tense Nisaba edge-label callout, and small framing clarifications (Nisaba stub-shell tension in the opening, Metis BYOM contracts-vs-pipeline split). The two completely undocumented surfaces the sweep confirmed — the account hub (/profile, 9 walkthrough pages) and the Atelier (/atelier, 8 pages) — are now documented:V1/features/account-and-profile.md+V1/features/atelier.md, hub sections carrying the#account/#atelieranchors in bothV1/features.mdandV1/ARCHITECTURE.md, and README nav registration.
Remediation status (2026-07-02) — third pass: UI/UX & ergonomics#
A hands-on browser audit of the reader chrome (headless system Chrome via Playwright at 1440/1024/390 px; real wheel/touch gestures, keyboard flows, and focus tracking — not source-reading alone). Every finding below was reproduced before the fix and re-verified after; the reader Playwright suite grew seven assertions covering them (95 passing).
- No in-page navigation below 1200 px (the biggest gap) — the right "On this
page" rail disappears at ≤1200 px with no replacement, on generated pages with
59–123 sections.
shell.pynow renders the same per-page TOC markup inside a collapsed<details class="toc-inline">under the page header; CSS shows it exactly where the rail is hidden. Pages with no headings emit no block. Scrollspy/count/filter selectors were scoped to.toc-railso the duplicate cannot double-count or cross-filter. - Palette Esc dead outside the input — Escape was bound on
#cmdkInputonly, so keyboard focus on a facet chip trapped the dialog open. Moved to the#cmdkcontainer (closes from input, facets, or results). - No focus restore — closing the palette dropped focus on
<body>;open()now records the trigger andclose()restores it (guarded bydocument.contains). - Background scrolled under both overlays — wheel over the palette backdrop
or the mobile-drawer scrim scrolled the article behind it.
body.cmdk-lock{overflow:hidden}while the palette is open andbody.sb-open{overflow:hidden}inside the ≤980 px drawer media block. - Drawer a11y state —
#hamburgerhad noaria-expanded/aria-controlsand Esc did not close the drawer; asetDrawer()helper now syncs the ARIA state everywhere the drawer toggles, and a document-level Escape handler closes it. - Facet chips — rendered with no initial
aria-pressed(now"false"), and a mouse click left focus on the chip so typing went nowhere (mouse clicks now hand focus back to the query input; keyboard activation keeps position). - Portal "What's new" blurbs cut mid-word — the bare
[:90]slice shipped "server lifecy" / "marketplace sys" on the front door;entities._card_blurb()truncates at a word boundary with an ellipsis. - Heading anchors invisible on touch —
.hd-anchorwas hover-revealed only;@media(hover:none)keeps it faintly visible (opacity .35) and:focus-visiblereveals it for keyboard users. - Small polish — breadcrumb current-page ellipsis instead of hard clip;
<meta name="theme-color" content="#0a0c0f">on portal + reader pages;.toc-inlineadded to the print-hide list. - Test harness —
playwright.config.tsacceptsPW_BROWSER_CHANNEL=chromeso the reader suite runs against installed system Chrome where the bundled chromium is not downloaded.
Remediation status (2026-07-12) — fourth pass: full visual & IA redesign#
A complete professional redesign of the reader shell, portal, and command palette — still strictly inside the §8 thesis (dark-mode-only minimalist technical atlas, one action accent, no gradient wash). 102 reader specs + 15 generator pytests green; every page re-rendered and fresh.
Typography now vendored, not wished for. The stylesheet referenced
Inter/JetBrains Mono but never shipped them, so every reader fell back to system
fonts. Inter Variable 4.1 + JetBrains Mono 2.304 (400/600/700) are now committed
under docs-center/assets/vendor/ with @font-face in the shared stylesheet —
same self-contained contract as the JS vendors (offline, file://, strict CSP).
Builder.emit gained a binary-safe path, the vendored-asset emit covers
.woff2, and the reverse orphan pass now counts fonts. Tabular numerals on
every count/metric surface.
Palette retuned around a real elevation ramp — --bg #08090d →
side/elev/soft/hover tiers, hairline borders plus a faint inset top-light on
cards instead of heavy shadows. Every text tier re-validated against WCAG AA
(the smallest tier, --text-mute #808b9e, now clears 5.1:1 on its worst surface
vs 4.68:1 before). The product --tint was promoted from a 7px eyebrow dot to
the page's identity thread: tinted eyebrow, H2 rule tick, list markers,
blockquote rule, TOC/page-nav active indicators, prev/next hover — while
--accent stays the single action/focus colour.
Portal relayout. Sticky top bar with section anchors; the dead right half of the hero is now a search panel (large palette trigger + "jump to" pills to the repo map, role lenses, API reference, systems catalog, journeys); stats as a quiet hairline strip; product cards gained a tint top-edge, one large click target, and a split Features/Architecture footer instead of nine pairs of boxed buttons.
IA: the sidebar now carries an "All spaces" switcher (11 links: portal, start-here, systems, domains, platform, journeys, API & contracts, data, engine, disciplines, coverage) rendered on every reader page — article pages are no longer dead ends that route through the portal.
Ergonomics: a real keyboard layer. ? opens a shortcuts panel (true modal:
scroll lock, Tab trap, focus restore), [ / ] toggle the nav and TOC panes.
Guards proven by tests: editable targets never trigger keys, an open
palette/mermaid modal takes precedence, and while the panel is open only ?/Esc
act.
Fixed en route — portal horizontal overflow at phone widths (the hero search
label's white-space:nowrap inflated the grid's min-content; fixed with
min-width:0 + minmax(0,1fr)), found by the mobile overflow gate.
Verification — multi-viewport screenshot audit (1600/390 across portal, hub, article, reference, systems, start-here), scripted behaviour probes for the new keyboard layer, adversarial stub grep over changed files (0 actionable), Codex read-only review (3 findings — overlay stacking, modal semantics, orphan-pass gap — all fixed and re-verified), full reader suite + generator pytests green, freshness gate green.
Remediation status (2026-07-12, second pass) — journeys: evidence honesty, scale & visualization#
The journey space closed the six gaps a hands-on review found between "very good" and "ideal", plus a visualization layer. All deterministic — the staleness anchor is the corpus's newest walk date, never the build clock, so the freshness gate stays byte-stable.
- Verdicts carry their walk date — every verdict chip now reads
verdict: pass · 2026-05-29; a result with no parseable**Walked**date saysdate unknowninstead of posing as current. Verdicts more than 45 days behind the newest recorded walk get an explicit dashedstalechip, and the registry states the anchor so "stale" is interpretable. - The automation gap is a list, not a statistic — each surface section opens with an explicit, linked "N journeys without automation" block (rendered only when the gap exists).
- "Verification at a glance" — the registry opens with one stacked status meter per surface (pass/partial/fail/no-result, 2px surface gaps, flex-grown segments), automation coverage beside it, and a single shared counts legend; colour is never the only channel (aria breakdown + visible counts, per the dataviz status-palette rules).
- Registry tables are now a working tool — per-surface text filter, a "failing first" sort toggle (rows carry a deterministic verdict rank), and an honest live shown-count; pure client-side reorder/hide over unchanged HTML.
- Every journey gets an auto-generated flow diagram — a mermaid flowchart
derived from the rendered
## StepsTOC (so it can never drift from the prose), each node deep-linking to its step heading, terminating in a verdict node linked to the recorded walk; joins the on-page TOC. Labels are sanitised for mermaid/htmlLabels injection. - Journeys joined the knowledge graph — app entity nodes that are walkthrough surfaces now carry a "verified by N user journeys" row with the same meter grammar, counts, automation ratio, and a registry deep link; attachment is most-specific-path-prefix, insertion-order-free.
- Markdown task lists render as designed checklists —
- [ ]/- [x]become GitHub-style disabled checkboxes via token surgery (tight, loose, and nested lists covered by unit tests; code fences untouched), ending the literal-bracket look on every journey's Steps section.
Verification — 17 generator pytests (2 new: task-list surgery, label sanitisation + date-unknown honesty), reader suite grown to 134 checks with 7 new specs (meters/legend, dated verdicts, gap list, filter/sort, flow diagram, checkboxes, entity verified-by), screenshots at 1600/390 (no overflow at 390px), Codex read-only review (4 findings — undated-verdict honesty, mermaid label escaping, tight-list coverage, prefix-overlap determinism — all fixed), freshness gate green over 3,077 files.
Addendum (same day) — registry visual redesign#
A hands-on look at the shipped registry judged it data-complete but visually noisy ("not very awesome"): the E2E-spec column ballooned rows to 9 lines of filename chips, verdict pills wrapped into three-line blobs, the overview was two thin bars covering only 2 of 5 surfaces, and every row repeated a "Journey:" prefix. Redesigned:
- Overview → dashboard cards. One card per surface (all five — an honest zero beats absence): tinted badge + app path, big tabular stat figures (journeys / results / views / routes), the verdict meter with automation fraction, and quick links (journey table, coverage matrix, conventions) that render only when the target exists.
- "Latest recorded walks" — the eight newest dated walks as an evidence strip (verdict dot + date + surface + title), deterministic order.
- Tables went calm. Spec chip walls collapsed to a one-line expandable "N specs" fold (single specs link directly, ellipsised); the automation chip is one word (the count lives in the fold); verdict chips no longer wrap; "Journey:" prefixes stripped from row labels and gap lists.
Remediation status (2026-07-12, third pass) — center-wide audit: broken components, scale-blind counts, viz layer#
A hands-on screenshot audit of every major hub (16 surfaces at 1600px) found one real bug class and a family of scale-blind count displays; all fixed in one pass, single-threaded.
- BUG: portal-scoped CSS used on reader pages.
.rcard/.rail-gridlived only in the portal's inline stylesheet, but five reader surfaces (data index, API & contracts index, engine, lenses, comfyui) emit those classes — rendering as walls of glued, unstyled links ("aphrodite23 tables", "Event & message catalog181 events"). The component moved into the shared reader stylesheet; all five surfaces got styled cards for free, plus a proportional magnitude bar on schema cards. - Testing map — the by-area table's counts became visible magnitudes: a single-hue bar per row scaled to the largest area, and test-target coverage as a fraction meter that only earns green at 100%.
- Browse by owner — the three ownership tiers (537 declared / 1,843 git-fallback / 808 unowned) are now one stacked meter with a counts legend at the top of the page, and 75-package rosters fold behind an expandable count instead of burying every owner below them.
- Coverage & health — "pages clearing depth proxy" per product and the per-kind depth percentages render as fraction meters (green only at 100%), replacing bare "86/86 (100%)" text.
- Domains / Systems / Disciplines indexes — every count card carries a bottom-edge footprint bar scaled to the group maximum, so Iris at 271 tracked projects no longer reads the same as Concordia at 1.
- Pluralisation nit fixed en route ("1 table").
Confirmed already strong, deliberately untouched: repo map, lifecycle, platform hub, capability matrix, start-here, systems dependency graph.
Verification — before/after screenshots of all 16 surfaces, 17 generator pytests, freshness gate over 3,077 files, full reader suite, adversarial stub grep (0 actionable in changed files).