← Working with agents
Technique Architecture as intent

Architecture as rules

One declaration, three views.

An architecture usually lives three times: as a picture, as a paragraph, and as whatever the checker happens to enforce. The three drift apart by construction, and the agent reads the paragraph. Let’s write the architecture once, and make the other two follow.

The skill to keep

State tiers, direction, doors, and exceptions as data, then derive the diagram, the doc, and the check from it, so a stale artifact is a failing test rather than a discovery.

TypeScript
01 / Draw

Name the tiers and the direction the arrows point.

Enforcement layer took a set of rules as given and taught how to run them. This lesson is the other half: where the rules come from, and how to keep them the same thing as the picture on the wall and the paragraph in the doc an agent reads before it starts.

The first move is the least technical and the most important: name the tiers. A tier is a folder pattern with a job. Routes, features, components, services. Then the direction: which tier may know about which. Write them top to bottom in the order the arrows point, because that order will matter in step three.

Case fileThree artifacts, one architecture, no agreement.
System
The two miniature repos from the enforcement lesson, a backend of eleven files and a frontend of twelve, and then this site’s own tree, about 1,080 modules when it was recorded on 12 September 2026.
Starting point
Each repo has a diagram drawn once, a paragraph in a doc, and a handwritten checker configuration. They were written by the same person on the same day and already disagree in small ways.
What must hold
After every change, the three say the same thing, and an agent reading any one of them reads the architecture that is actually enforced.

Here is the whole declaration model. Read it once; the rest of the lesson fills each field in. A tier has a path and a list of tiers it may import. Some tiers are shared by everyone. Some tiers have doors. A few sentences are easier to say as a deny. And an exception is a dated edge with a reason.

architecture.ts
export interface Tier {
	/** Declaration order is precedence when paths overlap: the earlier tier wins. */
	id: string;
	label: string;
	/** Regular expression a module path matches to belong to this tier. */
	path: string;
	/** Tiers this tier may import. Importing itself is always allowed. */
	may: string[];
	/** Optional: split the tier into isolated groups (a capture group) that may not import each other. */
	isolated?: { groups: string; shared?: string[] };
	/** Optional: the only paths other tiers may import from this tier. */
	doors?: string[];
}

export interface Deny {
	name: string;
	comment: string;
	/** A tier id, or a regular expression when it starts with `^`. */
	from: string;
	/** Regular expression over the imported path or bare specifier. */
	to: string;
	fromNot?: string;
}

export interface Exception {
	rule: string;
	from: string;
	to: string;
	reason: string;
	/** ISO date after which the exception should be revisited. */
	until: string;
}

export interface Architecture {
	name: string;
	tiers: Tier[];
	/** Tier ids every tier may import without saying so. */
	shared?: string[];
	/** Cross-cutting sentences that are easier to say as a deny. */
	deny?: Deny[];
	exceptions?: Exception[];
}
Leave this step with

A list of tiers in arrow order, each with a path pattern and a one-line job. If two tiers have the same job, they are one tier. If one tier has two jobs, it is two.

02 / Allow

Say what each tier may import. Everything else is forbidden.

The enforcement lesson’s handwritten rules were deny-lists: “services never import server”. A deny-list is easy to start and impossible to finish, because it only forbids what you thought of. An allow-list says what a tier may import, and the derivation turns that into one forbidden rule per tier: anything inside the architecture that is not on the list.

This is the backend declared. Six tiers. Kernel is shared, so every tier may import it without saying so. One sentence, memory adapters, stays a deny because it cuts across tiers by file name rather than by folder.

backend.ts
import type { Architecture } from './architecture';

// The enforcement lesson's miniature backend, declared once. Compare with the
// four handwritten rules it replaces: same verdicts, one source.
export const backend: Architecture = {
	name: 'Miniature backend-for-frontend',
	tiers: [
		{ id: 'routes', label: 'Routes', path: '^routes/', may: ['server', 'services'] },
		{
			id: 'server',
			label: 'Server',
			path: '^server/',
			may: ['root', 'services'],
			doors: ['^server/root\\.ts$']
		},
		{ id: 'root', label: 'Root', path: '^root/', may: ['services'] },
		{ id: 'services', label: 'Services', path: '^services/', may: ['http'] },
		{ id: 'http', label: 'Http', path: '^http/', may: [] },
		{ id: 'kernel', label: 'Kernel', path: '^kernel/', may: [] }
	],
	shared: ['kernel'],
	deny: [
		{
			name: 'memory-adapters-are-for-root-and-tests',
			comment: 'Memory mode is chosen at the root or in a test, never by a handler or a service.',
			from: '^(?!root/)',
			fromNot: '\\.spec\\.ts$',
			to: '\\.memory\\.ts$'
		}
	]
};

And the derivation, in one function. Each tier becomes a rule whose to matches anything inside the architecture and whose pathNot is the tier itself, its permissions, and the shared tiers. Doors and isolated groups add a rule each. Denies pass through.

