Disciplines · Audits

Monorepo Deep Audit — 2026-07-18 (Second Pass)

ratchet green at exact baselines, services→apps and R-8 path rewiring left zero stale references in workflows, express is down to one dormant package, Rust/Python lock coverage held, file naming is 99.8 % kebab-case.

7sections27 minread

On this page

A full re-audit of the Oshun monorepo, run the same day the 2026-07-16 architecture audit closed its reconciliation ledger. This pass has three jobs the first audit could not do:

  1. Verify the remediations actually hold in-tree today (guards wired, gates green, no regressions), not just in execution logs.
  2. Audit what the remediation itself created — new conventions (ratchets, registries, presets) have their own failure modes.
  3. Go one level more granular — per-project target/executor census, index-SHA duplicate census, workflow filter glob-testing, foundation-lib adoption counts — places the first audit sampled.

Every number below was measured directly on this tree (branch oshun-v1-nisaba, head 5d7ed35d4b) on 2026-07-18. No number is carried forward from the previous report.


1. Executive summary#

The 2026-07-16 remediation held. Forbidden-path guard green, conventions ratchet green at exact baselines, services→apps and R-8 path rewiring left zero stale references in workflows, express is down to one dormant package, Rust/Python lock coverage held, file naming is 99.8 % kebab-case. The repo is in materially better shape than it was two days ago.

But the audit machinery has three cracks, and the taxonomy has regrown weeds:

