Reference implementation for the Phoenix Architecture. Work in progress. aicoding.leaflet.pub/
ai coding crazy
at main 122 lines 3.8 kB view raw
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}