Disciplines · Audits

R-8 Product Placement — Decision Record (2026-07-18)

25 workflows path-filter on V*/ trees; 8 on apps/v*/libs/v*.

5sections4 minread

On this page

Audit finding B-3/H-5: V-series products live under two placement conventions, and "where does V code live" has different answers per N.

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:

  1. V5 (2,122 files; V5/services is in pnpm-workspace.yaml): update the workspace glob, V5 workflows' filters, and the V5 checker scripts that self-assert paths. V5 nightly uses lfs: true — content stays under V5/ue, unaffected.
  2. V2 (6,448 files; V2/services glob; V2 checker scripts self-assert manifest paths — the cargo-umbrella work already catalogued them).
  3. V4 last (3,053 files; the heaviest coupling: V4/.ci/ gates-manifest.json carries 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:

  1. V5: verify-services 16/16, docs + cell validators, service typechecks and the auth contract suite identical before/after.
  2. V2: the full 1,155-gate check-v2 fleet identical before/after (all green); remote-config suite 28/28; content inventory + seed registry regenerated at identical counts.
  3. V4: the @v4/scripts gate 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 gaining apps/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/esportsapps/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 nonexistent apps/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/tools stays — it is the V5 validation/authoring gate fleet (validate-*-content.py, templates), invoked from repo root by v5-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-slice V2/{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-observability remains 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.