# Severity Finding
1 P0 The domains registry gate (check-registry.mjs) is red on this branch today: testing/prompt-injection uses unregistered scope:testing.
2 P0 The conventions ratchet has an institutionalized bypass: 4× "grandfather pre-ratchet files merged from main" commits raised the .test.ts baseline 12,602 → 12,699.
3 P0 Root markdown landfill regrew within 48 h of the cleanup: 5 dated audit/TODO files (07-15…07-17) at root; nothing enforces the docs/audits/ convention.
4 P1 Two zero-consumer foundation libs (@oshun/resilience, @oshun/collections) while the ratchet grandfathers 23 local CircuitBreakers + 128 local sleep()s with no burn-down.
5 P1 apps/v4 and apps/v5 (arrived via R-8) contain zero project.json — invisible to the nx graph, recreating the "hollow apps" class H-6 fixed.
6 P1 R-8 residue: V2/tools/esports is a live nx project under a V2/tools/* workspace glob; V5/tools/ holds 137 code files — "product roots keep ue/docs/ops only" is not yet true.
7 P1 Target-name drift across 3,445 projects: typecheck/type-check/typecheck:ts, fmt×422 vs format×30, test-integration AND test:integration — ~550 check-style targets uncached.
8 P2 240 exact-duplicate source clusters (309 redundant copies), incl. a 270-line math.rs duplicated 10× across neith crates and pairwise-duplicated React components (arete↔demeter).
9 P2 97 npm deps hard-pinned at conflicting ranges (vitest at 11 distinct ranges); catalog adherence 66 %.
10 P2 3,445 generated READMEs with no CI drift check; infra/README.md already stale (documents deploy/* as live after its absorption).

The deep pattern behind findings 1–4: the remediation built enforcement machinery, but parallel sessions route around it. Gates that only run on this branch's PRs don't bind the five other worktrees pushing to main; baselines that can be bumped in a chore: merge commit aren't ratchets, they're counters. The highest-leverage work now is not more cleanup — it's making the existing machinery binding at the merge point.


2. Methodology#

  • Scale: 129,219 tracked files; 3,445 nx projects (3,445 project.json); 3,287 package.json; 953 Cargo.toml under 17 workspace roots; 83 pyproject.toml (78 locks); 7,080 tsconfig files; 2,669 vitest configs; 102 workflows; 1.7 GB shared pack.
  • Census tools: every project.json/package.json parsed with node (0 parse failures); exact-duplicate detection via git ls-files -s index SHAs (free, whole-repo, no sampling); workflow path filters glob-tested against git ls-files with GitHub's *-does-not-cross-/ semantics; guards (check-forbidden-paths.sh, check-conventions-ratchet.mjs, check-registry.mjs) executed live.
  • Parallelism: main-loop only (session-limit discipline); no agent fan-out.
  • Findings are lettered by dimension (A hygiene, B structure, C build, D deps, E DRY, F conventions, G CI, H architecture, I testing/docs, J deferrals) and numbered within.

3. What held (verification of the 2026-07-16 remediation)#

Checked directly, all green today:

  • scripts/check-forbidden-paths.sh — passes; wired in the ci.yml quality job; correctly guards .build/, backups, build-info, generated HTML, and the six retired roots (deploy/, infrastructure/, platform/, systems/, DOMAINS/, tara/).
  • tools/conventions/check-conventions-ratchet.mjs — passes; all 7 families exactly at baseline (pino 29, pg 17, ioredis 26, process.env 1302, CircuitBreaker 23, sleep 128, .test.ts 12,699 — but see A-2 on how that last baseline moved).
  • services→apps (B-5/R-9) and R-8 (V5/V2/V4) — zero workflow path filters reference any moved tree; apps/{concordia,psyche,metis} live.
  • R-6 infra unification is now COMPLETE — one infra/ root; the deploy/* half (still "queued" in the prior ledger) has been absorbed (infra/compose, infra/vault-deploy, infra/k8s-deploy, per-domain trees), and the deploy/ root is resurrection-guarded.
  • D-3 framework policy — express: 1 package (a docs extras fixture), fastify 96, hono 52, Nest 16 frozen, koa 0.
  • D-5/D-6 — 953 Cargo.toml under 17 workspace roots; Python 78/83 locks.
  • F kebab naming — 29,144 kebab vs 64 camelCase source files in libs/.
  • No tracked junk — zero __pycache__/.pyc/.DS_Store; only 8 .log files, all inside the deliberate evidence/ bundle.
  • Largest tracked blob is pnpm-lock.yaml (4.7 MB) — the history-rewrite-sized artifacts are gone from the tip.

4. Findings#

A. Repo & git hygiene#

A-1 (P0) Root landfill regrew within 48 hours. Five dated audit/TODO documents were added at the repo root after the A-5 cleanup established docs/audits/ as the home for reports:

File Lines Added
V1_V9_AUTONOMOUS_CONTENT_SOTA_AUDIT_2026-07-15.md 2,182 2026-07-15
V1_V9_AUTONOMOUS_CONTENT_SOTA_GAP_CLOSURE_TODOS...md 2,051 2026-07-15
V1_V9_SOTA_AUDIT_REVIEW_2026-07-15.md 334 2026-07-15
DOCS_CENTER_PAGE_AUDIT_2026-07-16.md 919 2026-07-16
HATHOR_YEMAYA_IDEATION_SOTA_TODOS_2026-07-17.md 751 2026-07-17

Plus pre-existing root strays never adjudicated: ISIS_TODOS.md (523), ISIS_TODOS_2.md (3,215), ISIS_GAPS/ (9 files), and V1_RESIDUAL_AUDIT_2026-06-11/ (13 files). The convention exists; nothing enforces it. Recommendation: extend check-forbidden-paths.sh with a root *.md allowlist (the ~14 legitimately root-level, tooling-parsed files: README, CLAUDE, AGENTS, ARCHITECTURE, DOMAINS, SECURITY, etc.). New reports then fail CI at the root and pass in docs/audits/.

A-2 (P0) The ratchet has an institutionalized bypass. Four commits titled chore(ci): grandfather pre-ratchet files merged from main appear in recent history; the .test.ts baseline moved 12,602 → 12,695 → 12,699. The mechanism: parallel sessions on other branches (without the ratchet, or predating it) add .test.ts files / raw imports; when main is merged into this branch, the gate goes red here, and the merge session "fixes" it by raising the baseline. Each individual bump is defensible; collectively they mean the ratchet binds only authors on this branch — exactly the population that already follows the convention. Recommendation (pick one, in preference order): (1) run the ratchet as a required check on the main branch itself (push-time), so the violation fails where it's introduced; (2) make baseline files CODEOWNERS-protected so bumps need explicit review; (3) at minimum, have the grandfathering commit rename incoming .test.ts files instead of counting them.

Live evidence, same day: while this audit was being committed, a merge from main brought in two new .test.ts files (apps/oshun/bff/src/data-deletion/saved-claim-passage-eraser.test.ts, .../nisaba/state-durability.integration.test.ts) and turned the gate red (12,701 vs 12,699), forcing the fifth bump in the sequence. Why the rename fix (option 3) was not applied at merge time, verified rather than assumed: the files' exact paths are pinned in the evidence/v1-v9/V1-002.md execution ledger (a machine-adjacent record), the owning app's local corpus is 845× .test.ts vs 7× .spec.ts, and the files belong to another session's in-flight work — a merge commit must not rewrite them. (A subtlety checked en route: apps/oshun/bff/vitest.config.ts declares include: ['src/**/*.test.ts'], which looks like it would orphan .spec.ts tests — but vitest's mergeConfig concatenates it with the base config's {test,spec} globs, so both suffixes run and the local line is merely redundant. Config-reading alone would have gotten this wrong; vitest list settled it.) This confirms the recommendation ordering above: only option 1 — running the ratchet where files are authored — actually stops the erosion; merge-time enforcement structurally cannot.

A-3 (P2) Playwright visual snapshots are tracked for two platforms. apps/oshun/web/e2e/visual-regression-pages.spec.ts-snapshots/ carries ~2 MB PNGs in -darwin and -linux variants for dozens of pages. Binary snapshot pairs are the single biggest recurring blob class at the tip. Recommendation: pick the CI platform (linux) as the only committed baseline and gate darwin runs behind --update-snapshots locally, or move snapshots to LFS (95 LFS patterns already exist; only 23 objects use them).

