diff --git a/apps/app/.eslintrc.cjs b/apps/app/.eslintrc.cjs
index ee51fb29..cc1069d7 100644
--- a/apps/app/.eslintrc.cjs
+++ b/apps/app/.eslintrc.cjs
@@ -252,9 +252,16 @@ module.exports = {
// (components-foundation) but NOT any other v1 component zone.
// No v1 `from` rule lists components-v2/pages-v2 → back-porting
// is structurally impossible (RFC-WS-GUI-REDESIGN AD-G5).
+ // layouts-v2 (src/layouts/*V2*.vue, e.g. OrganizerLayoutV2) is
+ // the v2 shell-composition zone: SAME v2 capability as pages-v2
+ // (may import components-v2 + navigation) so AD-G2's
+ // "OrganizerLayoutV2 wraps AppShellV2" holds, WITHOUT widening
+ // the v1 `layouts` zone (still cannot reach components-v2 →
+ // AD-G5 isolation intact). Locked by tests/unit/boundaries-v2.spec.ts.
{ from: 'components-foundation', allow: ['types', 'utils', 'lib', 'composables', 'composables-forms', 'stores', 'components-foundation'] },
{ from: 'components-v2', allow: ['types', 'utils', 'lib', 'composables', 'composables-forms', 'stores', 'components-v2', 'components-foundation'] },
{ from: 'pages-v2', allow: ['types', 'utils', 'lib', 'composables', 'composables-forms', 'stores', 'navigation', 'components-v2', 'components-foundation', 'layouts', 'plugins'] },
+ { from: 'layouts-v2', allow: ['types', 'utils', 'lib', 'composables', 'composables-forms', 'stores', 'navigation', 'components-v2', 'components-foundation', 'layouts', 'plugins'] },
{ from: 'components', allow: ['types', 'utils', 'lib', 'composables', 'composables-forms', 'stores', 'components', 'components-shared', 'components-organizer'] },
{ from: 'layouts', allow: ['types', 'utils', 'lib', 'composables', 'composables-forms', 'stores', 'stores-portal', 'navigation', 'components', 'components-shared', 'components-portal', 'components-organizer', 'layouts'] },
@@ -315,6 +322,14 @@ module.exports = {
{ type: 'components-foundation', pattern: 'src/components/Icon.vue', mode: 'file' },
{ type: 'components-v2', pattern: 'src/components-v2/**' },
{ type: 'components', pattern: 'src/components/**' },
+ // layouts-v2 MUST precede the generic `layouts` element: first
+ // match wins. The single `*` does not cross `/`, so this matches
+ // only top-level v2 layout files (src/layouts/OrganizerLayoutV2.vue)
+ // and NOT src/layouts/components/AppShellV2.vue (subdir → stays
+ // `layouts`, which is correct: AppShellV2 imports only stores).
+ // mode:'file' is REQUIRED for a file-glob element (same reason as
+ // the Icon.vue bridge above) — RFC AD-G5 / boundaries-v2.spec.ts.
+ { type: 'layouts-v2', pattern: 'src/layouts/*V2*.vue', mode: 'file' },
{ type: 'layouts', pattern: 'src/layouts/**' },
{ type: 'pages-register', pattern: 'src/pages/register/**' },
{ type: 'pages-portal', pattern: 'src/pages/portal/**' },
diff --git a/apps/app/tests/unit/boundaries-v2.spec.ts b/apps/app/tests/unit/boundaries-v2.spec.ts
index 8b1b7be7..d0baead9 100644
--- a/apps/app/tests/unit/boundaries-v2.spec.ts
+++ b/apps/app/tests/unit/boundaries-v2.spec.ts
@@ -67,4 +67,38 @@ describe('boundaries — v2 zones', () => {
expect(errs.length).toBeGreaterThan(0)
})
+
+ // -------------------------------------------------------------------------
+ // layouts-v2 zone (RFC AD-G5): the v2 shell layout (src/layouts/*V2*.vue)
+ // gets pages-v2-equivalent v2 capability so AD-G2 ("OrganizerLayoutV2
+ // wraps AppShellV2") holds — WITHOUT widening the v1 `layouts` zone.
+ // These lock both halves: the new edge AND the preserved isolation.
+ // -------------------------------------------------------------------------
+
+ it('allows layouts-v2 → components-v2 (AD-G5: v2 shell composition)', async () => {
+ const errs = await boundaryErrors(
+ 'src/layouts/OrganizerLayoutV2.vue',
+ '',
+ )
+
+ expect(errs).toHaveLength(0)
+ })
+
+ it('allows layouts-v2 → navigation (v2 layout sources nav data)', async () => {
+ const errs = await boundaryErrors(
+ 'src/layouts/OrganizerLayoutV2.vue',
+ '{{ orgNavItems.length }}
',
+ )
+
+ expect(errs).toHaveLength(0)
+ })
+
+ it('forbids v1 layouts → components-v2 (AD-G5 isolation preserved)', async () => {
+ const errs = await boundaryErrors(
+ 'src/layouts/OrganizerLayout.vue',
+ '',
+ )
+
+ expect(errs.length).toBeGreaterThan(0)
+ })
})
diff --git a/dev-docs/RFC-WS-GUI-REDESIGN-CREWLI-STARTER.md b/dev-docs/RFC-WS-GUI-REDESIGN-CREWLI-STARTER.md
index ed7efe60..e63a7b80 100644
--- a/dev-docs/RFC-WS-GUI-REDESIGN-CREWLI-STARTER.md
+++ b/dev-docs/RFC-WS-GUI-REDESIGN-CREWLI-STARTER.md
@@ -30,9 +30,19 @@ Tailwind + FormField + DataTable conventions) remain binding.
`useAuthStore`/`useOrganisationStore`. One new `useShellUiStore` holds
only sidebar/theme/density + right-drawer state. `provide`/`inject`
from crewli-starter is replaced per-port (no `inject()` survives).
-- **AD-G5 — Boundaries.** New `components-v2`/`pages-v2` zones; the only
- v1→v2 bridge is a narrow `components-foundation` zone (FormField,
- Icon). No back-porting (structurally enforced).
+- **AD-G5 — Boundaries.** New `components-v2`/`pages-v2`/`layouts-v2`
+ zones; the only v1→v2 bridge is a narrow `components-foundation` zone
+ (FormField, Icon). No back-porting (structurally enforced). The v2
+ shell layout (`src/layouts/*V2*.vue`, e.g. `OrganizerLayoutV2`) is the
+ `layouts-v2` zone — same v2 capability as `pages-v2` (may import
+ `components-v2` + `navigation`) so AD-G2's "`OrganizerLayoutV2` wraps
+ `AppShellV2`" holds. The v1 `layouts` zone is unchanged and still
+ cannot import `components-v2`, so v2 isolation is preserved (only
+ top-level `*V2*.vue` layout files gain v2 capability;
+ `src/layouts/components/AppShellV2.vue` stays in `layouts` since it
+ imports only `stores`). Locked by `tests/unit/boundaries-v2.spec.ts`
+ (`layouts-v2 → components-v2` allowed; v1 `layouts → components-v2`
+ forbidden).
- **AD-G6 — Testing.** TEST-INFRA-001 (✅ Resolved) Playwright-CT +
visual foundation is kept as the CI gate; Storybook a11y is
complementary. v2 visual baselines are captured from the v2 component