architecture.ts
/** The forbidden rules a checker runs, derived from the tiers. */
export function toRules(architecture: Architecture): Rule[] {
	const { tiers, shared = [], deny = [] } = architecture;
	const byId = new Map(tiers.map((tier) => [tier.id, tier]));
	const tierPath = (id: string) => {
		const tier = byId.get(id);
		if (!tier) throw new Error(`Unknown tier "${id}" in ${architecture.name}`);
		return tier.path;
	};
	const inside = group(tiers.map((tier) => tier.path));
	const rules: Rule[] = [];
	for (const [index, tier] of tiers.entries()) {
		// Declaration order is precedence: a file that matches two tiers belongs to the earlier one,
		// so declare the specific tier before the general one it sits inside.
		const earlier = tiers.slice(0, index).map((entry) => entry.path);
		const allowed = [tier.path, ...tier.may.map(tierPath), ...shared.map(tierPath)];
		const named = [...tier.may, ...shared.filter((id) => id !== tier.id)];
		rules.push({
			name: `${tier.id}-imports-only-${named.length ? named.join('-') : 'itself'}`,
			severity: 'error',
			comment: named.length
				? `${tier.label} may import ${named.map((id) => byId.get(id)?.label ?? id).join(', ')} and nothing else.`
				: `${tier.label} imports nothing outside itself.`,
			from: { path: tier.path, pathNot: earlier.length ? group(earlier) : undefined },
			to: { path: inside, pathNot: group(allowed) }
		});
		if (tier.isolated) {
			const { groups, shared: sharedGroups = [] } = tier.isolated;
			rules.push({
				name: `${tier.id}-groups-stay-apart`,
				severity: 'error',
				comment: `One ${tier.label.toLowerCase()} per folder. What two share is passed in, or lives in a named shared one.`,
				from: { path: groups },
				to: {
					path: tier.path,
					pathNot: groups.replace(/\([^)]*\)/, group(['$1', ...sharedGroups]))
				}
			});
		}
		if (tier.doors?.length) {
			rules.push({
				name: `${tier.id}-through-its-doors`,
				severity: 'error',
				comment: `Other tiers reach ${tier.label} only through ${tier.doors.join(', ')}.`,
				from: { pathNot: tier.path },
				to: { path: tier.path, pathNot: group(tier.doors) }
			});
		}
	}
	for (const entry of deny) {
		rules.push({
			name: entry.name,
			severity: 'error',
			comment: entry.comment,
			from: {
				path: entry.from.startsWith('^') ? entry.from : tierPath(entry.from),
				pathNot: entry.fromNot
			},
			to: { path: entry.to }
		});
	}
	return rules;
}

The test that matters: on every agent change from the enforcement lesson, the derived rules reach the same verdict as the handwritten ones. Where the deny-list said nothing, the allow-list is stricter. A route importing the transport port broke no handwritten rule; it breaks the derived one, because nobody said routes may see http.

Leave this step with

Every tier’s permissions written down, and a run on the tree as it is. Expect surprises: an allow-list is a census of the permissions you had granted without noticing.

03 / Doors

Name the entry points, not the files.

A permission says who may look at a tier. It does not say where. “Routes may import server” allowed the handler in the enforcement lesson to import a Supabase adapter, because the adapter lives inside server. The handwritten fix forbade the adapter’s folder by name. That rule is defeated by the next barrel file that re-exports it.

Doors say it the other way round: the only paths other tiers may reach in this tier. The server tier’s door is its root. Add an adapter, add a barrel, move a file; the door is still the door. Two more fields do similar work. An isolated tier is split into groups that may not import each other, which is how one feature stays one feature. And shared names the tiers everyone may import, so the permission is stated once instead of on every row.

Declaration order matters here. A file that matches two tier patterns belongs to the earlier one, so declare the specific tier before the general one it sits inside. The animation studio on this site lives in the design-system folder; it is declared first, so its files’ own imports are judged as studio, not design.

Order decides only which tier a file’s imports are judged under. It does not narrow who may import the file: the design tier’s pattern still covers the studio’s folder, so any tier allowed to import design can reach the studio’s files too. To keep a nested tier private, exclude its folder from the general tier’s pattern as well.

frontend.ts
import type { Architecture } from './architecture';

// The enforcement lesson's miniature frontend, declared once.
export const frontend: Architecture = {
	name: 'Miniature frontend',
	tiers: [
		{ id: 'routes', label: 'Routes', path: '^routes/', may: ['features', 'components', 'app'] },
		{
			id: 'features',
			label: 'Features',
			path: '^features/',
			may: ['components', 'display', 'services'],
			isolated: { groups: '^features/([^/]+)/' }
		},
		{ id: 'components', label: 'Components', path: '^components/chrome/', may: ['display'] },
		{ id: 'display', label: 'Display', path: '^components/display/', may: [] },
		{ id: 'app', label: 'App', path: '^app/', may: ['services', 'http'] },
		{ id: 'services', label: 'Services', path: '^services/', may: ['http'] },
		{ id: 'http', label: 'Http', path: '^http/', may: [] }
	],
	deny: [
		{
			name: 'display-primitives-stay-portable',
			comment: 'A primitive is props in, markup out. No app state, no navigation, no content.',
			from: 'display',
			to: '^\\$app/'
		}
	]
};
Leave this step with

Doors on any tier that has internals, isolation on any tier that holds many small things, and a shared list short enough to read aloud.

04 / Derive

Generate the diagram, the doc, and the check. Never edit them.

Three more functions read the same declaration. One draws the diagram: a node per tier in declaration order, an arrow per permission. One prints the doc table an agent will read. One writes the real tool’s configuration, with every path prefixed for wherever the cruise starts, and turns the dated exceptions into the tool’s known-violations baseline.

The three derivationsDiagram, doc, and checker
architecture.ts
/** The diagram: one node per tier, one arrow per permission, top to bottom in declaration order. */
export function toDiagram(architecture: Architecture) {
	const { tiers, shared = [] } = architecture;
	const nodes = tiers.map((tier, index) => ({
		id: tier.id,
		label: tier.label,
		kind: shared.includes(tier.id)
			? 'shared'
			: tier.doors
				? `doors: ${tier.doors.length}`
				: undefined,
		x: 130 + (index % 2) * 240,
		y: 56 + Math.floor(index / 2) * 120
	}));
	const edges = tiers.flatMap((tier) =>
		[...tier.may, ...shared.filter((id) => id !== tier.id && !tier.may.includes(id))].map((to) => ({
			id: `${tier.id}→${to}`,
			from: tier.id,
			to,
			label: shared.includes(to) && !tier.may.includes(to) ? 'shared' : undefined
		}))
	);
	return { nodes, edges };
}

