Reference implementation for the Phoenix Architecture. Work in progress. aicoding.leaflet.pub/
ai coding crazy

feat: add phoenix-self example — Phoenix specs itself (dog-food)

Decompose PRD into 6 focused specs: ingestion, canonicalization,
implementation, integrity, operations, platform. Each has full
implementation stubs with Phoenix metadata, server, and tests.

Pipeline eval on its own PRD: recall 97%, typeAcc 86%, coverage 100%,
D-rate 8%, hierarchy 99%. Composite score 0.9445 across 18 total specs.
All 52 example tests + 413 root tests pass.

+3978
+1454
examples/phoenix-self/package-lock.json
··· 1 + { 2 + "name": "phoenix-self", 3 + "version": "0.1.0", 4 + "lockfileVersion": 3, 5 + "requires": true, 6 + "packages": { 7 + "": { 8 + "name": "phoenix-self", 9 + "version": "0.1.0", 10 + "devDependencies": { 11 + "@types/node": "^22.0.0", 12 + "typescript": "^5.4.0", 13 + "vitest": "^2.0.0" 14 + } 15 + }, 16 + "node_modules/@esbuild/aix-ppc64": { 17 + "version": "0.21.5", 18 + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", 19 + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", 20 + "cpu": [ 21 + "ppc64" 22 + ], 23 + "dev": true, 24 + "license": "MIT", 25 + "optional": true, 26 + "os": [ 27 + "aix" 28 + ], 29 + "engines": { 30 + "node": ">=12" 31 + } 32 + }, 33 + "node_modules/@esbuild/android-arm": { 34 + "version": "0.21.5", 35 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", 36 + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", 37 + "cpu": [ 38 + "arm" 39 + ], 40 + "dev": true, 41 + "license": "MIT", 42 + "optional": true, 43 + "os": [ 44 + "android" 45 + ], 46 + "engines": { 47 + "node": ">=12" 48 + } 49 + }, 50 + "node_modules/@esbuild/android-arm64": { 51 + "version": "0.21.5", 52 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", 53 + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", 54 + "cpu": [ 55 + "arm64" 56 + ], 57 + "dev": true, 58 + "license": "MIT", 59 + "optional": true, 60 + "os": [ 61 + "android" 62 + ], 63 + "engines": { 64 + "node": ">=12" 65 + } 66 + }, 67 + "node_modules/@esbuild/android-x64": { 68 + "version": "0.21.5", 69 + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", 70 + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", 71 + "cpu": [ 72 + "x64" 73 + ], 74 + "dev": true, 75 + "license": "MIT", 76 + "optional": true, 77 + "os": [ 78 + "android" 79 + ], 80 + "engines": { 81 + "node": ">=12" 82 + } 83 + }, 84 + "node_modules/@esbuild/darwin-arm64": { 85 + "version": "0.21.5", 86 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", 87 + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", 88 + "cpu": [ 89 + "arm64" 90 + ], 91 + "dev": true, 92 + "license": "MIT", 93 + "optional": true, 94 + "os": [ 95 + "darwin" 96 + ], 97 + "engines": { 98 + "node": ">=12" 99 + } 100 + }, 101 + "node_modules/@esbuild/darwin-x64": { 102 + "version": "0.21.5", 103 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", 104 + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", 105 + "cpu": [ 106 + "x64" 107 + ], 108 + "dev": true, 109 + "license": "MIT", 110 + "optional": true, 111 + "os": [ 112 + "darwin" 113 + ], 114 + "engines": { 115 + "node": ">=12" 116 + } 117 + }, 118 + "node_modules/@esbuild/freebsd-arm64": { 119 + "version": "0.21.5", 120 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", 121 + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", 122 + "cpu": [ 123 + "arm64" 124 + ], 125 + "dev": true, 126 + "license": "MIT", 127 + "optional": true, 128 + "os": [ 129 + "freebsd" 130 + ], 131 + "engines": { 132 + "node": ">=12" 133 + } 134 + }, 135 + "node_modules/@esbuild/freebsd-x64": { 136 + "version": "0.21.5", 137 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", 138 + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", 139 + "cpu": [ 140 + "x64" 141 + ], 142 + "dev": true, 143 + "license": "MIT", 144 + "optional": true, 145 + "os": [ 146 + "freebsd" 147 + ], 148 + "engines": { 149 + "node": ">=12" 150 + } 151 + }, 152 + "node_modules/@esbuild/linux-arm": { 153 + "version": "0.21.5", 154 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", 155 + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", 156 + "cpu": [ 157 + "arm" 158 + ], 159 + "dev": true, 160 + "license": "MIT", 161 + "optional": true, 162 + "os": [ 163 + "linux" 164 + ], 165 + "engines": { 166 + "node": ">=12" 167 + } 168 + }, 169 + "node_modules/@esbuild/linux-arm64": { 170 + "version": "0.21.5", 171 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", 172 + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", 173 + "cpu": [ 174 + "arm64" 175 + ], 176 + "dev": true, 177 + "license": "MIT", 178 + "optional": true, 179 + "os": [ 180 + "linux" 181 + ], 182 + "engines": { 183 + "node": ">=12" 184 + } 185 + }, 186 + "node_modules/@esbuild/linux-ia32": { 187 + "version": "0.21.5", 188 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", 189 + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", 190 + "cpu": [ 191 + "ia32" 192 + ], 193 + "dev": true, 194 + "license": "MIT", 195 + "optional": true, 196 + "os": [ 197 + "linux" 198 + ], 199 + "engines": { 200 + "node": ">=12" 201 + } 202 + }, 203 + "node_modules/@esbuild/linux-loong64": { 204 + "version": "0.21.5", 205 + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", 206 + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", 207 + "cpu": [ 208 + "loong64" 209 + ], 210 + "dev": true, 211 + "license": "MIT", 212 + "optional": true, 213 + "os": [ 214 + "linux" 215 + ], 216 + "engines": { 217 + "node": ">=12" 218 + } 219 + }, 220 + "node_modules/@esbuild/linux-mips64el": { 221 + "version": "0.21.5", 222 + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", 223 + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", 224 + "cpu": [ 225 + "mips64el" 226 + ], 227 + "dev": true, 228 + "license": "MIT", 229 + "optional": true, 230 + "os": [ 231 + "linux" 232 + ], 233 + "engines": { 234 + "node": ">=12" 235 + } 236 + }, 237 + "node_modules/@esbuild/linux-ppc64": { 238 + "version": "0.21.5", 239 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", 240 + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", 241 + "cpu": [ 242 + "ppc64" 243 + ], 244 + "dev": true, 245 + "license": "MIT", 246 + "optional": true, 247 + "os": [ 248 + "linux" 249 + ], 250 + "engines": { 251 + "node": ">=12" 252 + } 253 + }, 254 + "node_modules/@esbuild/linux-riscv64": { 255 + "version": "0.21.5", 256 + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", 257 + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", 258 + "cpu": [ 259 + "riscv64" 260 + ], 261 + "dev": true, 262 + "license": "MIT", 263 + "optional": true, 264 + "os": [ 265 + "linux" 266 + ], 267 + "engines": { 268 + "node": ">=12" 269 + } 270 + }, 271 + "node_modules/@esbuild/linux-s390x": { 272 + "version": "0.21.5", 273 + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", 274 + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", 275 + "cpu": [ 276 + "s390x" 277 + ], 278 + "dev": true, 279 + "license": "MIT", 280 + "optional": true, 281 + "os": [ 282 + "linux" 283 + ], 284 + "engines": { 285 + "node": ">=12" 286 + } 287 + }, 288 + "node_modules/@esbuild/linux-x64": { 289 + "version": "0.21.5", 290 + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", 291 + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", 292 + "cpu": [ 293 + "x64" 294 + ], 295 + "dev": true, 296 + "license": "MIT", 297 + "optional": true, 298 + "os": [ 299 + "linux" 300 + ], 301 + "engines": { 302 + "node": ">=12" 303 + } 304 + }, 305 + "node_modules/@esbuild/netbsd-x64": { 306 + "version": "0.21.5", 307 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", 308 + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", 309 + "cpu": [ 310 + "x64" 311 + ], 312 + "dev": true, 313 + "license": "MIT", 314 + "optional": true, 315 + "os": [ 316 + "netbsd" 317 + ], 318 + "engines": { 319 + "node": ">=12" 320 + } 321 + }, 322 + "node_modules/@esbuild/openbsd-x64": { 323 + "version": "0.21.5", 324 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", 325 + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", 326 + "cpu": [ 327 + "x64" 328 + ], 329 + "dev": true, 330 + "license": "MIT", 331 + "optional": true, 332 + "os": [ 333 + "openbsd" 334 + ], 335 + "engines": { 336 + "node": ">=12" 337 + } 338 + }, 339 + "node_modules/@esbuild/sunos-x64": { 340 + "version": "0.21.5", 341 + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", 342 + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", 343 + "cpu": [ 344 + "x64" 345 + ], 346 + "dev": true, 347 + "license": "MIT", 348 + "optional": true, 349 + "os": [ 350 + "sunos" 351 + ], 352 + "engines": { 353 + "node": ">=12" 354 + } 355 + }, 356 + "node_modules/@esbuild/win32-arm64": { 357 + "version": "0.21.5", 358 + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", 359 + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", 360 + "cpu": [ 361 + "arm64" 362 + ], 363 + "dev": true, 364 + "license": "MIT", 365 + "optional": true, 366 + "os": [ 367 + "win32" 368 + ], 369 + "engines": { 370 + "node": ">=12" 371 + } 372 + }, 373 + "node_modules/@esbuild/win32-ia32": { 374 + "version": "0.21.5", 375 + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", 376 + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", 377 + "cpu": [ 378 + "ia32" 379 + ], 380 + "dev": true, 381 + "license": "MIT", 382 + "optional": true, 383 + "os": [ 384 + "win32" 385 + ], 386 + "engines": { 387 + "node": ">=12" 388 + } 389 + }, 390 + "node_modules/@esbuild/win32-x64": { 391 + "version": "0.21.5", 392 + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", 393 + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", 394 + "cpu": [ 395 + "x64" 396 + ], 397 + "dev": true, 398 + "license": "MIT", 399 + "optional": true, 400 + "os": [ 401 + "win32" 402 + ], 403 + "engines": { 404 + "node": ">=12" 405 + } 406 + }, 407 + "node_modules/@jridgewell/sourcemap-codec": { 408 + "version": "1.5.5", 409 + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", 410 + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", 411 + "dev": true, 412 + "license": "MIT" 413 + }, 414 + "node_modules/@rollup/rollup-android-arm-eabi": { 415 + "version": "4.60.0", 416 + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.0.tgz", 417 + "integrity": "sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==", 418 + "cpu": [ 419 + "arm" 420 + ], 421 + "dev": true, 422 + "license": "MIT", 423 + "optional": true, 424 + "os": [ 425 + "android" 426 + ] 427 + }, 428 + "node_modules/@rollup/rollup-android-arm64": { 429 + "version": "4.60.0", 430 + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.0.tgz", 431 + "integrity": "sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==", 432 + "cpu": [ 433 + "arm64" 434 + ], 435 + "dev": true, 436 + "license": "MIT", 437 + "optional": true, 438 + "os": [ 439 + "android" 440 + ] 441 + }, 442 + "node_modules/@rollup/rollup-darwin-arm64": { 443 + "version": "4.60.0", 444 + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.0.tgz", 445 + "integrity": "sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==", 446 + "cpu": [ 447 + "arm64" 448 + ], 449 + "dev": true, 450 + "license": "MIT", 451 + "optional": true, 452 + "os": [ 453 + "darwin" 454 + ] 455 + }, 456 + "node_modules/@rollup/rollup-darwin-x64": { 457 + "version": "4.60.0", 458 + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.0.tgz", 459 + "integrity": "sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==", 460 + "cpu": [ 461 + "x64" 462 + ], 463 + "dev": true, 464 + "license": "MIT", 465 + "optional": true, 466 + "os": [ 467 + "darwin" 468 + ] 469 + }, 470 + "node_modules/@rollup/rollup-freebsd-arm64": { 471 + "version": "4.60.0", 472 + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.0.tgz", 473 + "integrity": "sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==", 474 + "cpu": [ 475 + "arm64" 476 + ], 477 + "dev": true, 478 + "license": "MIT", 479 + "optional": true, 480 + "os": [ 481 + "freebsd" 482 + ] 483 + }, 484 + "node_modules/@rollup/rollup-freebsd-x64": { 485 + "version": "4.60.0", 486 + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.0.tgz", 487 + "integrity": "sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==", 488 + "cpu": [ 489 + "x64" 490 + ], 491 + "dev": true, 492 + "license": "MIT", 493 + "optional": true, 494 + "os": [ 495 + "freebsd" 496 + ] 497 + }, 498 + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { 499 + "version": "4.60.0", 500 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.0.tgz", 501 + "integrity": "sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==", 502 + "cpu": [ 503 + "arm" 504 + ], 505 + "dev": true, 506 + "license": "MIT", 507 + "optional": true, 508 + "os": [ 509 + "linux" 510 + ] 511 + }, 512 + "node_modules/@rollup/rollup-linux-arm-musleabihf": { 513 + "version": "4.60.0", 514 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.0.tgz", 515 + "integrity": "sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==", 516 + "cpu": [ 517 + "arm" 518 + ], 519 + "dev": true, 520 + "license": "MIT", 521 + "optional": true, 522 + "os": [ 523 + "linux" 524 + ] 525 + }, 526 + "node_modules/@rollup/rollup-linux-arm64-gnu": { 527 + "version": "4.60.0", 528 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.0.tgz", 529 + "integrity": "sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==", 530 + "cpu": [ 531 + "arm64" 532 + ], 533 + "dev": true, 534 + "license": "MIT", 535 + "optional": true, 536 + "os": [ 537 + "linux" 538 + ] 539 + }, 540 + "node_modules/@rollup/rollup-linux-arm64-musl": { 541 + "version": "4.60.0", 542 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.0.tgz", 543 + "integrity": "sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==", 544 + "cpu": [ 545 + "arm64" 546 + ], 547 + "dev": true, 548 + "license": "MIT", 549 + "optional": true, 550 + "os": [ 551 + "linux" 552 + ] 553 + }, 554 + "node_modules/@rollup/rollup-linux-loong64-gnu": { 555 + "version": "4.60.0", 556 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.0.tgz", 557 + "integrity": "sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==", 558 + "cpu": [ 559 + "loong64" 560 + ], 561 + "dev": true, 562 + "license": "MIT", 563 + "optional": true, 564 + "os": [ 565 + "linux" 566 + ] 567 + }, 568 + "node_modules/@rollup/rollup-linux-loong64-musl": { 569 + "version": "4.60.0", 570 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.0.tgz", 571 + "integrity": "sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==", 572 + "cpu": [ 573 + "loong64" 574 + ], 575 + "dev": true, 576 + "license": "MIT", 577 + "optional": true, 578 + "os": [ 579 + "linux" 580 + ] 581 + }, 582 + "node_modules/@rollup/rollup-linux-ppc64-gnu": { 583 + "version": "4.60.0", 584 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.0.tgz", 585 + "integrity": "sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==", 586 + "cpu": [ 587 + "ppc64" 588 + ], 589 + "dev": true, 590 + "license": "MIT", 591 + "optional": true, 592 + "os": [ 593 + "linux" 594 + ] 595 + }, 596 + "node_modules/@rollup/rollup-linux-ppc64-musl": { 597 + "version": "4.60.0", 598 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.0.tgz", 599 + "integrity": "sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==", 600 + "cpu": [ 601 + "ppc64" 602 + ], 603 + "dev": true, 604 + "license": "MIT", 605 + "optional": true, 606 + "os": [ 607 + "linux" 608 + ] 609 + }, 610 + "node_modules/@rollup/rollup-linux-riscv64-gnu": { 611 + "version": "4.60.0", 612 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.0.tgz", 613 + "integrity": "sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==", 614 + "cpu": [ 615 + "riscv64" 616 + ], 617 + "dev": true, 618 + "license": "MIT", 619 + "optional": true, 620 + "os": [ 621 + "linux" 622 + ] 623 + }, 624 + "node_modules/@rollup/rollup-linux-riscv64-musl": { 625 + "version": "4.60.0", 626 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.0.tgz", 627 + "integrity": "sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==", 628 + "cpu": [ 629 + "riscv64" 630 + ], 631 + "dev": true, 632 + "license": "MIT", 633 + "optional": true, 634 + "os": [ 635 + "linux" 636 + ] 637 + }, 638 + "node_modules/@rollup/rollup-linux-s390x-gnu": { 639 + "version": "4.60.0", 640 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.0.tgz", 641 + "integrity": "sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==", 642 + "cpu": [ 643 + "s390x" 644 + ], 645 + "dev": true, 646 + "license": "MIT", 647 + "optional": true, 648 + "os": [ 649 + "linux" 650 + ] 651 + }, 652 + "node_modules/@rollup/rollup-linux-x64-gnu": { 653 + "version": "4.60.0", 654 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.0.tgz", 655 + "integrity": "sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==", 656 + "cpu": [ 657 + "x64" 658 + ], 659 + "dev": true, 660 + "license": "MIT", 661 + "optional": true, 662 + "os": [ 663 + "linux" 664 + ] 665 + }, 666 + "node_modules/@rollup/rollup-linux-x64-musl": { 667 + "version": "4.60.0", 668 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.0.tgz", 669 + "integrity": "sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==", 670 + "cpu": [ 671 + "x64" 672 + ], 673 + "dev": true, 674 + "license": "MIT", 675 + "optional": true, 676 + "os": [ 677 + "linux" 678 + ] 679 + }, 680 + "node_modules/@rollup/rollup-openbsd-x64": { 681 + "version": "4.60.0", 682 + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.0.tgz", 683 + "integrity": "sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==", 684 + "cpu": [ 685 + "x64" 686 + ], 687 + "dev": true, 688 + "license": "MIT", 689 + "optional": true, 690 + "os": [ 691 + "openbsd" 692 + ] 693 + }, 694 + "node_modules/@rollup/rollup-openharmony-arm64": { 695 + "version": "4.60.0", 696 + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.0.tgz", 697 + "integrity": "sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==", 698 + "cpu": [ 699 + "arm64" 700 + ], 701 + "dev": true, 702 + "license": "MIT", 703 + "optional": true, 704 + "os": [ 705 + "openharmony" 706 + ] 707 + }, 708 + "node_modules/@rollup/rollup-win32-arm64-msvc": { 709 + "version": "4.60.0", 710 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.0.tgz", 711 + "integrity": "sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==", 712 + "cpu": [ 713 + "arm64" 714 + ], 715 + "dev": true, 716 + "license": "MIT", 717 + "optional": true, 718 + "os": [ 719 + "win32" 720 + ] 721 + }, 722 + "node_modules/@rollup/rollup-win32-ia32-msvc": { 723 + "version": "4.60.0", 724 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.0.tgz", 725 + "integrity": "sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==", 726 + "cpu": [ 727 + "ia32" 728 + ], 729 + "dev": true, 730 + "license": "MIT", 731 + "optional": true, 732 + "os": [ 733 + "win32" 734 + ] 735 + }, 736 + "node_modules/@rollup/rollup-win32-x64-gnu": { 737 + "version": "4.60.0", 738 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.0.tgz", 739 + "integrity": "sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==", 740 + "cpu": [ 741 + "x64" 742 + ], 743 + "dev": true, 744 + "license": "MIT", 745 + "optional": true, 746 + "os": [ 747 + "win32" 748 + ] 749 + }, 750 + "node_modules/@rollup/rollup-win32-x64-msvc": { 751 + "version": "4.60.0", 752 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.0.tgz", 753 + "integrity": "sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==", 754 + "cpu": [ 755 + "x64" 756 + ], 757 + "dev": true, 758 + "license": "MIT", 759 + "optional": true, 760 + "os": [ 761 + "win32" 762 + ] 763 + }, 764 + "node_modules/@types/estree": { 765 + "version": "1.0.8", 766 + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", 767 + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", 768 + "dev": true, 769 + "license": "MIT" 770 + }, 771 + "node_modules/@types/node": { 772 + "version": "22.19.15", 773 + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", 774 + "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", 775 + "dev": true, 776 + "license": "MIT", 777 + "dependencies": { 778 + "undici-types": "~6.21.0" 779 + } 780 + }, 781 + "node_modules/@vitest/expect": { 782 + "version": "2.1.9", 783 + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", 784 + "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", 785 + "dev": true, 786 + "license": "MIT", 787 + "dependencies": { 788 + "@vitest/spy": "2.1.9", 789 + "@vitest/utils": "2.1.9", 790 + "chai": "^5.1.2", 791 + "tinyrainbow": "^1.2.0" 792 + }, 793 + "funding": { 794 + "url": "https://opencollective.com/vitest" 795 + } 796 + }, 797 + "node_modules/@vitest/mocker": { 798 + "version": "2.1.9", 799 + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz", 800 + "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", 801 + "dev": true, 802 + "license": "MIT", 803 + "dependencies": { 804 + "@vitest/spy": "2.1.9", 805 + "estree-walker": "^3.0.3", 806 + "magic-string": "^0.30.12" 807 + }, 808 + "funding": { 809 + "url": "https://opencollective.com/vitest" 810 + }, 811 + "peerDependencies": { 812 + "msw": "^2.4.9", 813 + "vite": "^5.0.0" 814 + }, 815 + "peerDependenciesMeta": { 816 + "msw": { 817 + "optional": true 818 + }, 819 + "vite": { 820 + "optional": true 821 + } 822 + } 823 + }, 824 + "node_modules/@vitest/pretty-format": { 825 + "version": "2.1.9", 826 + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", 827 + "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", 828 + "dev": true, 829 + "license": "MIT", 830 + "dependencies": { 831 + "tinyrainbow": "^1.2.0" 832 + }, 833 + "funding": { 834 + "url": "https://opencollective.com/vitest" 835 + } 836 + }, 837 + "node_modules/@vitest/runner": { 838 + "version": "2.1.9", 839 + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", 840 + "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", 841 + "dev": true, 842 + "license": "MIT", 843 + "dependencies": { 844 + "@vitest/utils": "2.1.9", 845 + "pathe": "^1.1.2" 846 + }, 847 + "funding": { 848 + "url": "https://opencollective.com/vitest" 849 + } 850 + }, 851 + "node_modules/@vitest/snapshot": { 852 + "version": "2.1.9", 853 + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", 854 + "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", 855 + "dev": true, 856 + "license": "MIT", 857 + "dependencies": { 858 + "@vitest/pretty-format": "2.1.9", 859 + "magic-string": "^0.30.12", 860 + "pathe": "^1.1.2" 861 + }, 862 + "funding": { 863 + "url": "https://opencollective.com/vitest" 864 + } 865 + }, 866 + "node_modules/@vitest/spy": { 867 + "version": "2.1.9", 868 + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", 869 + "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", 870 + "dev": true, 871 + "license": "MIT", 872 + "dependencies": { 873 + "tinyspy": "^3.0.2" 874 + }, 875 + "funding": { 876 + "url": "https://opencollective.com/vitest" 877 + } 878 + }, 879 + "node_modules/@vitest/utils": { 880 + "version": "2.1.9", 881 + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", 882 + "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", 883 + "dev": true, 884 + "license": "MIT", 885 + "dependencies": { 886 + "@vitest/pretty-format": "2.1.9", 887 + "loupe": "^3.1.2", 888 + "tinyrainbow": "^1.2.0" 889 + }, 890 + "funding": { 891 + "url": "https://opencollective.com/vitest" 892 + } 893 + }, 894 + "node_modules/assertion-error": { 895 + "version": "2.0.1", 896 + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", 897 + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", 898 + "dev": true, 899 + "license": "MIT", 900 + "engines": { 901 + "node": ">=12" 902 + } 903 + }, 904 + "node_modules/cac": { 905 + "version": "6.7.14", 906 + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", 907 + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", 908 + "dev": true, 909 + "license": "MIT", 910 + "engines": { 911 + "node": ">=8" 912 + } 913 + }, 914 + "node_modules/chai": { 915 + "version": "5.3.3", 916 + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", 917 + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", 918 + "dev": true, 919 + "license": "MIT", 920 + "dependencies": { 921 + "assertion-error": "^2.0.1", 922 + "check-error": "^2.1.1", 923 + "deep-eql": "^5.0.1", 924 + "loupe": "^3.1.0", 925 + "pathval": "^2.0.0" 926 + }, 927 + "engines": { 928 + "node": ">=18" 929 + } 930 + }, 931 + "node_modules/check-error": { 932 + "version": "2.1.3", 933 + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", 934 + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", 935 + "dev": true, 936 + "license": "MIT", 937 + "engines": { 938 + "node": ">= 16" 939 + } 940 + }, 941 + "node_modules/debug": { 942 + "version": "4.4.3", 943 + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", 944 + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", 945 + "dev": true, 946 + "license": "MIT", 947 + "dependencies": { 948 + "ms": "^2.1.3" 949 + }, 950 + "engines": { 951 + "node": ">=6.0" 952 + }, 953 + "peerDependenciesMeta": { 954 + "supports-color": { 955 + "optional": true 956 + } 957 + } 958 + }, 959 + "node_modules/deep-eql": { 960 + "version": "5.0.2", 961 + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", 962 + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", 963 + "dev": true, 964 + "license": "MIT", 965 + "engines": { 966 + "node": ">=6" 967 + } 968 + }, 969 + "node_modules/es-module-lexer": { 970 + "version": "1.7.0", 971 + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", 972 + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", 973 + "dev": true, 974 + "license": "MIT" 975 + }, 976 + "node_modules/esbuild": { 977 + "version": "0.21.5", 978 + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", 979 + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", 980 + "dev": true, 981 + "hasInstallScript": true, 982 + "license": "MIT", 983 + "bin": { 984 + "esbuild": "bin/esbuild" 985 + }, 986 + "engines": { 987 + "node": ">=12" 988 + }, 989 + "optionalDependencies": { 990 + "@esbuild/aix-ppc64": "0.21.5", 991 + "@esbuild/android-arm": "0.21.5", 992 + "@esbuild/android-arm64": "0.21.5", 993 + "@esbuild/android-x64": "0.21.5", 994 + "@esbuild/darwin-arm64": "0.21.5", 995 + "@esbuild/darwin-x64": "0.21.5", 996 + "@esbuild/freebsd-arm64": "0.21.5", 997 + "@esbuild/freebsd-x64": "0.21.5", 998 + "@esbuild/linux-arm": "0.21.5", 999 + "@esbuild/linux-arm64": "0.21.5", 1000 + "@esbuild/linux-ia32": "0.21.5", 1001 + "@esbuild/linux-loong64": "0.21.5", 1002 + "@esbuild/linux-mips64el": "0.21.5", 1003 + "@esbuild/linux-ppc64": "0.21.5", 1004 + "@esbuild/linux-riscv64": "0.21.5", 1005 + "@esbuild/linux-s390x": "0.21.5", 1006 + "@esbuild/linux-x64": "0.21.5", 1007 + "@esbuild/netbsd-x64": "0.21.5", 1008 + "@esbuild/openbsd-x64": "0.21.5", 1009 + "@esbuild/sunos-x64": "0.21.5", 1010 + "@esbuild/win32-arm64": "0.21.5", 1011 + "@esbuild/win32-ia32": "0.21.5", 1012 + "@esbuild/win32-x64": "0.21.5" 1013 + } 1014 + }, 1015 + "node_modules/estree-walker": { 1016 + "version": "3.0.3", 1017 + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", 1018 + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", 1019 + "dev": true, 1020 + "license": "MIT", 1021 + "dependencies": { 1022 + "@types/estree": "^1.0.0" 1023 + } 1024 + }, 1025 + "node_modules/expect-type": { 1026 + "version": "1.3.0", 1027 + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", 1028 + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", 1029 + "dev": true, 1030 + "license": "Apache-2.0", 1031 + "engines": { 1032 + "node": ">=12.0.0" 1033 + } 1034 + }, 1035 + "node_modules/fsevents": { 1036 + "version": "2.3.3", 1037 + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 1038 + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 1039 + "dev": true, 1040 + "hasInstallScript": true, 1041 + "license": "MIT", 1042 + "optional": true, 1043 + "os": [ 1044 + "darwin" 1045 + ], 1046 + "engines": { 1047 + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 1048 + } 1049 + }, 1050 + "node_modules/loupe": { 1051 + "version": "3.2.1", 1052 + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", 1053 + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", 1054 + "dev": true, 1055 + "license": "MIT" 1056 + }, 1057 + "node_modules/magic-string": { 1058 + "version": "0.30.21", 1059 + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", 1060 + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", 1061 + "dev": true, 1062 + "license": "MIT", 1063 + "dependencies": { 1064 + "@jridgewell/sourcemap-codec": "^1.5.5" 1065 + } 1066 + }, 1067 + "node_modules/ms": { 1068 + "version": "2.1.3", 1069 + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1070 + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1071 + "dev": true, 1072 + "license": "MIT" 1073 + }, 1074 + "node_modules/nanoid": { 1075 + "version": "3.3.11", 1076 + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", 1077 + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", 1078 + "dev": true, 1079 + "funding": [ 1080 + { 1081 + "type": "github", 1082 + "url": "https://github.com/sponsors/ai" 1083 + } 1084 + ], 1085 + "license": "MIT", 1086 + "bin": { 1087 + "nanoid": "bin/nanoid.cjs" 1088 + }, 1089 + "engines": { 1090 + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 1091 + } 1092 + }, 1093 + "node_modules/pathe": { 1094 + "version": "1.1.2", 1095 + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", 1096 + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", 1097 + "dev": true, 1098 + "license": "MIT" 1099 + }, 1100 + "node_modules/pathval": { 1101 + "version": "2.0.1", 1102 + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", 1103 + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", 1104 + "dev": true, 1105 + "license": "MIT", 1106 + "engines": { 1107 + "node": ">= 14.16" 1108 + } 1109 + }, 1110 + "node_modules/picocolors": { 1111 + "version": "1.1.1", 1112 + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", 1113 + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", 1114 + "dev": true, 1115 + "license": "ISC" 1116 + }, 1117 + "node_modules/postcss": { 1118 + "version": "8.5.8", 1119 + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", 1120 + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", 1121 + "dev": true, 1122 + "funding": [ 1123 + { 1124 + "type": "opencollective", 1125 + "url": "https://opencollective.com/postcss/" 1126 + }, 1127 + { 1128 + "type": "tidelift", 1129 + "url": "https://tidelift.com/funding/github/npm/postcss" 1130 + }, 1131 + { 1132 + "type": "github", 1133 + "url": "https://github.com/sponsors/ai" 1134 + } 1135 + ], 1136 + "license": "MIT", 1137 + "dependencies": { 1138 + "nanoid": "^3.3.11", 1139 + "picocolors": "^1.1.1", 1140 + "source-map-js": "^1.2.1" 1141 + }, 1142 + "engines": { 1143 + "node": "^10 || ^12 || >=14" 1144 + } 1145 + }, 1146 + "node_modules/rollup": { 1147 + "version": "4.60.0", 1148 + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz", 1149 + "integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==", 1150 + "dev": true, 1151 + "license": "MIT", 1152 + "dependencies": { 1153 + "@types/estree": "1.0.8" 1154 + }, 1155 + "bin": { 1156 + "rollup": "dist/bin/rollup" 1157 + }, 1158 + "engines": { 1159 + "node": ">=18.0.0", 1160 + "npm": ">=8.0.0" 1161 + }, 1162 + "optionalDependencies": { 1163 + "@rollup/rollup-android-arm-eabi": "4.60.0", 1164 + "@rollup/rollup-android-arm64": "4.60.0", 1165 + "@rollup/rollup-darwin-arm64": "4.60.0", 1166 + "@rollup/rollup-darwin-x64": "4.60.0", 1167 + "@rollup/rollup-freebsd-arm64": "4.60.0", 1168 + "@rollup/rollup-freebsd-x64": "4.60.0", 1169 + "@rollup/rollup-linux-arm-gnueabihf": "4.60.0", 1170 + "@rollup/rollup-linux-arm-musleabihf": "4.60.0", 1171 + "@rollup/rollup-linux-arm64-gnu": "4.60.0", 1172 + "@rollup/rollup-linux-arm64-musl": "4.60.0", 1173 + "@rollup/rollup-linux-loong64-gnu": "4.60.0", 1174 + "@rollup/rollup-linux-loong64-musl": "4.60.0", 1175 + "@rollup/rollup-linux-ppc64-gnu": "4.60.0", 1176 + "@rollup/rollup-linux-ppc64-musl": "4.60.0", 1177 + "@rollup/rollup-linux-riscv64-gnu": "4.60.0", 1178 + "@rollup/rollup-linux-riscv64-musl": "4.60.0", 1179 + "@rollup/rollup-linux-s390x-gnu": "4.60.0", 1180 + "@rollup/rollup-linux-x64-gnu": "4.60.0", 1181 + "@rollup/rollup-linux-x64-musl": "4.60.0", 1182 + "@rollup/rollup-openbsd-x64": "4.60.0", 1183 + "@rollup/rollup-openharmony-arm64": "4.60.0", 1184 + "@rollup/rollup-win32-arm64-msvc": "4.60.0", 1185 + "@rollup/rollup-win32-ia32-msvc": "4.60.0", 1186 + "@rollup/rollup-win32-x64-gnu": "4.60.0", 1187 + "@rollup/rollup-win32-x64-msvc": "4.60.0", 1188 + "fsevents": "~2.3.2" 1189 + } 1190 + }, 1191 + "node_modules/siginfo": { 1192 + "version": "2.0.0", 1193 + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", 1194 + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", 1195 + "dev": true, 1196 + "license": "ISC" 1197 + }, 1198 + "node_modules/source-map-js": { 1199 + "version": "1.2.1", 1200 + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", 1201 + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", 1202 + "dev": true, 1203 + "license": "BSD-3-Clause", 1204 + "engines": { 1205 + "node": ">=0.10.0" 1206 + } 1207 + }, 1208 + "node_modules/stackback": { 1209 + "version": "0.0.2", 1210 + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", 1211 + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", 1212 + "dev": true, 1213 + "license": "MIT" 1214 + }, 1215 + "node_modules/std-env": { 1216 + "version": "3.10.0", 1217 + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", 1218 + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", 1219 + "dev": true, 1220 + "license": "MIT" 1221 + }, 1222 + "node_modules/tinybench": { 1223 + "version": "2.9.0", 1224 + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", 1225 + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", 1226 + "dev": true, 1227 + "license": "MIT" 1228 + }, 1229 + "node_modules/tinyexec": { 1230 + "version": "0.3.2", 1231 + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", 1232 + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", 1233 + "dev": true, 1234 + "license": "MIT" 1235 + }, 1236 + "node_modules/tinypool": { 1237 + "version": "1.1.1", 1238 + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", 1239 + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", 1240 + "dev": true, 1241 + "license": "MIT", 1242 + "engines": { 1243 + "node": "^18.0.0 || >=20.0.0" 1244 + } 1245 + }, 1246 + "node_modules/tinyrainbow": { 1247 + "version": "1.2.0", 1248 + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", 1249 + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", 1250 + "dev": true, 1251 + "license": "MIT", 1252 + "engines": { 1253 + "node": ">=14.0.0" 1254 + } 1255 + }, 1256 + "node_modules/tinyspy": { 1257 + "version": "3.0.2", 1258 + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", 1259 + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", 1260 + "dev": true, 1261 + "license": "MIT", 1262 + "engines": { 1263 + "node": ">=14.0.0" 1264 + } 1265 + }, 1266 + "node_modules/typescript": { 1267 + "version": "5.9.3", 1268 + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", 1269 + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", 1270 + "dev": true, 1271 + "license": "Apache-2.0", 1272 + "bin": { 1273 + "tsc": "bin/tsc", 1274 + "tsserver": "bin/tsserver" 1275 + }, 1276 + "engines": { 1277 + "node": ">=14.17" 1278 + } 1279 + }, 1280 + "node_modules/undici-types": { 1281 + "version": "6.21.0", 1282 + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", 1283 + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", 1284 + "dev": true, 1285 + "license": "MIT" 1286 + }, 1287 + "node_modules/vite": { 1288 + "version": "5.4.21", 1289 + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", 1290 + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", 1291 + "dev": true, 1292 + "license": "MIT", 1293 + "dependencies": { 1294 + "esbuild": "^0.21.3", 1295 + "postcss": "^8.4.43", 1296 + "rollup": "^4.20.0" 1297 + }, 1298 + "bin": { 1299 + "vite": "bin/vite.js" 1300 + }, 1301 + "engines": { 1302 + "node": "^18.0.0 || >=20.0.0" 1303 + }, 1304 + "funding": { 1305 + "url": "https://github.com/vitejs/vite?sponsor=1" 1306 + }, 1307 + "optionalDependencies": { 1308 + "fsevents": "~2.3.3" 1309 + }, 1310 + "peerDependencies": { 1311 + "@types/node": "^18.0.0 || >=20.0.0", 1312 + "less": "*", 1313 + "lightningcss": "^1.21.0", 1314 + "sass": "*", 1315 + "sass-embedded": "*", 1316 + "stylus": "*", 1317 + "sugarss": "*", 1318 + "terser": "^5.4.0" 1319 + }, 1320 + "peerDependenciesMeta": { 1321 + "@types/node": { 1322 + "optional": true 1323 + }, 1324 + "less": { 1325 + "optional": true 1326 + }, 1327 + "lightningcss": { 1328 + "optional": true 1329 + }, 1330 + "sass": { 1331 + "optional": true 1332 + }, 1333 + "sass-embedded": { 1334 + "optional": true 1335 + }, 1336 + "stylus": { 1337 + "optional": true 1338 + }, 1339 + "sugarss": { 1340 + "optional": true 1341 + }, 1342 + "terser": { 1343 + "optional": true 1344 + } 1345 + } 1346 + }, 1347 + "node_modules/vite-node": { 1348 + "version": "2.1.9", 1349 + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz", 1350 + "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", 1351 + "dev": true, 1352 + "license": "MIT", 1353 + "dependencies": { 1354 + "cac": "^6.7.14", 1355 + "debug": "^4.3.7", 1356 + "es-module-lexer": "^1.5.4", 1357 + "pathe": "^1.1.2", 1358 + "vite": "^5.0.0" 1359 + }, 1360 + "bin": { 1361 + "vite-node": "vite-node.mjs" 1362 + }, 1363 + "engines": { 1364 + "node": "^18.0.0 || >=20.0.0" 1365 + }, 1366 + "funding": { 1367 + "url": "https://opencollective.com/vitest" 1368 + } 1369 + }, 1370 + "node_modules/vitest": { 1371 + "version": "2.1.9", 1372 + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", 1373 + "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", 1374 + "dev": true, 1375 + "license": "MIT", 1376 + "dependencies": { 1377 + "@vitest/expect": "2.1.9", 1378 + "@vitest/mocker": "2.1.9", 1379 + "@vitest/pretty-format": "^2.1.9", 1380 + "@vitest/runner": "2.1.9", 1381 + "@vitest/snapshot": "2.1.9", 1382 + "@vitest/spy": "2.1.9", 1383 + "@vitest/utils": "2.1.9", 1384 + "chai": "^5.1.2", 1385 + "debug": "^4.3.7", 1386 + "expect-type": "^1.1.0", 1387 + "magic-string": "^0.30.12", 1388 + "pathe": "^1.1.2", 1389 + "std-env": "^3.8.0", 1390 + "tinybench": "^2.9.0", 1391 + "tinyexec": "^0.3.1", 1392 + "tinypool": "^1.0.1", 1393 + "tinyrainbow": "^1.2.0", 1394 + "vite": "^5.0.0", 1395 + "vite-node": "2.1.9", 1396 + "why-is-node-running": "^2.3.0" 1397 + }, 1398 + "bin": { 1399 + "vitest": "vitest.mjs" 1400 + }, 1401 + "engines": { 1402 + "node": "^18.0.0 || >=20.0.0" 1403 + }, 1404 + "funding": { 1405 + "url": "https://opencollective.com/vitest" 1406 + }, 1407 + "peerDependencies": { 1408 + "@edge-runtime/vm": "*", 1409 + "@types/node": "^18.0.0 || >=20.0.0", 1410 + "@vitest/browser": "2.1.9", 1411 + "@vitest/ui": "2.1.9", 1412 + "happy-dom": "*", 1413 + "jsdom": "*" 1414 + }, 1415 + "peerDependenciesMeta": { 1416 + "@edge-runtime/vm": { 1417 + "optional": true 1418 + }, 1419 + "@types/node": { 1420 + "optional": true 1421 + }, 1422 + "@vitest/browser": { 1423 + "optional": true 1424 + }, 1425 + "@vitest/ui": { 1426 + "optional": true 1427 + }, 1428 + "happy-dom": { 1429 + "optional": true 1430 + }, 1431 + "jsdom": { 1432 + "optional": true 1433 + } 1434 + } 1435 + }, 1436 + "node_modules/why-is-node-running": { 1437 + "version": "2.3.0", 1438 + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", 1439 + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", 1440 + "dev": true, 1441 + "license": "MIT", 1442 + "dependencies": { 1443 + "siginfo": "^2.0.0", 1444 + "stackback": "0.0.2" 1445 + }, 1446 + "bin": { 1447 + "why-is-node-running": "cli.js" 1448 + }, 1449 + "engines": { 1450 + "node": ">=8" 1451 + } 1452 + } 1453 + } 1454 + }
+24
examples/phoenix-self/package.json
··· 1 + { 2 + "name": "phoenix-self", 3 + "version": "0.1.0", 4 + "description": "Generated by Phoenix VCS — 6 services (dog-food: Phoenix specs itself)", 5 + "type": "module", 6 + "scripts": { 7 + "build": "tsc", 8 + "typecheck": "tsc --noEmit", 9 + "test": "vitest run", 10 + "test:watch": "vitest", 11 + "start:ingestion": "tsc && node dist/generated/ingestion/server.js", 12 + "start:canonicalization": "tsc && node dist/generated/canonicalization/server.js", 13 + "start:implementation": "tsc && node dist/generated/implementation/server.js", 14 + "start:integrity": "tsc && node dist/generated/integrity/server.js", 15 + "start:operations": "tsc && node dist/generated/operations/server.js", 16 + "start:platform": "tsc && node dist/generated/platform/server.js", 17 + "start": "tsc && node dist/generated/platform/server.js" 18 + }, 19 + "devDependencies": { 20 + "typescript": "^5.4.0", 21 + "vitest": "^2.0.0", 22 + "@types/node": "^22.0.0" 23 + } 24 + }
+26
examples/phoenix-self/spec/canonicalization.md
··· 1 + # Canonicalization Pipelines 2 + 3 + Canonicalization transforms extracted clauses into a structured canonical requirement graph. The pipeline is versioned and explicitly tracked. 4 + 5 + ## Pipeline Identity 6 + 7 + - Each canonicalization pipeline must be identified by: canon_pipeline_id, model_id, promptpack_version, extraction_rules_version, and diff_policy_version 8 + - Pipeline upgrades must produce a meta-node of type PipelineUpgrade in the provenance graph 9 + - Pipeline versions must never be silently changed; every upgrade must be explicit and auditable 10 + 11 + ## Canonical Node Extraction 12 + 13 + - The pipeline must extract canonical nodes of five types: REQUIREMENT, CONSTRAINT, INVARIANT, DEFINITION, and CONTEXT 14 + - Each canonical node must have a content-addressed canon_id, a type, a normalized statement, confidence score, source clause IDs, tags, and linked canon IDs 15 + - Extraction must assign a confidence score between 0.0 and 1.0 to each node 16 + - Nodes with no actionable keywords must default to type CONTEXT 17 + - The extraction pipeline must track coverage: the percentage of source sentences that produced canonical nodes 18 + 19 + ## Shadow Canonicalization 20 + 21 + - When upgrading pipelines, the system must run old and new pipelines in parallel (shadow mode) 22 + - Shadow mode must compute diff metrics: node_change_pct, edge_change_pct, risk_escalations, orphan_nodes, out_of_scope_growth, and semantic_stmt_drift 23 + - An upgrade is classified as SAFE when node_change_pct is at most 3%, there are no orphan nodes, and no risk escalations 24 + - An upgrade is classified as COMPACTION EVENT when node_change_pct is at most 25%, no orphan nodes, and limited risk escalations 25 + - An upgrade must be classified as REJECT when orphan nodes exist, churn is excessive, or semantic drift is large 26 + - Shadow canonicalization results must never be applied to the live graph without explicit acceptance
+34
examples/phoenix-self/spec/implementation.md
··· 1 + # Implementation Units & Boundary Policies 2 + 3 + Implementation Units (IUs) are stable compilation boundaries that map canonical requirements to generated code. Boundary policies enforce architectural constraints. 4 + 5 + ## Implementation Unit Structure 6 + 7 + - An Implementation Unit is defined by: iu_id, kind (module or function), risk_tier, contract, dependencies, boundary_policy, impact, and evidence_policy 8 + - IU IDs must be content-addressed and deterministic 9 + - IUs must declare their risk tier: low, medium, high, or critical 10 + - Bots must propose IU changes; humans or policy must accept them 11 + - Each IU must declare its contract: the set of canonical nodes it implements 12 + 13 + ## Boundary Policy Schema 14 + 15 + - Each IU must declare its allowed and forbidden code dependencies: allowed_ius, allowed_packages, forbidden_ius, forbidden_packages, and forbidden_paths 16 + - Each IU must declare its side-channel dependencies: databases, queues, caches, config, external_apis, and files 17 + - Side-channel dependencies must create edges in the invalidation graph 18 + - Boundary policies must be enforced post-generation by the architectural linter 19 + 20 + ## Architectural Linter 21 + 22 + - After code generation, the linter must extract the dependency graph from generated code 23 + - The linter must validate all dependencies against the IU's boundary policy 24 + - Dependency violations must be emitted as diagnostics with configurable severity (error or warning) 25 + - Side-channel violations must be emitted as diagnostics with configurable severity 26 + - The linter must never silently ignore a boundary violation 27 + 28 + ## Regeneration Engine 29 + 30 + - Regeneration must operate at IU granularity: only invalidated IUs are regenerated 31 + - Each regeneration must record: model_id, promptpack hash, toolchain version, and normalization steps 32 + - Generated artifacts must produce a generated_manifest with per-file and per-IU hashes 33 + - The manifest must be stored at .phoenix/generated_manifest 34 + - Regeneration must be reproducible: same inputs must produce semantically equivalent outputs
+37
examples/phoenix-self/spec/ingestion.md
··· 1 + # Spec Ingestion & Change Classification 2 + 3 + The ingestion pipeline transforms raw specification documents into structured, content-addressed clauses and classifies changes between spec versions. 4 + 5 + ## Clause Extraction 6 + 7 + - Each spec document must be parsed into atomic clauses with unique clause_id 8 + - A clause is a content-addressed unit containing: clause_id, source_doc_id, source_line_range, raw_text, normalized_text, and section_path 9 + - Clause IDs must be deterministic: identical content always produces the same ID 10 + - The parser must preserve section hierarchy from markdown headings 11 + - Clauses must never span multiple top-level sections 12 + 13 + ## Semantic Hashing 14 + 15 + - Each clause must have a clause_semhash computed from its normalized text via SHA-256 16 + - Each clause must have a context_semhash_cold computed from normalized text, section path, and neighbor clause hashes 17 + - After canonicalization, a context_semhash_warm must be computed incorporating canonical graph context 18 + - Semantic hashes must be stable: identical input always produces identical hash 19 + - The warm hash must exclude weak 'relates_to' edges to prevent incidental invalidation 20 + 21 + ## Change Classification 22 + 23 + - Every clause change must be classified into exactly one of four classes: A (trivial), B (local semantic), C (contextual shift), or D (uncertain) 24 + - Class A changes include formatting-only modifications where the semantic hash is unchanged 25 + - Class B changes are local semantic modifications with moderate edit distance 26 + - Class C changes affect the canonical graph or structural context 27 + - Class D changes are uncertain and require human or LLM review 28 + - The classifier must use multiple signals: normalized edit distance, semhash delta, context hash delta, term-reference deltas, and section structure deltas 29 + 30 + ## D-Rate Trust Loop 31 + 32 + - The D-rate is defined as the percentage of changes classified as D (uncertain) over a rolling window 33 + - The target D-rate must be at most 5% 34 + - D-rate above 10% is acceptable but triggers a warning 35 + - D-rate above 15% must trigger an alarm requiring classifier tuning 36 + - When the D-rate alarm fires, override friction must increase and PolicyBot must surface a trust degradation warning 37 + - D-rate must always be tracked as a first-class metric
+29
examples/phoenix-self/spec/integrity.md
··· 1 + # Integrity: Drift Detection, Evidence & Cascading Failures 2 + 3 + The integrity subsystem ensures generated code stays consistent with specifications through drift detection, risk-tiered evidence requirements, and explicit failure propagation. 4 + 5 + ## Drift Detection 6 + 7 + - On status, the system must compare the working tree against the generated_manifest 8 + - If a mismatch exists and no waiver is present, the system must emit an ERROR diagnostic 9 + - Unlabeled manual edits must block acceptance of the affected IU 10 + - Manual edits must be labeled as one of: promote_to_requirement, waiver (signed), or temporary_patch (expires) 11 + - Temporary patches must have an expiration date and must trigger a warning when expired 12 + 13 + ## Evidence & Policy Engine 14 + 15 + - Evidence requirements must be tiered by IU risk level 16 + - Low-tier IUs require: typecheck, lint, and boundary validation 17 + - Medium-tier IUs require: unit tests in addition to low-tier evidence 18 + - High-tier IUs require: unit tests, property tests, threat note, and static analysis 19 + - Critical-tier IUs require: human signoff or formal verification / simulation evidence 20 + - Evidence must bind to canonical nodes, IU IDs, and generated artifact hashes 21 + - Evidence bindings must always be traceable: given any evidence artifact, the system must identify which canonical nodes and IUs it covers 22 + 23 + ## Cascading Failure Semantics 24 + 25 + - If evidence for IU-X fails, IU-X must be blocked from acceptance 26 + - When IU-X is blocked, all dependent IUs must re-run: typecheck, boundary checks, and relevant tagged tests 27 + - Failure propagation must be explicit and graph-based: the system must traverse the IU dependency graph 28 + - Cascade depth must be bounded to prevent infinite propagation loops 29 + - The system must report the full cascade chain in diagnostics so developers can trace the root cause
+28
examples/phoenix-self/spec/operations.md
··· 1 + # Operations: Compaction, Diagnostics & Bootstrap 2 + 3 + The operations subsystem manages storage lifecycle, system diagnostics, and the bootstrap initialization flow. 4 + 5 + ## Compaction 6 + 7 + - The system must maintain three storage tiers: Hot Graph (last 30 days), Ancestry Index (forever metadata), and Cold Packs (heavy blobs) 8 + - Compaction must never delete: node headers, provenance edges, approvals, or signatures 9 + - Compaction must be triggered by: size threshold exceeded, pipeline upgrade accepted, or time-based fallback 10 + - Each compaction must produce a CompactionEvent meta-node in the provenance graph 11 + - PolicyBot must announce every compaction event 12 + 13 + ## Diagnostics & Severity Model 14 + 15 + - Every status item must include: severity (error, warning, or info), category, subject, message, and recommended_actions 16 + - Status items must be grouped by severity in display output 17 + - The diagnostics system is the primary user experience surface for Phoenix 18 + - phoenix status must always be explainable, conservative, and correct-enough to rely on 19 + - Diagnostic messages must never be ambiguous about what action the developer should take 20 + 21 + ## Bootstrap Flow 22 + 23 + - The bootstrap command must execute in order: cold pass, canonicalization, warm pass, generate Trust Dashboard, set system state to WARMING 24 + - D-rate alarms must be suppressed during the BOOTSTRAP_COLD phase 25 + - Severity must be downgraded during the BOOTSTRAP_WARMING phase 26 + - After stabilization, the system must transition to STEADY_STATE 27 + - Bootstrap must be resumable: if interrupted, it must pick up from the last completed phase 28 + - The system must never transition to STEADY_STATE until the D-rate is within acceptable bounds
+27
examples/phoenix-self/spec/platform.md
··· 1 + # Platform: Core Graphs, Bot Integration & Brownfield Wrapping 2 + 3 + The platform layer provides the foundational graph data model, bot command interface, and progressive integration strategy for existing codebases. 4 + 5 + ## Core Graph Model 6 + 7 + - Phoenix must maintain five interconnected graphs: Spec Graph (clauses), Canonical Graph (requirements, constraints, invariants, definitions), Implementation Graph (IUs), Evidence Graph (tests, analysis, reviews), and Provenance Graph (all transformation edges and meta-events) 8 + - Everything in Phoenix must be content-addressed and versioned 9 + - Every transformation between graphs must emit provenance edges 10 + - Selective invalidation is the defining capability: changing one spec line must invalidate only the dependent subtree, not the entire repository 11 + - The Provenance Graph must never lose edges: all transformations are append-only 12 + 13 + ## Bot Integration 14 + 15 + - Bots must behave as normal users with no elevated privileges 16 + - Mutating commands must follow the confirmation model: the bot echoes parsed intent, the user replies with ok or phx confirm 17 + - Read-only commands must execute immediately without confirmation 18 + - Each bot must expose help, commands, and version subcommands 19 + - Phoenix must not use fuzzy NLU for command parsing in v1; commands must be explicit and deterministic 20 + - Three core bots must be supported: SpecBot (ingest), ImplBot (regen), and PolicyBot (status) 21 + 22 + ## Brownfield Progressive Wrapping 23 + 24 + - Step 1 (Wrap Module): the system must support defining an IU boundary around an existing module, writing a minimal spec, and enforcing boundary and evidence policies without full regeneration 25 + - Step 2 (Annotate Provenance): the system must support manually mapping functions to requirement IDs and gradually increasing the regeneration surface 26 + - Brownfield wrapping must never require full codebase regeneration to start providing value 27 + - The system must track which IUs are fully regenerated vs boundary-only wrapped
+78
examples/phoenix-self/src/generated/canonicalization/__tests__/canonicalization.test.ts
··· 1 + /** 2 + * Canonicalization — Generated Tests 3 + * 4 + * AUTO-GENERATED by Phoenix VCS 5 + * Tests module structure, server health, and Phoenix traceability. 6 + */ 7 + 8 + import { describe, it, expect, afterAll } from 'vitest'; 9 + import { startServer } from '../server.js'; 10 + 11 + import * as canonPipeline from '../canon-pipeline.js'; 12 + import * as shadowMode from '../shadow-mode.js'; 13 + 14 + describe('Canonicalization modules', () => { 15 + describe('Canon Pipeline', () => { 16 + it('exports Phoenix traceability metadata', () => { 17 + expect(canonPipeline._phoenix).toBeDefined(); 18 + expect(canonPipeline._phoenix.name).toBe('Canon Pipeline'); 19 + expect(canonPipeline._phoenix.risk_tier).toBeTruthy(); 20 + }); 21 + 22 + it('has exported functions', () => { 23 + const exports = Object.keys(canonPipeline).filter(k => k !== '_phoenix'); 24 + expect(exports.length).toBeGreaterThan(0); 25 + }); 26 + }); 27 + 28 + describe('Shadow Mode', () => { 29 + it('exports Phoenix traceability metadata', () => { 30 + expect(shadowMode._phoenix).toBeDefined(); 31 + expect(shadowMode._phoenix.name).toBe('Shadow Mode'); 32 + expect(shadowMode._phoenix.risk_tier).toBeTruthy(); 33 + }); 34 + 35 + it('has exported functions', () => { 36 + const exports = Object.keys(shadowMode).filter(k => k !== '_phoenix'); 37 + expect(exports.length).toBeGreaterThan(0); 38 + }); 39 + }); 40 + 41 + }); 42 + 43 + describe('Canonicalization server', () => { 44 + const instance = startServer(0); // random port 45 + 46 + afterAll(() => new Promise<void>(resolve => instance.server.close(() => resolve()))); 47 + 48 + it('GET /health returns 200', async () => { 49 + await instance.ready; 50 + const res = await fetch(`http://localhost:${instance.port}/health`); 51 + expect(res.status).toBe(200); 52 + const body = await res.json() as Record<string, unknown>; 53 + expect(body.status).toBe('ok'); 54 + expect(body.service).toBe('Canonicalization'); 55 + }); 56 + 57 + it('GET /metrics returns request counts', async () => { 58 + await instance.ready; 59 + const res = await fetch(`http://localhost:${instance.port}/metrics`); 60 + expect(res.status).toBe(200); 61 + const body = await res.json() as Record<string, unknown>; 62 + expect(typeof body.requests_total).toBe('number'); 63 + }); 64 + 65 + it('GET /modules lists all registered modules', async () => { 66 + await instance.ready; 67 + const res = await fetch(`http://localhost:${instance.port}/modules`); 68 + expect(res.status).toBe(200); 69 + const body = await res.json() as Array<Record<string, unknown>>; 70 + expect(body.length).toBe(2); 71 + }); 72 + 73 + it('GET /unknown returns 404', async () => { 74 + await instance.ready; 75 + const res = await fetch(`http://localhost:${instance.port}/unknown`); 76 + expect(res.status).toBe(404); 77 + }); 78 + });
+74
examples/phoenix-self/src/generated/canonicalization/canon-pipeline.ts
··· 1 + export type CanonicalType = 'REQUIREMENT' | 'CONSTRAINT' | 'INVARIANT' | 'DEFINITION' | 'CONTEXT'; 2 + 3 + export interface CanonicalNode { 4 + canon_id: string; 5 + type: CanonicalType; 6 + statement: string; 7 + confidence: number; 8 + source_clause_ids: string[]; 9 + tags: string[]; 10 + } 11 + 12 + export interface PipelineConfig { 13 + canon_pipeline_id: string; 14 + model_id: string; 15 + promptpack_version: string; 16 + extraction_rules_version: string; 17 + diff_policy_version: string; 18 + } 19 + 20 + export interface ExtractionCoverage { 21 + total_sentences: number; 22 + extracted: number; 23 + coverage_pct: number; 24 + } 25 + 26 + export class CanonPipeline { 27 + private config: PipelineConfig; 28 + 29 + constructor(config: PipelineConfig) { 30 + this.config = config; 31 + } 32 + 33 + getConfig(): PipelineConfig { return { ...this.config }; } 34 + 35 + extractNodes(clauseTexts: string[]): { nodes: CanonicalNode[]; coverage: ExtractionCoverage } { 36 + const nodes: CanonicalNode[] = []; 37 + for (const text of clauseTexts) { 38 + const type = this.classifyType(text); 39 + nodes.push({ 40 + canon_id: `canon_${nodes.length}`, 41 + type, 42 + statement: text.trim(), 43 + confidence: type === 'CONTEXT' ? 0.3 : 0.7, 44 + source_clause_ids: [], 45 + tags: this.extractTags(text), 46 + }); 47 + } 48 + return { nodes, coverage: { total_sentences: clauseTexts.length, extracted: nodes.length, coverage_pct: 100 } }; 49 + } 50 + 51 + private classifyType(text: string): CanonicalType { 52 + const lower = text.toLowerCase(); 53 + if (/\b(?:must not|cannot|forbidden|limited to|maximum|minimum)\b/.test(lower)) return 'CONSTRAINT'; 54 + if (/\b(?:always|never|guaranteed|must remain)\b/.test(lower)) return 'INVARIANT'; 55 + if (/\b(?:is defined as|means|refers to)\b/.test(lower)) return 'DEFINITION'; 56 + if (/\b(?:must|shall|required)\b/.test(lower)) return 'REQUIREMENT'; 57 + return 'CONTEXT'; 58 + } 59 + 60 + private extractTags(text: string): string[] { 61 + return text.toLowerCase().split(/\s+/).filter(w => w.length > 3 && !/^(must|shall|the|and|for|with|from|that|this)$/.test(w)).slice(0, 5); 62 + } 63 + } 64 + 65 + export function createCanonPipeline(config: PipelineConfig): CanonPipeline { 66 + return new CanonPipeline(config); 67 + } 68 + 69 + export const _phoenix = { 70 + iu_id: 'c9d0e1f2', 71 + name: 'Canon Pipeline', 72 + risk_tier: 'high', 73 + canon_ids: [5, 6], 74 + } as const;
+2
examples/phoenix-self/src/generated/canonicalization/index.ts
··· 1 + export * as canonPipeline from './canon-pipeline.js'; 2 + export * as shadowMode from './shadow-mode.js';
+121
examples/phoenix-self/src/generated/canonicalization/server.ts
··· 1 + /** 2 + * Canonicalization — HTTP Server 3 + * 4 + * AUTO-GENERATED by Phoenix VCS 5 + * Provides health check, metrics, and module endpoints. 6 + */ 7 + 8 + import { createServer, IncomingMessage, ServerResponse } from 'node:http'; 9 + 10 + import * as canonPipeline from './canon-pipeline.js'; 11 + import * as shadowMode from './shadow-mode.js'; 12 + 13 + // ─── Metrics ───────────────────────────────────────────────────────────────── 14 + 15 + const _svcMetrics = { 16 + requests_total: 0, 17 + requests_by_path: {} as Record<string, number>, 18 + errors_total: 0, 19 + uptime_start: Date.now(), 20 + }; 21 + 22 + // ─── Module Registry ───────────────────────────────────────────────────────── 23 + 24 + const _svcModules = { 25 + 'canon-pipeline': canonPipeline, 26 + 'shadow-mode': shadowMode, 27 + }; 28 + 29 + // ─── Router ────────────────────────────────────────────────────────────────── 30 + 31 + type Handler = (req: IncomingMessage, res: ServerResponse) => void | Promise<void>; 32 + 33 + const routes: Record<string, Handler> = { 34 + '/health': (_req, res) => { 35 + res.writeHead(200, { 'Content-Type': 'application/json' }); 36 + res.end(JSON.stringify({ 37 + status: 'ok', 38 + service: 'Canonicalization', 39 + uptime: Math.floor((Date.now() - _svcMetrics.uptime_start) / 1000), 40 + modules: Object.keys(_svcModules), 41 + })); 42 + }, 43 + 44 + '/metrics': (_req, res) => { 45 + res.writeHead(200, { 'Content-Type': 'application/json' }); 46 + res.end(JSON.stringify({ 47 + ..._svcMetrics, 48 + uptime_seconds: Math.floor((Date.now() - _svcMetrics.uptime_start) / 1000), 49 + }, null, 2)); 50 + }, 51 + 52 + '/modules': (_req, res) => { 53 + const info = Object.entries(_svcModules).map(([name, mod]) => { 54 + const phoenix = (mod as Record<string, unknown>)._phoenix as Record<string, unknown> | undefined; 55 + return { 56 + name, 57 + risk_tier: phoenix?.risk_tier ?? 'unknown', 58 + exports: Object.keys(mod).filter(k => k !== '_phoenix'), 59 + }; 60 + }); 61 + res.writeHead(200, { 'Content-Type': 'application/json' }); 62 + res.end(JSON.stringify(info, null, 2)); 63 + }, 64 + }; 65 + 66 + // ─── Server ────────────────────────────────────────────────────────────────── 67 + 68 + function handleRequest(req: IncomingMessage, res: ServerResponse): void { 69 + const url = req.url ?? '/'; 70 + const path = url.split('?')[0]; 71 + 72 + _svcMetrics.requests_total++; 73 + _svcMetrics.requests_by_path[path] = (_svcMetrics.requests_by_path[path] ?? 0) + 1; 74 + 75 + const handler = routes[path]; 76 + if (handler) { 77 + try { 78 + handler(req, res); 79 + } catch (err) { 80 + _svcMetrics.errors_total++; 81 + res.writeHead(500, { 'Content-Type': 'application/json' }); 82 + res.end(JSON.stringify({ error: String(err) })); 83 + } 84 + } else { 85 + res.writeHead(404, { 'Content-Type': 'application/json' }); 86 + res.end(JSON.stringify({ 87 + error: 'Not Found', 88 + path, 89 + available: Object.keys(routes), 90 + })); 91 + } 92 + } 93 + 94 + export function startServer(port?: number): { server: ReturnType<typeof createServer>; port: number; ready: Promise<void> } { 95 + const requestedPort = port ?? parseInt(process.env.CANONICALIZATION_PORT ?? process.env.PORT ?? '3002', 10); 96 + const server = createServer(handleRequest); 97 + let actualPort = requestedPort; 98 + 99 + const ready = new Promise<void>(resolve => { 100 + server.listen(requestedPort, () => { 101 + const addr = server.address(); 102 + if (addr && typeof addr === 'object') actualPort = addr.port; 103 + result.port = actualPort; 104 + console.log(`Canonicalization listening on http://localhost:${actualPort}`); 105 + console.log(` /health — health check`); 106 + console.log(` /metrics — request metrics`); 107 + console.log(` /modules — registered modules`); 108 + resolve(); 109 + }); 110 + }); 111 + 112 + const result = { server, port: actualPort, ready }; 113 + return result; 114 + } 115 + 116 + // Start when run directly 117 + const isMain = process.argv[1]?.endsWith('/canonicalization/server.js') || 118 + process.argv[1]?.endsWith('/canonicalization/server.ts'); 119 + if (isMain) { 120 + startServer(); 121 + }
+47
examples/phoenix-self/src/generated/canonicalization/shadow-mode.ts
··· 1 + import type { CanonicalNode } from './canon-pipeline.js'; 2 + 3 + export type UpgradeClassification = 'SAFE' | 'COMPACTION_EVENT' | 'REJECT'; 4 + 5 + export interface ShadowDiffMetrics { 6 + node_change_pct: number; 7 + edge_change_pct: number; 8 + risk_escalations: number; 9 + orphan_nodes: number; 10 + out_of_scope_growth: number; 11 + semantic_stmt_drift: number; 12 + } 13 + 14 + export class ShadowPipeline { 15 + comparePipelines(oldNodes: CanonicalNode[], newNodes: CanonicalNode[]): ShadowDiffMetrics { 16 + const oldIds = new Set(oldNodes.map(n => n.canon_id)); 17 + const newIds = new Set(newNodes.map(n => n.canon_id)); 18 + const added = [...newIds].filter(id => !oldIds.has(id)).length; 19 + const removed = [...oldIds].filter(id => !newIds.has(id)).length; 20 + const total = Math.max(oldNodes.length, 1); 21 + return { 22 + node_change_pct: ((added + removed) / total) * 100, 23 + edge_change_pct: 0, 24 + risk_escalations: 0, 25 + orphan_nodes: removed, 26 + out_of_scope_growth: 0, 27 + semantic_stmt_drift: 0, 28 + }; 29 + } 30 + 31 + classifyUpgrade(metrics: ShadowDiffMetrics): UpgradeClassification { 32 + if (metrics.orphan_nodes > 0 || metrics.node_change_pct > 25) return 'REJECT'; 33 + if (metrics.node_change_pct <= 3 && metrics.risk_escalations === 0) return 'SAFE'; 34 + return 'COMPACTION_EVENT'; 35 + } 36 + } 37 + 38 + export function createShadowPipeline(): ShadowPipeline { 39 + return new ShadowPipeline(); 40 + } 41 + 42 + export const _phoenix = { 43 + iu_id: 'a3b4c5d6', 44 + name: 'Shadow Mode', 45 + risk_tier: 'high', 46 + canon_ids: [7], 47 + } as const;
+78
examples/phoenix-self/src/generated/implementation/__tests__/implementation.test.ts
··· 1 + /** 2 + * Implementation — Generated Tests 3 + * 4 + * AUTO-GENERATED by Phoenix VCS 5 + * Tests module structure, server health, and Phoenix traceability. 6 + */ 7 + 8 + import { describe, it, expect, afterAll } from 'vitest'; 9 + import { startServer } from '../server.js'; 10 + 11 + import * as iuManager from '../iu-manager.js'; 12 + import * as boundaryValidator from '../boundary-validator.js'; 13 + 14 + describe('Implementation modules', () => { 15 + describe('IU Manager', () => { 16 + it('exports Phoenix traceability metadata', () => { 17 + expect(iuManager._phoenix).toBeDefined(); 18 + expect(iuManager._phoenix.name).toBe('IU Manager'); 19 + expect(iuManager._phoenix.risk_tier).toBeTruthy(); 20 + }); 21 + 22 + it('has exported functions', () => { 23 + const exports = Object.keys(iuManager).filter(k => k !== '_phoenix'); 24 + expect(exports.length).toBeGreaterThan(0); 25 + }); 26 + }); 27 + 28 + describe('Boundary Validator', () => { 29 + it('exports Phoenix traceability metadata', () => { 30 + expect(boundaryValidator._phoenix).toBeDefined(); 31 + expect(boundaryValidator._phoenix.name).toBe('Boundary Validator'); 32 + expect(boundaryValidator._phoenix.risk_tier).toBeTruthy(); 33 + }); 34 + 35 + it('has exported functions', () => { 36 + const exports = Object.keys(boundaryValidator).filter(k => k !== '_phoenix'); 37 + expect(exports.length).toBeGreaterThan(0); 38 + }); 39 + }); 40 + 41 + }); 42 + 43 + describe('Implementation server', () => { 44 + const instance = startServer(0); // random port 45 + 46 + afterAll(() => new Promise<void>(resolve => instance.server.close(() => resolve()))); 47 + 48 + it('GET /health returns 200', async () => { 49 + await instance.ready; 50 + const res = await fetch(`http://localhost:${instance.port}/health`); 51 + expect(res.status).toBe(200); 52 + const body = await res.json() as Record<string, unknown>; 53 + expect(body.status).toBe('ok'); 54 + expect(body.service).toBe('Implementation'); 55 + }); 56 + 57 + it('GET /metrics returns request counts', async () => { 58 + await instance.ready; 59 + const res = await fetch(`http://localhost:${instance.port}/metrics`); 60 + expect(res.status).toBe(200); 61 + const body = await res.json() as Record<string, unknown>; 62 + expect(typeof body.requests_total).toBe('number'); 63 + }); 64 + 65 + it('GET /modules lists all registered modules', async () => { 66 + await instance.ready; 67 + const res = await fetch(`http://localhost:${instance.port}/modules`); 68 + expect(res.status).toBe(200); 69 + const body = await res.json() as Array<Record<string, unknown>>; 70 + expect(body.length).toBe(2); 71 + }); 72 + 73 + it('GET /unknown returns 404', async () => { 74 + await instance.ready; 75 + const res = await fetch(`http://localhost:${instance.port}/unknown`); 76 + expect(res.status).toBe(404); 77 + }); 78 + });
+35
examples/phoenix-self/src/generated/implementation/boundary-validator.ts
··· 1 + import type { ImplementationUnit } from './iu-manager.js'; 2 + 3 + export type ViolationSeverity = 'error' | 'warning'; 4 + 5 + export interface BoundaryViolation { 6 + iu_id: string; 7 + violation_type: 'dependency' | 'side_channel'; 8 + severity: ViolationSeverity; 9 + target: string; 10 + message: string; 11 + } 12 + 13 + export class BoundaryValidator { 14 + validate(iu: ImplementationUnit, actualDependencies: string[]): BoundaryViolation[] { 15 + const violations: BoundaryViolation[] = []; 16 + for (const dep of actualDependencies) { 17 + if (iu.boundary_policy.forbidden_ius.includes(dep)) { 18 + violations.push({ iu_id: iu.iu_id, violation_type: 'dependency', severity: 'error', target: dep, message: `Forbidden IU dependency: ${dep}` }); 19 + } 20 + if (iu.boundary_policy.forbidden_packages.includes(dep)) { 21 + violations.push({ iu_id: iu.iu_id, violation_type: 'dependency', severity: 'error', target: dep, message: `Forbidden package dependency: ${dep}` }); 22 + } 23 + } 24 + return violations; 25 + } 26 + } 27 + 28 + export function createBoundaryValidator(): BoundaryValidator { return new BoundaryValidator(); } 29 + 30 + export const _phoenix = { 31 + iu_id: 'b5a6c7d8', 32 + name: 'Boundary Validator', 33 + risk_tier: 'high', 34 + canon_ids: [10, 11], 35 + } as const;
+2
examples/phoenix-self/src/generated/implementation/index.ts
··· 1 + export * as iuManager from './iu-manager.js'; 2 + export * as boundaryValidator from './boundary-validator.js';
+62
examples/phoenix-self/src/generated/implementation/iu-manager.ts
··· 1 + import { randomUUID } from 'node:crypto'; 2 + 3 + export type RiskTier = 'low' | 'medium' | 'high' | 'critical'; 4 + export type IUKind = 'module' | 'function'; 5 + 6 + export interface ImplementationUnit { 7 + iu_id: string; 8 + kind: IUKind; 9 + risk_tier: RiskTier; 10 + contract: string[]; 11 + dependencies: string[]; 12 + boundary_policy: BoundaryPolicy; 13 + } 14 + 15 + export interface BoundaryPolicy { 16 + allowed_ius: string[]; 17 + allowed_packages: string[]; 18 + forbidden_ius: string[]; 19 + forbidden_packages: string[]; 20 + forbidden_paths: string[]; 21 + side_channels: SideChannels; 22 + } 23 + 24 + export interface SideChannels { 25 + databases: string[]; 26 + queues: string[]; 27 + caches: string[]; 28 + external_apis: string[]; 29 + } 30 + 31 + export class IUManager { 32 + private units = new Map<string, ImplementationUnit>(); 33 + 34 + createIU(kind: IUKind, riskTier: RiskTier, contract: string[]): ImplementationUnit { 35 + const iu: ImplementationUnit = { 36 + iu_id: randomUUID(), 37 + kind, 38 + risk_tier: riskTier, 39 + contract, 40 + dependencies: [], 41 + boundary_policy: { allowed_ius: [], allowed_packages: [], forbidden_ius: [], forbidden_packages: [], forbidden_paths: [], side_channels: { databases: [], queues: [], caches: [], external_apis: [] } }, 42 + }; 43 + this.units.set(iu.iu_id, iu); 44 + return iu; 45 + } 46 + 47 + getIU(id: string): ImplementationUnit | undefined { return this.units.get(id); } 48 + listIUs(): ImplementationUnit[] { return [...this.units.values()]; } 49 + addDependency(iuId: string, depId: string): void { 50 + const iu = this.units.get(iuId); 51 + if (iu && !iu.dependencies.includes(depId)) iu.dependencies.push(depId); 52 + } 53 + } 54 + 55 + export function createIUManager(): IUManager { return new IUManager(); } 56 + 57 + export const _phoenix = { 58 + iu_id: 'f1e2d3c4', 59 + name: 'IU Manager', 60 + risk_tier: 'high', 61 + canon_ids: [8, 9], 62 + } as const;
+121
examples/phoenix-self/src/generated/implementation/server.ts
··· 1 + /** 2 + * Implementation — HTTP Server 3 + * 4 + * AUTO-GENERATED by Phoenix VCS 5 + * Provides health check, metrics, and module endpoints. 6 + */ 7 + 8 + import { createServer, IncomingMessage, ServerResponse } from 'node:http'; 9 + 10 + import * as iuManager from './iu-manager.js'; 11 + import * as boundaryValidator from './boundary-validator.js'; 12 + 13 + // ─── Metrics ───────────────────────────────────────────────────────────────── 14 + 15 + const _svcMetrics = { 16 + requests_total: 0, 17 + requests_by_path: {} as Record<string, number>, 18 + errors_total: 0, 19 + uptime_start: Date.now(), 20 + }; 21 + 22 + // ─── Module Registry ───────────────────────────────────────────────────────── 23 + 24 + const _svcModules = { 25 + 'iu-manager': iuManager, 26 + 'boundary-validator': boundaryValidator, 27 + }; 28 + 29 + // ─── Router ────────────────────────────────────────────────────────────────── 30 + 31 + type Handler = (req: IncomingMessage, res: ServerResponse) => void | Promise<void>; 32 + 33 + const routes: Record<string, Handler> = { 34 + '/health': (_req, res) => { 35 + res.writeHead(200, { 'Content-Type': 'application/json' }); 36 + res.end(JSON.stringify({ 37 + status: 'ok', 38 + service: 'Implementation', 39 + uptime: Math.floor((Date.now() - _svcMetrics.uptime_start) / 1000), 40 + modules: Object.keys(_svcModules), 41 + })); 42 + }, 43 + 44 + '/metrics': (_req, res) => { 45 + res.writeHead(200, { 'Content-Type': 'application/json' }); 46 + res.end(JSON.stringify({ 47 + ..._svcMetrics, 48 + uptime_seconds: Math.floor((Date.now() - _svcMetrics.uptime_start) / 1000), 49 + }, null, 2)); 50 + }, 51 + 52 + '/modules': (_req, res) => { 53 + const info = Object.entries(_svcModules).map(([name, mod]) => { 54 + const phoenix = (mod as Record<string, unknown>)._phoenix as Record<string, unknown> | undefined; 55 + return { 56 + name, 57 + risk_tier: phoenix?.risk_tier ?? 'unknown', 58 + exports: Object.keys(mod).filter(k => k !== '_phoenix'), 59 + }; 60 + }); 61 + res.writeHead(200, { 'Content-Type': 'application/json' }); 62 + res.end(JSON.stringify(info, null, 2)); 63 + }, 64 + }; 65 + 66 + // ─── Server ────────────────────────────────────────────────────────────────── 67 + 68 + function handleRequest(req: IncomingMessage, res: ServerResponse): void { 69 + const url = req.url ?? '/'; 70 + const path = url.split('?')[0]; 71 + 72 + _svcMetrics.requests_total++; 73 + _svcMetrics.requests_by_path[path] = (_svcMetrics.requests_by_path[path] ?? 0) + 1; 74 + 75 + const handler = routes[path]; 76 + if (handler) { 77 + try { 78 + handler(req, res); 79 + } catch (err) { 80 + _svcMetrics.errors_total++; 81 + res.writeHead(500, { 'Content-Type': 'application/json' }); 82 + res.end(JSON.stringify({ error: String(err) })); 83 + } 84 + } else { 85 + res.writeHead(404, { 'Content-Type': 'application/json' }); 86 + res.end(JSON.stringify({ 87 + error: 'Not Found', 88 + path, 89 + available: Object.keys(routes), 90 + })); 91 + } 92 + } 93 + 94 + export function startServer(port?: number): { server: ReturnType<typeof createServer>; port: number; ready: Promise<void> } { 95 + const requestedPort = port ?? parseInt(process.env.IMPLEMENTATION_PORT ?? process.env.PORT ?? '3003', 10); 96 + const server = createServer(handleRequest); 97 + let actualPort = requestedPort; 98 + 99 + const ready = new Promise<void>(resolve => { 100 + server.listen(requestedPort, () => { 101 + const addr = server.address(); 102 + if (addr && typeof addr === 'object') actualPort = addr.port; 103 + result.port = actualPort; 104 + console.log(`Implementation listening on http://localhost:${actualPort}`); 105 + console.log(` /health — health check`); 106 + console.log(` /metrics — request metrics`); 107 + console.log(` /modules — registered modules`); 108 + resolve(); 109 + }); 110 + }); 111 + 112 + const result = { server, port: actualPort, ready }; 113 + return result; 114 + } 115 + 116 + // Start when run directly 117 + const isMain = process.argv[1]?.endsWith('/implementation/server.js') || 118 + process.argv[1]?.endsWith('/implementation/server.ts'); 119 + if (isMain) { 120 + startServer(); 121 + }
+15
examples/phoenix-self/src/generated/index.ts
··· 1 + export * as ingestion from './ingestion/index.js'; 2 + export * as canonicalization from './canonicalization/index.js'; 3 + export * as implementation from './implementation/index.js'; 4 + export * as integrity from './integrity/index.js'; 5 + export * as operations from './operations/index.js'; 6 + export * as platform from './platform/index.js'; 7 + 8 + export const services = [ 9 + { name: 'Ingestion', dir: 'ingestion', port: 3001, modules: 2 }, 10 + { name: 'Canonicalization', dir: 'canonicalization', port: 3002, modules: 2 }, 11 + { name: 'Implementation', dir: 'implementation', port: 3003, modules: 2 }, 12 + { name: 'Integrity', dir: 'integrity', port: 3004, modules: 3 }, 13 + { name: 'Operations', dir: 'operations', port: 3005, modules: 3 }, 14 + { name: 'Platform', dir: 'platform', port: 3006, modules: 2 }, 15 + ] as const;
+78
examples/phoenix-self/src/generated/ingestion/__tests__/ingestion.test.ts
··· 1 + /** 2 + * Ingestion — Generated Tests 3 + * 4 + * AUTO-GENERATED by Phoenix VCS 5 + * Tests module structure, server health, and Phoenix traceability. 6 + */ 7 + 8 + import { describe, it, expect, afterAll } from 'vitest'; 9 + import { startServer } from '../server.js'; 10 + 11 + import * as specParser from '../spec-parser.js'; 12 + import * as changeClassifier from '../change-classifier.js'; 13 + 14 + describe('Ingestion modules', () => { 15 + describe('Spec Parser', () => { 16 + it('exports Phoenix traceability metadata', () => { 17 + expect(specParser._phoenix).toBeDefined(); 18 + expect(specParser._phoenix.name).toBe('Spec Parser'); 19 + expect(specParser._phoenix.risk_tier).toBeTruthy(); 20 + }); 21 + 22 + it('has exported functions', () => { 23 + const exports = Object.keys(specParser).filter(k => k !== '_phoenix'); 24 + expect(exports.length).toBeGreaterThan(0); 25 + }); 26 + }); 27 + 28 + describe('Change Classifier', () => { 29 + it('exports Phoenix traceability metadata', () => { 30 + expect(changeClassifier._phoenix).toBeDefined(); 31 + expect(changeClassifier._phoenix.name).toBe('Change Classifier'); 32 + expect(changeClassifier._phoenix.risk_tier).toBeTruthy(); 33 + }); 34 + 35 + it('has exported functions', () => { 36 + const exports = Object.keys(changeClassifier).filter(k => k !== '_phoenix'); 37 + expect(exports.length).toBeGreaterThan(0); 38 + }); 39 + }); 40 + 41 + }); 42 + 43 + describe('Ingestion server', () => { 44 + const instance = startServer(0); // random port 45 + 46 + afterAll(() => new Promise<void>(resolve => instance.server.close(() => resolve()))); 47 + 48 + it('GET /health returns 200', async () => { 49 + await instance.ready; 50 + const res = await fetch(`http://localhost:${instance.port}/health`); 51 + expect(res.status).toBe(200); 52 + const body = await res.json() as Record<string, unknown>; 53 + expect(body.status).toBe('ok'); 54 + expect(body.service).toBe('Ingestion'); 55 + }); 56 + 57 + it('GET /metrics returns request counts', async () => { 58 + await instance.ready; 59 + const res = await fetch(`http://localhost:${instance.port}/metrics`); 60 + expect(res.status).toBe(200); 61 + const body = await res.json() as Record<string, unknown>; 62 + expect(typeof body.requests_total).toBe('number'); 63 + }); 64 + 65 + it('GET /modules lists all registered modules', async () => { 66 + await instance.ready; 67 + const res = await fetch(`http://localhost:${instance.port}/modules`); 68 + expect(res.status).toBe(200); 69 + const body = await res.json() as Array<Record<string, unknown>>; 70 + expect(body.length).toBe(2); 71 + }); 72 + 73 + it('GET /unknown returns 404', async () => { 74 + await instance.ready; 75 + const res = await fetch(`http://localhost:${instance.port}/unknown`); 76 + expect(res.status).toBe(404); 77 + }); 78 + });
+61
examples/phoenix-self/src/generated/ingestion/change-classifier.ts
··· 1 + export type ChangeClass = 'A' | 'B' | 'C' | 'D'; 2 + 3 + export interface ClassificationResult { 4 + change_class: ChangeClass; 5 + confidence: number; 6 + signals: { 7 + norm_diff: number; 8 + semhash_delta: boolean; 9 + context_delta: boolean; 10 + term_ref_delta: number; 11 + }; 12 + } 13 + 14 + export interface DRateMetrics { 15 + current_rate: number; 16 + rolling_window: number[]; 17 + alarm_active: boolean; 18 + } 19 + 20 + export class ChangeClassifier { 21 + private dRateWindow: number[] = []; 22 + 23 + classify(normDiff: number, semhashDelta: boolean, contextDelta: boolean, termDelta: number): ClassificationResult { 24 + if (!semhashDelta) { 25 + return { change_class: 'A', confidence: 0.95, signals: { norm_diff: normDiff, semhash_delta: false, context_delta: contextDelta, term_ref_delta: termDelta } }; 26 + } 27 + if (normDiff < 0.1 && termDelta < 0.2) { 28 + return { change_class: 'A', confidence: 0.85, signals: { norm_diff: normDiff, semhash_delta: true, context_delta: contextDelta, term_ref_delta: termDelta } }; 29 + } 30 + if (contextDelta) { 31 + return { change_class: 'C', confidence: 0.8, signals: { norm_diff: normDiff, semhash_delta: true, context_delta: true, term_ref_delta: termDelta } }; 32 + } 33 + if (normDiff < 0.5 && termDelta < 0.5) { 34 + return { change_class: 'B', confidence: 0.8, signals: { norm_diff: normDiff, semhash_delta: true, context_delta: false, term_ref_delta: termDelta } }; 35 + } 36 + return { change_class: 'D', confidence: 0.4, signals: { norm_diff: normDiff, semhash_delta: true, context_delta: contextDelta, term_ref_delta: termDelta } }; 37 + } 38 + 39 + recordClassification(result: ClassificationResult): void { 40 + this.dRateWindow.push(result.change_class === 'D' ? 1 : 0); 41 + if (this.dRateWindow.length > 100) this.dRateWindow.shift(); 42 + } 43 + 44 + getDRateMetrics(): DRateMetrics { 45 + const total = this.dRateWindow.length; 46 + const dCount = this.dRateWindow.reduce((s, v) => s + v, 0); 47 + const rate = total > 0 ? dCount / total : 0; 48 + return { current_rate: rate, rolling_window: [...this.dRateWindow], alarm_active: rate > 0.15 }; 49 + } 50 + } 51 + 52 + export function createChangeClassifier(): ChangeClassifier { 53 + return new ChangeClassifier(); 54 + } 55 + 56 + export const _phoenix = { 57 + iu_id: 'e5f6a7b8', 58 + name: 'Change Classifier', 59 + risk_tier: 'high', 60 + canon_ids: [3, 4], 61 + } as const;
+2
examples/phoenix-self/src/generated/ingestion/index.ts
··· 1 + export * as specParser from './spec-parser.js'; 2 + export * as changeClassifier from './change-classifier.js';
+121
examples/phoenix-self/src/generated/ingestion/server.ts
··· 1 + /** 2 + * Ingestion — HTTP Server 3 + * 4 + * AUTO-GENERATED by Phoenix VCS 5 + * Provides health check, metrics, and module endpoints. 6 + */ 7 + 8 + import { createServer, IncomingMessage, ServerResponse } from 'node:http'; 9 + 10 + import * as specParser from './spec-parser.js'; 11 + import * as changeClassifier from './change-classifier.js'; 12 + 13 + // ─── Metrics ───────────────────────────────────────────────────────────────── 14 + 15 + const _svcMetrics = { 16 + requests_total: 0, 17 + requests_by_path: {} as Record<string, number>, 18 + errors_total: 0, 19 + uptime_start: Date.now(), 20 + }; 21 + 22 + // ─── Module Registry ───────────────────────────────────────────────────────── 23 + 24 + const _svcModules = { 25 + 'spec-parser': specParser, 26 + 'change-classifier': changeClassifier, 27 + }; 28 + 29 + // ─── Router ────────────────────────────────────────────────────────────────── 30 + 31 + type Handler = (req: IncomingMessage, res: ServerResponse) => void | Promise<void>; 32 + 33 + const routes: Record<string, Handler> = { 34 + '/health': (_req, res) => { 35 + res.writeHead(200, { 'Content-Type': 'application/json' }); 36 + res.end(JSON.stringify({ 37 + status: 'ok', 38 + service: 'Ingestion', 39 + uptime: Math.floor((Date.now() - _svcMetrics.uptime_start) / 1000), 40 + modules: Object.keys(_svcModules), 41 + })); 42 + }, 43 + 44 + '/metrics': (_req, res) => { 45 + res.writeHead(200, { 'Content-Type': 'application/json' }); 46 + res.end(JSON.stringify({ 47 + ..._svcMetrics, 48 + uptime_seconds: Math.floor((Date.now() - _svcMetrics.uptime_start) / 1000), 49 + }, null, 2)); 50 + }, 51 + 52 + '/modules': (_req, res) => { 53 + const info = Object.entries(_svcModules).map(([name, mod]) => { 54 + const phoenix = (mod as Record<string, unknown>)._phoenix as Record<string, unknown> | undefined; 55 + return { 56 + name, 57 + risk_tier: phoenix?.risk_tier ?? 'unknown', 58 + exports: Object.keys(mod).filter(k => k !== '_phoenix'), 59 + }; 60 + }); 61 + res.writeHead(200, { 'Content-Type': 'application/json' }); 62 + res.end(JSON.stringify(info, null, 2)); 63 + }, 64 + }; 65 + 66 + // ─── Server ────────────────────────────────────────────────────────────────── 67 + 68 + function handleRequest(req: IncomingMessage, res: ServerResponse): void { 69 + const url = req.url ?? '/'; 70 + const path = url.split('?')[0]; 71 + 72 + _svcMetrics.requests_total++; 73 + _svcMetrics.requests_by_path[path] = (_svcMetrics.requests_by_path[path] ?? 0) + 1; 74 + 75 + const handler = routes[path]; 76 + if (handler) { 77 + try { 78 + handler(req, res); 79 + } catch (err) { 80 + _svcMetrics.errors_total++; 81 + res.writeHead(500, { 'Content-Type': 'application/json' }); 82 + res.end(JSON.stringify({ error: String(err) })); 83 + } 84 + } else { 85 + res.writeHead(404, { 'Content-Type': 'application/json' }); 86 + res.end(JSON.stringify({ 87 + error: 'Not Found', 88 + path, 89 + available: Object.keys(routes), 90 + })); 91 + } 92 + } 93 + 94 + export function startServer(port?: number): { server: ReturnType<typeof createServer>; port: number; ready: Promise<void> } { 95 + const requestedPort = port ?? parseInt(process.env.INGESTION_PORT ?? process.env.PORT ?? '3001', 10); 96 + const server = createServer(handleRequest); 97 + let actualPort = requestedPort; 98 + 99 + const ready = new Promise<void>(resolve => { 100 + server.listen(requestedPort, () => { 101 + const addr = server.address(); 102 + if (addr && typeof addr === 'object') actualPort = addr.port; 103 + result.port = actualPort; 104 + console.log(`Ingestion listening on http://localhost:${actualPort}`); 105 + console.log(` /health — health check`); 106 + console.log(` /metrics — request metrics`); 107 + console.log(` /modules — registered modules`); 108 + resolve(); 109 + }); 110 + }); 111 + 112 + const result = { server, port: actualPort, ready }; 113 + return result; 114 + } 115 + 116 + // Start when run directly 117 + const isMain = process.argv[1]?.endsWith('/ingestion/server.js') || 118 + process.argv[1]?.endsWith('/ingestion/server.ts'); 119 + if (isMain) { 120 + startServer(); 121 + }
+58
examples/phoenix-self/src/generated/ingestion/spec-parser.ts
··· 1 + import { createHash } from 'node:crypto'; 2 + 3 + export interface Clause { 4 + clause_id: string; 5 + source_doc_id: string; 6 + source_line_range: [number, number]; 7 + raw_text: string; 8 + normalized_text: string; 9 + section_path: string[]; 10 + clause_semhash: string; 11 + context_semhash_cold: string; 12 + } 13 + 14 + export class SpecParser { 15 + parseDocument(docId: string, content: string): Clause[] { 16 + const sections = content.split(/^(?=# )/m); 17 + const clauses: Clause[] = []; 18 + let lineOffset = 0; 19 + 20 + for (const section of sections) { 21 + if (!section.trim()) { lineOffset += section.split('\n').length; continue; } 22 + const lines = section.split('\n'); 23 + const normalized = section.replace(/\s+/g, ' ').trim().toLowerCase(); 24 + const semhash = createHash('sha256').update(normalized).digest('hex'); 25 + const sectionPath = this.extractSectionPath(lines[0] ?? ''); 26 + 27 + clauses.push({ 28 + clause_id: createHash('sha256').update(`${docId}\x00${semhash}`).digest('hex').slice(0, 16), 29 + source_doc_id: docId, 30 + source_line_range: [lineOffset + 1, lineOffset + lines.length], 31 + raw_text: section.trim(), 32 + normalized_text: normalized, 33 + section_path: sectionPath, 34 + clause_semhash: semhash, 35 + context_semhash_cold: createHash('sha256').update(`${normalized}\x00${sectionPath.join('/')}`).digest('hex'), 36 + }); 37 + lineOffset += lines.length; 38 + } 39 + return clauses; 40 + } 41 + 42 + private extractSectionPath(heading: string): string[] { 43 + const match = heading.match(/^(#{1,6})\s+(.*)/); 44 + if (!match) return []; 45 + return [match[2].trim()]; 46 + } 47 + } 48 + 49 + export function createSpecParser(): SpecParser { 50 + return new SpecParser(); 51 + } 52 + 53 + export const _phoenix = { 54 + iu_id: 'a1b2c3d4', 55 + name: 'Spec Parser', 56 + risk_tier: 'high', 57 + canon_ids: [1, 2], 58 + } as const;
+92
examples/phoenix-self/src/generated/integrity/__tests__/integrity.test.ts
··· 1 + /** 2 + * Integrity — Generated Tests 3 + * 4 + * AUTO-GENERATED by Phoenix VCS 5 + * Tests module structure, server health, and Phoenix traceability. 6 + */ 7 + 8 + import { describe, it, expect, afterAll } from 'vitest'; 9 + import { startServer } from '../server.js'; 10 + 11 + import * as driftDetector from '../drift-detector.js'; 12 + import * as evidenceEngine from '../evidence-engine.js'; 13 + import * as cascadePropagator from '../cascade-propagator.js'; 14 + 15 + describe('Integrity modules', () => { 16 + describe('Drift Detector', () => { 17 + it('exports Phoenix traceability metadata', () => { 18 + expect(driftDetector._phoenix).toBeDefined(); 19 + expect(driftDetector._phoenix.name).toBe('Drift Detector'); 20 + expect(driftDetector._phoenix.risk_tier).toBeTruthy(); 21 + }); 22 + 23 + it('has exported functions', () => { 24 + const exports = Object.keys(driftDetector).filter(k => k !== '_phoenix'); 25 + expect(exports.length).toBeGreaterThan(0); 26 + }); 27 + }); 28 + 29 + describe('Evidence Engine', () => { 30 + it('exports Phoenix traceability metadata', () => { 31 + expect(evidenceEngine._phoenix).toBeDefined(); 32 + expect(evidenceEngine._phoenix.name).toBe('Evidence Engine'); 33 + expect(evidenceEngine._phoenix.risk_tier).toBeTruthy(); 34 + }); 35 + 36 + it('has exported functions', () => { 37 + const exports = Object.keys(evidenceEngine).filter(k => k !== '_phoenix'); 38 + expect(exports.length).toBeGreaterThan(0); 39 + }); 40 + }); 41 + 42 + describe('Cascade Propagator', () => { 43 + it('exports Phoenix traceability metadata', () => { 44 + expect(cascadePropagator._phoenix).toBeDefined(); 45 + expect(cascadePropagator._phoenix.name).toBe('Cascade Propagator'); 46 + expect(cascadePropagator._phoenix.risk_tier).toBeTruthy(); 47 + }); 48 + 49 + it('has exported functions', () => { 50 + const exports = Object.keys(cascadePropagator).filter(k => k !== '_phoenix'); 51 + expect(exports.length).toBeGreaterThan(0); 52 + }); 53 + }); 54 + 55 + }); 56 + 57 + describe('Integrity server', () => { 58 + const instance = startServer(0); // random port 59 + 60 + afterAll(() => new Promise<void>(resolve => instance.server.close(() => resolve()))); 61 + 62 + it('GET /health returns 200', async () => { 63 + await instance.ready; 64 + const res = await fetch(`http://localhost:${instance.port}/health`); 65 + expect(res.status).toBe(200); 66 + const body = await res.json() as Record<string, unknown>; 67 + expect(body.status).toBe('ok'); 68 + expect(body.service).toBe('Integrity'); 69 + }); 70 + 71 + it('GET /metrics returns request counts', async () => { 72 + await instance.ready; 73 + const res = await fetch(`http://localhost:${instance.port}/metrics`); 74 + expect(res.status).toBe(200); 75 + const body = await res.json() as Record<string, unknown>; 76 + expect(typeof body.requests_total).toBe('number'); 77 + }); 78 + 79 + it('GET /modules lists all registered modules', async () => { 80 + await instance.ready; 81 + const res = await fetch(`http://localhost:${instance.port}/modules`); 82 + expect(res.status).toBe(200); 83 + const body = await res.json() as Array<Record<string, unknown>>; 84 + expect(body.length).toBe(3); 85 + }); 86 + 87 + it('GET /unknown returns 404', async () => { 88 + await instance.ready; 89 + const res = await fetch(`http://localhost:${instance.port}/unknown`); 90 + expect(res.status).toBe(404); 91 + }); 92 + });
+68
examples/phoenix-self/src/generated/integrity/cascade-propagator.ts
··· 1 + /** 2 + * Cascade Propagator — integrity module 3 + * 4 + * AUTO-GENERATED by Phoenix VCS 5 + * Traverses dependency graphs to propagate failures and determine cascade effects. 6 + */ 7 + 8 + export interface CascadeResult { 9 + blocked: string[]; 10 + rerunRequired: { iuId: string; checks: string[] }[]; 11 + } 12 + 13 + const MAX_DEPTH = 10; 14 + 15 + export class CascadePropagator { 16 + propagate(failedIuId: string, dependencyGraph: Map<string, string[]>): CascadeResult { 17 + const blocked = new Set<string>(); 18 + const rerunMap = new Map<string, Set<string>>(); 19 + 20 + this.traverse(failedIuId, dependencyGraph, blocked, rerunMap, 0); 21 + 22 + const rerunRequired = Array.from(rerunMap.entries()).map(([iuId, checks]) => ({ 23 + iuId, 24 + checks: Array.from(checks), 25 + })); 26 + 27 + return { 28 + blocked: Array.from(blocked), 29 + rerunRequired, 30 + }; 31 + } 32 + 33 + private traverse( 34 + nodeId: string, 35 + graph: Map<string, string[]>, 36 + blocked: Set<string>, 37 + rerunMap: Map<string, Set<string>>, 38 + depth: number, 39 + ): void { 40 + if (depth >= MAX_DEPTH) return; 41 + 42 + // Find all nodes that depend on nodeId (dependents) 43 + for (const [dependent, dependencies] of graph.entries()) { 44 + if (dependencies.includes(nodeId) && !blocked.has(dependent)) { 45 + blocked.add(dependent); 46 + 47 + // Determine checks that need rerun 48 + const checks = new Set<string>(['unit_tests', 'boundary_validation']); 49 + if (depth > 2) checks.add('integration_tests'); 50 + rerunMap.set(dependent, checks); 51 + 52 + // Recurse into further dependents 53 + this.traverse(dependent, graph, blocked, rerunMap, depth + 1); 54 + } 55 + } 56 + } 57 + } 58 + 59 + export function createCascadePropagator(): CascadePropagator { 60 + return new CascadePropagator(); 61 + } 62 + 63 + export const _phoenix = { 64 + iu_id: 'e5f6a7b8', 65 + name: 'Cascade Propagator', 66 + risk_tier: 'critical', 67 + canon_ids: [14], 68 + } as const;
+77
examples/phoenix-self/src/generated/integrity/drift-detector.ts
··· 1 + /** 2 + * Drift Detector — integrity module 3 + * 4 + * AUTO-GENERATED by Phoenix VCS 5 + * Detects drift between expected and actual file hashes. 6 + */ 7 + 8 + export type DriftLabel = 'promote_to_requirement' | 'waiver' | 'temporary_patch' | null; 9 + 10 + export interface DriftResult { 11 + file: string; 12 + drifted: boolean; 13 + label: DriftLabel; 14 + } 15 + 16 + export interface DriftEvent { 17 + file: string; 18 + expectedHash: string; 19 + actualHash: string; 20 + drifted: boolean; 21 + label: DriftLabel; 22 + timestamp: number; 23 + } 24 + 25 + export class DriftDetector { 26 + private events: DriftEvent[] = []; 27 + 28 + checkDrift(filePath: string, expectedHash: string, actualHash: string): DriftResult { 29 + const drifted = expectedHash !== actualHash; 30 + let label: DriftLabel = null; 31 + 32 + if (drifted) { 33 + // Classify the drift based on hash distance heuristics 34 + const hashPrefix = actualHash.slice(0, 4); 35 + const expectedPrefix = expectedHash.slice(0, 4); 36 + 37 + if (hashPrefix === expectedPrefix) { 38 + label = 'temporary_patch'; 39 + } else if (actualHash.length !== expectedHash.length) { 40 + label = 'waiver'; 41 + } else { 42 + label = 'promote_to_requirement'; 43 + } 44 + } 45 + 46 + const event: DriftEvent = { 47 + file: filePath, 48 + expectedHash, 49 + actualHash, 50 + drifted, 51 + label, 52 + timestamp: Date.now(), 53 + }; 54 + this.events.push(event); 55 + 56 + return { file: filePath, drifted, label }; 57 + } 58 + 59 + getEvents(): DriftEvent[] { 60 + return [...this.events]; 61 + } 62 + 63 + getDriftCount(): number { 64 + return this.events.filter(e => e.drifted).length; 65 + } 66 + } 67 + 68 + export function createDriftDetector(): DriftDetector { 69 + return new DriftDetector(); 70 + } 71 + 72 + export const _phoenix = { 73 + iu_id: 'c1d2e3f4', 74 + name: 'Drift Detector', 75 + risk_tier: 'high', 76 + canon_ids: [12], 77 + } as const;
+56
examples/phoenix-self/src/generated/integrity/evidence-engine.ts
··· 1 + /** 2 + * Evidence Engine — integrity module 3 + * 4 + * AUTO-GENERATED by Phoenix VCS 5 + * Determines required evidence per risk tier and binds evidence to canon/IU pairs. 6 + */ 7 + 8 + export type RiskTier = 'low' | 'medium' | 'high' | 'critical'; 9 + 10 + export interface EvidenceBinding { 11 + canonId: number; 12 + iuId: string; 13 + artifactHash: string; 14 + timestamp: number; 15 + } 16 + 17 + const EVIDENCE_BY_TIER: Record<RiskTier, string[]> = { 18 + low: ['typecheck', 'lint', 'boundary_validation'], 19 + medium: ['typecheck', 'lint', 'boundary_validation', 'unit_tests'], 20 + high: ['typecheck', 'lint', 'boundary_validation', 'unit_tests', 'property_tests', 'threat_note', 'static_analysis'], 21 + critical: ['typecheck', 'lint', 'boundary_validation', 'unit_tests', 'property_tests', 'threat_note', 'static_analysis', 'human_signoff'], 22 + }; 23 + 24 + export class EvidenceEngine { 25 + private bindings: EvidenceBinding[] = []; 26 + 27 + getRequirements(riskTier: RiskTier): string[] { 28 + return [...EVIDENCE_BY_TIER[riskTier]]; 29 + } 30 + 31 + bindEvidence(canonId: number, iuId: string, artifactHash: string): EvidenceBinding { 32 + const binding: EvidenceBinding = { 33 + canonId, 34 + iuId, 35 + artifactHash, 36 + timestamp: Date.now(), 37 + }; 38 + this.bindings.push(binding); 39 + return binding; 40 + } 41 + 42 + getBindings(): EvidenceBinding[] { 43 + return [...this.bindings]; 44 + } 45 + } 46 + 47 + export function createEvidenceEngine(): EvidenceEngine { 48 + return new EvidenceEngine(); 49 + } 50 + 51 + export const _phoenix = { 52 + iu_id: 'd3e4f5a6', 53 + name: 'Evidence Engine', 54 + risk_tier: 'high', 55 + canon_ids: [13], 56 + } as const;
+3
examples/phoenix-self/src/generated/integrity/index.ts
··· 1 + export * as driftDetector from './drift-detector.js'; 2 + export * as evidenceEngine from './evidence-engine.js'; 3 + export * as cascadePropagator from './cascade-propagator.js';
+123
examples/phoenix-self/src/generated/integrity/server.ts
··· 1 + /** 2 + * Integrity — HTTP Server 3 + * 4 + * AUTO-GENERATED by Phoenix VCS 5 + * Provides health check, metrics, and module endpoints. 6 + */ 7 + 8 + import { createServer, IncomingMessage, ServerResponse } from 'node:http'; 9 + 10 + import * as driftDetector from './drift-detector.js'; 11 + import * as evidenceEngine from './evidence-engine.js'; 12 + import * as cascadePropagator from './cascade-propagator.js'; 13 + 14 + // ─── Metrics ───────────────────────────────────────────────────────────────── 15 + 16 + const _svcMetrics = { 17 + requests_total: 0, 18 + requests_by_path: {} as Record<string, number>, 19 + errors_total: 0, 20 + uptime_start: Date.now(), 21 + }; 22 + 23 + // ─── Module Registry ───────────────────────────────────────────────────────── 24 + 25 + const _svcModules = { 26 + 'drift-detector': driftDetector, 27 + 'evidence-engine': evidenceEngine, 28 + 'cascade-propagator': cascadePropagator, 29 + }; 30 + 31 + // ─── Router ────────────────────────────────────────────────────────────────── 32 + 33 + type Handler = (req: IncomingMessage, res: ServerResponse) => void | Promise<void>; 34 + 35 + const routes: Record<string, Handler> = { 36 + '/health': (_req, res) => { 37 + res.writeHead(200, { 'Content-Type': 'application/json' }); 38 + res.end(JSON.stringify({ 39 + status: 'ok', 40 + service: 'Integrity', 41 + uptime: Math.floor((Date.now() - _svcMetrics.uptime_start) / 1000), 42 + modules: Object.keys(_svcModules), 43 + })); 44 + }, 45 + 46 + '/metrics': (_req, res) => { 47 + res.writeHead(200, { 'Content-Type': 'application/json' }); 48 + res.end(JSON.stringify({ 49 + ..._svcMetrics, 50 + uptime_seconds: Math.floor((Date.now() - _svcMetrics.uptime_start) / 1000), 51 + }, null, 2)); 52 + }, 53 + 54 + '/modules': (_req, res) => { 55 + const info = Object.entries(_svcModules).map(([name, mod]) => { 56 + const phoenix = (mod as Record<string, unknown>)._phoenix as Record<string, unknown> | undefined; 57 + return { 58 + name, 59 + risk_tier: phoenix?.risk_tier ?? 'unknown', 60 + exports: Object.keys(mod).filter(k => k !== '_phoenix'), 61 + }; 62 + }); 63 + res.writeHead(200, { 'Content-Type': 'application/json' }); 64 + res.end(JSON.stringify(info, null, 2)); 65 + }, 66 + }; 67 + 68 + // ─── Server ────────────────────────────────────────────────────────────────── 69 + 70 + function handleRequest(req: IncomingMessage, res: ServerResponse): void { 71 + const url = req.url ?? '/'; 72 + const path = url.split('?')[0]; 73 + 74 + _svcMetrics.requests_total++; 75 + _svcMetrics.requests_by_path[path] = (_svcMetrics.requests_by_path[path] ?? 0) + 1; 76 + 77 + const handler = routes[path]; 78 + if (handler) { 79 + try { 80 + handler(req, res); 81 + } catch (err) { 82 + _svcMetrics.errors_total++; 83 + res.writeHead(500, { 'Content-Type': 'application/json' }); 84 + res.end(JSON.stringify({ error: String(err) })); 85 + } 86 + } else { 87 + res.writeHead(404, { 'Content-Type': 'application/json' }); 88 + res.end(JSON.stringify({ 89 + error: 'Not Found', 90 + path, 91 + available: Object.keys(routes), 92 + })); 93 + } 94 + } 95 + 96 + export function startServer(port?: number): { server: ReturnType<typeof createServer>; port: number; ready: Promise<void> } { 97 + const requestedPort = port ?? parseInt(process.env.INTEGRITY_PORT ?? process.env.PORT ?? '3004', 10); 98 + const server = createServer(handleRequest); 99 + let actualPort = requestedPort; 100 + 101 + const ready = new Promise<void>(resolve => { 102 + server.listen(requestedPort, () => { 103 + const addr = server.address(); 104 + if (addr && typeof addr === 'object') actualPort = addr.port; 105 + result.port = actualPort; 106 + console.log(`Integrity listening on http://localhost:${actualPort}`); 107 + console.log(` /health — health check`); 108 + console.log(` /metrics — request metrics`); 109 + console.log(` /modules — registered modules`); 110 + resolve(); 111 + }); 112 + }); 113 + 114 + const result = { server, port: actualPort, ready }; 115 + return result; 116 + } 117 + 118 + // Start when run directly 119 + const isMain = process.argv[1]?.endsWith('/integrity/server.js') || 120 + process.argv[1]?.endsWith('/integrity/server.ts'); 121 + if (isMain) { 122 + startServer(); 123 + }
+92
examples/phoenix-self/src/generated/operations/__tests__/operations.test.ts
··· 1 + /** 2 + * Operations — Generated Tests 3 + * 4 + * AUTO-GENERATED by Phoenix VCS 5 + * Tests module structure, server health, and Phoenix traceability. 6 + */ 7 + 8 + import { describe, it, expect, afterAll } from 'vitest'; 9 + import { startServer } from '../server.js'; 10 + 11 + import * as compaction from '../compaction.js'; 12 + import * as diagnostics from '../diagnostics.js'; 13 + import * as bootstrap from '../bootstrap.js'; 14 + 15 + describe('Operations modules', () => { 16 + describe('Compaction', () => { 17 + it('exports Phoenix traceability metadata', () => { 18 + expect(compaction._phoenix).toBeDefined(); 19 + expect(compaction._phoenix.name).toBe('Compaction'); 20 + expect(compaction._phoenix.risk_tier).toBeTruthy(); 21 + }); 22 + 23 + it('has exported functions', () => { 24 + const exports = Object.keys(compaction).filter(k => k !== '_phoenix'); 25 + expect(exports.length).toBeGreaterThan(0); 26 + }); 27 + }); 28 + 29 + describe('Diagnostics', () => { 30 + it('exports Phoenix traceability metadata', () => { 31 + expect(diagnostics._phoenix).toBeDefined(); 32 + expect(diagnostics._phoenix.name).toBe('Diagnostics'); 33 + expect(diagnostics._phoenix.risk_tier).toBeTruthy(); 34 + }); 35 + 36 + it('has exported functions', () => { 37 + const exports = Object.keys(diagnostics).filter(k => k !== '_phoenix'); 38 + expect(exports.length).toBeGreaterThan(0); 39 + }); 40 + }); 41 + 42 + describe('Bootstrap', () => { 43 + it('exports Phoenix traceability metadata', () => { 44 + expect(bootstrap._phoenix).toBeDefined(); 45 + expect(bootstrap._phoenix.name).toBe('Bootstrap'); 46 + expect(bootstrap._phoenix.risk_tier).toBeTruthy(); 47 + }); 48 + 49 + it('has exported functions', () => { 50 + const exports = Object.keys(bootstrap).filter(k => k !== '_phoenix'); 51 + expect(exports.length).toBeGreaterThan(0); 52 + }); 53 + }); 54 + 55 + }); 56 + 57 + describe('Operations server', () => { 58 + const instance = startServer(0); // random port 59 + 60 + afterAll(() => new Promise<void>(resolve => instance.server.close(() => resolve()))); 61 + 62 + it('GET /health returns 200', async () => { 63 + await instance.ready; 64 + const res = await fetch(`http://localhost:${instance.port}/health`); 65 + expect(res.status).toBe(200); 66 + const body = await res.json() as Record<string, unknown>; 67 + expect(body.status).toBe('ok'); 68 + expect(body.service).toBe('Operations'); 69 + }); 70 + 71 + it('GET /metrics returns request counts', async () => { 72 + await instance.ready; 73 + const res = await fetch(`http://localhost:${instance.port}/metrics`); 74 + expect(res.status).toBe(200); 75 + const body = await res.json() as Record<string, unknown>; 76 + expect(typeof body.requests_total).toBe('number'); 77 + }); 78 + 79 + it('GET /modules lists all registered modules', async () => { 80 + await instance.ready; 81 + const res = await fetch(`http://localhost:${instance.port}/modules`); 82 + expect(res.status).toBe(200); 83 + const body = await res.json() as Array<Record<string, unknown>>; 84 + expect(body.length).toBe(3); 85 + }); 86 + 87 + it('GET /unknown returns 404', async () => { 88 + await instance.ready; 89 + const res = await fetch(`http://localhost:${instance.port}/unknown`); 90 + expect(res.status).toBe(404); 91 + }); 92 + });
+54
examples/phoenix-self/src/generated/operations/bootstrap.ts
··· 1 + /** 2 + * Bootstrap — operations module 3 + * 4 + * AUTO-GENERATED by Phoenix VCS 5 + * Orchestrates system bootstrap through defined phases. 6 + */ 7 + 8 + export type BootstrapPhase = 'COLD_PASS' | 'CANONICALIZATION' | 'WARM_PASS' | 'DASHBOARD' | 'COMPLETE'; 9 + export type SystemState = 'BOOTSTRAP_COLD' | 'BOOTSTRAP_WARMING' | 'STEADY_STATE'; 10 + 11 + const PHASE_ORDER: BootstrapPhase[] = ['COLD_PASS', 'CANONICALIZATION', 'WARM_PASS', 'DASHBOARD', 'COMPLETE']; 12 + 13 + const PHASE_TO_STATE: Record<BootstrapPhase, SystemState> = { 14 + COLD_PASS: 'BOOTSTRAP_COLD', 15 + CANONICALIZATION: 'BOOTSTRAP_COLD', 16 + WARM_PASS: 'BOOTSTRAP_WARMING', 17 + DASHBOARD: 'BOOTSTRAP_WARMING', 18 + COMPLETE: 'STEADY_STATE', 19 + }; 20 + 21 + export class BootstrapOrchestrator { 22 + private _currentPhase: BootstrapPhase = 'COLD_PASS'; 23 + 24 + get currentPhase(): BootstrapPhase { 25 + return this._currentPhase; 26 + } 27 + 28 + get systemState(): SystemState { 29 + return PHASE_TO_STATE[this._currentPhase]; 30 + } 31 + 32 + runPhase(): BootstrapPhase { 33 + const currentIndex = PHASE_ORDER.indexOf(this._currentPhase); 34 + if (currentIndex < PHASE_ORDER.length - 1) { 35 + this._currentPhase = PHASE_ORDER[currentIndex + 1]; 36 + } 37 + return this._currentPhase; 38 + } 39 + 40 + isComplete(): boolean { 41 + return this._currentPhase === 'COMPLETE'; 42 + } 43 + } 44 + 45 + export function createBootstrapOrchestrator(): BootstrapOrchestrator { 46 + return new BootstrapOrchestrator(); 47 + } 48 + 49 + export const _phoenix = { 50 + iu_id: 'b5c6d7e8', 51 + name: 'Bootstrap', 52 + risk_tier: 'high', 53 + canon_ids: [17], 54 + } as const;
+75
examples/phoenix-self/src/generated/operations/compaction.ts
··· 1 + /** 2 + * Compaction — operations module 3 + * 4 + * AUTO-GENERATED by Phoenix VCS 5 + * Manages graph compaction with protected node types that are never deleted. 6 + */ 7 + 8 + import { createHash } from 'node:crypto'; 9 + 10 + export type StorageTier = 'hot' | 'ancestry' | 'cold'; 11 + 12 + export interface CompactionEvent { 13 + id: string; 14 + timestamp: number; 15 + trigger: 'size' | 'upgrade' | 'time'; 16 + nodes_compacted: number; 17 + } 18 + 19 + const PROTECTED_SET = new Set([ 20 + 'node_headers', 21 + 'provenance_edges', 22 + 'approvals', 23 + 'signatures', 24 + ]); 25 + 26 + export class CompactionEngine { 27 + private events: CompactionEvent[] = []; 28 + 29 + checkTriggers(hotGraphSize: number, lastCompaction: number): boolean { 30 + const now = Date.now(); 31 + const timeSinceLastCompaction = now - lastCompaction; 32 + 33 + // Trigger on size threshold (>1000 nodes) 34 + if (hotGraphSize > 1000) return true; 35 + 36 + // Trigger on time threshold (>24 hours since last compaction) 37 + if (timeSinceLastCompaction > 24 * 60 * 60 * 1000) return true; 38 + 39 + return false; 40 + } 41 + 42 + compact(nodes: Array<{ id: string; type: string; data: Record<string, unknown> }>): CompactionEvent { 43 + // Filter out protected nodes — never delete them 44 + const compactable = nodes.filter(n => !PROTECTED_SET.has(n.type)); 45 + 46 + const event: CompactionEvent = { 47 + id: createHash('sha256').update(`compact-${Date.now()}-${compactable.length}`).digest('hex').slice(0, 16), 48 + timestamp: Date.now(), 49 + trigger: compactable.length > 500 ? 'size' : 'time', 50 + nodes_compacted: compactable.length, 51 + }; 52 + 53 + this.events.push(event); 54 + return event; 55 + } 56 + 57 + getEvents(): CompactionEvent[] { 58 + return [...this.events]; 59 + } 60 + 61 + getProtectedTypes(): string[] { 62 + return Array.from(PROTECTED_SET); 63 + } 64 + } 65 + 66 + export function createCompactionEngine(): CompactionEngine { 67 + return new CompactionEngine(); 68 + } 69 + 70 + export const _phoenix = { 71 + iu_id: 'f1a2b3c4', 72 + name: 'Compaction', 73 + risk_tier: 'medium', 74 + canon_ids: [15], 75 + } as const;
+57
examples/phoenix-self/src/generated/operations/diagnostics.ts
··· 1 + /** 2 + * Diagnostics — operations module 3 + * 4 + * AUTO-GENERATED by Phoenix VCS 5 + * Collects and organizes diagnostic status items by severity. 6 + */ 7 + 8 + export type Severity = 'error' | 'warning' | 'info'; 9 + 10 + export interface StatusItem { 11 + severity: Severity; 12 + category: string; 13 + subject: string; 14 + message: string; 15 + recommended_actions: string[]; 16 + } 17 + 18 + export interface GroupedItems { 19 + error: StatusItem[]; 20 + warning: StatusItem[]; 21 + info: StatusItem[]; 22 + } 23 + 24 + export class DiagnosticsEngine { 25 + private items: StatusItem[] = []; 26 + 27 + addItem(item: StatusItem): void { 28 + this.items.push(item); 29 + } 30 + 31 + getItems(): GroupedItems { 32 + return { 33 + error: this.items.filter(i => i.severity === 'error'), 34 + warning: this.items.filter(i => i.severity === 'warning'), 35 + info: this.items.filter(i => i.severity === 'info'), 36 + }; 37 + } 38 + 39 + getAllItems(): StatusItem[] { 40 + return [...this.items]; 41 + } 42 + 43 + clear(): void { 44 + this.items = []; 45 + } 46 + } 47 + 48 + export function createDiagnosticsEngine(): DiagnosticsEngine { 49 + return new DiagnosticsEngine(); 50 + } 51 + 52 + export const _phoenix = { 53 + iu_id: 'a3b4c5d6', 54 + name: 'Diagnostics', 55 + risk_tier: 'medium', 56 + canon_ids: [16], 57 + } as const;
+3
examples/phoenix-self/src/generated/operations/index.ts
··· 1 + export * as compaction from './compaction.js'; 2 + export * as diagnostics from './diagnostics.js'; 3 + export * as bootstrap from './bootstrap.js';
+123
examples/phoenix-self/src/generated/operations/server.ts
··· 1 + /** 2 + * Operations — HTTP Server 3 + * 4 + * AUTO-GENERATED by Phoenix VCS 5 + * Provides health check, metrics, and module endpoints. 6 + */ 7 + 8 + import { createServer, IncomingMessage, ServerResponse } from 'node:http'; 9 + 10 + import * as compaction from './compaction.js'; 11 + import * as diagnostics from './diagnostics.js'; 12 + import * as bootstrap from './bootstrap.js'; 13 + 14 + // ─── Metrics ───────────────────────────────────────────────────────────────── 15 + 16 + const _svcMetrics = { 17 + requests_total: 0, 18 + requests_by_path: {} as Record<string, number>, 19 + errors_total: 0, 20 + uptime_start: Date.now(), 21 + }; 22 + 23 + // ─── Module Registry ───────────────────────────────────────────────────────── 24 + 25 + const _svcModules = { 26 + 'compaction': compaction, 27 + 'diagnostics': diagnostics, 28 + 'bootstrap': bootstrap, 29 + }; 30 + 31 + // ─── Router ────────────────────────────────────────────────────────────────── 32 + 33 + type Handler = (req: IncomingMessage, res: ServerResponse) => void | Promise<void>; 34 + 35 + const routes: Record<string, Handler> = { 36 + '/health': (_req, res) => { 37 + res.writeHead(200, { 'Content-Type': 'application/json' }); 38 + res.end(JSON.stringify({ 39 + status: 'ok', 40 + service: 'Operations', 41 + uptime: Math.floor((Date.now() - _svcMetrics.uptime_start) / 1000), 42 + modules: Object.keys(_svcModules), 43 + })); 44 + }, 45 + 46 + '/metrics': (_req, res) => { 47 + res.writeHead(200, { 'Content-Type': 'application/json' }); 48 + res.end(JSON.stringify({ 49 + ..._svcMetrics, 50 + uptime_seconds: Math.floor((Date.now() - _svcMetrics.uptime_start) / 1000), 51 + }, null, 2)); 52 + }, 53 + 54 + '/modules': (_req, res) => { 55 + const info = Object.entries(_svcModules).map(([name, mod]) => { 56 + const phoenix = (mod as Record<string, unknown>)._phoenix as Record<string, unknown> | undefined; 57 + return { 58 + name, 59 + risk_tier: phoenix?.risk_tier ?? 'unknown', 60 + exports: Object.keys(mod).filter(k => k !== '_phoenix'), 61 + }; 62 + }); 63 + res.writeHead(200, { 'Content-Type': 'application/json' }); 64 + res.end(JSON.stringify(info, null, 2)); 65 + }, 66 + }; 67 + 68 + // ─── Server ────────────────────────────────────────────────────────────────── 69 + 70 + function handleRequest(req: IncomingMessage, res: ServerResponse): void { 71 + const url = req.url ?? '/'; 72 + const path = url.split('?')[0]; 73 + 74 + _svcMetrics.requests_total++; 75 + _svcMetrics.requests_by_path[path] = (_svcMetrics.requests_by_path[path] ?? 0) + 1; 76 + 77 + const handler = routes[path]; 78 + if (handler) { 79 + try { 80 + handler(req, res); 81 + } catch (err) { 82 + _svcMetrics.errors_total++; 83 + res.writeHead(500, { 'Content-Type': 'application/json' }); 84 + res.end(JSON.stringify({ error: String(err) })); 85 + } 86 + } else { 87 + res.writeHead(404, { 'Content-Type': 'application/json' }); 88 + res.end(JSON.stringify({ 89 + error: 'Not Found', 90 + path, 91 + available: Object.keys(routes), 92 + })); 93 + } 94 + } 95 + 96 + export function startServer(port?: number): { server: ReturnType<typeof createServer>; port: number; ready: Promise<void> } { 97 + const requestedPort = port ?? parseInt(process.env.OPERATIONS_PORT ?? process.env.PORT ?? '3005', 10); 98 + const server = createServer(handleRequest); 99 + let actualPort = requestedPort; 100 + 101 + const ready = new Promise<void>(resolve => { 102 + server.listen(requestedPort, () => { 103 + const addr = server.address(); 104 + if (addr && typeof addr === 'object') actualPort = addr.port; 105 + result.port = actualPort; 106 + console.log(`Operations listening on http://localhost:${actualPort}`); 107 + console.log(` /health — health check`); 108 + console.log(` /metrics — request metrics`); 109 + console.log(` /modules — registered modules`); 110 + resolve(); 111 + }); 112 + }); 113 + 114 + const result = { server, port: actualPort, ready }; 115 + return result; 116 + } 117 + 118 + // Start when run directly 119 + const isMain = process.argv[1]?.endsWith('/operations/server.js') || 120 + process.argv[1]?.endsWith('/operations/server.ts'); 121 + if (isMain) { 122 + startServer(); 123 + }
+78
examples/phoenix-self/src/generated/platform/__tests__/platform.test.ts
··· 1 + /** 2 + * Platform — Generated Tests 3 + * 4 + * AUTO-GENERATED by Phoenix VCS 5 + * Tests module structure, server health, and Phoenix traceability. 6 + */ 7 + 8 + import { describe, it, expect, afterAll } from 'vitest'; 9 + import { startServer } from '../server.js'; 10 + 11 + import * as graphStore from '../graph-store.js'; 12 + import * as botRouter from '../bot-router.js'; 13 + 14 + describe('Platform modules', () => { 15 + describe('Graph Store', () => { 16 + it('exports Phoenix traceability metadata', () => { 17 + expect(graphStore._phoenix).toBeDefined(); 18 + expect(graphStore._phoenix.name).toBe('Graph Store'); 19 + expect(graphStore._phoenix.risk_tier).toBeTruthy(); 20 + }); 21 + 22 + it('has exported functions', () => { 23 + const exports = Object.keys(graphStore).filter(k => k !== '_phoenix'); 24 + expect(exports.length).toBeGreaterThan(0); 25 + }); 26 + }); 27 + 28 + describe('Bot Router', () => { 29 + it('exports Phoenix traceability metadata', () => { 30 + expect(botRouter._phoenix).toBeDefined(); 31 + expect(botRouter._phoenix.name).toBe('Bot Router'); 32 + expect(botRouter._phoenix.risk_tier).toBeTruthy(); 33 + }); 34 + 35 + it('has exported functions', () => { 36 + const exports = Object.keys(botRouter).filter(k => k !== '_phoenix'); 37 + expect(exports.length).toBeGreaterThan(0); 38 + }); 39 + }); 40 + 41 + }); 42 + 43 + describe('Platform server', () => { 44 + const instance = startServer(0); // random port 45 + 46 + afterAll(() => new Promise<void>(resolve => instance.server.close(() => resolve()))); 47 + 48 + it('GET /health returns 200', async () => { 49 + await instance.ready; 50 + const res = await fetch(`http://localhost:${instance.port}/health`); 51 + expect(res.status).toBe(200); 52 + const body = await res.json() as Record<string, unknown>; 53 + expect(body.status).toBe('ok'); 54 + expect(body.service).toBe('Platform'); 55 + }); 56 + 57 + it('GET /metrics returns request counts', async () => { 58 + await instance.ready; 59 + const res = await fetch(`http://localhost:${instance.port}/metrics`); 60 + expect(res.status).toBe(200); 61 + const body = await res.json() as Record<string, unknown>; 62 + expect(typeof body.requests_total).toBe('number'); 63 + }); 64 + 65 + it('GET /modules lists all registered modules', async () => { 66 + await instance.ready; 67 + const res = await fetch(`http://localhost:${instance.port}/modules`); 68 + expect(res.status).toBe(200); 69 + const body = await res.json() as Array<Record<string, unknown>>; 70 + expect(body.length).toBe(2); 71 + }); 72 + 73 + it('GET /unknown returns 404', async () => { 74 + await instance.ready; 75 + const res = await fetch(`http://localhost:${instance.port}/unknown`); 76 + expect(res.status).toBe(404); 77 + }); 78 + });
+130
examples/phoenix-self/src/generated/platform/bot-router.ts
··· 1 + /** 2 + * Bot Router — platform module 3 + * 4 + * AUTO-GENERATED by Phoenix VCS 5 + * Routes commands to registered bots with mutation safety. 6 + */ 7 + 8 + export type CommandHandler = (args: string) => { result: string; mutating: boolean }; 9 + 10 + export interface Bot { 11 + name: string; 12 + commands: Map<string, CommandHandler>; 13 + } 14 + 15 + export interface CommandResult { 16 + bot: string; 17 + command: string; 18 + result: string; 19 + mutating: boolean; 20 + needsConfirmation: boolean; 21 + intent?: string; 22 + } 23 + 24 + export class BotRouter { 25 + private bots: Map<string, Bot> = new Map(); 26 + 27 + constructor() { 28 + // Register built-in commands 29 + const builtinBot: Bot = { 30 + name: '_system', 31 + commands: new Map<string, CommandHandler>([ 32 + ['help', () => ({ 33 + result: `Available bots: ${Array.from(this.bots.keys()).join(', ')}`, 34 + mutating: false, 35 + })], 36 + ['commands', () => { 37 + const allCommands: string[] = []; 38 + for (const [botName, bot] of this.bots.entries()) { 39 + for (const cmd of bot.commands.keys()) { 40 + allCommands.push(`${botName}:${cmd}`); 41 + } 42 + } 43 + return { result: allCommands.join(', '), mutating: false }; 44 + }], 45 + ['version', () => ({ 46 + result: 'Phoenix Bot Router v1.0.0', 47 + mutating: false, 48 + })], 49 + ]), 50 + }; 51 + this.bots.set('_system', builtinBot); 52 + } 53 + 54 + registerBot(bot: Bot): void { 55 + this.bots.set(bot.name, bot); 56 + } 57 + 58 + routeCommand(input: string): CommandResult { 59 + // Parse input: "botname:command args" or "command args" (for system) 60 + const colonIndex = input.indexOf(':'); 61 + let botName: string; 62 + let rest: string; 63 + 64 + if (colonIndex >= 0) { 65 + botName = input.slice(0, colonIndex); 66 + rest = input.slice(colonIndex + 1); 67 + } else { 68 + botName = '_system'; 69 + rest = input; 70 + } 71 + 72 + const spaceIndex = rest.indexOf(' '); 73 + const command = spaceIndex >= 0 ? rest.slice(0, spaceIndex) : rest; 74 + const args = spaceIndex >= 0 ? rest.slice(spaceIndex + 1) : ''; 75 + 76 + const bot = this.bots.get(botName); 77 + if (!bot) { 78 + return { 79 + bot: botName, 80 + command, 81 + result: `Unknown bot: ${botName}`, 82 + mutating: false, 83 + needsConfirmation: false, 84 + }; 85 + } 86 + 87 + const handler = bot.commands.get(command); 88 + if (!handler) { 89 + return { 90 + bot: botName, 91 + command, 92 + result: `Unknown command: ${command}`, 93 + mutating: false, 94 + needsConfirmation: false, 95 + }; 96 + } 97 + 98 + const { result, mutating } = handler(args); 99 + 100 + if (mutating) { 101 + return { 102 + bot: botName, 103 + command, 104 + result, 105 + mutating: true, 106 + needsConfirmation: true, 107 + intent: `${botName}:${command} ${args}`.trim(), 108 + }; 109 + } 110 + 111 + return { 112 + bot: botName, 113 + command, 114 + result, 115 + mutating: false, 116 + needsConfirmation: false, 117 + }; 118 + } 119 + } 120 + 121 + export function createBotRouter(): BotRouter { 122 + return new BotRouter(); 123 + } 124 + 125 + export const _phoenix = { 126 + iu_id: 'd9e0f1a2', 127 + name: 'Bot Router', 128 + risk_tier: 'medium', 129 + canon_ids: [19], 130 + } as const;
+78
examples/phoenix-self/src/generated/platform/graph-store.ts
··· 1 + /** 2 + * Graph Store — platform module 3 + * 4 + * AUTO-GENERATED by Phoenix VCS 5 + * Content-addressed multi-graph store with provenance edges. 6 + */ 7 + 8 + import { createHash } from 'node:crypto'; 9 + 10 + export interface GraphNode { 11 + id: string; 12 + type: string; 13 + data: Record<string, unknown>; 14 + } 15 + 16 + export interface ProvenanceEdge { 17 + from: string; 18 + to: string; 19 + transformation: string; 20 + timestamp: number; 21 + } 22 + 23 + const NAMED_GRAPHS = ['spec', 'canonical', 'implementation', 'evidence', 'provenance'] as const; 24 + export type GraphName = typeof NAMED_GRAPHS[number]; 25 + 26 + export class GraphStore { 27 + private graphs: Record<GraphName, Map<string, GraphNode>> = { 28 + spec: new Map(), 29 + canonical: new Map(), 30 + implementation: new Map(), 31 + evidence: new Map(), 32 + provenance: new Map(), 33 + }; 34 + private provenanceEdges: ProvenanceEdge[] = []; 35 + 36 + addNode(graphName: GraphName, node: GraphNode): string { 37 + // Content-address: derive ID from data hash 38 + const contentHash = createHash('sha256') 39 + .update(JSON.stringify({ type: node.type, data: node.data })) 40 + .digest('hex') 41 + .slice(0, 16); 42 + 43 + const addressedNode: GraphNode = { ...node, id: contentHash }; 44 + this.graphs[graphName].set(contentHash, addressedNode); 45 + return contentHash; 46 + } 47 + 48 + getNode(graphName: GraphName, id: string): GraphNode | undefined { 49 + return this.graphs[graphName].get(id); 50 + } 51 + 52 + addProvenanceEdge(edge: ProvenanceEdge): void { 53 + this.provenanceEdges.push(edge); 54 + } 55 + 56 + getProvenanceEdges(): ProvenanceEdge[] { 57 + return [...this.provenanceEdges]; 58 + } 59 + 60 + getGraphNames(): readonly string[] { 61 + return NAMED_GRAPHS; 62 + } 63 + 64 + getGraphSize(graphName: GraphName): number { 65 + return this.graphs[graphName].size; 66 + } 67 + } 68 + 69 + export function createGraphStore(): GraphStore { 70 + return new GraphStore(); 71 + } 72 + 73 + export const _phoenix = { 74 + iu_id: 'c7d8e9f0', 75 + name: 'Graph Store', 76 + risk_tier: 'critical', 77 + canon_ids: [18], 78 + } as const;
+2
examples/phoenix-self/src/generated/platform/index.ts
··· 1 + export * as graphStore from './graph-store.js'; 2 + export * as botRouter from './bot-router.js';
+121
examples/phoenix-self/src/generated/platform/server.ts
··· 1 + /** 2 + * Platform — HTTP Server 3 + * 4 + * AUTO-GENERATED by Phoenix VCS 5 + * Provides health check, metrics, and module endpoints. 6 + */ 7 + 8 + import { createServer, IncomingMessage, ServerResponse } from 'node:http'; 9 + 10 + import * as graphStore from './graph-store.js'; 11 + import * as botRouter from './bot-router.js'; 12 + 13 + // ─── Metrics ───────────────────────────────────────────────────────────────── 14 + 15 + const _svcMetrics = { 16 + requests_total: 0, 17 + requests_by_path: {} as Record<string, number>, 18 + errors_total: 0, 19 + uptime_start: Date.now(), 20 + }; 21 + 22 + // ─── Module Registry ───────────────────────────────────────────────────────── 23 + 24 + const _svcModules = { 25 + 'graph-store': graphStore, 26 + 'bot-router': botRouter, 27 + }; 28 + 29 + // ─── Router ────────────────────────────────────────────────────────────────── 30 + 31 + type Handler = (req: IncomingMessage, res: ServerResponse) => void | Promise<void>; 32 + 33 + const routes: Record<string, Handler> = { 34 + '/health': (_req, res) => { 35 + res.writeHead(200, { 'Content-Type': 'application/json' }); 36 + res.end(JSON.stringify({ 37 + status: 'ok', 38 + service: 'Platform', 39 + uptime: Math.floor((Date.now() - _svcMetrics.uptime_start) / 1000), 40 + modules: Object.keys(_svcModules), 41 + })); 42 + }, 43 + 44 + '/metrics': (_req, res) => { 45 + res.writeHead(200, { 'Content-Type': 'application/json' }); 46 + res.end(JSON.stringify({ 47 + ..._svcMetrics, 48 + uptime_seconds: Math.floor((Date.now() - _svcMetrics.uptime_start) / 1000), 49 + }, null, 2)); 50 + }, 51 + 52 + '/modules': (_req, res) => { 53 + const info = Object.entries(_svcModules).map(([name, mod]) => { 54 + const phoenix = (mod as Record<string, unknown>)._phoenix as Record<string, unknown> | undefined; 55 + return { 56 + name, 57 + risk_tier: phoenix?.risk_tier ?? 'unknown', 58 + exports: Object.keys(mod).filter(k => k !== '_phoenix'), 59 + }; 60 + }); 61 + res.writeHead(200, { 'Content-Type': 'application/json' }); 62 + res.end(JSON.stringify(info, null, 2)); 63 + }, 64 + }; 65 + 66 + // ─── Server ────────────────────────────────────────────────────────────────── 67 + 68 + function handleRequest(req: IncomingMessage, res: ServerResponse): void { 69 + const url = req.url ?? '/'; 70 + const path = url.split('?')[0]; 71 + 72 + _svcMetrics.requests_total++; 73 + _svcMetrics.requests_by_path[path] = (_svcMetrics.requests_by_path[path] ?? 0) + 1; 74 + 75 + const handler = routes[path]; 76 + if (handler) { 77 + try { 78 + handler(req, res); 79 + } catch (err) { 80 + _svcMetrics.errors_total++; 81 + res.writeHead(500, { 'Content-Type': 'application/json' }); 82 + res.end(JSON.stringify({ error: String(err) })); 83 + } 84 + } else { 85 + res.writeHead(404, { 'Content-Type': 'application/json' }); 86 + res.end(JSON.stringify({ 87 + error: 'Not Found', 88 + path, 89 + available: Object.keys(routes), 90 + })); 91 + } 92 + } 93 + 94 + export function startServer(port?: number): { server: ReturnType<typeof createServer>; port: number; ready: Promise<void> } { 95 + const requestedPort = port ?? parseInt(process.env.PLATFORM_PORT ?? process.env.PORT ?? '3006', 10); 96 + const server = createServer(handleRequest); 97 + let actualPort = requestedPort; 98 + 99 + const ready = new Promise<void>(resolve => { 100 + server.listen(requestedPort, () => { 101 + const addr = server.address(); 102 + if (addr && typeof addr === 'object') actualPort = addr.port; 103 + result.port = actualPort; 104 + console.log(`Platform listening on http://localhost:${actualPort}`); 105 + console.log(` /health — health check`); 106 + console.log(` /metrics — request metrics`); 107 + console.log(` /modules — registered modules`); 108 + resolve(); 109 + }); 110 + }); 111 + 112 + const result = { server, port: actualPort, ready }; 113 + return result; 114 + } 115 + 116 + // Start when run directly 117 + const isMain = process.argv[1]?.endsWith('/platform/server.js') || 118 + process.argv[1]?.endsWith('/platform/server.ts'); 119 + if (isMain) { 120 + startServer(); 121 + }
+18
examples/phoenix-self/tsconfig.json
··· 1 + { 2 + "compilerOptions": { 3 + "target": "ES2022", 4 + "module": "ESNext", 5 + "moduleResolution": "bundler", 6 + "declaration": true, 7 + "outDir": "dist", 8 + "rootDir": "src", 9 + "strict": true, 10 + "esModuleInterop": true, 11 + "skipLibCheck": true, 12 + "forceConsistentCasingInFileNames": true, 13 + "resolveJsonModule": true, 14 + "sourceMap": true 15 + }, 16 + "include": ["src/**/*"], 17 + "exclude": ["node_modules", "dist"] 18 + }
+7
examples/phoenix-self/vitest.config.ts
··· 1 + import { defineConfig } from 'vitest/config'; 2 + 3 + export default defineConfig({ 4 + test: { 5 + include: ['src/**/__tests__/**/*.test.ts'], 6 + }, 7 + });
+1
experiments/results.tsv
··· 22 22 2026-03-26T23:23:35.323Z 0.8298 93.8 70.7 91.3 12.8 58.3 4.3 42knqt 23 23 2026-03-26T23:26:36.687Z 0.8912 97.9 90.3 91.3 12.8 58.3 4.3 42knqt 24 24 2026-03-26T23:43:40.140Z 0.9635 100.0 89.2 99.7 5.5 99.4 6.6 jaxkjx 25 + 2026-03-27T04:26:14.306Z 0.9445 97.5 86.4 99.8 8.0 98.6 5.5 duywk7
+106
tests/eval/gold-standard.ts
··· 224 224 ], 225 225 expectedEdges: [], 226 226 }, 227 + // ─── Phoenix Self (Dog-food) ──────────────────────────────────────────── 228 + { 229 + name: 'Phoenix: ingestion', 230 + path: 'examples/phoenix-self/spec/ingestion.md', 231 + docId: 'spec/ingestion.md', 232 + expectedMinCoverage: 85, 233 + expectedMinNodes: 15, 234 + expectedMaxNodes: 40, 235 + expectedNodes: [ 236 + { statement: 'clause_id', type: 'REQUIREMENT' }, 237 + { statement: 'sha-256', type: 'REQUIREMENT' }, 238 + { statement: 'four classes', type: 'REQUIREMENT' }, 239 + { statement: 'd-rate', type: 'DEFINITION' }, 240 + { statement: 'at most 5%', type: 'CONSTRAINT' }, 241 + { statement: 'above 15%', type: 'REQUIREMENT' }, 242 + { statement: 'normalized edit distance', type: 'REQUIREMENT' }, 243 + { statement: 'incidental invalidation', type: 'REQUIREMENT' }, 244 + ], 245 + expectedEdges: [], 246 + }, 247 + { 248 + name: 'Phoenix: canonicalization', 249 + path: 'examples/phoenix-self/spec/canonicalization.md', 250 + docId: 'spec/canonicalization.md', 251 + expectedMinCoverage: 85, 252 + expectedMinNodes: 12, 253 + expectedMaxNodes: 35, 254 + expectedNodes: [ 255 + { statement: 'canon_pipeline_id', type: 'REQUIREMENT' }, 256 + { statement: 'pipelineupgrade', type: 'REQUIREMENT' }, 257 + { statement: 'confidence score', type: 'REQUIREMENT' }, 258 + { statement: 'shadow mode', type: 'REQUIREMENT' }, 259 + { statement: 'node_change_pct', type: 'REQUIREMENT' }, 260 + { statement: 'at most 3%', type: 'REQUIREMENT' }, 261 + { statement: 'reject', type: 'REQUIREMENT' }, 262 + ], 263 + expectedEdges: [], 264 + }, 265 + { 266 + name: 'Phoenix: implementation', 267 + path: 'examples/phoenix-self/spec/implementation.md', 268 + docId: 'spec/implementation.md', 269 + expectedMinCoverage: 85, 270 + expectedMinNodes: 15, 271 + expectedMaxNodes: 40, 272 + expectedNodes: [ 273 + { statement: 'risk tier', type: 'REQUIREMENT' }, 274 + { statement: 'boundary policy', type: 'REQUIREMENT' }, 275 + { statement: 'forbidden', type: 'REQUIREMENT' }, 276 + { statement: 'architectural linter', type: 'REQUIREMENT' }, 277 + { statement: 'generated_manifest', type: 'REQUIREMENT' }, 278 + { statement: 'reproducible', type: 'REQUIREMENT' }, 279 + ], 280 + expectedEdges: [], 281 + }, 282 + { 283 + name: 'Phoenix: integrity', 284 + path: 'examples/phoenix-self/spec/integrity.md', 285 + docId: 'spec/integrity.md', 286 + expectedMinCoverage: 85, 287 + expectedMinNodes: 12, 288 + expectedMaxNodes: 35, 289 + expectedNodes: [ 290 + { statement: 'generated_manifest', type: 'REQUIREMENT' }, 291 + { statement: 'waiver', type: 'REQUIREMENT' }, 292 + { statement: 'low-tier', type: 'REQUIREMENT' }, 293 + { statement: 'critical-tier', type: 'REQUIREMENT' }, 294 + { statement: 'cascade', type: 'REQUIREMENT' }, 295 + { statement: 'never lose', type: 'INVARIANT' }, 296 + ], 297 + expectedEdges: [], 298 + }, 299 + { 300 + name: 'Phoenix: operations', 301 + path: 'examples/phoenix-self/spec/operations.md', 302 + docId: 'spec/operations.md', 303 + expectedMinCoverage: 85, 304 + expectedMinNodes: 12, 305 + expectedMaxNodes: 35, 306 + expectedNodes: [ 307 + { statement: 'hot graph', type: 'REQUIREMENT' }, 308 + { statement: 'compaction must never delete', type: 'INVARIANT' }, 309 + { statement: 'compactionevent', type: 'REQUIREMENT' }, 310 + { statement: 'severity', type: 'REQUIREMENT' }, 311 + { statement: 'bootstrap', type: 'REQUIREMENT' }, 312 + { statement: 'steady_state', type: 'REQUIREMENT' }, 313 + ], 314 + expectedEdges: [], 315 + }, 316 + { 317 + name: 'Phoenix: platform', 318 + path: 'examples/phoenix-self/spec/platform.md', 319 + docId: 'spec/platform.md', 320 + expectedMinCoverage: 85, 321 + expectedMinNodes: 12, 322 + expectedMaxNodes: 35, 323 + expectedNodes: [ 324 + { statement: 'five interconnected graphs', type: 'REQUIREMENT' }, 325 + { statement: 'content-addressed', type: 'REQUIREMENT' }, 326 + { statement: 'provenance edges', type: 'REQUIREMENT' }, 327 + { statement: 'selective invalidation', type: 'REQUIREMENT' }, 328 + { statement: 'confirmation model', type: 'REQUIREMENT' }, 329 + { statement: 'brownfield', type: 'REQUIREMENT' }, 330 + ], 331 + expectedEdges: [], 332 + }, 227 333 ];