A-4 (P3) Pack size. 1.7 GB shared across worktrees; the measured rewrite (runbook HISTORY_REWRITE_RUNBOOK_2026-07-18.md) would yield ~889 MB. Remains owner-gated — correctly so.

A-5 (P3) Root script count: 132 (was 128 at D-4 close). Watch, don't act.

B. Folder structure & taxonomy#

B-1 (P1) R-8 is "complete" but V-root code remains. The decision record states "Product roots keep ue/docs/ops only." Measured today, outside ue/:

  • V2/tools/esports — a live nx project (@v2/esports-tools, 8 TS sources) reached via the V2/tools/* glob still present in pnpm-workspace.yaml line 88;
  • V5/tools/137 code files (198 total: missions, vehicles, xr, workshop-moderator, vtuber-companion, …);
  • V2/tools/validate-v2-docs.py; V4/.ci/{run-gate,changed-gates}.mjs (the latter two are gate infrastructure and legitimately "ops").

The three staged moves covered {services,web,scripts}; tools/ was never in scope. Recommendation: a fourth R-8 change: V2/tools, V5/toolsapps/v<n>/tools-* (or libs/v<n>/… for the library-shaped ones), delete the V2/tools/* workspace glob, and only then update the decision record's completion claim.

B-2 (P1) apps/v4 and apps/v5 are nx-invisible. Zero project.json under either tree (only package.json/Cargo.toml), while siblings v3/v6/v7/v8/v10 have per-service nx projects. Consequences: nx affected cannot schedule them, the boundary/tag system cannot constrain them, and their verification runs only through per-product script fleets. This recreates the "hollow apps" class the prior audit's H-6 pass eliminated — introduced this time by the R-8 move itself. Recommendation: per-service project.json with scope:v4/scope:v5 tags wrapping the existing cargo/pnpm commands (the v3/v6/v7 files are the template).

B-3 (P0) The domains registry gate is red. node tools/domains/check-registry.mjs fails today: scope tag "scope:testing" is used by projects but not registered — from testing/prompt-injection/project.json. Whoever added the prompt-injection harness tagged it with a scope the registry doesn't know. Two distinct fixes, both needed: register (or retag to an existing governance scope) and note that this proves the gate isn't running where that project landed (see A-2's pattern).

B-4 (P2) Flat-app naming breaches. apps/euterpe-studio-web (should be apps/euterpe/studio-web; the euterpe domain exists in libs and the app even has its own nested node_modules) and apps/neith-vault (→ apps/neith/vault). apps/infra is a third oddity: infrastructure services (gpu-dispatcher, runpod-metrics) under a pseudo-domain named identically to the root IaC tree infra/ — rename to a real owner domain or apps/platform-services/.

B-5 (P2) Root directory dregs (each small, all confusing):

  • crates/maya-terrain-deform/ — an orphan duplicate of libs/maya/engine-core/crates/maya-terrain-deform (only the Cargo.toml is tracked; src/ exists untracked on disk; the engine-core workspace references its own internal copy). Delete.
  • static/ — a compiled webpack bundle (2.2 MB main.js + runtime/styles), committed 2026-01-10 during workspace init; nothing references it. Delete.
  • types/ — one file (global-jsx.d.ts); fold into configs/ or the consuming tsconfig.
  • evaluation/ — one file (human-gold/acquisition-registry.v1.json); fold into evidence/ or the owning domain.
  • openapi/ — 4 files including two timestamped .backup-* files tracked in git (openapi/lilith/openapi.yaml.backup-1760592604742, .schema-backup-1760592952186) plus docs/domains/lilith/extras/stoplight/.spectral.yaml.backup. Delete the backups; then decide whether root openapi/ merges into per-app apps/<domain>/*/openapi/ (the dominant pattern — e.g. apps/yemaya/api/openapi/).
  • tests/ (28 files) vs testing/ (296 files) — two root test trees with no README stating the split. testing/ = per-domain harnesses (bellona remote-control, prompt-injection, e2e page objects); tests/ = cross-domain integration flows. Merge tests/ into testing/integration/ or document the rule.

B-6 (P2) Nested/overlapping project roots. libs/psyche/project.json (psyche-platform) declares sourceRoot: libs/psyche — overlapping all 134 child projects — plus a 40-entry hand-maintained implicitDependencies list. Any file change in any psyche lib invalidates psyche-platform's cache and the list drifts silently. Same pattern smaller: libs/iris/agents/computer-use/native/ contains a child project at native/ts/. Recommendation: replace domain-level umbrella projects with an nx run-many --projects=tag:scope:psyche alias; if the umbrella must stay, generate implicitDependencies from the registry.