/** The doc table, as Markdown, so the paragraph is generated too. */
export function toDoc(architecture: Architecture): string {
	const { tiers, shared = [], deny = [], exceptions = [] } = architecture;
	const lines = [
		`# ${architecture.name}`,
		'',
		'| Tier | May import | Doors |',
		'| --- | --- | --- |',
		...tiers.map(
			(tier) =>
				`| ${tier.label} | ${[...tier.may, ...shared.filter((id) => id !== tier.id)].join(', ') || 'nothing'} | ${tier.doors?.join(', ') ?? 'any file'} |`
		)
	];
	if (deny.length) lines.push('', ...deny.map((entry) => `- **${entry.name}**: ${entry.comment}`));
	if (exceptions.length)
		lines.push(
			'',
			'Tolerated until the date shown:',
			...exceptions.map((e) => `- ${e.from} → ${e.to} (${e.rule}, until ${e.until}): ${e.reason}`)
		);
	return lines.join('\n') + '\n';
}
architecture.ts
/** The real tool's configuration, with every path prefixed for where the cruise starts. */
export function toCruiserConfig(architecture: Architecture, prefix = '') {
	const fix = (pattern?: string) =>
		pattern === undefined ? undefined : pattern.replaceAll('^', `^${prefix}`);
	return {
		forbidden: toRules(architecture).map((rule) => ({
			name: rule.name,
			severity: rule.severity,
			comment: rule.comment,
			from: { path: fix(rule.from.path), pathNot: fix(rule.from.pathNot) },
			to: { path: fix(rule.to.path), pathNot: fix(rule.to.pathNot) }
		}))
	};
}

/** The tool's known-violations baseline, derived from the dated exceptions. */
export function toKnownViolations(architecture: Architecture, prefix = '') {
	return (architecture.exceptions ?? []).map((exception) => ({
		type: 'dependency',
		from: `${prefix}${exception.from}`,
		to: `${prefix}${exception.to}`,
		rule: { severity: 'error', name: exception.rule }
	}));
}

/** Exceptions past their date are findings again. */
export function expiredExceptions(architecture: Architecture, today: string): Exception[] {
	return (architecture.exceptions ?? []).filter((exception) => exception.until < today);
}

A script writes the generated files next to the declaration, and a test compares each committed file with a fresh derivation. Edit a declaration without regenerating and the test fails. Edit a generated file by hand and the test fails. That is the whole discipline: exactly one thing is a source.

generated/backend.dependency-cruiser.cjs
// GENERATED by generate.ts from the declaration next to it. Do not edit.
module.exports = {
	"forbidden": [
		{
			"name": "routes-imports-only-server-services-kernel",
			"severity": "error",
			"comment": "Routes may import Server, Services, Kernel and nothing else.",
			"from": {
				"path": "^repo/routes/"
			},
			"to": {
				"path": "(^repo/routes/|^repo/server/|^repo/root/|^repo/services/|^repo/http/|^repo/kernel/)",
				"pathNot": "(^repo/routes/|^repo/server/|^repo/services/|^repo/kernel/)"
			}
		},
		{
			"name": "server-imports-only-root-services-kernel",
			"severity": "error",
			"comment": "Server may import Root, Services, Kernel and nothing else.",
			"from": {
				"path": "^repo/server/",
				"pathNot": "(^repo/routes/)"
			},
			"to": {
				"path": "(^repo/routes/|^repo/server/|^repo/root/|^repo/services/|^repo/http/|^repo/kernel/)",
				"pathNot": "(^repo/server/|^repo/root/|^repo/services/|^repo/kernel/)"
			}
		},
		{
			"name": "server-through-its-doors",
			"severity": "error",
			"comment": "Other tiers reach Server only through ^server/root\\.ts$.",
			"from": {
				"pathNot": "^repo/server/"
			},
			"to": {
				"path": "^repo/server/",
				"pathNot": "(^repo/server/root\\.ts$)"
			}
		},
		{
			"name": "root-imports-only-services-kernel",
			"severity": "error",
			"comment": "Root may import Services, Kernel and nothing else.",
			"from": {
				"path": "^repo/root/",
				"pathNot": "(^repo/routes/|^repo/server/)"
			},
			"to": {
				"path": "(^repo/routes/|^repo/server/|^repo/root/|^repo/services/|^repo/http/|^repo/kernel/)",
				"pathNot": "(^repo/root/|^repo/services/|^repo/kernel/)"
			}
		},
		{
			"name": "services-imports-only-http-kernel",
			"severity": "error",
			"comment": "Services may import Http, Kernel and nothing else.",
			"from": {
				"path": "^repo/services/",
				"pathNot": "(^repo/routes/|^repo/server/|^repo/root/)"
			},
			"to": {
				"path": "(^repo/routes/|^repo/server/|^repo/root/|^repo/services/|^repo/http/|^repo/kernel/)",
				"pathNot": "(^repo/services/|^repo/http/|^repo/kernel/)"
			}
		},
		{
			"name": "http-imports-only-kernel",
			"severity": "error",
			"comment": "Http may import Kernel and nothing else.",
			"from": {
				"path": "^repo/http/",
				"pathNot": "(^repo/routes/|^repo/server/|^repo/root/|^repo/services/)"
			},
			"to": {
				"path": "(^repo/routes/|^repo/server/|^repo/root/|^repo/services/|^repo/http/|^repo/kernel/)",
				"pathNot": "(^repo/http/|^repo/kernel/)"
			}
		},
		{
			"name": "kernel-imports-only-itself",
			"severity": "error",
			"comment": "Kernel imports nothing outside itself.",
			"from": {
				"path": "^repo/kernel/",
				"pathNot": "(^repo/routes/|^repo/server/|^repo/root/|^repo/services/|^repo/http/)"
			},
			"to": {
				"path": "(^repo/routes/|^repo/server/|^repo/root/|^repo/services/|^repo/http/|^repo/kernel/)",
				"pathNot": "(^repo/kernel/|^repo/kernel/)"
			}
		},
		{
			"name": "memory-adapters-are-for-root-and-tests",
			"severity": "error",
			"comment": "Memory mode is chosen at the root or in a test, never by a handler or a service.",
			"from": {
				"path": "^repo/(?!root/)",
				"pathNot": "\\.spec\\.ts$"
			},
			"to": {
				"path": "\\.memory\\.ts$"
			}
		}
	],
	"options": {
		"tsPreCompilationDeps": true,
		"exclude": {
			"path": "node_modules|\\.(spec|test)\\.ts$"
		},
		"doNotFollow": {
			"path": "node_modules"
		}
	}
};
generated/backend.md
# Miniature backend-for-frontend

