diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c7fd0ee6..489b525d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -122,6 +122,7 @@ jobs: runs-on: ubuntu-latest env: LIBS: chat,langgraph,ag-ui,render,a2ui,telemetry + FOUNDATIONS: core,content,angular,react steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 @@ -130,9 +131,21 @@ jobs: cache: npm - run: npm ci - run: npx nx run-many -t lint --projects=$LIBS + - name: React migration baseline and boundaries + run: | + node --test scripts/react-parity/*.spec.mjs fixtures/react-parity/traces.spec.mjs + node scripts/react-parity/inventory.mjs --check + node scripts/react-parity/verify-boundaries.mjs + - name: Build and validate private React foundations + run: npx nx run-many -t lint test type-tests build --projects=$FOUNDATIONS --parallel=2 - run: npx nx test langgraph --coverage --maxWorkers=2 --reporter=default - run: npx nx run-many -t test --projects=chat,ag-ui,render,a2ui,telemetry --coverage --parallel=1 --maxWorkers=2 - run: npx nx run-many -t build --projects=$LIBS --configuration=production + - name: Verify emitted boundaries and isolated packages + run: | + node scripts/react-parity/verify-boundaries.mjs --built + node scripts/react-parity/verify-packages.mjs + node scripts/react-parity/verify-angular-package.mjs - run: node scripts/verify-release-versions.mjs - name: DX-coverage — public dev-facing functions must have a JSDoc summary run: node scripts/check-dx-coverage.mjs diff --git a/eslint.config.mjs b/eslint.config.mjs index 6fc832f55..b2c2b2dae 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -34,6 +34,31 @@ export default [ '^.*/pricing/tiers\\.config$', ], depConstraints: [ + { + sourceTag: 'layer:core', + onlyDependOnLibsWithTags: ['layer:core'], + bannedExternalImports: ['@angular/*', 'react', 'react-dom', 'rxjs', '@langchain/*', '@ag-ui/*'], + }, + { + sourceTag: 'layer:content', + onlyDependOnLibsWithTags: ['layer:core', 'layer:content'], + bannedExternalImports: ['@angular/*', 'react', 'react-dom', 'rxjs', '@langchain/*', '@ag-ui/*'], + }, + { + sourceTag: 'layer:backend', + onlyDependOnLibsWithTags: ['layer:core', 'layer:content', 'layer:backend'], + bannedExternalImports: ['@angular/*', 'react', 'react-dom'], + }, + { + sourceTag: 'layer:angular', + onlyDependOnLibsWithTags: ['layer:core', 'layer:content', 'layer:render', 'layer:a2ui', 'layer:angular'], + bannedExternalImports: ['react', 'react/*', 'react-dom', 'react-dom/*', '@types/react', '@types/react-dom', '@langchain/*', '@ag-ui/*'], + }, + { + sourceTag: 'layer:react', + onlyDependOnLibsWithTags: ['layer:core', 'layer:content', 'layer:react'], + bannedExternalImports: ['@angular/*', '@langchain/*', '@ag-ui/*'], + }, { sourceTag: '*', onlyDependOnLibsWithTags: ['*'], diff --git a/fixtures/react-parity/README.md b/fixtures/react-parity/README.md new file mode 100644 index 000000000..da5461da4 --- /dev/null +++ b/fixtures/react-parity/README.md @@ -0,0 +1,224 @@ +# React parity foundations + +This directory supports the first migration increment: a reviewed Angular baseline +and four private, empty package scaffolds. It does not provide React components, +state management, or extracted backend runtimes. +The existing Angular packages and release group remain the production path. + +## Reviewed baseline + +The current facts were generated from the uncommitted foundation working tree at +base HEAD `7e80cebd607f5c605ef1ec81f11e5ebe3324f81b`. The baseline records the four +reviewed configuration changes: CI foundation projects and Angular consumer gate, +the direct semver tooling dependency, workspace lockfile links, and package aliases. +No inventoried API or implementation changed. This follows the main integration +at `a93977ff1e75796336ddd93a882eab2e40f0ca7b`, including v0.2.0 and its package-version +alignment fix. The inventory contains **1,438 records**: 550 public export +occurrences (514 distinct local definitions), 102 decorated components in 100 +files, 461 non-test source files, 128 package assets, 16 distribution/configuration +files, 12 entry points, 41 cockpit topics, and 128 documentation pages. These +counts cover the 16 existing libraries selected for migration; the four new empty +packages are checked separately by the boundary and packaging gates. + +The earlier research snapshot at `b1685838069c6b1ec56d5e97a1a01170b26aa125` +had 547 export occurrences and 1,435 records. Integration added `AgentRecovery`, +`AGENT_RECOVERY_MESSAGES`, and `AGENT_RECOVERY_DETAILS`. Reviewed changed contracts +also include optional `Agent.checkStatus`, `AgentError.recovery/detail`, recovery +UI actions, unexpected stream closure handling, and development-session observation. +The ownership ledger records these additions. A status check must remain read-only: +a dropped stream does not justify resubmitting work that may already have run. + +## Inventory workflow + +Run from the repository root after `npm ci`: + +```sh +node --test scripts/react-parity/*.spec.mjs fixtures/react-parity/traces.spec.mjs +node scripts/react-parity/inventory.mjs --check +node scripts/react-parity/verify-boundaries.mjs +``` + +`baseline.json` records source facts and generation provenance; `dispositions.json` +assigns every fact to migration tasks. Both live in `scripts/react-parity/`. +`planned` means assigned future work, not implemented parity. Declaration hashes +include implementation bodies/templates; source and lockfile hashes catch changes +outside declaration names. The checker is a migration drift gate, not a semver +compatibility analyzer. It does not inspect external package declarations. + +When a gate detects drift, review the changed API, implementation, asset, docs or +configuration. Update its disposition explicitly, then run: + +```sh +node scripts/react-parity/inventory.mjs --write-baseline +node scripts/react-parity/inventory.mjs --check +``` + +Writing facts never rewrites dispositions. Review the diff, especially removed or +renamed entries, before committing both. CI routes changes to the inventory's +selected libraries, docs, topic definitions and configuration into these gates, +including rootless scripts and fixtures. This intentionally requires review when +an Angular fix or dependency update changes the migration baseline. + +## Private package boundaries + +| Current private scaffold | Intended responsibility | +| --- | --- | +| `@threadplane/core` | Framework-free data, observation, execution and tool contracts | +| `@threadplane/content` | Shared Markdown, JSON, A2UI and rendering data processing | +| `@threadplane/angular` | Native Angular binding and presentation | +| `@threadplane/react` | Native React binding, rendering and presentation | + +All four are private version `0.0.0`, with empty entry points. Core, content and +React use plain ESM packaging; Angular uses Angular Package Format (APF). Their +intended responsibilities are not implemented. The source/declaration verifier follows +module edges, including type-only imports, aliases and re-exports. It blocks +framework dependencies in neutral layers, UI dependencies in backend layers, +backend SDKs in framework layers, Angular/React crossover, and optional/testing +paths reachable from package roots. The populated chat, LangGraph, AG-UI and render +packages retain explicit Angular transition allowances, as does telemetry/browser. +Nx lint constraints provide an additional source gate. The final release gate +remains blocked until those transition allowances and retired packages are removed. + +The final package map is a separate destination, not the implemented topology: + +| Final package | Responsibility | +| --- | --- | +| `@threadplane/core` | Dependency-free data, observation, execution and tool contracts | +| `@threadplane/langgraph` | Neutral LangGraph transport and lifecycle | +| `@threadplane/ag-ui` | Neutral AG-UI transport and recovery | +| `@threadplane/render` | Neutral render data and registry contracts | +| `@threadplane/a2ui` | A2UI protocol data and processing | +| `@threadplane/content` | Optional Markdown, JSON and A2UI processing | +| `@threadplane/angular` | Angular binding, render components and UI | +| `@threadplane/react` | React binding, render components and UI | +| `@threadplane/telemetry` | Neutral collector; native Angular providers belong to `@threadplane/angular` | + +There are no suffixed backend packages or separate React renderer in that map. +The first runtime proof keeps the execution owner and publisher private to the +backend; these foundations do not introduce a general shared store. +The new tool contract deliberately omits a schema DSL, automatic argument +validation/transformation and validator-to-JSON-Schema conversion. Callers may +supply optional JSON Schema metadata and own any validation in their handlers. +The ledger retains these legacy capabilities as explicit migration omissions; +current Angular behavior is unchanged. Protocol schema assets, form validation and +transport decoding remain in scope. + +```sh +NX_DAEMON=false CI=true npx nx run-many -t lint test type-tests build --projects=core,content,angular,react --parallel=2 --skip-nx-cache +NX_DAEMON=false CI=true npx nx run-many -t build --projects=chat,langgraph,ag-ui,render,a2ui,telemetry --configuration=production --parallel=1 --skip-nx-cache +node scripts/react-parity/verify-boundaries.mjs --built +node scripts/react-parity/verify-packages.mjs +node scripts/react-parity/verify-angular-package.mjs +``` + +The plain packaging check packs core, content and React, validates their nine export +paths, README/license inclusion and production exclusions, then imports and +type-checks the tarballs outside workspace aliases with `skipLibCheck: false`. +Its core-only consumer checks all three core exports and rejects extra dependencies. +The separate Angular check packs the one Angular APF entry and proves CLI +compilation/linking with `skipLibCheck: false`. Both inspect consumer module inputs +for unwanted parsers. Installation footprints include actual installed files; +lockfile locations also include optional platform packages. The Angular footprint +includes CLI/compiler/build tooling. These measurements prove scaffold packaging +and isolation, not runtime performance, React SSR or shared-runtime correctness. + +## Existing Angular regression checks + +```sh +NX_DAEMON=false CI=true npx nx run-many -t test --projects=chat,langgraph,ag-ui,render,a2ui,telemetry --parallel=1 --maxWorkers=2 --skip-nx-cache +NX_DAEMON=false CI=true npx nx run-many -t type-tests --projects=chat,langgraph,ag-ui --parallel=1 --skip-nx-cache +NX_DAEMON=false CI=true npx nx run-many -t build --projects=chat,langgraph,ag-ui,render,a2ui,telemetry --configuration=production --parallel=1 --skip-nx-cache +node --test examples/chat/smoke/*.spec.mjs scripts/verify-angular-support.spec.mjs +NX_DAEMON=false CI=true npx nx run telemetry:test-install-pack --skip-nx-cache +CI=true node examples/chat/smoke/cli.mjs --non-interactive --fresh --target tmp/react-parity/angular-21 --local-dist-root dist/libs --angular-major 21 --install --build --runtime +``` + +`--fresh` replaces the disposable generated consumer. The consumer uses local +Threadplane tarballs but independently resolves external ranges; that graph can +differ from the workspace lockfile. Its browser smoke is backend-free. Angular +20/21/22 remain separate existing CI lanes. See [baseline-evidence.json](./baseline-evidence.json) +for commands actually executed, environment and limitations. + +## Deterministic traces and performance limits + +`traces/*.sse` are newly authored synthetic fixtures with no captured user data or +credentials. Tests use the locked LangGraph SDK and AG-UI HTTP client, splitting +responses into three-byte chunks including a UTF-8 character boundary. They check +text/final state, request identity and terminal reduction without duplicate messages. +They establish decoder behavior, not parity between Threadplane framework bindings. + +The existing LangGraph stream-manager/agent tests cover cancellation, delivery +generations, queues and staged results. AG-UI interruption, resume-wire and +persistence tests cover recovery and request serialization. Client-tool tests cover +claim/record, cancellation and completed-result reuse. Later migration tasks must +replay these behaviors through the extracted runtime and both bindings. + +The parser work test checks linear character processing under duplicate cumulative +argument updates. A single test duration is not a calibrated performance budget. +Consumer bundle warnings describe the canonical example, not the headless package. +T38 still requires stream-to-paint latency, React commit work, long tasks, retained +heap and emitted-module graphs on recorded hardware, including long transcripts, +burst streams and repeated agent/thread disposal. + +## Maintenance and release + +The integrated candidate is `codex/react-support-baseline`. The local maintenance +branch `codex/angular-maintenance-v0.2` points to released tag `v0.2.0` +(`8daea78d35bfa27513474bd624d0e9495af3cfab`) and retains its released lockfile. +Creating that local branch does not establish an operated release lane: a maintainer +must own the backport/publication workflow before it is used. No Angular facade +currently depends on a new package. Version/tag enforcement, remote maintenance +policy and a tested backport/rollback remain T37 work. The existing release group +is unchanged, and none of the scaffolds is publishable. + +## Task identifiers + +The following task index makes the ownership ledger readable independently of local +research documents. It is a scope map, not evidence that the tasks are complete. +T01/T02 are this foundation increment. The next G1 proof is deliberately limited +to shared LangGraph text streaming and fixed function-tool execution with borrowed native +Angular and React bindings: the runtime owns execution while each binding observes +it. Renderer reuse and SSR are deferred gates, alongside the broader T01–T39 map. +No runtime proof or parity is claimed by these foundations. + +| Task | Scope | +| --- | --- | +| T01 | Establish the parity baseline and finding ledger | +| T02 | Create package scaffolding and dependency boundaries | +| T03 | Extract data contracts and error identity; omit tool schema ownership | +| T04 | Implement immutable publication and execution scope | +| T05 | Build behavioral replay and unchanged Angular consumer fixtures | +| T06 | Prove native binding and execution ownership; full SSR is deferred to T21 | +| T07 | Prove renderer reuse before writing the catalog | +| T08 | Extract LangGraph transport and request normalization | +| T09 | Extract LangGraph event reduction and delivery projection | +| T10 | Extract LangGraph cancellation, history, branching, queues and subagents | +| T11 | Rebind Angular LangGraph and extract thread services | +| T12 | Extract AG-UI event processing and transaction state | +| T13 | Extract AG-UI interrupts and durable recovery | +| T14 | Extract AG-UI lifecycle and rebind Angular | +| T15 | Extract client-tool declarations and execution; callers own argument validation | +| T16 | Unify tool coordination and correlate presentation results | +| T17 | Separate neutral telemetry from framework integration | +| T18 | Extract Markdown documents and citation projections | +| T19 | Extract JSON classification and A2UI surface processing | +| T20 | Extract render state, readiness and form-session helpers | +| T21 | Introduce safe SSR dehydration and browser boundaries | +| T22 | Build React component foundations, styles and overlays | +| T23 | Implement transcript, composer and basic chat composition | +| T24 | Implement React render registry, context and element lifecycle | +| T25 | Implement render actions, host events and tool views | +| T26 | Implement all Markdown views and citation UI | +| T27 | Implement A2UI surface and all 18 catalog views | +| T28 | Implement approvals, tools, reasoning and subagent UI | +| T29 | Implement thread/project/search/history/timeline/debug surfaces | +| T30 | Implement popup, sidebar and sidenav compositions | +| T31 | Create canonical React applications and public testing utilities | +| T32 | Add frontend identity to registry and runtime bridge | +| T33 | Build React cockpit host and all 41 scenario variants | +| T34 | Make docs routes, navigation and search framework-aware | +| T35 | Author and generate React documentation and agent context | +| T36 | Expand CI and packed consumer compatibility | +| T37 | Prepare prerelease, publishing, provenance and rollback | +| T38 | Measure and optimize streaming, bundles and retention | +| T39 | Complete accessibility, parity audit and maintenance handoff | diff --git a/fixtures/react-parity/baseline-evidence.json b/fixtures/react-parity/baseline-evidence.json new file mode 100644 index 000000000..d4a9e9fd5 --- /dev/null +++ b/fixtures/react-parity/baseline-evidence.json @@ -0,0 +1,254 @@ +{ + "schemaVersion": 1, + "observedOn": "2026-09-21", + "sourceHead": "7e80cebd607f5c605ef1ec81f11e5ebe3324f81b", + "sourceState": "Uncommitted foundation working tree based on sourceHead; no eventual commit SHA is asserted.", + "scope": "Four private empty scaffolds and regression/boundary/packaging gates; no G1 runtime proof or React parity.", + "environment": { + "os": "Darwin", + "architecture": "arm64", + "cpu": "Apple M1 Max", + "memoryBytes": 34359738368, + "node": "v24.20.0", + "npm": "11.19.0", + "typescript": "5.9.3", + "vitest": "4.1.0", + "nx": "22.5.1", + "nxVitestExecutor": "22.6.0", + "ciNodeNotUsed": "22.22.3" + }, + "rootLockfileSha256": "b16eed343e4be6e7d677ffccd7ed004191682eaf248287b1ed4e0fd3bd9f4b5e", + "checks": [ + { + "id": "foundation-node-suites", + "exitCode": 0, + "logSha256": "bd7b2a542f1321ce4b4d3214e61d374e5ebdc948d6391d34f1cdb361c5d5fa8b", + "logRetention": "Local execution log is ephemeral; rerun the recorded command for fresh evidence.", + "reproductionCommand": "node --test scripts/ci-scope.spec.mjs scripts/ci-workflow.spec.mjs scripts/react-parity/*.spec.mjs fixtures/react-parity/traces.spec.mjs", + "note": "384 tests passed; independent rerun below also passed 384." + }, + { + "id": "independent-node-suites", + "exitCode": 0, + "logSha256": "3018af731147c3e8292f9a63ce249915b4e6ec318ad2b078f729f8d5cf7fcb4d", + "logRetention": "Local execution log is ephemeral; rerun the recorded command for fresh evidence.", + "command": "node --test scripts/ci-scope.spec.mjs scripts/ci-workflow.spec.mjs scripts/react-parity/*.spec.mjs fixtures/react-parity/traces.spec.mjs" + }, + { + "id": "foundation-targets", + "exitCode": 0, + "logSha256": "569c4cf93d399046a0974c24361d68897ba0f307d52ec7ae837b9149c03fa449", + "logRetention": "Local execution log is ephemeral; rerun the recorded command for fresh evidence.", + "reproductionCommand": "NX_DAEMON=false NX_TUI=false CI=true npx nx run-many -t lint test type-tests build --projects=core,content,angular,react --parallel=2 --skip-nx-cache --outputStyle=static", + "note": "16 target configurations across four empty scaffolds; no runtime behavior tests." + }, + { + "id": "source-boundaries", + "exitCode": 0, + "logSha256": "995db896b38cf7de5ca9db6590dba47e082d26abdf69bab04b92eee484696063", + "logRetention": "Local execution log is ephemeral; rerun the recorded command for fresh evidence.", + "command": "node scripts/react-parity/verify-boundaries.mjs" + }, + { + "id": "built-boundaries", + "exitCode": 0, + "logSha256": "c983a97d17ff7ced7aa9113afa031f464103f566f1bc94eec8160558ec2553f7", + "logRetention": "Local execution log is ephemeral; rerun the recorded command for fresh evidence.", + "command": "node scripts/react-parity/verify-boundaries.mjs --built" + }, + { + "id": "plain-foundation-consumers", + "exitCode": 0, + "logSha256": "90a04e6f3e258aa716f2f59fdb9589bfbe30e30509bf414941f21be23762e1f3", + "logRetention": "Local execution log is ephemeral; rerun the recorded command for fresh evidence.", + "command": "node scripts/react-parity/verify-packages.mjs" + }, + { + "id": "angular-foundation-consumer", + "exitCode": 0, + "logSha256": "c6b87b930c2611fde7d3428a974c81280810f99179aa208fb7e47c93d23f0a1b", + "logRetention": "Local execution log is ephemeral; rerun the recorded command for fresh evidence.", + "command": "node scripts/react-parity/verify-angular-package.mjs" + }, + { + "id": "inventory-check", + "exitCode": 0, + "logSha256": "f074835105020dcfd8dbbbd8fd594e164ee791e5df68413b52c8ec48eaa4e985", + "logRetention": "Local execution log is ephemeral; rerun the recorded command for fresh evidence.", + "command": "node scripts/react-parity/inventory.mjs --check" + }, + { + "id": "library-tests", + "exitCode": 0, + "logSha256": "a92f99b91e44eeee4ead10e54e1f6b1b230ba800c8413f4b642a3bd153028e85", + "logRetention": "Local execution log is ephemeral; rerun the recorded command for fresh evidence.", + "command": "NX_DAEMON=false NX_TUI=false CI=true npx nx run-many -t test --projects=chat,langgraph,ag-ui,render,a2ui,telemetry --parallel=1 --maxWorkers=2 --skip-nx-cache --outputStyle=static" + }, + { + "id": "type-tests", + "exitCode": 0, + "logSha256": "e6cd13c8a6492171f33578e6c6f6644b067722013006a2cabc4f50f403ecf26b", + "logRetention": "Local execution log is ephemeral; rerun the recorded command for fresh evidence.", + "command": "NX_DAEMON=false NX_TUI=false CI=true npx nx run-many -t type-tests --projects=chat,langgraph,ag-ui --parallel=1 --skip-nx-cache --outputStyle=static" + }, + { + "id": "existing-library-lint", + "exitCode": 0, + "logSha256": "020d0db8c7d98adfd96994508ee837bf2ce6d6efc83c3e93836d5ce5ea208bd1", + "logRetention": "Local execution log is ephemeral; rerun the recorded command for fresh evidence.", + "command": "NX_DAEMON=false NX_TUI=false CI=true npx nx run-many -t lint --projects=chat,langgraph,ag-ui,render,a2ui,telemetry --parallel=2 --skip-nx-cache --outputStyle=static" + }, + { + "id": "production-builds", + "exitCode": 0, + "logSha256": "726d7d105c725e6fd4f422bcbfc49107a3f6cbbffd6444c207c49d8d00450a41", + "logRetention": "Local execution log is ephemeral; rerun the recorded command for fresh evidence.", + "command": "NX_DAEMON=false NX_TUI=false CI=true npx nx run-many -t build --projects=chat,langgraph,ag-ui,render,a2ui,telemetry --configuration=production --parallel=1 --skip-nx-cache --outputStyle=static" + }, + { + "id": "consumer-harness-tests", + "exitCode": 0, + "logSha256": "8c58dfb1cde1ef0688049f63b695350c8f9fcc3fa951aed0d03653028a7ebda9", + "logRetention": "Local execution log is ephemeral; rerun the recorded command for fresh evidence.", + "command": "node --test examples/chat/smoke/*.spec.mjs scripts/verify-angular-support.spec.mjs" + }, + { + "id": "angular-21-legacy-consumer", + "exitCode": 0, + "logSha256": "b138985a27837cd0ed1e856ec86bd80ed2ae627620cf4e12672d37511d6ac1e6", + "logRetention": "Local execution log is ephemeral; rerun the recorded command for fresh evidence.", + "reproductionCommand": "NX_DAEMON=false NX_TUI=false CI=true PUPPETEER_SKIP_DOWNLOAD=true PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 node examples/chat/smoke/cli.mjs --non-interactive --fresh --target tmp/react-parity/angular-21 --local-dist-root dist/libs --angular-major 21 --install --build --runtime" + }, + { + "id": "verification-script-lint", + "exitCode": 0, + "logSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "logRetention": "Local execution log is ephemeral; rerun the recorded command for fresh evidence.", + "command": "node node_modules/eslint/bin/eslint.js scripts/react-parity/package-policy.mjs scripts/react-parity/package-policy.spec.mjs scripts/react-parity/verify-boundaries.mjs scripts/react-parity/verify-boundaries.spec.mjs scripts/react-parity/verify-packages.mjs scripts/react-parity/verify-packages.spec.mjs scripts/react-parity/verify-angular-package.mjs scripts/react-parity/verify-angular-package.spec.mjs scripts/ci-scope.mjs scripts/ci-scope.spec.mjs scripts/ci-workflow.spec.mjs" + } + ], + "lockfileAudit": { + "method": "Compare registry package entries against base HEAD separately from reviewed workspace links and root metadata.", + "exitCode": 0, + "logSha256": "70e8222a915522423503273e6a0b9e68beef6609b0f00a3d74ffc2c2ca7856e3", + "changedRegistryPackageEntries": [], + "lockEntries": 3047, + "reviewedChanges": [ + "Direct semver 7.7.4 tooling dependency, already resolved in the lockfile", + "Replace retired scaffold workspace links with the Angular scaffold and its peer metadata" + ] + }, + "scopeCounts": { + "libraryTestTargetsPassed": 6, + "typeTestTargetsPassed": 3, + "libraryLintTargetsPassed": 6, + "productionBuildTargetsPassed": 6, + "consumerHarnessTestsPassed": 44, + "foundationTargetConfigurationsPassed": 16, + "foundationLibraryBehaviorTests": 0, + "privateTarballs": 4, + "plainExportPaths": 9, + "angularExportPaths": 1, + "focusedNodeTestsPassed": 384, + "independentFocusedNodeTestsPassed": 384, + "inventoryRows": 1438, + "componentDeclarations": 102, + "publicExportOccurrences": 550, + "sourceFiles": 461, + "packageSupportAssets": 128, + "distributionConfigs": 16, + "publicEntryPoints": 12, + "cockpitTopics": 41, + "docsPages": 128, + "uniqueExportDefinitions": 514 + }, + "foundationConsumers": { + "core": { + "installedPackages": 1, + "installedFileBytes": 3639, + "lockLocations": 1, + "exportsChecked": 3 + }, + "plain": { + "installedPackages": 3, + "installedFileBytes": 10837, + "lockLocations": 3, + "exportsChecked": 9, + "packages": [ + "core", + "content", + "react" + ] + }, + "reactRootBundle": { + "inputModules": 2, + "bytes": 128, + "contentParserInputs": 0 + }, + "angular": { + "installedPackages": 410, + "installedFileBytes": 196886714, + "lockLocationsIncludingOptionalPlatforms": 515, + "includes": "Angular CLI, compiler and build development tooling", + "exportsChecked": 1, + "initialBundleRawReported": "92.07 kB", + "initialBundleEstimatedTransferReported": "27.62 kB", + "bundleInputModules": 252, + "threadplaneAPFInputModules": 1, + "contentParserInputs": 0, + "angularCore": "21.1.6", + "angularCli": "21.1.5" + }, + "limits": "Installed files, lockfile locations and bundle inputs measure different surfaces. These empty-scaffold measurements are not runtime benchmarks. Both consumers use skipLibCheck:false." + }, + "consumer": { + "outputDirectory": "tmp/react-parity/angular-21", + "threadplaneVersion": "0.2.0", + "angularMajor": 21, + "install": "local tarballs for all six published libraries, independently resolved external dependency ranges", + "runtime": "backend-free Playwright Chromium compatibility smoke", + "resolvedExternalDependencies": { + "node_modules/@ag-ui/client": "0.0.59", + "node_modules/@angular/core": "21.2.22", + "node_modules/@cacheplane/partial-json": "0.2.2", + "node_modules/@cacheplane/partial-markdown": "0.3.2", + "node_modules/@langchain/core": "1.2.12", + "node_modules/@langchain/langgraph-sdk": "1.11.2", + "node_modules/@threadplane/chat/node_modules/@cacheplane/partial-markdown": "0.5.8", + "node_modules/typescript": "5.9.3" + }, + "initialBundleRawReported": "1.91 MB", + "initialBundleEstimatedTransferReported": "390.10 kB", + "budgetWarning": "500.00 kB initial budget exceeded by 1.41 MB" + }, + "stableLane": { + "tag": "v0.2.0", + "peeledCommit": "8daea78d35bfa27513474bd624d0e9495af3cfab", + "localBranch": "codex/angular-maintenance-v0.2", + "lockfileSha256": "a8c25ef403eedaad064d5da45ec4f7dc2b66538597a29ee4796b5e52b2f42e26", + "verification": "Local branch/tag commit and lockfile verified. Remote branch status, publication policy and operated maintenance workflow were not established." + }, + "candidateLane": { + "branch": "codex/react-support-baseline", + "baseCommit": "7e80cebd607f5c605ef1ec81f11e5ebe3324f81b", + "workingTree": "Uncommitted P01\u2013P06 foundation changes; populated Angular implementations unchanged." + }, + "warnings": [ + "Existing Angular lint targets pass with existing warnings.", + "Native builds report stale Browserslist data and packaging lifecycle-script notices.", + "Legacy Angular consumer emits NG8107, initial bundle budget and p-queue CommonJS warnings." + ], + "notRun": [ + "Angular20 and Angular22 consumer lanes", + "CI Node22.22.3 environment", + "Live backend/deployment smoke", + "Calibrated latency, heap retention or stream-to-paint benchmarks", + "Shared runtime extraction or native binding behavior proof", + "React runtime/component/SSR tests and renderer reuse proof", + "Release publication or backport rehearsal" + ], + "review": { + "scope": "P01\u2013P06 foundation code, ledger and evidence received independent specification and quality approval. Review findings were addressed before commit.", + "implementationParity": false + } +} diff --git a/fixtures/react-parity/consumers/angular/angular.json b/fixtures/react-parity/consumers/angular/angular.json new file mode 100644 index 000000000..7e1f2a543 --- /dev/null +++ b/fixtures/react-parity/consumers/angular/angular.json @@ -0,0 +1,25 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "cli": { "analytics": false, "packageManager": "npm" }, + "projects": { + "consumer": { + "projectType": "application", + "root": "", + "sourceRoot": "src", + "architect": { + "build": { + "builder": "@angular/build:application", + "options": { + "browser": "src/main.ts", + "index": "src/index.html", + "tsConfig": "tsconfig.app.json", + "outputPath": "dist/consumer" + }, + "configurations": { "production": { "optimization": true, "outputHashing": "all" } }, + "defaultConfiguration": "production" + } + } + } + } +} diff --git a/fixtures/react-parity/consumers/angular/package.json b/fixtures/react-parity/consumers/angular/package.json new file mode 100644 index 000000000..8ff3eaa3a --- /dev/null +++ b/fixtures/react-parity/consumers/angular/package.json @@ -0,0 +1,7 @@ +{ + "name": "threadplane-angular-foundation-consumer", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { "build": "ng build --configuration=production --stats-json" } +} diff --git a/fixtures/react-parity/consumers/angular/src/index.html b/fixtures/react-parity/consumers/angular/src/index.html new file mode 100644 index 000000000..dabfde832 --- /dev/null +++ b/fixtures/react-parity/consumers/angular/src/index.html @@ -0,0 +1,5 @@ + + +
Private Angular scaffold: {{ supportedExportCount }} supported exports.
', +}) +class App { + readonly supportedExportCount = Object.keys(angular).length /* PACKAGE_EXPORT_COUNT */; +} + +bootstrapApplication(App).catch(console.error); diff --git a/fixtures/react-parity/consumers/angular/tsconfig.app.json b/fixtures/react-parity/consumers/angular/tsconfig.app.json new file mode 100644 index 000000000..60a1469b2 --- /dev/null +++ b/fixtures/react-parity/consumers/angular/tsconfig.app.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { "outDir": "./out-tsc/app", "types": [] }, + "files": ["src/main.ts"] +} diff --git a/fixtures/react-parity/consumers/angular/tsconfig.json b/fixtures/react-parity/consumers/angular/tsconfig.json new file mode 100644 index 000000000..960967cc8 --- /dev/null +++ b/fixtures/react-parity/consumers/angular/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "preserve", + "moduleResolution": "bundler", + "lib": ["ES2022", "DOM"], + "strict": true, + "skipLibCheck": false, + "isolatedModules": true, + "experimentalDecorators": true, + "importHelpers": true + }, + "angularCompilerOptions": { + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/fixtures/react-parity/traces.spec.mjs b/fixtures/react-parity/traces.spec.mjs new file mode 100644 index 000000000..aadb5e81d --- /dev/null +++ b/fixtures/react-parity/traces.spec.mjs @@ -0,0 +1,66 @@ +import assert from 'node:assert/strict'; +import { readFile } from 'node:fs/promises'; +import test from 'node:test'; +import { Client } from '@langchain/langgraph-sdk'; +import { HttpAgent } from '@ag-ui/client'; + +// Synthetic wire fixtures only. Production adapter/conformance suites remain +// authoritative for reduction, execution ownership, interrupts, and recovery. +async function response(name) { + const bytes = new Uint8Array(await readFile(new URL(`./traces/${name}`, import.meta.url))); + let offset = 0; + return new Response(new ReadableStream({ + pull(controller) { + if (offset === bytes.length) return controller.close(); + const end = Math.min(offset + 3, bytes.length); + controller.enqueue(bytes.slice(offset, end)); + offset = end; + }, + }), { headers: { 'content-type': 'text/event-stream' } }); +} + +test('LangGraph SDK decodes the synthetic delta/canonical trace across split UTF-8 chunks', async () => { + const requests = []; + const client = new Client({ + apiUrl: 'https://parity.invalid', + callerOptions: { + maxRetries: 0, + fetch: async (url, init) => { + requests.push({ url: String(url), body: JSON.parse(init.body) }); + return response('langgraph-text-state.sse'); + }, + }, + }); + const events = []; + for await (const event of client.runs.stream('thread-parity', 'parity', { + input: { messages: [{ role: 'user', content: 'Say hello.' }] }, + streamMode: ['messages', 'values'], + })) events.push(event); + assert.equal(requests.length, 1); + assert.match(requests[0].url, /\/threads\/thread-parity\/runs\/stream$/); + assert.deepEqual(requests[0].body.input.messages, [{ role: 'user', content: 'Say hello.' }]); + const deltas = events.filter(event => event.event === 'messages'); + assert.equal(deltas.map(event => event.data[0].content).join(''), 'Hello 🌍.'); + const final = events.findLast(event => event.event === 'values'); + assert.equal(final.data.messages[0].content, 'Hello 🌍.'); + assert.equal(final.data.messages[0].id, 'message-parity'); + assert.equal(final.data.stage, 'complete'); +}); + +test('AG-UI client completes the synthetic text/state trace without duplicate messages', async () => { + const requests = []; + const source = new HttpAgent({ + url: 'https://parity.invalid/agent', + threadId: 'thread-parity', + fetch: async (url, init) => { + requests.push({ url: String(url), body: JSON.parse(init.body) }); + return response('ag-ui-text-state.sse'); + }, + }); + await source.runAgent({ runId: 'run-parity' }); + assert.equal(requests.length, 1); + assert.equal(requests[0].body.threadId, 'thread-parity'); + assert.equal(source.isRunning, false); + assert.deepEqual(source.messages, [{ id: 'message-parity', role: 'assistant', content: 'Hello 🌍.' }]); + assert.deepEqual(source.state, { stage: 'complete' }); +}); diff --git a/fixtures/react-parity/traces/.gitattributes b/fixtures/react-parity/traces/.gitattributes new file mode 100644 index 000000000..2b503887f --- /dev/null +++ b/fixtures/react-parity/traces/.gitattributes @@ -0,0 +1,2 @@ +# SSE dispatches the final event at its terminating blank line. +*.sse whitespace=-blank-at-eof diff --git a/fixtures/react-parity/traces/ag-ui-text-state.sse b/fixtures/react-parity/traces/ag-ui-text-state.sse new file mode 100644 index 000000000..6a66384ee --- /dev/null +++ b/fixtures/react-parity/traces/ag-ui-text-state.sse @@ -0,0 +1,16 @@ +data: {"type":"RUN_STARTED","threadId":"thread-parity","runId":"run-parity"} + +data: {"type":"STATE_SNAPSHOT","snapshot":{"stage":"streaming"}} + +data: {"type":"TEXT_MESSAGE_START","messageId":"message-parity","role":"assistant"} + +data: {"type":"TEXT_MESSAGE_CONTENT","messageId":"message-parity","delta":"Hello "} + +data: {"type":"TEXT_MESSAGE_CONTENT","messageId":"message-parity","delta":"🌍."} + +data: {"type":"TEXT_MESSAGE_END","messageId":"message-parity"} + +data: {"type":"STATE_SNAPSHOT","snapshot":{"stage":"complete"}} + +data: {"type":"RUN_FINISHED","threadId":"thread-parity","runId":"run-parity"} + diff --git a/fixtures/react-parity/traces/langgraph-text-state.sse b/fixtures/react-parity/traces/langgraph-text-state.sse new file mode 100644 index 000000000..ceacdcff7 --- /dev/null +++ b/fixtures/react-parity/traces/langgraph-text-state.sse @@ -0,0 +1,12 @@ +event: metadata +data: {"run_id":"run-parity","attempt":1} + +event: messages +data: [{"type":"AIMessageChunk","id":"message-parity","content":"Hello "},{"langgraph_node":"assistant"}] + +event: messages +data: [{"type":"AIMessageChunk","id":"message-parity","content":"🌍."},{"langgraph_node":"assistant"}] + +event: values +data: {"messages":[{"type":"ai","id":"message-parity","content":"Hello 🌍."}],"stage":"complete"} + diff --git a/libs/angular/LICENSE.md b/libs/angular/LICENSE.md new file mode 100644 index 000000000..c1a0122f7 --- /dev/null +++ b/libs/angular/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Brian Love d/b/a cacheplane + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/libs/angular/README.md b/libs/angular/README.md new file mode 100644 index 000000000..63063389d --- /dev/null +++ b/libs/angular/README.md @@ -0,0 +1,14 @@ +# @threadplane/angular + +Private, unpublished Angular foundation scaffolding. The empty root entry point +provides no supported runtime API or secondary entry points. Angular bindings, +providers, components, and backend integrations are not implemented. + +Build with `npx nx build angular`. The production target uses ng-packagr and +partial Angular compilation to emit the Angular Package Format. Tests currently +contain no cases and pass with `passWithNoTests`; the type-tests target checks +the empty source with TypeScript. + +The declared Angular peer range follows the existing workspace packages. It is +not a compatibility-matrix claim. An installed Angular CLI consumer must verify +package resolution and linking separately before this foundation is complete. diff --git a/libs/angular/ng-package.json b/libs/angular/ng-package.json new file mode 100644 index 000000000..f836f38de --- /dev/null +++ b/libs/angular/ng-package.json @@ -0,0 +1,6 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/libs/angular", + "lib": { "entryFile": "src/public-api.ts" }, + "assets": ["LICENSE.md", "README.md"] +} diff --git a/libs/angular/package.json b/libs/angular/package.json new file mode 100644 index 000000000..bd0dbf964 --- /dev/null +++ b/libs/angular/package.json @@ -0,0 +1,19 @@ +{ + "name": "@threadplane/angular", + "version": "0.0.0", + "private": true, + "description": "Private Angular foundation scaffolding. No supported runtime API yet.", + "license": "MIT", + "type": "module", + "sideEffects": false, + "peerDependencies": { + "@angular/core": "^20.0.0 || ^21.0.0 || ^22.0.0" + }, + "files": [ + "fesm2022/*.mjs", + "fesm2022/*.mjs.map", + "types/**/*.d.ts", + "LICENSE.md", + "README.md" + ] +} diff --git a/libs/angular/project.json b/libs/angular/project.json new file mode 100644 index 000000000..38a2ec79d --- /dev/null +++ b/libs/angular/project.json @@ -0,0 +1,35 @@ +{ + "name": "angular", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/angular/src", + "projectType": "library", + "tags": ["scope:react-parity", "scope:library", "type:lib", "layer:angular"], + "targets": { + "build": { + "executor": "@nx/angular:package", + "outputs": ["{workspaceRoot}/dist/{projectRoot}"], + "options": { + "project": "libs/angular/ng-package.json", + "tsConfig": "libs/angular/tsconfig.lib.json" + }, + "configurations": { + "production": { "tsConfig": "libs/angular/tsconfig.lib.prod.json" }, + "development": {} + }, + "defaultConfiguration": "production" + }, + "lint": { "executor": "@nx/eslint:lint" }, + "test": { + "executor": "@nx/vitest:test", + "options": { "configFile": "libs/angular/vite.config.mts" } + }, + "type-tests": { + "executor": "nx:run-commands", + "cache": true, + "inputs": ["default", "^production"], + "options": { + "command": "node node_modules/typescript/bin/tsc --project libs/angular/tsconfig.spec.json --noEmit --emitDeclarationOnly false --composite false --incremental false" + } + } + } +} diff --git a/libs/angular/src/public-api.ts b/libs/angular/src/public-api.ts new file mode 100644 index 000000000..cb0ff5c3b --- /dev/null +++ b/libs/angular/src/public-api.ts @@ -0,0 +1 @@ +export {}; diff --git a/libs/angular/tsconfig.json b/libs/angular/tsconfig.json new file mode 100644 index 000000000..83e52b464 --- /dev/null +++ b/libs/angular/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "experimentalDecorators": true, + "noPropertyAccessFromIndexSignature": true, + "module": "preserve", + "emitDeclarationOnly": false, + "composite": false + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + }, + "files": [], + "include": [], + "references": [ + { "path": "./tsconfig.lib.json" }, + { "path": "./tsconfig.spec.json" } + ] +} diff --git a/libs/angular/tsconfig.lib.json b/libs/angular/tsconfig.lib.json new file mode 100644 index 000000000..9860c8eec --- /dev/null +++ b/libs/angular/tsconfig.lib.json @@ -0,0 +1,18 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc/angular", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "lib": ["es2022", "dom"], + "types": [] + }, + "include": ["src/**/*.ts"], + "exclude": [ + "src/**/*.spec.ts", + "src/**/*.test.ts", + "src/**/*.type-test.ts", + "src/test-setup.ts" + ] +} diff --git a/libs/angular/tsconfig.lib.prod.json b/libs/angular/tsconfig.lib.prod.json new file mode 100644 index 000000000..1d3d0bab7 --- /dev/null +++ b/libs/angular/tsconfig.lib.prod.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { "declarationMap": false }, + "angularCompilerOptions": { "compilationMode": "partial" } +} diff --git a/libs/angular/tsconfig.spec.json b/libs/angular/tsconfig.spec.json new file mode 100644 index 000000000..786a68ab2 --- /dev/null +++ b/libs/angular/tsconfig.spec.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declaration": false, + "declarationMap": false, + "inlineSources": false, + "types": ["vitest/globals"] + }, + "include": ["src/**/*.ts"], + "exclude": [] +} diff --git a/libs/angular/vite.config.mts b/libs/angular/vite.config.mts new file mode 100644 index 000000000..382d0ec66 --- /dev/null +++ b/libs/angular/vite.config.mts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + root: import.meta.dirname, + test: { + environment: 'node', + include: ['src/**/*.spec.ts', 'src/**/*.test.ts'], + passWithNoTests: true, + }, +}); diff --git a/libs/content/LICENSE.md b/libs/content/LICENSE.md new file mode 100644 index 000000000..c1a0122f7 --- /dev/null +++ b/libs/content/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Brian Love d/b/a cacheplane + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/libs/content/README.md b/libs/content/README.md new file mode 100644 index 000000000..3fc701ae1 --- /dev/null +++ b/libs/content/README.md @@ -0,0 +1,14 @@ +# @threadplane/content + +Private, unpublished foundation scaffolding for the React parity work. These empty +entry points reserve planned package boundaries; they provide no supported runtime +API. No React bindings, stores, renderers, or backend adapters are implemented. + +Reserved exports: `@threadplane/content`, `@threadplane/content/markdown`, `@threadplane/content/json`, `@threadplane/content/a2ui`, `@threadplane/content/testing`. + +Framework-neutral render contracts belong to `@threadplane/render`; content owns +the higher-level content protocols that use those contracts. + +Build with `npx nx build content`. Packaging and dependency boundaries are +verified by the scripts in `scripts/react-parity`. Optional and testing entry +points must remain unreachable from the root runtime and declarations. diff --git a/libs/content/package.json b/libs/content/package.json new file mode 100644 index 000000000..98ac5d462 --- /dev/null +++ b/libs/content/package.json @@ -0,0 +1,43 @@ +{ + "name": "@threadplane/content", + "version": "0.0.0", + "private": true, + "description": "Private content foundation scaffolding. No supported runtime API yet.", + "license": "MIT", + "type": "module", + "sideEffects": false, + "files": [ + "src/**/*.js", + "src/**/*.d.ts", + "src/**/*.d.ts.map", + "LICENSE.md", + "README.md" + ], + "exports": { + ".": { + "types": "./src/index.d.ts", + "import": "./src/index.js", + "default": "./src/index.js" + }, + "./markdown": { + "types": "./src/markdown/index.d.ts", + "import": "./src/markdown/index.js", + "default": "./src/markdown/index.js" + }, + "./json": { + "types": "./src/json/index.d.ts", + "import": "./src/json/index.js", + "default": "./src/json/index.js" + }, + "./a2ui": { + "types": "./src/a2ui/index.d.ts", + "import": "./src/a2ui/index.js", + "default": "./src/a2ui/index.js" + }, + "./testing": { + "types": "./src/testing/index.d.ts", + "import": "./src/testing/index.js", + "default": "./src/testing/index.js" + } + } +} diff --git a/libs/content/project.json b/libs/content/project.json new file mode 100644 index 000000000..baf0c8495 --- /dev/null +++ b/libs/content/project.json @@ -0,0 +1,59 @@ +{ + "name": "content", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/content/src", + "projectType": "library", + "tags": [ + "scope:react-parity", + "type:lib", + "layer:content", + "scope:library" + ], + "targets": { + "build": { + "executor": "@nx/js:tsc", + "cache": true, + "dependsOn": [ + "^build" + ], + "inputs": [ + "production", + "^production" + ], + "outputs": [ + "{workspaceRoot}/dist/libs/content" + ], + "options": { + "outputPath": "dist/libs/content", + "main": "libs/content/src/index.ts", + "tsConfig": "libs/content/tsconfig.lib.json", + "generatePackageJson": false, + "assets": [ + "libs/content/package.json", + "libs/content/LICENSE.md", + "libs/content/README.md" + ] + } + }, + "lint": { + "executor": "@nx/eslint:lint" + }, + "test": { + "executor": "@nx/vitest:test", + "options": { + "configFile": "libs/content/vite.config.mts" + } + }, + "type-tests": { + "executor": "nx:run-commands", + "cache": true, + "inputs": [ + "default", + "^production" + ], + "options": { + "command": "node node_modules/typescript/bin/tsc --project libs/content/tsconfig.spec.json --noEmit --emitDeclarationOnly false --composite false --incremental false" + } + } + } +} diff --git a/libs/content/src/a2ui/index.ts b/libs/content/src/a2ui/index.ts new file mode 100644 index 000000000..d045c0857 --- /dev/null +++ b/libs/content/src/a2ui/index.ts @@ -0,0 +1,2 @@ +// Reserved private entry point. Runtime implementation follows in later work. +export {}; diff --git a/libs/content/src/index.ts b/libs/content/src/index.ts new file mode 100644 index 000000000..d045c0857 --- /dev/null +++ b/libs/content/src/index.ts @@ -0,0 +1,2 @@ +// Reserved private entry point. Runtime implementation follows in later work. +export {}; diff --git a/libs/content/src/json/index.ts b/libs/content/src/json/index.ts new file mode 100644 index 000000000..d045c0857 --- /dev/null +++ b/libs/content/src/json/index.ts @@ -0,0 +1,2 @@ +// Reserved private entry point. Runtime implementation follows in later work. +export {}; diff --git a/libs/content/src/markdown/index.ts b/libs/content/src/markdown/index.ts new file mode 100644 index 000000000..d045c0857 --- /dev/null +++ b/libs/content/src/markdown/index.ts @@ -0,0 +1,2 @@ +// Reserved private entry point. Runtime implementation follows in later work. +export {}; diff --git a/libs/content/src/testing/index.ts b/libs/content/src/testing/index.ts new file mode 100644 index 000000000..d045c0857 --- /dev/null +++ b/libs/content/src/testing/index.ts @@ -0,0 +1,2 @@ +// Reserved private entry point. Runtime implementation follows in later work. +export {}; diff --git a/libs/content/tsconfig.json b/libs/content/tsconfig.json new file mode 100644 index 000000000..64d2d4b34 --- /dev/null +++ b/libs/content/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "types": [], + "lib": [ + "ES2022" + ] + }, + "files": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/content/tsconfig.lib.json b/libs/content/tsconfig.lib.json new file mode 100644 index 000000000..00695e4fa --- /dev/null +++ b/libs/content/tsconfig.lib.json @@ -0,0 +1,24 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "outDir": "../../dist/libs/content", + "declaration": true, + "emitDeclarationOnly": false, + "module": "ES2022", + "target": "ES2022" + }, + "include": [ + "src/**/*.ts", + "src/**/*.tsx" + ], + "exclude": [ + "src/**/*.spec.ts", + "src/**/*.spec.tsx", + "src/**/*.test.ts", + "src/**/*.test.tsx", + "src/**/*.type-test.ts", + "src/**/*.type-test.tsx", + "src/test-setup.ts" + ] +} diff --git a/libs/content/tsconfig.spec.json b/libs/content/tsconfig.spec.json new file mode 100644 index 000000000..c0047ae5d --- /dev/null +++ b/libs/content/tsconfig.spec.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "types": [ + "vitest/globals" + ], + "outDir": "../../dist/out-tsc/content" + }, + "include": [ + "src/**/*.ts", + "src/**/*.tsx" + ], + "exclude": [] +} diff --git a/libs/content/vite.config.mts b/libs/content/vite.config.mts new file mode 100644 index 000000000..f05211ebe --- /dev/null +++ b/libs/content/vite.config.mts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + root: import.meta.dirname, + test: { + environment: 'node', + include: ['src/**/*.spec.{ts,tsx}', 'src/**/*.test.{ts,tsx}'], + passWithNoTests: true, + }, +}); diff --git a/libs/core/LICENSE.md b/libs/core/LICENSE.md new file mode 100644 index 000000000..c1a0122f7 --- /dev/null +++ b/libs/core/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Brian Love d/b/a cacheplane + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/libs/core/README.md b/libs/core/README.md new file mode 100644 index 000000000..6edd905a7 --- /dev/null +++ b/libs/core/README.md @@ -0,0 +1,14 @@ +# @threadplane/core + +Private, unpublished foundation scaffolding for the React parity work. These empty +entry points reserve planned package boundaries; they provide no supported runtime +API. No React bindings, stores, renderers, or backend adapters are implemented. + +Reserved exports: `@threadplane/core`, `@threadplane/core/tools`, `@threadplane/core/testing`. + +Core owns dependency-free agent contracts. Schema validation belongs to consumers +and their chosen libraries. + +Build with `npx nx build core`. Packaging and dependency boundaries are +verified by the scripts in `scripts/react-parity`. Optional and testing entry +points must remain unreachable from the root runtime and declarations. diff --git a/libs/core/package.json b/libs/core/package.json new file mode 100644 index 000000000..3bc539bb0 --- /dev/null +++ b/libs/core/package.json @@ -0,0 +1,33 @@ +{ + "name": "@threadplane/core", + "version": "0.0.0", + "private": true, + "description": "Private core foundation scaffolding. No supported runtime API yet.", + "license": "MIT", + "type": "module", + "sideEffects": false, + "files": [ + "src/**/*.js", + "src/**/*.d.ts", + "src/**/*.d.ts.map", + "LICENSE.md", + "README.md" + ], + "exports": { + ".": { + "types": "./src/index.d.ts", + "import": "./src/index.js", + "default": "./src/index.js" + }, + "./tools": { + "types": "./src/tools/index.d.ts", + "import": "./src/tools/index.js", + "default": "./src/tools/index.js" + }, + "./testing": { + "types": "./src/testing/index.d.ts", + "import": "./src/testing/index.js", + "default": "./src/testing/index.js" + } + } +} diff --git a/libs/core/project.json b/libs/core/project.json new file mode 100644 index 000000000..cf7842e59 --- /dev/null +++ b/libs/core/project.json @@ -0,0 +1,59 @@ +{ + "name": "core", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/core/src", + "projectType": "library", + "tags": [ + "scope:react-parity", + "type:lib", + "layer:core", + "scope:library" + ], + "targets": { + "build": { + "executor": "@nx/js:tsc", + "cache": true, + "dependsOn": [ + "^build" + ], + "inputs": [ + "production", + "^production" + ], + "outputs": [ + "{workspaceRoot}/dist/libs/core" + ], + "options": { + "outputPath": "dist/libs/core", + "main": "libs/core/src/index.ts", + "tsConfig": "libs/core/tsconfig.lib.json", + "generatePackageJson": false, + "assets": [ + "libs/core/package.json", + "libs/core/LICENSE.md", + "libs/core/README.md" + ] + } + }, + "lint": { + "executor": "@nx/eslint:lint" + }, + "test": { + "executor": "@nx/vitest:test", + "options": { + "configFile": "libs/core/vite.config.mts" + } + }, + "type-tests": { + "executor": "nx:run-commands", + "cache": true, + "inputs": [ + "default", + "^production" + ], + "options": { + "command": "node node_modules/typescript/bin/tsc --project libs/core/tsconfig.spec.json --noEmit --emitDeclarationOnly false --composite false --incremental false" + } + } + } +} diff --git a/libs/core/src/index.ts b/libs/core/src/index.ts new file mode 100644 index 000000000..d045c0857 --- /dev/null +++ b/libs/core/src/index.ts @@ -0,0 +1,2 @@ +// Reserved private entry point. Runtime implementation follows in later work. +export {}; diff --git a/libs/core/src/testing/index.ts b/libs/core/src/testing/index.ts new file mode 100644 index 000000000..d045c0857 --- /dev/null +++ b/libs/core/src/testing/index.ts @@ -0,0 +1,2 @@ +// Reserved private entry point. Runtime implementation follows in later work. +export {}; diff --git a/libs/core/src/tools/index.ts b/libs/core/src/tools/index.ts new file mode 100644 index 000000000..d045c0857 --- /dev/null +++ b/libs/core/src/tools/index.ts @@ -0,0 +1,2 @@ +// Reserved private entry point. Runtime implementation follows in later work. +export {}; diff --git a/libs/core/tsconfig.json b/libs/core/tsconfig.json new file mode 100644 index 000000000..64d2d4b34 --- /dev/null +++ b/libs/core/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "types": [], + "lib": [ + "ES2022" + ] + }, + "files": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/core/tsconfig.lib.json b/libs/core/tsconfig.lib.json new file mode 100644 index 000000000..d0b6ad56c --- /dev/null +++ b/libs/core/tsconfig.lib.json @@ -0,0 +1,24 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "outDir": "../../dist/libs/core", + "declaration": true, + "emitDeclarationOnly": false, + "module": "ES2022", + "target": "ES2022" + }, + "include": [ + "src/**/*.ts", + "src/**/*.tsx" + ], + "exclude": [ + "src/**/*.spec.ts", + "src/**/*.spec.tsx", + "src/**/*.test.ts", + "src/**/*.test.tsx", + "src/**/*.type-test.ts", + "src/**/*.type-test.tsx", + "src/test-setup.ts" + ] +} diff --git a/libs/core/tsconfig.spec.json b/libs/core/tsconfig.spec.json new file mode 100644 index 000000000..0aafa3b7b --- /dev/null +++ b/libs/core/tsconfig.spec.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "types": [ + "vitest/globals" + ], + "outDir": "../../dist/out-tsc/core" + }, + "include": [ + "src/**/*.ts", + "src/**/*.tsx" + ], + "exclude": [] +} diff --git a/libs/core/vite.config.mts b/libs/core/vite.config.mts new file mode 100644 index 000000000..f05211ebe --- /dev/null +++ b/libs/core/vite.config.mts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + root: import.meta.dirname, + test: { + environment: 'node', + include: ['src/**/*.spec.{ts,tsx}', 'src/**/*.test.{ts,tsx}'], + passWithNoTests: true, + }, +}); diff --git a/libs/react/LICENSE.md b/libs/react/LICENSE.md new file mode 100644 index 000000000..c1a0122f7 --- /dev/null +++ b/libs/react/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Brian Love d/b/a cacheplane + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/libs/react/README.md b/libs/react/README.md new file mode 100644 index 000000000..8da3a8372 --- /dev/null +++ b/libs/react/README.md @@ -0,0 +1,15 @@ +# @threadplane/react + +Private, unpublished foundation scaffolding for the React parity work. The empty +root entry point provides no supported runtime API. No React bindings, stores, +renderers, or backend adapters are implemented. + +The only current export is `@threadplane/react`. React rendering and feature +bindings belong to this package; feature subpaths will be added with their +implementations. + +Build with `npx nx build react`. Packaging and dependency boundaries are +verified by the scripts in `scripts/react-parity`. Feature entry points must +remain unreachable from the root runtime and declarations. + +The root entry point retains `use client`. diff --git a/libs/react/package.json b/libs/react/package.json new file mode 100644 index 000000000..d55ec30d9 --- /dev/null +++ b/libs/react/package.json @@ -0,0 +1,23 @@ +{ + "name": "@threadplane/react", + "version": "0.0.0", + "private": true, + "description": "Private react foundation scaffolding. No supported runtime API yet.", + "license": "MIT", + "type": "module", + "sideEffects": false, + "files": [ + "src/**/*.js", + "src/**/*.d.ts", + "src/**/*.d.ts.map", + "LICENSE.md", + "README.md" + ], + "exports": { + ".": { + "types": "./src/index.d.ts", + "import": "./src/index.js", + "default": "./src/index.js" + } + } +} diff --git a/libs/react/project.json b/libs/react/project.json new file mode 100644 index 000000000..6ad5bdc20 --- /dev/null +++ b/libs/react/project.json @@ -0,0 +1,59 @@ +{ + "name": "react", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/react/src", + "projectType": "library", + "tags": [ + "scope:react-parity", + "type:lib", + "layer:react", + "scope:library" + ], + "targets": { + "build": { + "executor": "@nx/js:tsc", + "cache": true, + "dependsOn": [ + "^build" + ], + "inputs": [ + "production", + "^production" + ], + "outputs": [ + "{workspaceRoot}/dist/libs/react" + ], + "options": { + "outputPath": "dist/libs/react", + "main": "libs/react/src/index.ts", + "tsConfig": "libs/react/tsconfig.lib.json", + "generatePackageJson": false, + "assets": [ + "libs/react/package.json", + "libs/react/LICENSE.md", + "libs/react/README.md" + ] + } + }, + "lint": { + "executor": "@nx/eslint:lint" + }, + "test": { + "executor": "@nx/vitest:test", + "options": { + "configFile": "libs/react/vite.config.mts" + } + }, + "type-tests": { + "executor": "nx:run-commands", + "cache": true, + "inputs": [ + "default", + "^production" + ], + "options": { + "command": "node node_modules/typescript/bin/tsc --project libs/react/tsconfig.spec.json --noEmit --emitDeclarationOnly false --composite false --incremental false" + } + } + } +} diff --git a/libs/react/src/index.ts b/libs/react/src/index.ts new file mode 100644 index 000000000..127e42d26 --- /dev/null +++ b/libs/react/src/index.ts @@ -0,0 +1,4 @@ +'use client'; + +// Reserved private entry point. Runtime implementation follows in later work. +export {}; diff --git a/libs/react/tsconfig.json b/libs/react/tsconfig.json new file mode 100644 index 000000000..435f5a08d --- /dev/null +++ b/libs/react/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "types": [], + "lib": [ + "ES2022", + "DOM", + "DOM.Iterable" + ], + "jsx": "react-jsx" + }, + "files": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/react/tsconfig.lib.json b/libs/react/tsconfig.lib.json new file mode 100644 index 000000000..2eae4aba1 --- /dev/null +++ b/libs/react/tsconfig.lib.json @@ -0,0 +1,24 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "outDir": "../../dist/libs/react", + "declaration": true, + "emitDeclarationOnly": false, + "module": "ES2022", + "target": "ES2022" + }, + "include": [ + "src/**/*.ts", + "src/**/*.tsx" + ], + "exclude": [ + "src/**/*.spec.ts", + "src/**/*.spec.tsx", + "src/**/*.test.ts", + "src/**/*.test.tsx", + "src/**/*.type-test.ts", + "src/**/*.type-test.tsx", + "src/test-setup.ts" + ] +} diff --git a/libs/react/tsconfig.spec.json b/libs/react/tsconfig.spec.json new file mode 100644 index 000000000..86fbbd4e0 --- /dev/null +++ b/libs/react/tsconfig.spec.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "types": [ + "vitest/globals" + ], + "outDir": "../../dist/out-tsc/react" + }, + "include": [ + "src/**/*.ts", + "src/**/*.tsx" + ], + "exclude": [] +} diff --git a/libs/react/vite.config.mts b/libs/react/vite.config.mts new file mode 100644 index 000000000..f05211ebe --- /dev/null +++ b/libs/react/vite.config.mts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + root: import.meta.dirname, + test: { + environment: 'node', + include: ['src/**/*.spec.{ts,tsx}', 'src/**/*.test.{ts,tsx}'], + passWithNoTests: true, + }, +}); diff --git a/nx.json b/nx.json index f37f5b792..840ab30f2 100644 --- a/nx.json +++ b/nx.json @@ -5,6 +5,11 @@ "production": [ "default", "!{projectRoot}/**/*.spec.ts", + "!{projectRoot}/**/*.spec.tsx", + "!{projectRoot}/**/*.test.ts", + "!{projectRoot}/**/*.test.tsx", + "!{projectRoot}/**/*.type-test.ts", + "!{projectRoot}/**/*.type-test.tsx", "!{projectRoot}/src/test-setup.ts", "!{projectRoot}/jest.config.ts", "!{projectRoot}/.eslintrc.json", diff --git a/package-lock.json b/package-lock.json index aca072aa3..0d53d8609 100644 --- a/package-lock.json +++ b/package-lock.json @@ -102,6 +102,7 @@ "puppeteer": "^22.0.0", "remark-gfm": "^4.0.1", "resend": "^6.10.0", + "semver": "7.7.4", "tailwindcss": "^4.3.0", "tslib": "^2.3.0", "tsx": "^4.21.0", @@ -418,6 +419,14 @@ "rxjs": "~7.8.0" } }, + "libs/angular": { + "name": "@threadplane/angular", + "version": "0.0.0", + "license": "MIT", + "peerDependencies": { + "@angular/core": "^20.0.0 || ^21.0.0 || ^22.0.0" + } + }, "libs/chat": { "name": "@threadplane/chat", "version": "0.2.0", @@ -524,6 +533,16 @@ "web-vitals": "^5.1.0" } }, + "libs/content": { + "name": "@threadplane/content", + "version": "0.0.0", + "license": "MIT" + }, + "libs/core": { + "name": "@threadplane/core", + "version": "0.0.0", + "license": "MIT" + }, "libs/design-tokens": { "name": "@threadplane/design-tokens", "version": "0.0.35", @@ -610,6 +629,11 @@ "@langchain/langgraph": "^1.0.0" } }, + "libs/react": { + "name": "@threadplane/react", + "version": "0.0.0", + "license": "MIT" + }, "libs/render": { "name": "@threadplane/render", "version": "0.2.0", @@ -21534,6 +21558,10 @@ "resolved": "libs/ag-ui", "link": true }, + "node_modules/@threadplane/angular": { + "resolved": "libs/angular", + "link": true + }, "node_modules/@threadplane/chat": { "resolved": "libs/chat", "link": true @@ -21554,6 +21582,14 @@ "resolved": "libs/cockpit-telemetry", "link": true }, + "node_modules/@threadplane/content": { + "resolved": "libs/content", + "link": true + }, + "node_modules/@threadplane/core": { + "resolved": "libs/core", + "link": true + }, "node_modules/@threadplane/design-tokens": { "resolved": "libs/design-tokens", "link": true @@ -21570,6 +21606,10 @@ "resolved": "libs/middleware", "link": true }, + "node_modules/@threadplane/react": { + "resolved": "libs/react", + "link": true + }, "node_modules/@threadplane/render": { "resolved": "libs/render", "link": true diff --git a/package.json b/package.json index 8c5ebcadd..ba6a94fbd 100644 --- a/package.json +++ b/package.json @@ -94,6 +94,7 @@ "puppeteer": "^22.0.0", "remark-gfm": "^4.0.1", "resend": "^6.10.0", + "semver": "7.7.4", "tailwindcss": "^4.3.0", "tslib": "^2.3.0", "tsx": "^4.21.0", diff --git a/scripts/ci-scope.mjs b/scripts/ci-scope.mjs index 3cfd9d0eb..8e8fcdd5e 100644 --- a/scripts/ci-scope.mjs +++ b/scripts/ci-scope.mjs @@ -1,8 +1,9 @@ #!/usr/bin/env node import { execFileSync } from 'node:child_process'; -import { appendFileSync } from 'node:fs'; +import { appendFileSync, readFileSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; +import { privateScaffoldProjects } from './react-parity/package-policy.mjs'; export const SCOPE_KEYS = [ 'library', @@ -90,6 +91,27 @@ const LINT_SCOPE_KEYS = [ * this regex only decides which CI scope a change to them lights up. */ const COCKPIT_ROOTLESS_SPEC = /^cockpit\/[^/]+\/[^/]+\.spec\.ts$/; +// Keep migration CI ownership aligned with the inventory's reviewed scope. +// Rootless fixtures and documentation can change it without affecting an Nx library. +const parityScope = JSON.parse( + readFileSync(new URL('./react-parity/baseline.json', import.meta.url), 'utf8') +).scope; +const PARITY_PREFIXES = [ + 'scripts/react-parity/', + 'fixtures/react-parity/', + `${parityScope.docsRoot}/`, + ...[...parityScope.libraries, ...privateScaffoldProjects] + .map((name) => `libs/${name}/`), +]; + +function isParityChange(file) { + const normalized = normalizePath(file); + return PARITY_PREFIXES.some((prefix) => normalized.startsWith(prefix)) || + parityScope.configFiles.includes(normalized) || + (normalized.startsWith(`${parityScope.topicsRoot}/`) && + /\/angular\/(?:project\.json|src\/index\.ts)$/.test(normalized)); +} + export function emptyScope() { return Object.fromEntries(SCOPE_KEYS.map((k) => [k, false])); } @@ -152,6 +174,9 @@ export function classifyFromAffected(changedFiles, affectedProjects) { if (isAngularCompatibilityChange(changedFiles)) { scope.angular_compatibility = true; } + if (changedFiles.some(isParityChange)) { + scope.library = true; + } return scope; } diff --git a/scripts/ci-scope.spec.mjs b/scripts/ci-scope.spec.mjs index f22645d79..3675037ae 100644 --- a/scripts/ci-scope.spec.mjs +++ b/scripts/ci-scope.spec.mjs @@ -39,6 +39,35 @@ const EXAMPLES_CHAT_TAGS = [ const POSTHOG_TAGS = ['scope:posthog']; const GROWTH_LIFECYCLE_TAGS = ['scope:growth-lifecycle']; +describe('React migration baseline scope', () => { + for (const file of [ + 'scripts/react-parity/inventory.mjs', + 'fixtures/react-parity/traces/ag-ui-text-state.sse', + 'libs/core/src/index.ts', + 'libs/angular/src/public-api.ts', + 'libs/react/src/index.ts', + 'scripts/react-parity/package-policy.mjs', + 'fixtures/react-parity/consumers/angular/src/main.ts', + 'fixtures/react-parity/consumers/plain/package.json', + 'libs/ui-react/src/button.tsx', + 'apps/website/content/docs/chat/api/example.mdx', + 'cockpit/chat/messages/angular/src/index.ts', + 'cockpit/chat/new-topic/angular/project.json', + 'scripts/verify-release-versions.mjs', + ]) { + it(`runs library gates for ${file} even without Nx ownership`, () => { + assert.equal(classifyFromAffected([file], []).library, true); + }); + } + + it('does not send unrelated marketing or backend changes to library gates', () => { + for (const file of [ + 'apps/website/content/blog/post.mdx', + 'cockpit/chat/messages/python/src/graph.py', + ]) assert.equal(classifyFromAffected([file], []).library, false); + }); +}); + function nxAffectedFiles(file) { return JSON.parse( execFileSync( diff --git a/scripts/ci-workflow.spec.mjs b/scripts/ci-workflow.spec.mjs index 1f22d6a60..d9d55b144 100644 --- a/scripts/ci-workflow.spec.mjs +++ b/scripts/ci-workflow.spec.mjs @@ -1,6 +1,7 @@ import { readdir, readFile } from 'node:fs/promises'; import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; +import { privateScaffoldProjects } from './react-parity/package-policy.mjs'; import { spawnSync } from 'node:child_process'; function escapeRegExp(value) { @@ -78,6 +79,23 @@ function readNamedStep(job, name) { } describe('CI workflow', () => { + it('enforces React migration source, build, and packaged-consumer gates', async () => { + const job = readJobBlock(await readFile('.github/workflows/ci.yml', 'utf8'), 'library'); + const source = readNamedStep(job, 'React migration baseline and boundaries'); + assert.match(source, /node --test scripts\/react-parity\/\*\.spec\.mjs fixtures\/react-parity\/traces\.spec\.mjs/); + assert.match(source, /inventory\.mjs --check/); + assert.match(source, /node scripts\/react-parity\/verify-boundaries\.mjs/); + const build = readNamedStep(job, 'Build and validate private React foundations'); + assert.ok(job.includes(`FOUNDATIONS: ${privateScaffoldProjects.join(',')}`)); + assert.match(job, /LIBS: chat,langgraph,ag-ui,render,a2ui,telemetry/); + assert.match(build, /run-many -t lint test type-tests build --projects=\$FOUNDATIONS/); + const packages = readNamedStep(job, 'Verify emitted boundaries and isolated packages'); + assert.match(packages, /verify-boundaries\.mjs --built/); + assert.match(packages, /verify-packages\.mjs/); + assert.match(packages, /verify-angular-package\.mjs/); + assert.ok(job.indexOf(build) < job.indexOf(packages)); + assert.ok(job.indexOf('run-many -t build --projects=$LIBS') < job.indexOf(packages)); + }); it('verifies stage scrolling and interaction against the matching local replay build', async () => { const workflow = await readFile('.github/workflows/ci.yml', 'utf8'); const step = readNamedStep(readJobBlock(workflow, 'website-e2e'), 'Stage scroll verification (scroll-craft harness)'); @@ -107,7 +125,7 @@ describe('CI workflow', () => { assert.match( workflow, - /^ merge_group:\s*$/m, + /^ {2}merge_group:\s*$/m, 'ci.yml must trigger on merge_group or a merge queue blocks forever' ); @@ -169,14 +187,6 @@ describe('CI workflow', () => { return readJobBlock(await readWorkflow(), 'angular-compatibility'); } - async function readLibraryJob() { - const workflow = await readWorkflow(); - return workflow.slice( - workflow.indexOf('\n library:\n'), - workflow.indexOf('\n website:\n') - ); - } - async function readCanonicalDemoJob() { return readJobBlock(await readWorkflow(), 'demo-deploy'); } diff --git a/scripts/react-parity/baseline.json b/scripts/react-parity/baseline.json new file mode 100644 index 000000000..9d25d26b8 --- /dev/null +++ b/scripts/react-parity/baseline.json @@ -0,0 +1,13629 @@ +{ + "schemaVersion": 1, + "baselineHead": "7e80cebd607f5c605ef1ec81f11e5ebe3324f81b", + "sourceState": { + "modified": [ + ".github/workflows/ci.yml", + "package-lock.json", + "package.json", + "tsconfig.base.json" + ], + "untracked": [] + }, + "scope": { + "libraries": [ + "a2ui", + "ag-ui", + "chat", + "cockpit-registry", + "cockpit-runtime-bridge", + "cockpit-shell", + "cockpit-telemetry", + "design-tokens", + "e2e-harness", + "example-layouts", + "langgraph", + "middleware", + "render", + "telemetry", + "ui-react", + "workspace-react" + ], + "entryPoints": [ + "libs/a2ui/src/index.ts", + "libs/ag-ui/src/public-api.ts", + "libs/chat/src/public-api.ts", + "libs/chat/debug/public-api.ts", + "libs/chat/testing/public-api.ts", + "libs/langgraph/src/public-api.ts", + "libs/middleware/src/langgraph/index.ts", + "libs/render/src/public-api.ts", + "libs/telemetry/src/index.ts", + "libs/telemetry/src/browser/public-api.ts", + "libs/telemetry/src/node/index.ts", + "libs/telemetry/src/shared/public-api.ts" + ], + "docsRoot": "apps/website/content/docs", + "topicsRoot": "cockpit", + "configFiles": [ + "package.json", + "package-lock.json", + "nx.json", + "tsconfig.base.json", + ".github/workflows/ci.yml", + ".github/workflows/publish.yml", + ".github/workflows/release-provenance.yml", + ".github/workflows/publish-middleware-npm.yml", + ".github/workflows/publish-middleware-python.yml", + "scripts/verify-release-versions.mjs", + "scripts/cockpit-matrix.mjs", + "scripts/assemble-examples.ts", + "scripts/examples/serve-example.ts", + "apps/website/scripts/generate-api-docs.ts", + "apps/website/scripts/generate-narrative-docs.ts", + "apps/website/scripts/generate-agent-context.ts" + ] + }, + "rows": [ + { + "id": "asset:libs/a2ui/LICENSE.md", + "kind": "asset", + "path": "libs/a2ui/LICENSE.md", + "sha256": "d20c571ef693b2ea2e1c552d688d596804d64795aee4c941ab12a21e01752ba4" + }, + { + "id": "asset:libs/a2ui/README.md", + "kind": "asset", + "path": "libs/a2ui/README.md", + "sha256": "64af0e330742433af441de75fc9c31617c034cf062c3abcfab6c7fa49d0902af" + }, + { + "id": "asset:libs/a2ui/package.json", + "kind": "asset", + "path": "libs/a2ui/package.json", + "sha256": "43ac544a06ad9bf6ea765c56962aea67553205c1319db52aa1c4ad60b9dc463d" + }, + { + "id": "asset:libs/a2ui/project.json", + "kind": "asset", + "path": "libs/a2ui/project.json", + "sha256": "4bb3a6df0955b314df44cbcc527958606bae0a0a8a5f1edc30ccbe049f277f1b" + }, + { + "id": "asset:libs/a2ui/schemas/README.md", + "kind": "asset", + "path": "libs/a2ui/schemas/README.md", + "sha256": "ba3fd7a7c1fc0e9dc42c068c2d01df550e7d6ed89e3391f90e143ff38c23b4c7" + }, + { + "id": "asset:libs/a2ui/schemas/basic-catalog.json", + "kind": "asset", + "path": "libs/a2ui/schemas/basic-catalog.json", + "sha256": "8cc94d0a482e67048f9fc989964ca5da56fe42f531d919315a508989fb22e13e" + }, + { + "id": "asset:libs/a2ui/schemas/common_types.json", + "kind": "asset", + "path": "libs/a2ui/schemas/common_types.json", + "sha256": "ac79788e95e5bdf0a39808953593a53c1bc9fcdcdb55480f4610613c6591e94c" + }, + { + "id": "asset:libs/a2ui/schemas/server_to_client.json", + "kind": "asset", + "path": "libs/a2ui/schemas/server_to_client.json", + "sha256": "da75d25378b0d02069cc3de54db76f6f71cffdf2a3fd95a8fabbb1f516c07cbe" + }, + { + "id": "asset:libs/a2ui/tsconfig.json", + "kind": "asset", + "path": "libs/a2ui/tsconfig.json", + "sha256": "09fb3febb18047e8433470b3ceabd1341c66e1f951b5d7120c52be80592f4cfa" + }, + { + "id": "asset:libs/a2ui/tsconfig.lib.json", + "kind": "asset", + "path": "libs/a2ui/tsconfig.lib.json", + "sha256": "a9a1f803de9abefe825450b26516345b8b3f4325489e215431d1ebf34472e23d" + }, + { + "id": "asset:libs/ag-ui/LICENSE.md", + "kind": "asset", + "path": "libs/ag-ui/LICENSE.md", + "sha256": "d20c571ef693b2ea2e1c552d688d596804d64795aee4c941ab12a21e01752ba4" + }, + { + "id": "asset:libs/ag-ui/README.md", + "kind": "asset", + "path": "libs/ag-ui/README.md", + "sha256": "ff308bf07651a3a6849db1dc841bd39d54bc585a6f882bc6d709b49aba9c5293" + }, + { + "id": "asset:libs/ag-ui/fixtures/runtime-transcripts/maf-hitl-interrupt.sse", + "kind": "asset", + "path": "libs/ag-ui/fixtures/runtime-transcripts/maf-hitl-interrupt.sse", + "sha256": "4a708036495330cc71e4b25fc2af73cd804420ee3bd8bcba4a84511022d18cdb" + }, + { + "id": "asset:libs/ag-ui/fixtures/runtime-transcripts/maf-hitl-resume.sse", + "kind": "asset", + "path": "libs/ag-ui/fixtures/runtime-transcripts/maf-hitl-resume.sse", + "sha256": "ec0abebb6bf31420dc00c8d70ac9fa0754aca5519022a91b457665af2161acb1" + }, + { + "id": "asset:libs/ag-ui/fixtures/runtime-transcripts/mastra-interrupt.sse", + "kind": "asset", + "path": "libs/ag-ui/fixtures/runtime-transcripts/mastra-interrupt.sse", + "sha256": "5b8a7f8ae7276176c836876ee12b969d40f23104e726a304edb7bb7a8080af7d" + }, + { + "id": "asset:libs/ag-ui/fixtures/runtime-transcripts/mastra-reinterrupt.sse", + "kind": "asset", + "path": "libs/ag-ui/fixtures/runtime-transcripts/mastra-reinterrupt.sse", + "sha256": "965f13948f5f503c3840221c50122a0c77a445bbbe5d172d7be629d6b784be6e" + }, + { + "id": "asset:libs/ag-ui/fixtures/runtime-transcripts/mastra-resume-correct.request.json", + "kind": "asset", + "path": "libs/ag-ui/fixtures/runtime-transcripts/mastra-resume-correct.request.json", + "sha256": "5ae57cfe51e43869e492e481e3afa1f9336b9610030f64a247d85bfc7978d57f" + }, + { + "id": "asset:libs/ag-ui/fixtures/runtime-transcripts/strands-interrupt.sse", + "kind": "asset", + "path": "libs/ag-ui/fixtures/runtime-transcripts/strands-interrupt.sse", + "sha256": "5c64091845cb2da7978730cba6fad10c6d15876c17e70a9a801256c178790d83" + }, + { + "id": "asset:libs/ag-ui/fixtures/runtime-transcripts/strands-plain-chat.sse", + "kind": "asset", + "path": "libs/ag-ui/fixtures/runtime-transcripts/strands-plain-chat.sse", + "sha256": "2e86620659e31153c8a8ece61afaf97c2a5434a646d212afd059862f8e0e4d82" + }, + { + "id": "asset:libs/ag-ui/fixtures/runtime-transcripts/strands-resume.request.json", + "kind": "asset", + "path": "libs/ag-ui/fixtures/runtime-transcripts/strands-resume.request.json", + "sha256": "5121ca1d69d7511f46b7d4b42b4ba315f13d10fc26dc07916a813ecdc3661130" + }, + { + "id": "asset:libs/ag-ui/fixtures/runtime-transcripts/subagent-lifecycle.json", + "kind": "asset", + "path": "libs/ag-ui/fixtures/runtime-transcripts/subagent-lifecycle.json", + "sha256": "3af9a41186b0090da68183a8454e972a2dd8d7b0a299bf4a6517bae012b9dbee" + }, + { + "id": "asset:libs/ag-ui/ng-package.json", + "kind": "asset", + "path": "libs/ag-ui/ng-package.json", + "sha256": "082cb09f9fb8e3f1b98c32fc51c2024cabce42f40e12d21c108751cf6ff9697f" + }, + { + "id": "asset:libs/ag-ui/package.json", + "kind": "asset", + "path": "libs/ag-ui/package.json", + "sha256": "27190919faa732b07c278bc78b68e0663fa39e6c7e96d6ba45fbafc65e2bd3ee" + }, + { + "id": "asset:libs/ag-ui/project.json", + "kind": "asset", + "path": "libs/ag-ui/project.json", + "sha256": "6bbdf56f7f8275a191b3bb24550f62022bead7d2d4b173ed3067e6dcfc89b2fa" + }, + { + "id": "asset:libs/ag-ui/tsconfig.json", + "kind": "asset", + "path": "libs/ag-ui/tsconfig.json", + "sha256": "346ce4348a6d17fd234f516939f4b4b31c70cd0d90f523a7e9dfbe6979469aa4" + }, + { + "id": "asset:libs/ag-ui/tsconfig.lib.json", + "kind": "asset", + "path": "libs/ag-ui/tsconfig.lib.json", + "sha256": "2d56829b5aa728927dca322cff6d7521ff9a65a8d8f258bee0f3a4d146826219" + }, + { + "id": "asset:libs/ag-ui/tsconfig.lib.prod.json", + "kind": "asset", + "path": "libs/ag-ui/tsconfig.lib.prod.json", + "sha256": "4815c5ece87bc626f79d81495b3b60bce7bca2dc355106a9b77bea8d11855a8b" + }, + { + "id": "asset:libs/ag-ui/tsconfig.type-tests.json", + "kind": "asset", + "path": "libs/ag-ui/tsconfig.type-tests.json", + "sha256": "c178776b9c33a23d56c7c6bb2bf99caedd3f05b1b3de0ebe5ec46076f9ff0ba6" + }, + { + "id": "asset:libs/chat/CHANGELOG.md", + "kind": "asset", + "path": "libs/chat/CHANGELOG.md", + "sha256": "756220afda94558c8796a60ed80b7124a33b34199ed2d83c765a1f4f2d969d3f" + }, + { + "id": "asset:libs/chat/LICENSE.md", + "kind": "asset", + "path": "libs/chat/LICENSE.md", + "sha256": "d20c571ef693b2ea2e1c552d688d596804d64795aee4c941ab12a21e01752ba4" + }, + { + "id": "asset:libs/chat/NOTICE.md", + "kind": "asset", + "path": "libs/chat/NOTICE.md", + "sha256": "af77ae590ec0dde1495cf3a413338d7afb19aa1882fbaae7db138bc1b4dca99b" + }, + { + "id": "asset:libs/chat/README.md", + "kind": "asset", + "path": "libs/chat/README.md", + "sha256": "1d839f635effebb4091a757d476f04cf01eecb36cf3e4295f55acb8a99ed06c7" + }, + { + "id": "asset:libs/chat/debug/ng-package.json", + "kind": "asset", + "path": "libs/chat/debug/ng-package.json", + "sha256": "da669baa95391ee10d693fa87f41deb8e647e2277076ee4663ca40598cbaceb7" + }, + { + "id": "asset:libs/chat/ng-package.json", + "kind": "asset", + "path": "libs/chat/ng-package.json", + "sha256": "5b1947224519b367fb619f61b560aa956ee62339a0efa45b3f0e23205e1bb52c" + }, + { + "id": "asset:libs/chat/package.json", + "kind": "asset", + "path": "libs/chat/package.json", + "sha256": "2f98085df292faf821cf9bb7225b7822e1af28d96cd59537b2a474a71e6a0adb" + }, + { + "id": "asset:libs/chat/project.json", + "kind": "asset", + "path": "libs/chat/project.json", + "sha256": "650f32a6f72513086685aeee1ad31e0de6686a4458a6f247e09e7d5689d4f68b" + }, + { + "id": "asset:libs/chat/src/themes/default-dark.css", + "kind": "asset", + "path": "libs/chat/src/themes/default-dark.css", + "sha256": "70b8384de67f6409a9b161fe2484167078bdd3418a97fa3934346fd07779cd02" + }, + { + "id": "asset:libs/chat/src/themes/default-light.css", + "kind": "asset", + "path": "libs/chat/src/themes/default-light.css", + "sha256": "06e2562965baed880c91fdb71526befc6c9bb65eec2cbf6d7b6bfe5101612926" + }, + { + "id": "asset:libs/chat/src/themes/material-dark.css", + "kind": "asset", + "path": "libs/chat/src/themes/material-dark.css", + "sha256": "096deedbf7001e1ab81c9c6df1450a250955d60b2d780a19aaf8ad300e4e2802" + }, + { + "id": "asset:libs/chat/src/themes/material-light.css", + "kind": "asset", + "path": "libs/chat/src/themes/material-light.css", + "sha256": "4c4782f2230c0340d450d35c6956109312be6adddc4f16d1dafa3060031938d5" + }, + { + "id": "asset:libs/chat/testing/ng-package.json", + "kind": "asset", + "path": "libs/chat/testing/ng-package.json", + "sha256": "da669baa95391ee10d693fa87f41deb8e647e2277076ee4663ca40598cbaceb7" + }, + { + "id": "asset:libs/chat/tsconfig.json", + "kind": "asset", + "path": "libs/chat/tsconfig.json", + "sha256": "346ce4348a6d17fd234f516939f4b4b31c70cd0d90f523a7e9dfbe6979469aa4" + }, + { + "id": "asset:libs/chat/tsconfig.lib.json", + "kind": "asset", + "path": "libs/chat/tsconfig.lib.json", + "sha256": "762e5d78bcbd48c9b72a55cf602603203c86011f7814e74a122a607e24906628" + }, + { + "id": "asset:libs/chat/tsconfig.lib.prod.json", + "kind": "asset", + "path": "libs/chat/tsconfig.lib.prod.json", + "sha256": "4815c5ece87bc626f79d81495b3b60bce7bca2dc355106a9b77bea8d11855a8b" + }, + { + "id": "asset:libs/chat/tsconfig.spec.json", + "kind": "asset", + "path": "libs/chat/tsconfig.spec.json", + "sha256": "440dbebe320488070e90cd28d50ed4e6b1e7a727762dceae581ff17e38133e8c" + }, + { + "id": "asset:libs/chat/tsconfig.type-tests.json", + "kind": "asset", + "path": "libs/chat/tsconfig.type-tests.json", + "sha256": "dc1c927e6591f9339047bab6b5d8b61f26112c6384fb1b356c5157b0b8ee946b" + }, + { + "id": "asset:libs/cockpit-registry/package.json", + "kind": "asset", + "path": "libs/cockpit-registry/package.json", + "sha256": "d902a2a4e45a696cafcba342a159ed19195452c71a37f26ae6e0ad38aa06896a" + }, + { + "id": "asset:libs/cockpit-registry/project.json", + "kind": "asset", + "path": "libs/cockpit-registry/project.json", + "sha256": "147ddacc5a66c7afbab40bdfcfde83d4d56848ce184ac40ec904f340a28c5982" + }, + { + "id": "asset:libs/cockpit-registry/tsconfig.json", + "kind": "asset", + "path": "libs/cockpit-registry/tsconfig.json", + "sha256": "8a7e70669e9d12c19027e61235f270d0b40b13c763bc5d872c468c09808a27f3" + }, + { + "id": "asset:libs/cockpit-registry/tsconfig.lib.json", + "kind": "asset", + "path": "libs/cockpit-registry/tsconfig.lib.json", + "sha256": "0ce8fcc728a55abcbb446802f632cc9bb38da7936723417b68c10cc550d99ec5" + }, + { + "id": "asset:libs/cockpit-runtime-bridge/package.json", + "kind": "asset", + "path": "libs/cockpit-runtime-bridge/package.json", + "sha256": "e16ee6400593700fd102b0409b5bdab3bf1b5350f04ba2d42f2a3c35cf930760" + }, + { + "id": "asset:libs/cockpit-runtime-bridge/project.json", + "kind": "asset", + "path": "libs/cockpit-runtime-bridge/project.json", + "sha256": "ac3cea9981fbea850d96a6ec8597b1b2f5d5277bec3752b47280c5e04ff792fe" + }, + { + "id": "asset:libs/cockpit-runtime-bridge/tsconfig.json", + "kind": "asset", + "path": "libs/cockpit-runtime-bridge/tsconfig.json", + "sha256": "8a7e70669e9d12c19027e61235f270d0b40b13c763bc5d872c468c09808a27f3" + }, + { + "id": "asset:libs/cockpit-runtime-bridge/tsconfig.lib.json", + "kind": "asset", + "path": "libs/cockpit-runtime-bridge/tsconfig.lib.json", + "sha256": "35fc76dec3884cdf6b551e4b5ca830705dd203117dc028fac2939badde5e3483" + }, + { + "id": "asset:libs/cockpit-shell/package.json", + "kind": "asset", + "path": "libs/cockpit-shell/package.json", + "sha256": "815556b4cbfbb8b8e95f334fa2038e2d269f0de518079e717bcc5a478c709669" + }, + { + "id": "asset:libs/cockpit-shell/project.json", + "kind": "asset", + "path": "libs/cockpit-shell/project.json", + "sha256": "7c7dcdb56030f53c03cc1df21ff8b88894ab574a02f59727ad4cf4823a2a7d36" + }, + { + "id": "asset:libs/cockpit-shell/tsconfig.json", + "kind": "asset", + "path": "libs/cockpit-shell/tsconfig.json", + "sha256": "8a7e70669e9d12c19027e61235f270d0b40b13c763bc5d872c468c09808a27f3" + }, + { + "id": "asset:libs/cockpit-shell/tsconfig.lib.json", + "kind": "asset", + "path": "libs/cockpit-shell/tsconfig.lib.json", + "sha256": "e33ccc531d242904beff0345b135526f3697b6d6f18ceb53122c75bb00272d22" + }, + { + "id": "asset:libs/cockpit-telemetry/README.md", + "kind": "asset", + "path": "libs/cockpit-telemetry/README.md", + "sha256": "51156dc3e88189e4611933fcbc5508788629a954a8647bf30c2e8d9bc39c86b3" + }, + { + "id": "asset:libs/cockpit-telemetry/ng-package.json", + "kind": "asset", + "path": "libs/cockpit-telemetry/ng-package.json", + "sha256": "8c2ae14f0d94d274008ab714564dcb740421ad5a8f27c5cc3945784e3814ac36" + }, + { + "id": "asset:libs/cockpit-telemetry/package.json", + "kind": "asset", + "path": "libs/cockpit-telemetry/package.json", + "sha256": "0aa354d862c286860495653842b6fcccd3236294e8587776703d56fb275c5820" + }, + { + "id": "asset:libs/cockpit-telemetry/project.json", + "kind": "asset", + "path": "libs/cockpit-telemetry/project.json", + "sha256": "bc0f45fad49e9ae0edf0b1430edba1b13d4615e6fbd4b531eb80f11c23441555" + }, + { + "id": "asset:libs/cockpit-telemetry/tsconfig.json", + "kind": "asset", + "path": "libs/cockpit-telemetry/tsconfig.json", + "sha256": "97ed5ceb01ae9e6f2f7abd6e313c8d48934a5688acf2933f04b4cf396ac4d536" + }, + { + "id": "asset:libs/cockpit-telemetry/tsconfig.lib.json", + "kind": "asset", + "path": "libs/cockpit-telemetry/tsconfig.lib.json", + "sha256": "d9ecc637cae84224d735e811dc95bd83ef50126436f26c5a48aac850e2a593a2" + }, + { + "id": "asset:libs/cockpit-telemetry/tsconfig.spec.json", + "kind": "asset", + "path": "libs/cockpit-telemetry/tsconfig.spec.json", + "sha256": "440dbebe320488070e90cd28d50ed4e6b1e7a727762dceae581ff17e38133e8c" + }, + { + "id": "asset:libs/design-tokens/package.json", + "kind": "asset", + "path": "libs/design-tokens/package.json", + "sha256": "5d3d69b881f6562910b70f37a382736d6ce14fbbe891212fabfcafb73c1d1135" + }, + { + "id": "asset:libs/design-tokens/project.json", + "kind": "asset", + "path": "libs/design-tokens/project.json", + "sha256": "118f350aaf0f5e5cfb02e31acc4b0fbddbf2049bd88046c36b318cf4f2a87bfc" + }, + { + "id": "asset:libs/design-tokens/src/lib/theme.css", + "kind": "asset", + "path": "libs/design-tokens/src/lib/theme.css", + "sha256": "62a11f8c405d0623422a8b56384b877b27e08f1ac0affb92c018ae8ffca2678b" + }, + { + "id": "asset:libs/design-tokens/src/lib/tokens-dark.css", + "kind": "asset", + "path": "libs/design-tokens/src/lib/tokens-dark.css", + "sha256": "79da4d7bf003cf70b9958db8b1dd7466c4d4b8b720e441c38d856b55b51e2a09" + }, + { + "id": "asset:libs/design-tokens/src/lib/tokens.css", + "kind": "asset", + "path": "libs/design-tokens/src/lib/tokens.css", + "sha256": "80d9d105bfacaefc9eb3b14691cb326c2e910574d3d0bb9c168269004afda8ec" + }, + { + "id": "asset:libs/design-tokens/tsconfig.json", + "kind": "asset", + "path": "libs/design-tokens/tsconfig.json", + "sha256": "cb6c39f6fde12afb9446c6b5675c3a54018aa8e9e465467bf2b55d6818589f8b" + }, + { + "id": "asset:libs/design-tokens/tsconfig.lib.json", + "kind": "asset", + "path": "libs/design-tokens/tsconfig.lib.json", + "sha256": "c30c0ead57f820cc2569bf7fc6512adc81aec89bb8096e13b2b2cc17a4f41e61" + }, + { + "id": "asset:libs/e2e-harness/README.md", + "kind": "asset", + "path": "libs/e2e-harness/README.md", + "sha256": "ade046c81687b13b35d8e94fa141ae74acbd5436332fa05136cfcffbb2a77858" + }, + { + "id": "asset:libs/e2e-harness/project.json", + "kind": "asset", + "path": "libs/e2e-harness/project.json", + "sha256": "4fed1b1085c486e1442ffbdec83b120a40b29d61c850340a17e8ed6a28ee272f" + }, + { + "id": "asset:libs/e2e-harness/tsconfig.json", + "kind": "asset", + "path": "libs/e2e-harness/tsconfig.json", + "sha256": "0bf7edd81c7eb1b3bbc4162e6fb5e1c7d4f12e11e1200a6e3c1baa889559b269" + }, + { + "id": "asset:libs/example-layouts/ng-package.json", + "kind": "asset", + "path": "libs/example-layouts/ng-package.json", + "sha256": "351451aadb83769b8c008637a35bf9d757846cc515f44982429fa8659d53d96f" + }, + { + "id": "asset:libs/example-layouts/package.json", + "kind": "asset", + "path": "libs/example-layouts/package.json", + "sha256": "4bbc799b31804cd537ff4676f3d357df49218495e1de6c06062a1a1299db4212" + }, + { + "id": "asset:libs/example-layouts/project.json", + "kind": "asset", + "path": "libs/example-layouts/project.json", + "sha256": "6c8dcafef216c17beb8aa038fc1c818418564a72a163a45246c4b67dcaaa9ab7" + }, + { + "id": "asset:libs/example-layouts/src/theme.css", + "kind": "asset", + "path": "libs/example-layouts/src/theme.css", + "sha256": "4f43c82173dc25ca7a2b23fa2c980a8cdc897ae6f491502c02794f2ddbc2817d" + }, + { + "id": "asset:libs/example-layouts/tsconfig.json", + "kind": "asset", + "path": "libs/example-layouts/tsconfig.json", + "sha256": "346ce4348a6d17fd234f516939f4b4b31c70cd0d90f523a7e9dfbe6979469aa4" + }, + { + "id": "asset:libs/example-layouts/tsconfig.lib.json", + "kind": "asset", + "path": "libs/example-layouts/tsconfig.lib.json", + "sha256": "7e74f6e3f731327559641a508535e9e745bb017fa8cd1ab6f3ef4862e8a20e56" + }, + { + "id": "asset:libs/example-layouts/tsconfig.lib.prod.json", + "kind": "asset", + "path": "libs/example-layouts/tsconfig.lib.prod.json", + "sha256": "4815c5ece87bc626f79d81495b3b60bce7bca2dc355106a9b77bea8d11855a8b" + }, + { + "id": "asset:libs/example-layouts/tsconfig.spec.json", + "kind": "asset", + "path": "libs/example-layouts/tsconfig.spec.json", + "sha256": "511690d3046bbdf57e55a1d50212daadd6b38348032216b2c1ff1819974e46a5" + }, + { + "id": "asset:libs/langgraph/LICENSE.md", + "kind": "asset", + "path": "libs/langgraph/LICENSE.md", + "sha256": "d20c571ef693b2ea2e1c552d688d596804d64795aee4c941ab12a21e01752ba4" + }, + { + "id": "asset:libs/langgraph/README.md", + "kind": "asset", + "path": "libs/langgraph/README.md", + "sha256": "c9e0dbf35d1d2906bf5115318b96e78c115a77454c4f3243a26d89d62989ae93" + }, + { + "id": "asset:libs/langgraph/ng-package.json", + "kind": "asset", + "path": "libs/langgraph/ng-package.json", + "sha256": "be986583fdfea5cf0bddbf2a67d90221db770bc5da947c5516bd0c2c83a9f6e1" + }, + { + "id": "asset:libs/langgraph/package.json", + "kind": "asset", + "path": "libs/langgraph/package.json", + "sha256": "78a1bbfe2d9341ad6d41f43929fbf8e129c51979d49dea7e8cfe3ea3074195b0" + }, + { + "id": "asset:libs/langgraph/project.json", + "kind": "asset", + "path": "libs/langgraph/project.json", + "sha256": "ec15f5414827294ea51ce5bf56755f576c28cd112961b97adbffbfa379b2fd73" + }, + { + "id": "asset:libs/langgraph/test/fixtures/streaming-reasoning-puzzle.json", + "kind": "asset", + "path": "libs/langgraph/test/fixtures/streaming-reasoning-puzzle.json", + "sha256": "2849a0b101e371f89a593a05e284e283289c53a15d775a7be3b80cc8e72dfe56" + }, + { + "id": "asset:libs/langgraph/tsconfig.json", + "kind": "asset", + "path": "libs/langgraph/tsconfig.json", + "sha256": "346ce4348a6d17fd234f516939f4b4b31c70cd0d90f523a7e9dfbe6979469aa4" + }, + { + "id": "asset:libs/langgraph/tsconfig.lib.json", + "kind": "asset", + "path": "libs/langgraph/tsconfig.lib.json", + "sha256": "2d56829b5aa728927dca322cff6d7521ff9a65a8d8f258bee0f3a4d146826219" + }, + { + "id": "asset:libs/langgraph/tsconfig.lib.prod.json", + "kind": "asset", + "path": "libs/langgraph/tsconfig.lib.prod.json", + "sha256": "4815c5ece87bc626f79d81495b3b60bce7bca2dc355106a9b77bea8d11855a8b" + }, + { + "id": "asset:libs/langgraph/tsconfig.type-tests.json", + "kind": "asset", + "path": "libs/langgraph/tsconfig.type-tests.json", + "sha256": "c178776b9c33a23d56c7c6bb2bf99caedd3f05b1b3de0ebe5ec46076f9ff0ba6" + }, + { + "id": "asset:libs/middleware/CHANGELOG.md", + "kind": "asset", + "path": "libs/middleware/CHANGELOG.md", + "sha256": "711b6aa1190931990668c14bc34e887e36ab6f45026703cf79c7c610668c496b" + }, + { + "id": "asset:libs/middleware/README.md", + "kind": "asset", + "path": "libs/middleware/README.md", + "sha256": "74ebc0a85ad48d3812d3a504c9b5764432ed754148c86518809782ad3fc04f40" + }, + { + "id": "asset:libs/middleware/package.json", + "kind": "asset", + "path": "libs/middleware/package.json", + "sha256": "e03c913eb465267e27e89c71710ea7ce96355b3ee92a461791ad60fb5b0ecb4c" + }, + { + "id": "asset:libs/middleware/project.json", + "kind": "asset", + "path": "libs/middleware/project.json", + "sha256": "182b82c6d3126234bfbff665a18aaabd230858345b7d6a1eaa2d541eddb8a185" + }, + { + "id": "asset:libs/middleware/tsconfig.json", + "kind": "asset", + "path": "libs/middleware/tsconfig.json", + "sha256": "45dfd1a77ff5e64ff918c34e2a7e8956af079c19dd01dc2dbcfd308ac8c5f6a4" + }, + { + "id": "asset:libs/middleware/tsconfig.lib.json", + "kind": "asset", + "path": "libs/middleware/tsconfig.lib.json", + "sha256": "61d1a2efe47f679afd45d5fbebaf2836f669f5e5daac243d2647462a13516c33" + }, + { + "id": "asset:libs/middleware/tsconfig.spec.json", + "kind": "asset", + "path": "libs/middleware/tsconfig.spec.json", + "sha256": "9a15688caeaea6c7159bdbff2a3bd53dd09dc6ffbe35b40f14d67b961567715c" + }, + { + "id": "asset:libs/render/LICENSE.md", + "kind": "asset", + "path": "libs/render/LICENSE.md", + "sha256": "d20c571ef693b2ea2e1c552d688d596804d64795aee4c941ab12a21e01752ba4" + }, + { + "id": "asset:libs/render/README.md", + "kind": "asset", + "path": "libs/render/README.md", + "sha256": "8c1ffd36a69279746e310e7e81414a461960bd53b7859b010e008739469727f6" + }, + { + "id": "asset:libs/render/ng-package.json", + "kind": "asset", + "path": "libs/render/ng-package.json", + "sha256": "240362e3ff5443fca5844266004d78e8ac08f73decce5cddaa990e06426078b3" + }, + { + "id": "asset:libs/render/package.json", + "kind": "asset", + "path": "libs/render/package.json", + "sha256": "a04e3afc50fe47d0ca4fae72af72113867b403d63bdfdc754952685aedeb1aec" + }, + { + "id": "asset:libs/render/project.json", + "kind": "asset", + "path": "libs/render/project.json", + "sha256": "88a51fd7c907aebebb2a7739ce134fa80fd1e7d83b9d5e7c48947d470b4f81a1" + }, + { + "id": "asset:libs/render/tsconfig.json", + "kind": "asset", + "path": "libs/render/tsconfig.json", + "sha256": "346ce4348a6d17fd234f516939f4b4b31c70cd0d90f523a7e9dfbe6979469aa4" + }, + { + "id": "asset:libs/render/tsconfig.lib.json", + "kind": "asset", + "path": "libs/render/tsconfig.lib.json", + "sha256": "2d56829b5aa728927dca322cff6d7521ff9a65a8d8f258bee0f3a4d146826219" + }, + { + "id": "asset:libs/render/tsconfig.lib.prod.json", + "kind": "asset", + "path": "libs/render/tsconfig.lib.prod.json", + "sha256": "4815c5ece87bc626f79d81495b3b60bce7bca2dc355106a9b77bea8d11855a8b" + }, + { + "id": "asset:libs/render/tsconfig.spec.json", + "kind": "asset", + "path": "libs/render/tsconfig.spec.json", + "sha256": "440dbebe320488070e90cd28d50ed4e6b1e7a727762dceae581ff17e38133e8c" + }, + { + "id": "asset:libs/telemetry/LICENSE.md", + "kind": "asset", + "path": "libs/telemetry/LICENSE.md", + "sha256": "d20c571ef693b2ea2e1c552d688d596804d64795aee4c941ab12a21e01752ba4" + }, + { + "id": "asset:libs/telemetry/README.md", + "kind": "asset", + "path": "libs/telemetry/README.md", + "sha256": "ac737f1a97ddac477be99875e958361d3ac07fa004d34d6fe3b3973e30f6db41" + }, + { + "id": "asset:libs/telemetry/ng-package.json", + "kind": "asset", + "path": "libs/telemetry/ng-package.json", + "sha256": "6bbb0c6fba5cabf5da76d91187bbece069a3576de1fc6fb96809c1c2ca21bfbf" + }, + { + "id": "asset:libs/telemetry/package.json", + "kind": "asset", + "path": "libs/telemetry/package.json", + "sha256": "ae9c6c0e0d6a268f3265ac0ae141c2396e4094f1f2883d6e3c49cc6f07a70512" + }, + { + "id": "asset:libs/telemetry/project.json", + "kind": "asset", + "path": "libs/telemetry/project.json", + "sha256": "df2dbdfa58e093e1c0618b7da718109e9b8c2db7a46fc98f34d7eec779c53b3c" + }, + { + "id": "asset:libs/telemetry/tsconfig.json", + "kind": "asset", + "path": "libs/telemetry/tsconfig.json", + "sha256": "ababd9a21bedd818fdeafbb6f884209ad5461418827d0bd2059b1b51199373b7" + }, + { + "id": "asset:libs/telemetry/tsconfig.lib.browser.json", + "kind": "asset", + "path": "libs/telemetry/tsconfig.lib.browser.json", + "sha256": "02149876c4882ab3cfcc83fb603ab55065d8dbe2609901281baccd4f53dfbed9" + }, + { + "id": "asset:libs/telemetry/tsconfig.lib.json", + "kind": "asset", + "path": "libs/telemetry/tsconfig.lib.json", + "sha256": "68b4d6fff1bf680da595aeb9eec4dfd99b3ec771f2419dca78b76db6c2e746cf" + }, + { + "id": "asset:libs/telemetry/tsconfig.spec.json", + "kind": "asset", + "path": "libs/telemetry/tsconfig.spec.json", + "sha256": "ecc7bb80f8719d4170a8f2da0ba981a4f6ea1106c019980828d0f09b347254bb" + }, + { + "id": "asset:libs/ui-react/package.json", + "kind": "asset", + "path": "libs/ui-react/package.json", + "sha256": "9374053061b02a2cd65b128c8456a036c81a06e49b65a6c3d775a96793f027a3" + }, + { + "id": "asset:libs/ui-react/project.json", + "kind": "asset", + "path": "libs/ui-react/project.json", + "sha256": "56be25a5f6c8a4d91f9c531750ca13aedc4cf3602ed418ec4c8ca801e902d97a" + }, + { + "id": "asset:libs/ui-react/src/lib/.gitkeep", + "kind": "asset", + "path": "libs/ui-react/src/lib/.gitkeep", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "id": "asset:libs/ui-react/tsconfig.json", + "kind": "asset", + "path": "libs/ui-react/tsconfig.json", + "sha256": "2d940b66b47d944b18198425d0a235877951d2f0d24ae307cb05a85e32309db0" + }, + { + "id": "asset:libs/ui-react/tsconfig.lib.json", + "kind": "asset", + "path": "libs/ui-react/tsconfig.lib.json", + "sha256": "f178f927edd8991650b3504c164228729a21a394e06cd406e9c88e6bee9a9c0a" + }, + { + "id": "asset:libs/workspace-react/package.json", + "kind": "asset", + "path": "libs/workspace-react/package.json", + "sha256": "ceac8226026346f427c8eced071393319f43199939dd70b13e0ead17ce039b5d" + }, + { + "id": "asset:libs/workspace-react/project.json", + "kind": "asset", + "path": "libs/workspace-react/project.json", + "sha256": "cb5f7051ca646de29574971bb6304ac699055936a3c6d081b610d98bcc1e0cf9" + }, + { + "id": "asset:libs/workspace-react/src/styles/workspace.css", + "kind": "asset", + "path": "libs/workspace-react/src/styles/workspace.css", + "sha256": "e13582704da42f9ab600c0409de451aa96533136ad78d487d973f16b3922d487" + }, + { + "id": "asset:libs/workspace-react/tsconfig.json", + "kind": "asset", + "path": "libs/workspace-react/tsconfig.json", + "sha256": "aa6318db64921756810642bcc4b001c61cd0c06044afc7c9f327b3f02b1fee5d" + }, + { + "id": "asset:libs/workspace-react/tsconfig.lib.json", + "kind": "asset", + "path": "libs/workspace-react/tsconfig.lib.json", + "sha256": "7b1c282944428eef146c336541cd5c6ac8e6b1cc4c6452ab39a55481896d6be0" + }, + { + "id": "component:libs/chat/debug/src/lib/compositions/chat-debug/chat-debug.component.ts#ChatDebugComponent", + "kind": "component", + "path": "libs/chat/debug/src/lib/compositions/chat-debug/chat-debug.component.ts", + "symbol": "ChatDebugComponent", + "decorators": [ + "Component" + ], + "signature": "@Component({\n selector: 'chat-debug',\n standalone: true,\n imports: [TimelineInspectorComponent, StateInspectorComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n styles: [\n CHAT_DEBUG_TOKENS,\n `\n :host {\n display: contents;\n }\n\n /* ── Status pill launcher ─────────────────────────────────────── */\n .launcher {\n position: fixed;\n top: 20px;\n right: 20px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n border-radius: var(--tplane-chat-debug-radius-pill);\n background: var(--tplane-chat-debug-bg);\n border: 1px solid var(--tplane-chat-debug-border);\n color: var(--tplane-chat-debug-text);\n cursor: pointer;\n z-index: 990;\n box-shadow: var(--tplane-chat-debug-shadow-pill);\n transition: background 120ms ease, border-color 120ms ease;\n padding: 0;\n }\n .launcher:hover {\n background: var(--tplane-chat-debug-surface);\n border-color: var(--tplane-chat-debug-border-strong);\n }\n .launcher__dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--tplane-chat-debug-success);\n box-shadow: 0 0 8px\n color-mix(in srgb, var(--tplane-chat-debug-success) 60%, transparent);\n }\n .launcher__dot--streaming {\n background: var(--tplane-chat-debug-accent);\n box-shadow: 0 0 8px\n color-mix(in srgb, var(--tplane-chat-debug-accent) 70%, transparent);\n animation: chat-debug-pill-pulse 1.2s ease-in-out infinite;\n }\n @keyframes chat-debug-pill-pulse {\n 0%,\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n 50% {\n opacity: 0.6;\n transform: scale(0.85);\n }\n }\n\n /* ── Docked panel ─────────────────────────────────────────────── */\n .panel {\n position: fixed;\n background: var(--tplane-chat-debug-bg);\n color: var(--tplane-chat-debug-text);\n border: 1px solid var(--tplane-chat-debug-border);\n z-index: 991;\n display: flex;\n flex-direction: column;\n box-shadow: var(--tplane-chat-debug-shadow-panel);\n animation: chat-debug-panel-enter 120ms ease;\n }\n .panel--right {\n top: 0;\n right: var(--tplane-chat-sidebar-claim-right, 0);\n bottom: 0;\n width: var(--panel-size, 420px);\n border-right: 0;\n border-top-left-radius: var(--tplane-chat-debug-radius-panel);\n border-bottom-left-radius: var(--tplane-chat-debug-radius-panel);\n transform-origin: bottom right;\n transition: right 200ms ease-out;\n }\n .panel--left {\n top: 0;\n left: 0;\n bottom: 0;\n width: var(--panel-size, 420px);\n border-left: 0;\n border-top-right-radius: var(--tplane-chat-debug-radius-panel);\n border-bottom-right-radius: var(--tplane-chat-debug-radius-panel);\n transform-origin: bottom left;\n }\n .panel--bottom {\n left: 0;\n right: var(--tplane-chat-sidebar-claim-right, 0);\n bottom: 0;\n height: var(--panel-size, 40vh);\n border-bottom: 0;\n border-top-left-radius: var(--tplane-chat-debug-radius-panel);\n border-top-right-radius: var(--tplane-chat-debug-radius-panel);\n transform-origin: bottom right;\n transition: right 200ms ease-out;\n }\n /* Mobile breakpoint: when an edge-claimer occupies the right and\n the device is narrow, the bottom strip's effective width is\n ~zero. Explicitly hide it so it doesn't intercept pointer events\n on the sidebar drawer. The chat-debug launcher remains visible. */\n @media (max-width: 767px) {\n .panel--bottom {\n display: none;\n }\n }\n @keyframes chat-debug-panel-enter {\n from {\n opacity: 0;\n transform: scale(0.96);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n }\n\n .panel__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n border-bottom: 1px solid var(--tplane-chat-debug-border);\n min-height: 44px;\n box-sizing: border-box;\n }\n .panel__title {\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n letter-spacing: -0.01em;\n color: var(--tplane-chat-debug-text);\n }\n .panel__actions {\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .panel__dock-group {\n display: inline-flex;\n gap: 0;\n padding: 2px;\n background: var(--tplane-chat-debug-bg-deep);\n border: 1px solid var(--tplane-chat-debug-border);\n border-radius: 6px;\n }\n .panel__dock-btn {\n appearance: none;\n background: transparent;\n border: 0;\n border-radius: 4px;\n width: 24px;\n height: 22px;\n padding: 0;\n color: var(--tplane-chat-debug-text-subtle);\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n transition: background 120ms ease, color 120ms ease;\n }\n .panel__dock-btn:hover {\n color: var(--tplane-chat-debug-text);\n }\n .panel__dock-btn.is-active {\n background: var(--tplane-chat-debug-border);\n color: var(--tplane-chat-debug-text);\n }\n .panel__dock-btn svg {\n display: block;\n }\n\n .panel__close {\n appearance: none;\n background: transparent;\n border: 0;\n border-radius: 6px;\n width: 26px;\n height: 26px;\n margin-left: 4px;\n color: var(--tplane-chat-debug-text-subtle);\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n transition: background 120ms ease, color 120ms ease;\n }\n .panel__close:hover {\n background: var(--tplane-chat-debug-surface);\n color: var(--tplane-chat-debug-text);\n }\n\n .panel__controls {\n border-bottom: 1px solid var(--tplane-chat-debug-border);\n overflow-y: auto;\n max-height: 50%;\n background: var(--tplane-chat-debug-bg);\n }\n .panel__controls:empty {\n display: none;\n }\n\n .panel__tabs {\n display: flex;\n gap: 4px;\n border-bottom: 1px solid var(--tplane-chat-debug-border);\n padding: 0 12px;\n background: var(--tplane-chat-debug-bg);\n }\n .panel__tab {\n appearance: none;\n background: transparent;\n border: 0;\n border-bottom: 2px solid transparent;\n padding: 10px 8px;\n font: inherit;\n font-size: 13px;\n font-weight: 500;\n color: var(--tplane-chat-debug-text-muted);\n cursor: pointer;\n transition: color 120ms ease, border-color 120ms ease;\n margin-bottom: -1px;\n }\n .panel__tab:hover {\n color: var(--tplane-chat-debug-text);\n }\n .panel__tab.is-active {\n color: var(--tplane-chat-debug-text);\n border-bottom-color: var(--tplane-chat-debug-accent);\n }\n\n .panel__body {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n background: var(--tplane-chat-debug-bg);\n }\n `,\n ],\n template: `\n @if (!open() && launcher() === 'floating') {\n \n } @else if (open() && agent(); as currentAgent) {\nNo changes
\n } @else {\n{{ state() | json }}\n \n \n Agent paused — review needed\n
\n{{ interruptReason() }}
\nInputs
\n{{ formatJson(toolCall().args) }}\n Output
\n{{ formatJson(toolCall().result) }}\n