B-7 (P3) Depth and taxonomy variance in libs/. 2,693 projects at the canonical libs/<domain>/<lib> depth; 237 at depth 4; 92 at depth 5–6 — iris alone has 154+ deep projects under a three-level taxonomy (accessibility/visual/alternatives) no other domain uses. Not worth a mass move; worth a written rule for new libs ("depth 3 unless the domain has an approved taxonomy README").

B-8 (P3) V3 root doc landfill. 53 SCREAMING_CASE validation .md files sit loose at V3/ root (alongside the same-purpose V3/validation/ and V3/docs/ dirs). Fold into V3/docs/validation/.

C. Build system & Nx#

C-1 (P1) Target-name drift, quantified. Across 3,445 projects / 12,600+ targets:

Concept Variants found
typecheck typecheck 1,265 · type-check 48 · typecheck:ts 33
format fmt 422 · format 30 · format-check 13
int. tests test:integration 10 · test-integration 8 (both live simultaneously)
coverage test-cov 24
cargo cargo-test 17 (beside test wrapping cargo elsewhere)

nx.json targetDefaults/cacheableOperations cover only the 6 canonical names, so 1,521 variant-named targets are uncached, ~550 of them check-style (fmt/type-check/test-cov/cargo-test/format-check) that would benefit directly. Recommendation: publish the canonical target vocabulary in docs/conventions/, add the five variant names to targetDefaults with cache: true as a bridge, then codemod the renames domain-by-domain (the 392-launcher normalization from the grind session is the template).

C-2 (P1) 7,173 nx:run-commands targets remain the dominant executor (vs 2,056 @nx/eslint:lint, 1,846 @nx/vite:test, 1,429 @nx/js:tsc). This is the standing R-14 debt; every standardization slice (presets, launchers, target names) shrinks it. Also: two vitest executors coexist — @nx/vite:test (1,846) and @nx/vitest:test (33); converge the 33.

C-3 (P2) nx.json staleness. The eslint plugin excludes ["lilith/**", "yemaya/**", "apps/**"] — the first two roots no longer exist (retired in R-7/R-9). CLAUDE.md's "Project Structure" line likewise still lists root lilith/, yemaya/. Both are five-minute fixes that end real confusion (this audit initially went looking for those roots).

C-4 (P2) Two tsconfig preset families. Root tsconfig.{app,lib,spec}.json (nx-classic) AND configs/tsconfig/{base,library,node,service}.json (83 adopters). Meanwhile 2,655 tsconfigs extend tsconfig.base.json directly with hand-rolled options. Pick configs/tsconfig/ as canonical (it matches the configs/vitest/, configs/eslint/ pattern), fold the root trio into it, and grow adoption with the same hash-class method used for vitest.

C-5 (P2) nx release glob likely inert. release.projects: ["libs/*", "apps/*"] — depth-2 path globs against project roots that live at depth 3+. If nx release is expected to version anything beyond the four depth-2 projects (libs/{contracts,openapi,proto,psyche}), verify nx release --dry-run enumerates what ADR-0073 intends.

