Reference implementation for the Phoenix Architecture. Work in progress.
aicoding.leaflet.pub/
ai
coding
crazy
1/**
2 * Pace Layer & Conservation Layer model.
3 *
4 * Different parts of a system change at different speeds.
5 * A layer's rate of change is a function of its blast radius.
6 * Conservation layers are surfaces where external trust accumulates.
7 *
8 * (See: Fowler, The Phoenix Architecture, Chapters 6 & 15)
9 */
10
11/**
12 * Pace layer classification — slowest to fastest.
13 *
14 * Foundation: correctness is load-bearing (billing, fulfillment). Changes yearly.
15 * Domain: event schemas, domain models. Changes quarterly.
16 * Service: API shapes, integration contracts. Changes monthly.
17 * Surface: UI, banners, display logic. Changes days-to-weeks.
18 */
19export type PaceLayer = 'foundation' | 'domain' | 'service' | 'surface';
20
21/**
22 * Extended IU metadata for pace-layer-aware regeneration.
23 */
24export interface PaceLayerMetadata {
25 /** Which pace layer this IU occupies */
26 pace_layer: PaceLayer;
27 /** Is this a conservation layer? (external trust depends on stability) */
28 conservation: boolean;
29 /** Why this classification was chosen */
30 classification_rationale: string;
31 /** How many other IUs depend on this one's interface */
32 dependency_weight: number;
33 /** Expected change cadence */
34 expected_change_cadence: 'daily' | 'weekly' | 'monthly' | 'quarterly' | 'yearly';
35 /** Last classification review date */
36 last_reviewed: string;
37}
38
39/**
40 * Pace layer violation diagnostic
41 */
42export interface PaceLayerViolation {
43 iu_id: string;
44 iu_name: string;
45 current_layer: PaceLayer;
46 violation_type: 'regen_too_fast' | 'dependency_crosses_layer' | 'conservation_unprotected';
47 message: string;
48 recommended_action: string;
49}
50
51/**
52 * Default pace layer metadata for an IU
53 */
54export function defaultPaceLayerMetadata(): PaceLayerMetadata {
55 return {
56 pace_layer: 'service',
57 conservation: false,
58 classification_rationale: 'Default classification — needs review',
59 dependency_weight: 0,
60 expected_change_cadence: 'monthly',
61 last_reviewed: new Date().toISOString(),
62 };
63}
64
65/**
66 * Infer pace layer from dependency weight heuristic.
67 * High dependency weight → slower layer.
68 */
69export function inferPaceLayer(dependencyWeight: number, hasExternalDependents: boolean): PaceLayer {
70 if (hasExternalDependents || dependencyWeight >= 5) return 'foundation';
71 if (dependencyWeight >= 3) return 'domain';
72 if (dependencyWeight >= 1) return 'service';
73 return 'surface';
74}
75
76/**
77 * Check if a regeneration speed is appropriate for a pace layer.
78 */
79export function isPaceAppropriate(
80 layer: PaceLayer,
81 daysSinceLastRegen: number,
82): boolean {
83 const minimums: Record<PaceLayer, number> = {
84 surface: 1, // can regen daily
85 service: 7, // at most weekly
86 domain: 30, // at most monthly
87 foundation: 90, // at most quarterly
88 };
89 return daysSinceLastRegen >= minimums[layer];
90}
91
92/**
93 * Layer ordering for comparison (lower = slower = more stable)
94 */
95const LAYER_ORDER: Record<PaceLayer, number> = {
96 foundation: 0,
97 domain: 1,
98 service: 2,
99 surface: 3,
100};
101
102/**
103 * Check if a dependency crosses pace layers in the wrong direction
104 * (fast layer depending on slow layer is fine; slow layer depending on fast layer is a violation)
105 */
106export function detectLayerCrossing(
107 sourceLayer: PaceLayer,
108 targetLayer: PaceLayer,
109): PaceLayerViolation | null {
110 if (LAYER_ORDER[sourceLayer] < LAYER_ORDER[targetLayer]) {
111 return {
112 iu_id: '',
113 iu_name: '',
114 current_layer: sourceLayer,
115 violation_type: 'dependency_crosses_layer',
116 message: `Slow layer (${sourceLayer}) depends on fast layer (${targetLayer}). ` +
117 `This couples slow-changing logic to fast-changing implementation.`,
118 recommended_action: `Introduce an interface boundary between the ${sourceLayer} and ${targetLayer} layers.`,
119 };
120 }
121 return null;
122}