| Tier | May import | Doors |
| --- | --- | --- |
| Routes | server, services, kernel | any file |
| Server | root, services, kernel | ^server/root\.ts$ |
| Root | services, kernel | any file |
| Services | http, kernel | any file |
| Http | kernel | any file |
| Kernel | nothing | any file |

- **memory-adapters-are-for-root-and-tests**: Memory mode is chosen at the root or in a test, never by a handler or a service.

The generated configuration, run by the real tool against the same agent change the enforcement lesson caught. The rule has a different name and the same verdict, on both surfaces.

miniature backend, generated config, after the agent’s change · 12 Sep 2026
$ npx dependency-cruiser --config generated/backend.dependency-cruiser.cjs --output-type err-long repo

  error server-through-its-doors: repo/routes/api/notes/+server.ts → repo/server/supabase/notes.ts
    Other tiers reach Server only through ^server/root\.ts$.

  error server-through-its-doors: repo/routes/api/notes/+server.ts → repo/server/supabase/client.ts
    Other tiers reach Server only through ^server/root\.ts$.


x 2 dependency violations (2 errors, 0 warnings). 10 modules, 17 dependencies cruised.
$ echo $?
2
miniature frontend, generated config, after the agent’s change · 12 Sep 2026
$ npx dependency-cruiser --config generated/frontend.dependency-cruiser.cjs --output-type err-long 'frontend-repo/**/*.svelte' 'frontend-repo/**/*.ts'

  error components-imports-only-display: frontend-repo/components/chrome/Header.svelte → frontend-repo/features/session/session.svelte.ts
    Components may import Display and nothing else.


x 1 dependency violations (1 errors, 0 warnings). 14 modules, 26 dependencies cruised.
$ echo $?
1

Now change the declaration yourself. The editor holds both miniature repos. Untick a permission and the rules, the diagram, the doc, and the verdict all move at once, because none of them is stored; each is a function of the declaration in front of you.

Edit the declaration

Change one permission. Watch the rules, the picture, and the verdict move together.

The declaration below is the one the lesson shows. Tick or untick a cell to change what a tier may import. Everything on the right is derived from it, live, against the miniature repo and the agent’s change you pick.

Surface
Rows may import columns. A shared column is allowed from every row.
TierRoutesServerRootServicesHttpKernel
Routes
Server
Root
Services
Http
Kernel
Shared by every tier
Verdict
0 dependency violations (0 errors, 0 warnings). 11 modules, 19 dependencies cruised.

The diagram, derived

One node per tier, one arrow per permission, in declaration order. A shared tier is reachable from every node.

View connections as text
  • Routes
  • Server
  • Root
  • Services
  • Http
  • Kernel
  • Routes points to Server
  • Routes points to Services
  • Routes shared Kernel
  • Server points to Root
  • Server points to Services
  • Server shared Kernel
  • Root points to Services
  • Root shared Kernel
  • Services points to Http
  • Services shared Kernel
  • Http shared Kernel
Select a tier to see what it may import.
  1. routes-imports-only-server-services-kernel

    Routes may import Server, Services, Kernel and nothing else.

    from ^routes/ → (^routes/|^server/|^root/|^services/|^http/|^kernel/) not (^routes/|^server/|^services/|^kernel/)
  2. server-imports-only-root-services-kernel

    Server may import Root, Services, Kernel and nothing else.

    from ^server/ not (^routes/) → (^routes/|^server/|^root/|^services/|^http/|^kernel/) not (^server/|^root/|^services/|^kernel/)
  3. server-through-its-doors

    Other tiers reach Server only through ^server/root\.ts$.

    from * not ^server/ → ^server/ not (^server/root\.ts$)
  4. root-imports-only-services-kernel

    Root may import Services, Kernel and nothing else.

    from ^root/ not (^routes/|^server/) → (^routes/|^server/|^root/|^services/|^http/|^kernel/) not (^root/|^services/|^kernel/)
  5. services-imports-only-http-kernel

    Services may import Http, Kernel and nothing else.

    from ^services/ not (^routes/|^server/|^root/) → (^routes/|^server/|^root/|^services/|^http/|^kernel/) not (^services/|^http/|^kernel/)
  6. http-imports-only-kernel

    Http may import Kernel and nothing else.

    from ^http/ not (^routes/|^server/|^root/|^services/) → (^routes/|^server/|^root/|^services/|^http/|^kernel/) not (^http/|^kernel/)
  7. kernel-imports-only-itself

    Kernel imports nothing outside itself.

    from ^kernel/ not (^routes/|^server/|^root/|^services/|^http/) → (^routes/|^server/|^root/|^services/|^http/|^kernel/) not (^kernel/|^kernel/)
  8. memory-adapters-are-for-root-and-tests

    Memory mode is chosen at the root or in a test, never by a handler or a service.

    from ^(?!root/) not \.spec\.ts$ → \.memory\.ts$

The engine, repos, and agent changes are the enforcement lesson’s. Only the declaration is new: it is the single source, and every panel on the right is a function of it.

Leave this step with

A generator, its output committed, and a test that fails when either side is stale. The diagram in the doc is now the diagram the checker enforces.

05 / Census

Run it on the real tree, and fix the declaration, not the tree.

This site, declared the same way: thirteen tiers at first, two shared, two denies. The first run is the honest part. Eighty-two edges. Not eighty-two problems: eighty-two permissions the tree had granted itself that nobody had written down, and a handful of real findings hiding among them.

