chore(eslint): activate boundary sub-zones (TECH-WS3-BOUNDARIES-SUBZONES)

Adds the WS-3 §4.2 sub-zone classification to the apps/app
boundaries matrix:

- components-{shared,portal,organizer} alongside the legacy
  components type. components/{auth,settings} are folded into
  components-shared as the legacy cross-context home for MFA dialogs
  + PasswordRequirements (used by both organizer reset-password and
  portal wachtwoord-instellen / profiel).
- composables-forms (src/composables/forms/**) — pure form-runtime
  helpers reusable from organizer Form Builder later.
- stores-portal (src/stores/portal/**) — keeps the portal auth +
  portal store walled off from the organizer auth surface.
- pages-{register,portal,platform,organizer} alongside the legacy
  pages type — register pages cannot reach into stores or
  components-portal/-organizer; portal pages cannot reach
  components-organizer; organizer + platform pages cannot reach
  stores-portal or components-portal.

Cross-context edges are forbidden (organizer ↛ portal,
shared ↛ portal/organizer). Two pragmatic exceptions are documented
inline:
  - components-shared accepts the legacy auth/ + settings/ paths
    until PR-B2 cleanup re-homes them under shared/{auth,settings}/.
  - pages-register may read stores-portal because success.vue
    optionally enriches with the portal user when authenticated.
    PR-B2 may move success.vue into pages-portal so this drops.

Lint: 0 errors / 0 new warnings (only the pre-existing
boundaries v5→v6 deprecation warnings, which apply to all 19 rules
now). Tests: 23 / 162 pass. Typecheck clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-05 19:29:32 +02:00
parent 5c689f42a0
commit a84742a01f

View File

@@ -208,26 +208,45 @@ module.exports = {
// The `lib → stores` edge is intentionally disallowed; lib/axios.ts // The `lib → stores` edge is intentionally disallowed; lib/axios.ts
// uses dynamic `await import('@/stores/...')` for its 4 store reads // uses dynamic `await import('@/stores/...')` for its 4 store reads
// so the static-import surface stays clean. // so the static-import surface stays clean.
// Sub-zone enforcement (components/{organizer,portal,shared}) is a //
// backlog item (TECH-WS3-BOUNDARIES-SUBZONES); it lands after the // WS-3 PR-B1 activated TECH-WS3-BOUNDARIES-SUBZONES: components and
// §4.2 consolidation directory layout. // pages now have organizer/portal/shared sub-zones (§4.2 charter).
// The cross-context edges (organizer ↛ portal, shared ↛ portal/organizer)
// are forbidden so a future portal-only refactor cannot leak into
// the organizer surface and vice versa.
//
// FORWARD-FLAG: when src/plugins/1.router/ migrates to src/router/ // FORWARD-FLAG: when src/plugins/1.router/ migrates to src/router/
// in a later WS-3 PR, add `{ type: 'router', pattern: 'src/router/**' }` // in a later WS-3 PR (TECH-WS3-BOUNDARIES-ROUTER-ZONE), add
// to boundaries/elements and `{ from: 'router', allow: ['types', // `{ type: 'router', pattern: 'src/router/**' }` to boundaries/elements
// 'utils', 'lib', 'plugins', 'stores'] }` to the rules. // and `{ from: 'router', allow: ['types', 'utils', 'lib', 'plugins',
// 'stores'] }` to the rules.
'boundaries/element-types': ['error', { 'boundaries/element-types': ['error', {
default: 'disallow', default: 'disallow',
rules: [ rules: [
{ from: 'types', allow: ['types'] }, { from: 'types', allow: ['types'] },
{ from: 'utils', allow: ['types', 'utils'] }, { from: 'utils', allow: ['types', 'utils'] },
{ from: 'lib', allow: ['types', 'utils', 'lib'] }, { from: 'lib', allow: ['types', 'utils', 'lib'] },
{ from: 'plugins', allow: ['types', 'utils', 'lib', 'plugins', 'stores'] }, { from: 'plugins', allow: ['types', 'utils', 'lib', 'plugins', 'stores', 'stores-portal'] },
{ from: 'composables', allow: ['types', 'utils', 'lib', 'composables', 'stores'] }, { from: 'composables-forms', allow: ['types', 'utils', 'lib', 'composables-forms'] },
{ from: 'stores', allow: ['types', 'utils', 'lib', 'composables', 'stores'] }, { from: 'composables', allow: ['types', 'utils', 'lib', 'composables', 'composables-forms', 'stores', 'stores-portal'] },
{ from: 'stores-portal', allow: ['types', 'utils', 'lib', 'composables', 'composables-forms', 'stores', 'stores-portal'] },
{ from: 'stores', allow: ['types', 'utils', 'lib', 'composables', 'composables-forms', 'stores'] },
{ from: 'navigation', allow: ['types', 'utils', 'navigation'] }, { from: 'navigation', allow: ['types', 'utils', 'navigation'] },
{ from: 'components', allow: ['types', 'utils', 'lib', 'composables', 'stores', 'components'] }, { from: 'components-shared', allow: ['types', 'utils', 'lib', 'composables', 'composables-forms', 'components-shared'] },
{ from: 'layouts', allow: ['types', 'utils', 'lib', 'composables', 'stores', 'navigation', 'components', 'layouts'] }, { from: 'components-portal', allow: ['types', 'utils', 'lib', 'composables', 'composables-forms', 'stores', 'stores-portal', 'components-shared', 'components-portal'] },
{ from: 'pages', allow: ['types', 'utils', 'lib', 'composables', 'stores', 'navigation', 'components', 'layouts'] }, { from: 'components-organizer', allow: ['types', 'utils', 'lib', 'composables', 'composables-forms', 'stores', 'components-shared', 'components-organizer'] },
{ 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'] },
// success.vue conditionally enriches with the portal auth user
// ("Welcome back, X") when authenticated. The page is still public-
// accessible; the store read is optional. PR-B2 cleanup may move
// success.vue into pages-portal so this exception can drop.
{ from: 'pages-register', allow: ['types', 'utils', 'lib', 'composables', 'composables-forms', 'plugins', 'components-shared', 'stores-portal', 'layouts'] },
{ from: 'pages-portal', allow: ['types', 'utils', 'lib', 'composables', 'composables-forms', 'stores', 'stores-portal', 'navigation', 'components-shared', 'components-portal', 'layouts', 'plugins'] },
{ from: 'pages-organizer', allow: ['types', 'utils', 'lib', 'composables', 'composables-forms', 'stores', 'navigation', 'components', 'components-shared', 'components-organizer', 'layouts', 'plugins'] },
{ from: 'pages-platform', allow: ['types', 'utils', 'lib', 'composables', 'composables-forms', 'stores', 'navigation', 'components', 'components-shared', 'components-organizer', 'layouts', 'plugins'] },
{ from: 'pages', allow: ['types', 'utils', 'lib', 'composables', 'composables-forms', 'stores', 'stores-portal', 'navigation', 'components', 'components-shared', 'components-portal', 'components-organizer', 'layouts'] },
], ],
}], }],
'boundaries/no-unknown': 'off', // External packages are fine. 'boundaries/no-unknown': 'off', // External packages are fine.
@@ -239,17 +258,33 @@ module.exports = {
typescript: {}, typescript: {},
}, },
// Element-type assignment: first-match-wins. Order matters. // Element-type assignment: first-match-wins. Order matters — narrower
// sub-zones declared before their broader parents.
'boundaries/elements': [ 'boundaries/elements': [
{ type: 'types', pattern: 'src/types/**' }, { type: 'types', pattern: 'src/types/**' },
{ type: 'utils', pattern: 'src/utils/**' }, { type: 'utils', pattern: 'src/utils/**' },
{ type: 'lib', pattern: 'src/lib/**' }, { type: 'lib', pattern: 'src/lib/**' },
{ type: 'plugins', pattern: 'src/plugins/**' }, { type: 'plugins', pattern: 'src/plugins/**' },
{ type: 'composables-forms', pattern: 'src/composables/forms/**' },
{ type: 'composables', pattern: 'src/composables/**' }, { type: 'composables', pattern: 'src/composables/**' },
{ type: 'stores-portal', pattern: 'src/stores/portal/**' },
{ type: 'stores', pattern: 'src/stores/**' }, { type: 'stores', pattern: 'src/stores/**' },
{ type: 'navigation', pattern: 'src/navigation/**' }, { type: 'navigation', pattern: 'src/navigation/**' },
// components/shared/** is the canonical sub-zone. components/auth/**
// and components/settings/** are folded in here as legacy cross-context
// siblings (MFA dialogs + password-requirements widgets used by both
// organizer reset-password and portal wachtwoord-instellen / profiel
// flows). PR-B2 may rehome these under components/shared/{auth,settings}/.
{ type: 'components-shared', pattern: 'src/components/{shared,auth,settings}/**' },
{ type: 'components-portal', pattern: 'src/components/portal/**' },
{ type: 'components-organizer', pattern: 'src/components/organizer/**' },
{ type: 'components', pattern: 'src/components/**' }, { type: 'components', pattern: 'src/components/**' },
{ type: 'layouts', pattern: 'src/layouts/**' }, { 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', pattern: 'src/pages/**' }, { type: 'pages', pattern: 'src/pages/**' },
], ],
'boundaries/ignore': [ 'boundaries/ignore': [