Audit finding B-3/H-5: V-series products live under two placement conventions,
and "where does V
Measured state (2026-07-18)#
| Convention | Products |
|---|---|
Split (apps/v<N> + libs/v<N> + V<N>/ for UE/docs/ops) |
V3, V6, V7, V8, V9, V10 (6 of 9 code-bearing) |
In-root (V<N>/{services,web,scripts}) |
V2, V4, V5 |
| Docs/planning only | V1 |
25 workflows path-filter on V*/ trees; 8 on apps/v*/libs/v*.
Decision#
The split convention wins — it is the 2:1 majority, matches the
workspace-wide apps/+libs/ taxonomy, keeps UE binary trees at product roots
(path-length and LFS-locality reasons the audit itself accepted), and is what
V10 adopted at birth per the audit's §0 addendum. products/… (the audit's
alternative) is rejected: it would move all nine trees instead of three and
invalidate all 25 V-tree workflow filters instead of the holdouts'.
Execution plan (three dedicated changes, smallest first)#
Move V<N>/{services,web,scripts} → apps/v<N>/… (deployables) and product TS
libs → libs/v<N>/…, one product per change:
- V5 (2,122 files;
V5/servicesis inpnpm-workspace.yaml): update the workspace glob, V5 workflows' filters, and the V5 checker scripts that self-assert paths. V5 nightly useslfs: true— content stays underV5/ue, unaffected. - V2 (6,448 files;
V2/servicesglob; V2 checker scripts self-assert manifest paths — the cargo-umbrella work already catalogued them). - V4 last (3,053 files; the heaviest coupling:
V4/.ci/ gates-manifest.jsoncarries 996 path filters and its validators grep the manifest for invocation strings — every filter row referencing a moved path must be rewritten in the same change and the round-trip validator re-run).
Per-change verification recipe (the audit G-2 method): glob-test every touched
workflow/manifest filter against git ls-files before and after; run the
product's own gate fleet (scripts/verify.mjs family) against its pre-move
baseline requiring zero pass/fail flips; pnpm install --lockfile-only for
workspace-glob changes.
Why not executed today: each move is a multi-thousand-file change wired into
product gate fleets that parallel sessions are actively extending; the zero-flip
baseline requires a quiet window per product. The convention decision above is
binding now — new product code goes to apps/v<N>/libs/v<N>, and V2/V4/V5
converge via the three staged changes.
Execution record (2026-07-18, prelaunch-authorized)#
All three staged changes landed the same day, each with its zero-flip proof:
- V5: verify-services 16/16, docs + cell validators, service typechecks and the auth contract suite identical before/after.
- V2: the full 1,155-gate
check-v2fleet identical before/after (all green); remote-config suite 28/28; content inventory + seed registry regenerated at identical counts. - V4: the
@v4/scriptsgate suite at its exact 190/191 baseline; all 996 gates-manifest filters glob-match tracked files before and after; cargo metadata green on the moved workspace. Depth couplings fixed en route: 98 repo-root anchors ('../../..' → four-up) and the checker path allowlist gainingapps/v4/.
R-8 is complete: every code-bearing product (V2–V10) now follows the split convention. Product roots keep ue/docs/ops only.
Slice 4 (deep-audit B-1, later 2026-07-18)#
The deep audit found the "product roots keep ue/docs/ops only" claim was not yet
true: V2/tools/esports (a live nx library reached via a leftover V2/tools/*
workspace glob) and V5/tools remained. Adjudication + action:
V2/tools/esports→apps/v2/esports-tools(this slice): workspace glob removed, tsconfig aliases, grpc-services vitest alias (which had been silently broken by slice 2 — it resolved to a nonexistentapps/tools/path, leaving the grpc-services suite unable to load), four esports checkers, the UE contract JSON, 10 doc references, and the lockfile all rewired. Esports-tools 7/7 tests + typecheck green; grpc-services loads again (13/16; the 3 failures are pre-existing commentary-seam contract drift, red-at-HEAD).V5/toolsstays — it is the V5 validation/authoring gate fleet (validate-*-content.py, templates), invoked from repo root byv5-doc-validation.yml,v5-nightly-cell-validation.yml, and the pre-commit hook. That is the "ops" class the split convention keeps at product roots; the audit's B-1 counted it as product code — corrected.- En route: the fleet-wide staleness this exposed was repaired — 54 checkers
still asserting the retired
DOMAINS/tree (→docs/domains/<x>/deep-dive/), 125 checkers asserting pre-sliceV2/{services,web}paths (→apps/v2/…), 18 deep-dive domain docs + 5 V2 docs with stale paths, and 12 checkers whose contiguous-phrase assertions broke on prettier line-wraps (now whitespace-normalized).check-v2-service-observabilityremains red on its pre-existing @oshun/metrics|tracing dependency debt across ~40 former V2/services packages (red before R-8 as well) — product-owner work, now visible again instead of vacuous.