this site, first declaration · 12 Sep 2026
$ npx dependency-cruiser --config src/lib/content/lessons/architecture-as-rules/examples/generated/site.dependency-cruiser.cjs 'src/**/*.svelte' 'src/**/*.ts'   # first declaration

  error server-imports-only-services-root-http-kernel-config: src/lib/server/root.ts → src/lib/app/return-to.ts
    Server may import Services, Root, Http, Kernel, Config and nothing else.

  error server-imports-only-services-root-http-kernel-config: src/lib/server/highlight.ts → src/lib/components/content/code.ts
    Server may import Services, Root, Http, Kernel, Config and nothing else.

  error pages-imports-only-features-components-content-app-kernel-config: src/routes/foundations/animations/+page.svelte → src/lib/design-system/animations/AnimationGallery.svelte
    Pages may import Features, Components, Content, Browser app, Kernel, Config
    and nothing else.

  error pages-imports-only-features-components-content-app-kernel-config: src/routes/foundations/+page.svelte → src/lib/design-system/visualizations/Visualizations.svelte
    Pages may import Features, Components, Content, Browser app, Kernel, Config
    and nothing else.

  error pages-imports-only-features-components-content-app-kernel-config: src/routes/auth/sign-in/+page.svelte → src/lib/services/session/index.ts
    Pages may import Features, Components, Content, Browser app, Kernel, Config
    and nothing else.

  error pages-imports-only-features-components-content-app-kernel-config: src/routes/animation-studio/+page.svelte → src/lib/design-system/animation-studio/AnimationStudio.svelte
    Pages may import Features, Components, Content, Browser app, Kernel, Config
    and nothing else.

  error pages-imports-only-features-components-content-app-kernel-config: src/routes/account/+page.svelte → src/lib/services/session/index.ts
    Pages may import Features, Components, Content, Browser app, Kernel, Config
    and nothing else.

  error pages-imports-only-features-components-content-app-kernel-config: src/routes/+layout.svelte → src/lib/styles/app.css
    Pages may import Features, Components, Content, Browser app, Kernel, Config
    and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/visitor/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/template-method/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/strategy/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/state-machine/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/singleton/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/registry/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/pub-sub/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/proxy/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/prototype/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/observer/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/object-pool/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/null-object/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/multiton/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/memento/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/mediator/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/lazy-initialization/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/iterator/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/interpreter/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/flyweight/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/facade/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/decorator/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/composite/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/command/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/chain-of-responsibility/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/builder/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/bridge/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/adapter/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/patterns/abstract-factory/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/foundations/+page.server.ts → src/lib/design-system/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/dsa/stack/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/dsa/linked-list/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/dsa/dynamic-array/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/dsa/binary-heap/+page.server.ts → src/lib/components/content/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/auth/sign-out/+server.ts → src/lib/app/return-to.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/auth/sign-in/+page.server.ts → src/lib/app/return-to.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-kernel-config: src/routes/auth/callback/+server.ts → src/lib/app/return-to.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Kernel, Config and nothing else.

  error features-imports-only-components-content-design-services-app-kernel-config: src/lib/features/engagement/tracker.ts → src/lib/http/index.ts
    Features may import Components, Content, Design system & styles, Services,
    Browser app, Kernel, Config and nothing else.

  error features-groups-stay-apart: src/lib/features/architecture-as-rules/types.ts → src/lib/features/enforcement-layer/types.ts
    One features per folder. What two share is passed in, or lives in a named
    shared one.

  error design-imports-only-kernel-config: src/lib/design-system/visualizations/Visualizations.svelte → src/lib/components/visualization/VisualizationFrame.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/visualizations/Visualizations.svelte → src/lib/components/visualization/KnowledgeGraph.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/visualizations/TraversalDemo.svelte → src/lib/components/visualization/VisualizationFrame.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/visualizations/TraversalDemo.svelte → src/lib/components/visualization/TreeDiagram.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/visualizations/TraversalDemo.svelte → src/lib/components/visualization/math.ts
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/visualizations/TraversalDemo.svelte → src/lib/components/navigation/SegmentedControl.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/visualizations/TraversalDemo.svelte → src/lib/components/display/Button.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/visualizations/MemoryDemo.svelte → src/lib/components/visualization/VisualizationFrame.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/visualizations/MemoryDemo.svelte → src/lib/components/visualization/MemoryLayout.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/visualizations/MemoryDemo.svelte → src/lib/components/navigation/SegmentedControl.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/visualizations/GrowthDemo.svelte → src/lib/components/visualization/VisualizationFrame.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/visualizations/GrowthDemo.svelte → src/lib/components/visualization/LineChart.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/visualizations/GrowthDemo.svelte → src/lib/components/navigation/SegmentedControl.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/visualizations/data.ts → src/lib/components/visualization/types.ts
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/visualizations/BenchmarkDemo.svelte → src/lib/components/visualization/VisualizationFrame.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/visualizations/BenchmarkDemo.svelte → src/lib/components/visualization/BarChart.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/visualizations/BenchmarkDemo.svelte → src/lib/components/navigation/SegmentedControl.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/animations/StudyScene.svelte → src/lib/features/stack/HistoryBoard.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/animations/StudyScene.svelte → src/lib/features/observer/ObserverScene.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/animations/studies.ts → src/lib/features/stack/history-film.ts
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/animations/studies.ts → src/lib/features/observer/observer-story.ts
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/animations/studies.ts → src/lib/components/animation/types.ts
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/animations/AnimationGallery.svelte → src/lib/components/layout/Shell.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/animations/AnimationGallery.svelte → src/lib/components/animation/AnimationPlayer.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/animation-studio/studio.ts → src/lib/components/animation/types.ts
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/animation-studio/observer-playground.ts → src/lib/content/lessons/observer/examples/cart.ts
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/animation-studio/observer-playground.ts → src/lib/components/animation/types.ts
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/animation-studio/identity-study.ts → src/lib/components/animation/types.ts
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/animation-studio/AnimationStudio.svelte → src/lib/components/layout/Shell.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/animation-studio/AnimationStudio.svelte → src/lib/components/animation/AnimationPlayer.svelte
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/animation-studio/algorithm-studies.ts → src/lib/content/lessons/levenshtein-distance/examples/locations.ts
    Design system & styles may import Kernel, Config and nothing else.

  error design-imports-only-kernel-config: src/lib/design-system/animation-studio/algorithm-studies.ts → src/lib/components/animation/types.ts
    Design system & styles may import Kernel, Config and nothing else.

  error components-imports-only-design-kernel-config: src/lib/components/chrome/ThemeToggle.svelte → src/lib/features/preferences/preferences.svelte.ts
    Components may import Design system & styles, Kernel, Config and nothing
    else.

  error components-imports-only-design-kernel-config: src/lib/components/chrome/AccountControl.svelte → src/lib/features/session/session.svelte.ts
    Components may import Design system & styles, Kernel, Config and nothing
    else.

  error components-imports-only-design-kernel-config: src/lib/components/catalog/TopicCard.svelte → src/lib/content/catalog/links.ts
    Components may import Design system & styles, Kernel, Config and nothing
    else.

  error components-imports-only-design-kernel-config: src/lib/components/catalog/CatalogPage.svelte → src/lib/features/catalog/CatalogPreview.svelte
    Components may import Design system & styles, Kernel, Config and nothing
    else.


