diff --git a/apps/app/.eslintrc.cjs b/apps/app/.eslintrc.cjs
index 7ff5661b..e9474110 100644
--- a/apps/app/.eslintrc.cjs
+++ b/apps/app/.eslintrc.cjs
@@ -247,6 +247,13 @@ module.exports = {
{ from: 'components-shared', allow: ['types', 'utils', 'lib', 'composables', 'composables-forms', 'stores', 'components-shared'] },
{ from: 'components-portal', allow: ['types', 'utils', 'lib', 'composables', 'composables-forms', 'stores', 'stores-portal', 'components-shared', 'components-portal'] },
{ from: 'components-organizer', allow: ['types', 'utils', 'lib', 'composables', 'composables-forms', 'stores', 'components-shared', 'components-organizer'] },
+ // v2 zones. components-v2 may use the FormField/Icon bridge
+ // (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).
+ { 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: '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'] },
@@ -287,12 +294,26 @@ module.exports = {
{ type: 'components-shared', pattern: 'src/components/{shared,auth,settings}/**' },
{ type: 'components-portal', pattern: 'src/components/portal/**' },
{ type: 'components-organizer', pattern: 'src/components/organizer/**' },
+ // GUI-redesign v2 zones (RFC-WS-GUI-REDESIGN AD-G5). Declared
+ // before the generic `components` catch-all. `components-foundation`
+ // is the ONLY sanctioned v1→v2 bridge (FormField + Icon — audited
+ // to live in the generic `components` zone, not components-shared).
+ // Two-entry form used: eslint-plugin-boundaries 6.0.2 does not honour
+ // micromatch brace expansion in `pattern` (the brace glob matched in
+ // isolation but the plugin's internal resolver did not produce the
+ // expected classification). RFC §14 fallback: two entries with the
+ // same `type` so both src/components/forms/** and src/components/Icon.vue
+ // are captured before the generic `components` catch-all.
+ { type: 'components-foundation', pattern: 'src/components/forms/**' },
+ { type: 'components-foundation', pattern: 'src/components/Icon.vue' },
+ { type: 'components-v2', pattern: 'src/components-v2/**' },
{ type: 'components', pattern: 'src/components/**' },
{ type: 'layouts', pattern: 'src/layouts/**' },
{ type: 'pages-register', pattern: 'src/pages/register/**' },
{ type: 'pages-portal', pattern: 'src/pages/portal/**' },
{ type: 'pages-platform', pattern: 'src/pages/platform/**' },
{ type: 'pages-organizer', pattern: 'src/pages/{events,members,organisation,account-settings,dashboard,invitations}/**' },
+ { type: 'pages-v2', pattern: 'src/pages-v2/**' },
{ type: 'pages', pattern: 'src/pages/**' },
],
'boundaries/ignore': [
diff --git a/apps/app/src/components-v2/shared/X.vue b/apps/app/src/components-v2/shared/X.vue
new file mode 100644
index 00000000..6640e9ed
--- /dev/null
+++ b/apps/app/src/components-v2/shared/X.vue
@@ -0,0 +1,10 @@
+
+
+
+
+
diff --git a/apps/app/tests/unit/boundaries-v2.spec.ts b/apps/app/tests/unit/boundaries-v2.spec.ts
new file mode 100644
index 00000000..b271a6d3
--- /dev/null
+++ b/apps/app/tests/unit/boundaries-v2.spec.ts
@@ -0,0 +1,50 @@
+// @vitest-environment node
+// ESLint Node API tests must run in the Node environment — the default
+// happy-dom environment's `document` object causes case-police's dirs.cjs
+// (which uses `document.currentScript.src` for __dirname resolution)
+// to fail with "The URL must be of scheme file".
+import { fileURLToPath } from 'node:url'
+import path from 'node:path'
+import { describe, expect, it } from 'vitest'
+import { ESLint } from 'eslint'
+
+const __filename = fileURLToPath(import.meta.url)
+const __dirname = path.dirname(__filename)
+const rootDir = path.resolve(__dirname, '../../')
+
+const eslint = new ESLint({ cwd: rootDir })
+
+async function boundaryErrors(filePath: string, code: string) {
+ const [result] = await eslint.lintText(code, { filePath })
+
+ return result.messages.filter(m => m.ruleId === 'boundaries/element-types')
+}
+
+describe('boundaries — v2 zones', () => {
+ it('allows pages-v2 → components-v2', async () => {
+ const errs = await boundaryErrors(
+ 'src/pages-v2/dashboard.vue',
+ '',
+ )
+
+ expect(errs).toHaveLength(0)
+ })
+
+ it('allows components-v2 → components-foundation (FormField bridge)', async () => {
+ const errs = await boundaryErrors(
+ 'src/components-v2/forms/Demo.vue',
+ '',
+ )
+
+ expect(errs).toHaveLength(0)
+ })
+
+ it('forbids v1 components → components-v2 (no back-porting)', async () => {
+ const errs = await boundaryErrors(
+ 'src/components/organizer/Legacy.vue',
+ '',
+ )
+
+ expect(errs.length).toBeGreaterThan(0)
+ })
+})