C-6 (P3) 14 projects have zero targets; configs/ mixes tooling presets with configs/nous/training/*.json experiment data (relocate to the nous domain).

D. Dependency management#

D-1 (P1) Hard-pin drift. 13,376 dependency entries: 66 % catalog:, 25 % workspace:, 1,207 hard pins, of which 97 packages are pinned at multiple conflicting ranges. Worst: vitest at 11 distinct ranges (^1.0.0^3.2.4), jose ×5, @fastify/websocket ×4 (^8^11), date-fns (^2/^3), uuid (^9/^10), ajv, semver, dompurify, nodemailer…. Recommendation: a catalog long-tail wave — for each multi-range dep, add the canonical version to the catalog and codemod "dep": "<range>""dep": "catalog:"; 354 catalog entries exist, the mechanism is proven.

D-2 (P3) Renovate covers docker digests + npm patch grouping; add a rule targeting pnpm-workspace.yaml catalog entries (or verify config:recommended already bumps them) so the catalog doesn't fossilize.

D-3 (P3) Python manifests: 78/83 locks (5 documented never-resolvable) — held; 14 legacy requirements.txt remain for eventual pyproject conversion.

E. DRY — whole-repo exact-duplicate census (index SHAs)#

Method: group every tracked blob by its index SHA — zero-cost, no sampling, exact-match only (near-duplicates are strictly additional).

E-1 (P2) Config duplication = the next preset slices, precomputed. 524 duplicate clusters among non-test code; the top clusters are ALL vitest.config.ts — the 12 largest hash classes alone cover ≈887 files (268× + 195× + 70× + 53× + 47× + 44× + 43× + 38× + 36× + 32× + 31× + 30×). With 760/2,669 configs already one-line preset re-exports, slice 3 of the R-14 dedup can be generated mechanically from this census.

E-2 (P2) Real source duplication: 240 clusters, 309 redundant copies. Notable families:

  • libs/neith/spatial-*: a 270-line f64 linear-algebra module (math.rs) duplicated 10× across crates. The header says "dependency-free" deliberately — but ten copies of Vec3/quaternion math drift independently. A neith-spatial-math micro-crate inside the existing umbrella preserves the no-external-deps property while deduplicating.
  • build.rs duplicated 11× (kalika eels/electronic-structure/…) and 7× (euterpe/kalika native crates).
  • version.ts one-liners duplicated 13× (bellona) + 12× (isis) — generate from package.json at build, or accept and stop counting.
  • apps/lilith/svc-*: types.ts (6× + 4×) and app.ts (3×) — service scaffold clones; extract to a lilith service-kit or leave, but record the decision.
  • arete ↔ demeter web apps share pairwise-identical components (ProtectedRoute.tsx, PageHeader.tsx, Input.tsx, test setup) — a cross-domain UI-kit candidate (or template lineage worth a docs/conventions/ note so they're allowed to diverge deliberately).
  • openapi-contract-utils.ts identical across bellona/hathor(+2 more); e2e fixtures.ts in two 5× families; .storybook/test-runner.ts 4×.

E-3 Ratcheted families (pino/pg/ioredis/env/CircuitBreaker/sleep) hold at exact baselines — see A-2 for the caveat on how they hold.

F. Conventions#

F-1 (P2) History is 40 % merge commits. 16 of the last 40 commits are Merge remote-tracking branch 'origin/main' (across at least three active session branches). Conventional-commit discipline is otherwise clean (0 non-merge violations in the sample). The two-line push protocol (branch + main) times N parallel worktrees is the cause. Worth an explicit owner decision: either accept merge noise as the cost of the workflow, or move to short-lived branches + a single fast-forward main.

F-2 (P2) 3,445 READMEs, no drift gate. The F-3 generator (tools/docs/generate-lib-readmes.mjs) produced 2,466 of them from registry + package descriptions, but no CI job re-runs it in --check mode. Package descriptions will change; READMEs will lie. Add a drift check beside the registry check in the quality job.

F-3 (P3) eslint monolith: 1,579 lines (one lilith carve-out landed in configs/eslint/). The F-5 composition plan remains valid; continue slices with the --print-config byte-identical proof method.

G. CI/CD#

102 workflows; 64 use the shared composite setup action; 26 have schedules (the two nightly COST-disabled ones documented). The services→apps and R-8 rewiring left zero stale references to moved trees — the glob-test method works. Remaining drift, found by glob-testing every paths: filter:

G-1 (P2) Nine genuinely dead filter lines (filters that can never match a tracked file — the workflow still fires via its other, live filters):

Workflow Dead filter Why
tara-ci.yml tara/** root retired (R-7)
sophia-phase6.yml libs/oshun/sophia-client/** no such lib (libs/oshun has evidence-sophia)
vdocs-reader-ui.yml vdocs-index.html, vdocs-search-index.js untracked generated files — push filters can't fire
v2-multiplatform-build.yml rust-toolchain.toml not at root
capability-truth-drift.yml evidence/placements/**, evidence/releases/** trees don't exist (only evidence/v1-v9/)
codeql.yml **.jsx zero tracked .jsx

Delete the lines (or create the anticipated evidence/ trees if capability-truth expects them soon). Cheap, and it keeps the glob-test method's signal clean for the next audit.

H. Architecture#

H-1 (P1) Foundation libs: build ≠ adoption. Import census (path-alias aware, both quote styles):

Lib External importers
@oshun/math 1,982
@oshun/config 24
@oshun/http-client 29
@oshun/events 15
@oshun/bff-kit 13
@oshun/ids 7
@oshun/collections 0
@oshun/resilience 0

@oshun/resilience (backoff/circuit-breaker/retry/sleep, built 2026-07-16 as the DRY-wave landing zone) has zero consumers, while the ratchet grandfathers 23 local CircuitBreaker classes and 128 local sleep() defs forever. The math lib proves adoption works when a wave drives it. Recommendation: either run one adoption wave (the 128 sleeps are mechanical; each adoption lowers the baseline, making the ratchet monotonically tighter) or delete the zero-consumer libs — an unused foundation lib is negative value: it's the thing the next duplicate-writer is blamed for not using.

H-2 (P1) 33 projects have no scope: tag (aphrodite chat/devices/payment, six veritas apps, aja distributed-workers, …) — the boundary rules cannot see them. The registry check catches unknown scopes but not absent ones; add an absent-scope assertion to check-registry.mjs and tag the 33.

H-3 (P2) libs/shared needs a curation pass (59 libs):

  • Overlap pairs with near-identical mandates: events (typed in-process emitter) vs event-bus (cross-domain bus) — names don't communicate the split; auth vs auth-primitives; logging/metrics/tracing as three micro-libs beside bff-kit's own tracing.
  • Domain code in shared: libs/shared/tara-live-class-booking (488-line feature lib whose only consumer is an oshun-web e2e spec) — move to libs/tara/.
  • A libs/shared/README.md taxonomy (what earns "shared", the overlap-pair adjudications) would prevent regrowth; the DEPRECATIONS.md pattern already exists for the exits.

H-4 (P3) infra/ post-consolidation duplicate pairs need a second-pass merge or a README note: k8s + k8s-deploy, vault + vault-deploy + neith-vault, terraform + terraform-v1. And infra/README.md's live-trees table still lists deploy/{…} as a live tree — stale the day after the absorption. Fix the table now; merge the pairs when each owner window allows.

I. Testing & docs#

I-1 (P2) Coverage thresholds are enforced in <5 % of projects. Of 2,669 vitest configs, ~121 enforce thresholds (70 via the node-lib-coverage-thresholds preset, ~81 hand-rolled). The isis coverage-formulas gate covers its domain; everywhere else, coverage reporting exists but nothing fails. Recommendation: make node-lib-coverage-thresholds the default preset for new libs (generator default), and promote domains to it one at a time — do not attempt a repo-wide threshold flag-day.

I-2 (P3) Suffix split frozen at 11,570 .spec.ts / 10,191 .test.ts — as designed, but see A-2: the freeze only binds where the ratchet runs.

I-3 (P3) docs/ taxonomy stragglers: docs/kalika/, docs/oshun/ sit beside the docs/domains/<domain>/ pattern that every other domain follows — fold in.

J. Deferral scorecard (from the 2026-07-16 ledger, verified in-tree)#

Deferral Status today
R-6 deploy/* half DONE since ledger (one infra/ root) — but README stale + duplicate pairs (H-4)
R-8 V5/V2/V4 convergence DONE with zero-flip proofs — residue: V2/tools, V5/tools (B-1); apps/v4+v5 nx-invisible (B-2)
History rewrite Owner-gated, runbook current, pack 1.7 GB → ~889 MB measured
F-5 eslint split 1 slice (lilith); 1,579-line monolith remains
C-5 tsconfig presets 83 adopters; two competing preset families now exist (C-4)
R-14 target standardization vitest presets 28 % (760/2,669); 7,173 run-commands; target-name drift measured (C-1)
R-13b generator collapse contract ×14 family = documented refusal (stands); scripts/ holds 2,764 code files per-domain
A-6 LFS tail 95 patterns in .gitattributes, 23 LFS objects — landed

5. Prioritized recommendations#

P0 — make the machinery binding (hours, not days)#

  1. Fix the red registry gate: register scope:testing (or retag testing/prompt-injection to an existing governance scope). One line + one tag edit.
  2. Close the ratchet bypass: run check-conventions-ratchet.mjs + check-registry.mjs as required checks on main pushes (they already run in ci.yml quality — confirm that job is required on every branch and on main), and CODEOWNERS-protect tools/conventions/conventions-ratchet-baseline.json so baseline bumps require explicit review.
  3. Guard the root: add a root-*.md allowlist to check-forbidden-paths.sh; move the five 07-15…07-17 reports plus ISIS_TODOS×2/ISIS_GAPS/V1_RESIDUAL_AUDIT into docs/audits/ (with redirects/pointers if tooling parses them).
  4. Delete tracked junk: openapi/*.backup-*, .spectral.yaml.backup, static/, crates/maya-terrain-deform/. Four git rm commands.

P1 — finish what R-8/R-12 started (days)#

  1. R-8 slice 4: move V2/tools, V5/tools under apps/; drop the V2/tools/* workspace glob; then the completion claim is true.
  2. nx-cover apps/v4 + apps/v5: per-service project.json with scope tags, wrapping existing cargo/pnpm commands (v3/v6/v7 files are the template).
  3. Tag the 33 untagged projects and extend check-registry.mjs to assert scope-tag presence, not just validity.
  4. Resilience adoption wave OR deletion: 128 grandfathered sleep()s are a mechanical codemod onto @oshun/resilience; each adoption lowers the ratchet baseline. If nobody will drive it, delete the zero-consumer libs and record the refusal — an unadopted foundation lib is worse than none.
  5. Target-name vocabulary: document canonical names; add type-check/fmt/test-cov/cargo-test/format-check to targetDefaults with cache:true as a bridge; codemod renames domain-by-domain.

P2 — steady-state debt burn (weeks, incremental)#

  1. Vitest preset slice 3 from the SHA census (≈887 files in 12 hash classes are literally identical — the conversion is sed-shaped and byte-equivalence-provable).
  2. Catalog long-tail wave for the 97 multi-range deps (vitest first).
  3. README drift check for the generated fleet; fix infra/README.md's stale table while there.
  4. Dead workflow filter lines (G-1 table) — nine deletions.
  5. libs/shared curation: taxonomy README, adjudicate events/event-bus + auth/auth-primitives, evict tara-live-class-booking.
  6. Snapshot policy: single-platform Playwright baselines or LFS.
  7. nx.json/CLAUDE.md stale-root cleanup (C-3) and nx release dry-run verification (C-5).
  8. neith math.rs micro-crate + build.rs dedup inside the umbrellas; lilith svc scaffolding decision; arete↔demeter UI-kit decision.

P3 — owner-gated / long-term#

  1. History rewrite (existing runbook; owner-executed).
  2. F-5 eslint composition slices; C-4/C-5 tsconfig preset unification onto configs/tsconfig/.
  3. Coverage-threshold promotion domain-by-domain (I-1).
  4. iris taxonomy documentation or flattening decision (B-7); psyche umbrella project replacement (B-6).
  5. Merge-noise policy decision (F-1).

6. Appendix — raw measurements (2026-07-18)#

text
tracked files                129,219
nx projects (project.json)     3,445   (33 missing scope tags; 14 no targets)
package.json                   3,287
dep entries                   13,376   (catalog 8,771 / workspace 3,398 / hard 1,207)
multi-range hard-pinned deps      97
Cargo.toml                       953   (17 workspace roots)
pyproject/locks                83/78
tsconfig files                 7,080   (configs/tsconfig adopters: 83)
vitest configs                 2,669   (preset re-exports: 760)
vitest coverage-threshold cfgs  ~121
workflows                        102   (composite action: 64; scheduled: 26)
eslint.config.js lines         1,579
executors: run-commands 7,173 · eslint 2,056 · vite:test 1,846 · js:tsc 1,429
            esbuild 156 · vitest:test 33 · js:node 34 · next 3
target names: test 3,400 · lint 3,286 · build 3,109 · typecheck 1,265
            fmt 422 · type-check 48 · typecheck:ts 33 · test-cov 24
exact-dup clusters (code, non-test)       524  (2,442 redundant copies)
exact-dup clusters (source, non-config)   240  (309 redundant copies)
.spec.ts 11,570 · .test.ts 10,191
kebab vs camel src files (libs)  29,144 : 64
LFS: 95 patterns / 23 objects
git pack (shared)              1.7 GB
ratchet: pino 29 · pg 17 · ioredis 26 · env 1,302 · CB 23 · sleep 128 ·
         .test.ts baseline 12,699 (was 12,602 at ledger close; 4 grandfather bumps)
foundation-lib importers: math 1,982 · http-client 29 · config 24 · events 15 ·
         bff-kit 13 · ids 7 · collections 0 · resilience 0

Method note for the next audit: the index-SHA duplicate census and the workflow-filter glob test are cheap enough to run every time; both are pure read-only one-liners over git ls-files and caught real drift this pass.


7. Resolution ledger (later 2026-07-18)#

Every finding adjudicated the same day: done (executed + verified), corrected (the finding's premise was wrong — the correction is the resolution), or declined/backlog (explicit decision with the reason recorded). Each row landed as its own pushed commit (branch + main).

Finding Resolution
1 / B-3 registry gate red Donetesting registered as a cross-domain harness governance scope; gate green
2 / A-2 ratchet bypass Done, structurally--staged authoring-time mode (60 ms) wired into pre-commit; test-suffix baseline converted count→file-set (closes the +1/−1 hole, names offenders); merges during this very session confirmed the mechanism (later main merges arrived already .spec.ts)
3 / A-1 root landfill Done, partially corrected — the "landfill" is largely the documented active-planning convention (docs/domains/README.md); guard added: root *.md outside the core allowlist + declared ledger list fails CI; stale V1_RESIDUAL_AUDIT_2026-06-11/ retired to docs/audits with refs fixed (plus two pre-existing broken links)
4 / H-1 zero-consumer foundation libs Done — adoption waves executed: 120 sleep/delay defs → @oshun/resilience (clamp semantics aligned, 23/23), 8 chunk clones → @oshun/collections; ratchet local-sleep-def 128→8 (4 domain-typed + 4 boundary-blocked refusals — contracts stays dependency-pure); 71 package.json gain explicit deps
5 / B-2 apps/v4+v5 nx-invisible Done, corrected — TS packages were pnpm-inferred all along; real gaps closed: 25 project.json (3 cargo crates truly invisible + tags/cached-inputs for 22), v4/v5 named inputs, v4 scope registered; smoke green
6 / B-1 R-8 residue Done — slice 4: V2/tools/esports → apps/v2/esports-tools (glob dropped, 7/7 + tsc green); V5/tools corrected as ops (the V5 gate fleet — stays); en route repaired 54 DOMAINS-stale + 125 V2-path-stale checkers, 23 stale docs, 12 brittle phrase assertions, and the slice-2-broken grpc vitest alias
7 / C-1 C-2 target drift Done (bridge) + doc — canonical vocabulary in docs/conventions/nx-target-names.md; variant names cache-bridged in targetDefaults; 33 @nx/vitest:test → @nx/vite:test; full renames = R-14 follow-up by design
8 / E-2 source duplication Done where real — 9× neith spatial math.rs → re-export of neith-spatial-core::math (2,430 lines deleted, cargo+tests green); corrected: build.rs copies are per-crate cargo idiom, version.ts one-liners are per-package data; lilith svc scaffolds + arete↔demeter UI kit = owner product decisions (backlog)
9 / D-1 dep drift Done (safe subset) — 218 pins → catalog (26 intersecting + 51 same-major consolidations); hard pins 1,207→991, multi-range 97→44; the 44 are cross-major migrations (vitest ^1/^2→^3 etc.) = real upgrade backlog, not config cleanup
10 / F-2 readme drift + H-4 infra README Done — generator --check (coverage invariant) wired in ci.yml; infra/README rewritten to the one-root reality incl. the vault-deploy/k8s-deploy same-name-different-origin note
A-3 snapshots Done via LFS (single-platform pruning rejected: darwin baselines serve the owner's local runs) — 66 PNGs → LFS pointers, workflow checkout lfs:true
A-4 pack/history rewrite Owner-gated as before (runbook current)
C-3/C-4 nx.json staleness Done — phantom excludes dropped; CLAUDE.md structure rewritten
C-5 nx release scope Verified working-as-documented (ADR-0073 deliberate depth-2 interface scope; dry-run enumerates it; first release needs --first-release)
C-6 zero-target projects Corrected — all 14 carry full package-script-inferred targets
E-1 vitest preset tail Done (slice 3) + corrected — the census's big classes were the converted re-exports themselves; of the real 99 hand-rolled classes, top 3 non-domain classes (106 files) converted with zero-flip proof (3/988/79); themis's 31-file class already routes through a domain factory (left; that IS dedup)
G-1 dead filters Done — nine lines deleted, six workflows YAML-verified
H-2 untagged projects Done — 33 legacy domain-tagged projects gain scope tags; absent-scope assertion added to the registry gate
H-3 shared curation Done — curation rule + adjudicated near-name pairs in libs/shared/README (events/event-bus, auth/auth-primitives, obs trio = deliberate); tara-live-class-booking → libs/tara as @tara/live-class-booking (audit's "sole consumer" claim corrected: real web/mobile consumers, all rewired)
I-1 coverage thresholds Started as designed — thresholds preset is now the generator default for new libs; domain-by-domain promotion remains owner-paced
I-2 suffix split Held by the new authoring-time gate; iris generator's .test.ts template renamed .spec.ts (it would have tripped the gate)
I-3 docs stragglers Declined with reason — docs/oshun is machine-wired (stub-scan allowlist path); docs/kalika move = churn without consumer benefit; both recorded
B-5 root dirs Done (crates/ orphan, static/, backups deleted; openapi backups purged); types/+evaluation/ single-file dirs left (tracked, cheap, no consumer risk today)
B-6 tests/ vs testing/ Done via documentation — tests/README.md states the split rule
B-7 depth variance Done via rule — placement-depth convention documented; existing deep taxonomies grandfathered
B-8 V3 root docs Declined with evidence — all 52 files externally referenced (workflows/checkers/docs); product-root placement, not repo-root landfill
B-4 flat apps Done (second pass, same day) — the deploy-window gating was already dissolved by the owner's prelaunch confirmation, so both renames executed: apps/euterpe-studio-webapps/euterpe/studio-web and apps/neith-vaultapps/neith/vault (workspace globs, project.json depths, tsconfig extends, source-of-truth registry mapping, env-baseline paths, docs all rewired; source-of-truth verifier + registry + ratchet green; nx resolves both at the new roots; the neith android gradlew relative path, broken at HEAD, now resolves at the new depth). apps/infra stays: it is a real platform-services home and renaming it is a naming-taste call for the owner
B-6(psyche/iris nesting), F-1 merge noise, F-3 eslint split Owner-gated/backlog with reasons: psyche umbrella + iris taxonomy = domain-owner design decisions (depth rule now prevents new cases); merge-noise is a workflow-policy decision only the owner can make; eslint composition continues on the proven --print-config slice method

Pre-existing reds surfaced (not introduced) and left visible: check-v2-service-observability (≈40 former V2/services packages lack @oshun/metrics|tracing — red before R-8 too, was scanning an empty glob at HEAD); 3 grpc-services commentary-seam contract failures (suite could not even load at HEAD); 2 aje/sdk SHA-256 KAT failures; svc-user-preferences jest-types baseline drift. Each is product-owner work now visible instead of silent.