x 82 dependency violations (82 errors, 0 warnings). 1083 modules, 3121 dependencies cruised.
$ echo $?
82

Read a census by rule, not by edge. Thirty-six under route handlers: they import component types for highlighting and a browser helper, so handlers may see components and app. Thirty-two under the design system, and two corrections behind them. Seventeen were the design system’s visualizations importing components, so design may now import components. The other fifteen came from the animation studies, which live in that folder but showcase components and lesson scenes, so the studio became a fourteenth tier, declared before design. One under feature isolation was a file written for this lesson an hour earlier, reaching into the enforcement lesson’s feature folder. The check found it before a reviewer did.

Every fix went into the declaration. Thirteen edges after the first correction, six after the second. The six that remain are the questions, and they are nearly the same six the handwritten deny-list found: two pages that call a service directly, three chrome and catalog components that read a feature’s state, and one the deny-list never mentioned, the server root importing a browser helper. The deny-list’s sixth, a feature importing the catalog’s filter, is gone because the declaration says catalog is shared. That is a decision, written where it can be read.

this site, second declaration · 12 Sep 2026
$ npx dependency-cruiser --config src/lib/content/lessons/architecture-as-rules/examples/generated/site.dependency-cruiser.cjs 'src/**/*.svelte' 'src/**/*.ts'   # second declaration: tiers corrected once
  error server-imports-only-services-root-http-components-kernel-config: src/lib/server/root.ts → src/lib/app/return-to.ts
    Server may import Services, Root, Http, Components, Kernel, Config and
    nothing else.

  error pages-imports-only-features-components-content-design-studio-app-kernel-config: src/routes/auth/sign-in/+page.svelte → src/lib/services/session/index.ts
    Pages may import Features, Components, Content, Design system & styles,
    Animation studio, Browser app, Kernel, Config and nothing else.

  error pages-imports-only-features-components-content-design-studio-app-kernel-config: src/routes/account/+page.svelte → src/lib/services/session/index.ts
    Pages may import Features, Components, Content, Design system & styles,
    Animation studio, Browser app, Kernel, Config and nothing else.

  error handlers-imports-only-server-services-content-root-http-features-components-app-kernel-config: src/routes/foundations/+page.server.ts → src/lib/design-system/examples.ts
    Route handlers may import Server, Services, Content, Root, Http, Features,
    Components, Browser app, Kernel, Config and nothing else.

  error design-imports-only-components-kernel-config: src/lib/design-system/animations/StudyScene.svelte → src/lib/features/stack/HistoryBoard.svelte
    Design system & styles may import Components, Kernel, Config and nothing
    else.

  error design-imports-only-components-kernel-config: src/lib/design-system/animations/StudyScene.svelte → src/lib/features/observer/ObserverScene.svelte
    Design system & styles may import Components, Kernel, Config and nothing
    else.

  error design-imports-only-components-kernel-config: src/lib/design-system/animations/studies.ts → src/lib/features/stack/history-film.ts
    Design system & styles may import Components, Kernel, Config and nothing
    else.

  error design-imports-only-components-kernel-config: src/lib/design-system/animations/studies.ts → src/lib/features/observer/observer-story.ts
    Design system & styles may import Components, Kernel, Config and nothing
    else.

  error design-imports-only-components-kernel-config: src/lib/design-system/animation-studio/observer-playground.ts → src/lib/content/lessons/observer/examples/cart.ts
    Design system & styles may import Components, Kernel, Config and nothing
    else.

  error design-imports-only-components-kernel-config: src/lib/design-system/animation-studio/algorithm-studies.ts → src/lib/content/lessons/levenshtein-distance/examples/locations.ts
    Design system & styles may import Components, Kernel, Config and nothing
    else.

  error components-imports-only-design-content-kernel-config: src/lib/components/chrome/ThemeToggle.svelte → src/lib/features/preferences/preferences.svelte.ts
    Components may import Design system & styles, Content, Kernel, Config and
    nothing else.

  error components-imports-only-design-content-kernel-config: src/lib/components/chrome/AccountControl.svelte → src/lib/features/session/session.svelte.ts
    Components may import Design system & styles, Content, Kernel, Config and
    nothing else.

  error components-imports-only-design-content-kernel-config: src/lib/components/catalog/CatalogPage.svelte → src/lib/features/catalog/CatalogPreview.svelte
    Components may import Design system & styles, Content, Kernel, Config and
    nothing else.


x 13 dependency violations (13 errors, 0 warnings). 1085 modules, 3129 dependencies cruised.

$ echo $?
13
this site, third declaration · 12 Sep 2026
$ npx dependency-cruiser --config src/lib/content/lessons/architecture-as-rules/examples/generated/site.dependency-cruiser.cjs 'src/**/*.svelte' 'src/**/*.ts'   # third declaration

  error server-imports-only-services-root-http-components-kernel-config: src/lib/server/root.ts → src/lib/app/return-to.ts
    Server may import Services, Root, Http, Components, Kernel, Config and
    nothing else.

  error pages-imports-only-features-components-content-design-studio-app-kernel-config: src/routes/auth/sign-in/+page.svelte → src/lib/services/session/index.ts
    Pages may import Features, Components, Content, Design system & styles,
    Animation studio, Browser app, Kernel, Config and nothing else.

  error pages-imports-only-features-components-content-design-studio-app-kernel-config: src/routes/account/+page.svelte → src/lib/services/session/index.ts
    Pages may import Features, Components, Content, Design system & styles,
    Animation studio, Browser app, Kernel, Config and nothing else.

  error components-imports-only-design-content-kernel-config: src/lib/components/chrome/ThemeToggle.svelte → src/lib/features/preferences/preferences.svelte.ts
    Components may import Design system & styles, Content, Kernel, Config and
    nothing else.

  error components-imports-only-design-content-kernel-config: src/lib/components/chrome/AccountControl.svelte → src/lib/features/session/session.svelte.ts
    Components may import Design system & styles, Content, Kernel, Config and
    nothing else.

  error components-imports-only-design-content-kernel-config: src/lib/components/catalog/CatalogPage.svelte → src/lib/features/catalog/CatalogPreview.svelte
    Components may import Design system & styles, Content, Kernel, Config and
    nothing else.


x 6 dependency violations (6 errors, 0 warnings). 1086 modules, 3129 dependencies cruised.
$ echo $?
6

The six become exceptions in the declaration: the edge, the rule, a reason, and a date. The generator turns them into the tool’s baseline, and the run that ignores the baseline is green with six known edges named. A test fails on the day an exception expires, so a tolerated edge becomes a finding again on schedule instead of forever.

This site’s declarationFourteen tiers, six dated exceptions
site.ts
import type { Architecture } from './architecture';

// This site's frontend and platform tiers, declared once. The first version
// of any declaration is wrong somewhere; the baseline run says where, and
// the fix goes here, never into the generated files.
export const site: Architecture = {
	name: 'heyrian.dev',
	tiers: [
		{
			id: 'pages',
			label: 'Pages',
			path: '^src/routes/.*\\+(page|layout)\\.svelte$',
			may: ['features', 'components', 'content', 'design', 'studio', 'app']
		},
		{
			id: 'handlers',
			label: 'Route handlers',
			path: '^src/routes/.*(\\+(page|layout)(\\.server)?\\.ts|\\+server\\.ts)$|^src/hooks\\.server\\.ts$',
			may: [
				'server',
				'services',
				'content',
				'root',
				'http',
				'features',
				'components',
				'app',
				'design'
			]
		},
		{
			id: 'features',
			label: 'Features',
			path: '^src/lib/features/',
			may: ['components', 'content', 'design', 'services', 'app', 'http'],
			isolated: {
				groups: '^src/lib/features/([^/]+)/',
				shared: ['reader-context', 'preferences', 'catalog']
			}
		},
		{
			id: 'content',
			label: 'Content',
			path: '^src/lib/content/',
			may: ['features', 'components', 'design']
		},
		{
			id: 'studio',
			label: 'Animation studio',
			path: '^src/lib/design-system/(animation-studio|animations)/',
			may: ['components', 'content', 'design', 'features']
		},
		{
			id: 'components',
			label: 'Components',
			path: '^src/lib/components/',
			may: ['design', 'content']
		},
		{
			id: 'design',
			label: 'Design system & styles',
			path: '^src/lib/(design-system|styles)/',
			may: ['components']
		},
		{ id: 'app', label: 'Browser app', path: '^src/lib/app/', may: ['services', 'root', 'http'] },
		{
			id: 'server',
			label: 'Server',
			path: '^src/lib/server/',
			may: ['services', 'root', 'http', 'components'],
			doors: ['^src/lib/server/(root|respond|highlight|regions)\\.ts$']
		},
		{ id: 'root', label: 'Root', path: '^src/lib/root/', may: ['services', 'http'] },
		{ id: 'services', label: 'Services', path: '^src/lib/services/', may: ['http'] },
		{ id: 'http', label: 'Http', path: '^src/lib/http/', may: [] },
		{ id: 'kernel', label: 'Kernel', path: '^src/lib/kernel/', may: [] },
		{ id: 'config', label: 'Config', path: '^src/lib/config/', may: ['content'] }
	],
	shared: ['kernel', 'config'],
	deny: [
		{
			name: 'display-primitives-stay-portable',
			comment: 'A primitive is props in, markup out. No app state, no navigation, no content.',
			from: '^src/lib/components/display/',
			to: '^src/lib/(features|content|server|services|app)/|^\\$app/'
		},
		{
			name: 'browser-never-imports-supabase',
			comment:
				'The browser never holds a key. Only server code may import the Supabase client or adapters.',
			from: '^(?!src/lib/server/)',
			fromNot: '\\+server\\.ts$|\\+(page|layout)\\.server\\.ts$|^src/hooks\\.server',
			to: 'node_modules/@supabase/|^src/lib/server/supabase/'
		}
	],
	exceptions: [
		{
			rule: 'pages-imports-only-features-components-content-design-studio-app-kernel-config',
			from: 'src/routes/auth/sign-in/+page.svelte',
			to: 'src/lib/services/session/index.ts',
			reason:
				'The page calls the session service’s browser half directly. Decide whether pages may, or route it through the browser root.',
			until: '2026-10-15'
		},
		{
			rule: 'pages-imports-only-features-components-content-design-studio-app-kernel-config',
			from: 'src/routes/account/+page.svelte',
			to: 'src/lib/services/session/index.ts',
			reason: 'Same question as sign-in.',
			until: '2026-10-15'
		},
		{
			rule: 'components-imports-only-design-content-kernel-config',
			from: 'src/lib/components/chrome/ThemeToggle.svelte',
			to: 'src/lib/features/preferences/preferences.svelte.ts',
			reason:
				'Chrome reads the preferences feature. Either preferences become shared state chrome may read, or the layout passes the theme down.',
			until: '2026-10-15'
		},
		{
			rule: 'components-imports-only-design-content-kernel-config',
			from: 'src/lib/components/chrome/AccountControl.svelte',
			to: 'src/lib/features/session/session.svelte.ts',
			reason: 'Chrome reads the session feature. Same decision as the theme toggle.',
			until: '2026-10-15'
		},
		{
			rule: 'components-imports-only-design-content-kernel-config',
			from: 'src/lib/components/catalog/CatalogPage.svelte',
			to: 'src/lib/features/catalog/CatalogPreview.svelte',
			reason:
				'A shared page component renders a feature’s preview. Either the preview moves below the line or catalog is declared a component.',
			until: '2026-10-15'
		},
		{
			rule: 'server-imports-only-services-root-http-components-kernel-config',
			from: 'src/lib/server/root.ts',
			to: 'src/lib/app/return-to.ts',
			reason:
				'A return-to helper is shared by the server and the browser app. It belongs in kernel or http.',
			until: '2026-10-15'
		}
	]
};
generated/site.known-violations.json
[
	{
		"type": "dependency",
		"from": "src/routes/auth/sign-in/+page.svelte",
		"to": "src/lib/services/session/index.ts",
		"rule": {
			"severity": "error",
			"name": "pages-imports-only-features-components-content-design-studio-app-kernel-config"
		}
	},
	{
		"type": "dependency",
		"from": "src/routes/account/+page.svelte",
		"to": "src/lib/services/session/index.ts",
		"rule": {
			"severity": "error",
			"name": "pages-imports-only-features-components-content-design-studio-app-kernel-config"
		}
	},
	{
		"type": "dependency",
		"from": "src/lib/components/chrome/ThemeToggle.svelte",
		"to": "src/lib/features/preferences/preferences.svelte.ts",
		"rule": {
			"severity": "error",
			"name": "components-imports-only-design-content-kernel-config"
		}
	},
	{
		"type": "dependency",
		"from": "src/lib/components/chrome/AccountControl.svelte",
		"to": "src/lib/features/session/session.svelte.ts",
		"rule": {
			"severity": "error",
			"name": "components-imports-only-design-content-kernel-config"
		}
	},
	{
		"type": "dependency",
		"from": "src/lib/components/catalog/CatalogPage.svelte",
		"to": "src/lib/features/catalog/CatalogPreview.svelte",
		"rule": {
			"severity": "error",
			"name": "components-imports-only-design-content-kernel-config"
		}
	},
	{
		"type": "dependency",
		"from": "src/lib/server/root.ts",
		"to": "src/lib/app/return-to.ts",
		"rule": {
			"severity": "error",
			"name": "server-imports-only-services-root-http-components-kernel-config"
		}
	}
]
this site, exceptions declared · 12 Sep 2026
$ node --experimental-strip-types generate.ts   # writes site.known-violations.json from the dated exceptions
$ npx dependency-cruiser --config src/lib/content/lessons/architecture-as-rules/examples/generated/site.dependency-cruiser.cjs 'src/**/*.svelte' 'src/**/*.ts' --ignore-known src/lib/content/lessons/architecture-as-rules/examples/generated/site.known-violations.json

✔ no dependency violations found (1086 modules, 3129 dependencies cruised)
‼ 6 known violations ignored. Run with --no-ignore-known to see them.
$ echo $?
0

Know what that declaration is on this site today. It is this lesson’s artifact, recorded on 12 September 2026, and it is not the site’s gate. The site’s CI runs yarn test:architecture and yarn check:architecture: a handwritten dependency-cruiser configuration that covers the platform tiers only. Nothing runs the generated one. On 23 September 2026 the same command on the same globs reported 78 errors over 3,943 modules, 72 of them outside the six known exceptions. Most (53) are route handlers importing server modules written after the doors were declared, such as the paywall and the lesson-example loader. A declaration that nothing runs drifts like any other document, which is the case for the last line of the checklist below.

Before you call it declared

  • Three views, one source. The diagram, the doc table, and the checker config are all generated, and a test compares each with a fresh derivation.
  • Allow, not deny. Every tier has an allow-list. Denies are for sentences that cut across tiers by file name or by package.
  • Doors where there are internals. A tier with adapters, helpers, or a barrel names its entry points; nothing forbids a file by name.
  • Exceptions expire. Every tolerated edge carries a reason and a date, and the date is enforced.
  • It runs. The generated check is wired into CI, a hook, or the agent’s definition of done. A declaration nothing runs is a document again.
  • The census was read by rule. A rule with dozens of edges is a tier declared wrong; a rule with one or two is a finding.

Know what this establishes. The declaration says which folder may know about which. It does not say the folders are the right ones, or that a permitted import is used well. A tier that may import everything is a declaration of nothing, and an allow-list written to match the tree as it is today merely photographs the drift. The moment to declare is when the picture on the wall still means something.

Field exercise

What belongs in the declaration?

Decide whether the fix is a permission, a door, an exception, or a source of truth.

The diagram was drawn at kickoff. The doc was updated when a tier was renamed. The checker config was edited when a violation was inconvenient. An agent reads the doc.

Your next move
Your declaration note

Leave the decisions next to the data.

Tiers
Each tier’s job in a sentence, and why the arrow points the way it does.
Doors and shared
Which tiers have entry points, and what everyone may import without asking.
Census
The first count, what each large rule turned out to be, and what remained.
Exceptions
Each tolerated edge, its reason, its date, and who decides.
Derived
Which files are generated, the command that regenerates them, and the test.

Put the note in the file the agent reads before it starts, and link the declaration. Once the generated check runs where nobody can skip it, an agent that reads the declaration reads the architecture that is enforced.

Leave this step with

A declaration whose census you have read by rule, the tolerated edges written down as dated exceptions, and the generated check wired where it runs on every change.

Take the idea with you

Explain the declaration without saying “architecture as rules.”

“There is one file that lists our folders, what each may import, and the few places another folder may reach into. The diagram, the doc, and the checker are printed from it, and a test fails if any of them is edited by hand.” That is the technique. The name is what you call it in a review.

Before moving on, explain three things without the name: why an allow-list finds edges a deny-list never will, why a door survives a new adapter where a per-file rule does not, and why a first run of eighty-two edges is a census rather than eighty-two defects.

Connections to follow nextRelated lessons
  • Enforcement layer runs rules like these where nobody can skip them: baseline, catch, wire.
  • Spec before code gives the tests the same treatment: a source of truth written before the code, outside the author.
  • Hexagonal / ports & adapters asks which way the arrows should point inside one application.
  • Layered architecture is the shape most first declarations describe, tiers in a line.
  • Modular monolith is where isolated groups earn their keep: modules that may not reach into each other.

Declare three tiers of a repository you work in, with one door each where it has internals, and run the generated check once. Read the first count by rule before you fix anything.

Back to working with agents →