From 15bf1b95ce904bbe69e408ce92886ede5ab6d837 Mon Sep 17 00:00:00 2001 From: Tom Dupuis <60640908+tomdps@users.noreply.github.com> Date: Fri, 7 Aug 2026 18:09:26 +0000 Subject: [PATCH 1/5] Purge deprecated development toolchain --- .gitignore | 7 +- .opcore/config | 25 + .zeroshot/settings.json | 2 +- AGENTS.md | 88 +- CLAUDE.md | 88 +- ace.json | 50 - crates/graph-core/src/extraction/ignore.rs | 5 - crates/graph-core/src/watch/args.rs | 23 +- docs/architecture/runtime-cli-ard.md | 27 +- docs/integration/pre-write-validation.md | 2 +- docs/planning/opcore-alpha-roadmap.md | 17 +- docs/release/artifact-attestation.md | 5 +- docs/release/asp-dogfood-receipt.json | 151 +- docs/release/asp-dogfood-receipt.summary.md | 27 +- docs/release/crg-graph-parity-ledger.md | 71 - docs/release/cutover-receipt.json | 661 +++++-- docs/release/cutover-receipt.summary.md | 5 +- docs/release/graph-release-handoff.md | 10 +- docs/release/graph-release-receipt.json | 100 +- .../graph-release-receipt.payload.json | 88 +- .../release/inspect-implementations-parity.md | 35 +- docs/release/inspect-signature-parity.md | 161 +- docs/release/provenance-receipts.md | 14 +- docs/release/retained-guardrail-matrix.md | 64 - .../2026-07-09-opcore-legacy-policy-parity.md | 319 --- docs/validation/rust-adapter-parity.md | 141 +- ...old-rox-comparison-receipt-2026-06-27.json | 90 - ...t-old-rox-comparison-receipt-2026-06-27.md | 18 - ...st-retained-tools-receipts-2026-06-23.json | 235 --- ...rust-retained-tools-receipts-2026-06-23.md | 65 - eslint.config.mjs | 7 +- package.json | 16 +- packages/asp-provider/README.md | 3 +- .../schemas/opcore-contracts.schema.json | 1707 +++-------------- packages/contracts/src/index.ts | 568 +----- packages/edit/src/path-policy.ts | 4 - .../graph-pipeline/pipeline-fixtures.json | 4 +- .../baseline-receipts.json | 115 -- .../daemon-socket-fixtures.json | 482 ----- .../golden-corpus.json | 335 ---- .../graph-reference-evidence/manifest.json | 696 ------- .../sqlite-fixtures.json | 174 -- .../release-readiness-fixture.json | 42 +- .../watch-roots/.code-review-graphignore | 1 - .../graph-robustness/watch-roots/.gitignore | 1 + .../watch-roots/crg-ignored/drop.ts | 1 - .../fixtures/graph-serve/serve-fixtures.json | 35 + packages/fixtures/package.json | 2 +- packages/fixtures/src/index.ts | 59 +- .../asp-warm/warm-project-registry.ts | 3 - .../src/advanced/inspect-language-service.ts | 3 - packages/opcore/src/doctor.ts | 6 +- packages/opcore/src/init.ts | 2 +- packages/opcore/src/source-policy.ts | 5 +- .../src/project-workspace.ts | 4 +- packages/validation-rust/src/materialize.ts | 7 +- .../src/lint-plugin-cache.ts | 1 - rox.json | 112 -- scripts/asp-dogfood-receipt-support.mjs | 168 +- scripts/check-packages.mjs | 5 +- scripts/check-provenance.mjs | 208 +- scripts/check-rust-graph-function-metrics.mjs | 76 - scripts/check-workspace.mjs | 198 +- scripts/ci/run-local-ci-equivalent.sh | 8 +- scripts/ci/run-rox-clean-changed-gate.mjs | 215 --- scripts/dev-env.sh | 23 - scripts/generate-asp-dogfood-receipt.mjs | 22 +- scripts/generate-cutover-receipt.mjs | 116 +- scripts/generate-graph-release-receipt.mjs | 53 +- scripts/generate-release-receipt.mjs | 12 +- scripts/lib/launch-claim-scrub.mjs | 8 - scripts/measure-graph-reference-baselines.mjs | 115 -- scripts/run-ace.sh | 55 - scripts/run-opcore-self-check.mjs | 84 + scripts/setup-current-tools.sh | 319 --- tests/asp-dogfood-receipt.test.mjs | 64 +- tests/asp-provider.test.mjs | 4 +- tests/cli-canonical-surface.test.mjs | 16 +- tests/command-router.test.mjs | 37 +- tests/conformance.test.mjs | 28 +- tests/contracts.test.mjs | 530 +---- tests/current-tools.test.mjs | 199 -- tests/cutover-release.test.mjs | 57 +- tests/edit-tree-policy-parity.test.mjs | 4 +- tests/gate-negative-fixtures.test.mjs | 247 +-- tests/graph-pipeline-cli.test.mjs | 20 +- tests/graph-reference-evidence.test.mjs | 227 --- tests/graph-store-conformance.test.mjs | 17 +- tests/helpers/asp-dogfood-fixture.mjs | 57 +- tests/import-boundaries.test.mjs | 4 +- tests/installed-bins.test.mjs | 12 +- tests/launch-claim-scrub.test.mjs | 6 +- tests/opcore-facade.test.mjs | 7 +- tests/provenance-policy.test.mjs | 4 +- tests/scaffold.test.mjs | 44 +- tests/schema-contracts.test.mjs | 517 +---- tests/validation-cli.test.mjs | 14 +- tests/validation-rust.test.mjs | 5 +- 98 files changed, 1683 insertions(+), 9111 deletions(-) create mode 100644 .opcore/config delete mode 100644 ace.json delete mode 100644 docs/release/crg-graph-parity-ledger.md delete mode 100644 docs/release/retained-guardrail-matrix.md delete mode 100644 docs/superpowers/plans/2026-07-09-opcore-legacy-policy-parity.md delete mode 100644 docs/validation/rust-old-rox-comparison-receipt-2026-06-27.json delete mode 100644 docs/validation/rust-old-rox-comparison-receipt-2026-06-27.md delete mode 100644 docs/validation/rust-retained-tools-receipts-2026-06-23.json delete mode 100644 docs/validation/rust-retained-tools-receipts-2026-06-23.md delete mode 100644 packages/fixtures/graph-reference-evidence/baseline-receipts.json delete mode 100644 packages/fixtures/graph-reference-evidence/daemon-socket-fixtures.json delete mode 100644 packages/fixtures/graph-reference-evidence/golden-corpus.json delete mode 100644 packages/fixtures/graph-reference-evidence/manifest.json delete mode 100644 packages/fixtures/graph-reference-evidence/sqlite-fixtures.json delete mode 100644 packages/fixtures/graph-robustness/watch-roots/.code-review-graphignore create mode 100644 packages/fixtures/graph-robustness/watch-roots/.gitignore delete mode 100644 packages/fixtures/graph-robustness/watch-roots/crg-ignored/drop.ts create mode 100644 packages/fixtures/graph-serve/serve-fixtures.json delete mode 100644 rox.json delete mode 100644 scripts/check-rust-graph-function-metrics.mjs delete mode 100644 scripts/ci/run-rox-clean-changed-gate.mjs delete mode 100755 scripts/dev-env.sh delete mode 100644 scripts/measure-graph-reference-baselines.mjs delete mode 100644 scripts/run-ace.sh create mode 100644 scripts/run-opcore-self-check.mjs delete mode 100755 scripts/setup-current-tools.sh delete mode 100644 tests/current-tools.test.mjs delete mode 100644 tests/graph-reference-evidence.test.mjs diff --git a/.gitignore b/.gitignore index f405b62..afcd905 100644 --- a/.gitignore +++ b/.gitignore @@ -2,18 +2,15 @@ node_modules/ dist/ *.tsbuildinfo target/ -.ace/ .agents/ .claude/ .codex/ .gemini/ .opencode/ -.code-review-graph/ -.rox-cache/ -.robustness-engine-cache/ .zeroshot/* !.zeroshot/settings.json -.opcore/ +.opcore/* +!.opcore/config .asp/ coverage/ *.tgz diff --git a/.opcore/config b/.opcore/config new file mode 100644 index 0000000..1632c85 --- /dev/null +++ b/.opcore/config @@ -0,0 +1,25 @@ +{ + "validation": { + "adapters": [ + "typescript", + "rust", + "python", + "docs", + "clone" + ], + "pathPolicy": { + "exclude": [ + "packages/fixtures/", + "tests/fixtures/", + "packages/opcore-graph-core-darwin-arm64/", + "packages/opcore-graph-core-darwin-x64/", + "packages/opcore-graph-core-linux-x64/" + ] + }, + "checks": { + "packs": [], + "disabled": [], + "defaults": [] + } + } +} diff --git a/.zeroshot/settings.json b/.zeroshot/settings.json index 5eee803..d6c3208 100644 --- a/.zeroshot/settings.json +++ b/.zeroshot/settings.json @@ -5,7 +5,7 @@ "worktree": { "baseRef": "origin/dev", "setup": [ - "npm run setup" + "npm ci" ] }, "ship": { diff --git a/AGENTS.md b/AGENTS.md index 9bedb52..45d49e4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,7 +2,7 @@ UPDATE THIS FILE when making architectural changes, adding patterns, or changing # Opcore -Opcore is the code-intelligence and robustness monorepo for graph context, edit planning, pre-write validation, repo robustness scanning/measurement, and the standalone ASP Core check provider for coding agents. Remaining old-name env vars, cache dirs, fixtures, receipts, and repo-path names are transitional implementation debt; do not introduce new public/product-facing old-name branding. The accepted runtime/CLI boundary is hybrid: Rust graph core with TypeScript contracts, CLI router, edit, validation, validation-typescript, validation-clone, ASP provider facade, npm/Opcore facade, and ACE descriptors. See @docs/architecture/runtime-cli-ard.md and @docs/planning/opcore-alpha-roadmap.md before changing language, package, provider, product, or CLI ownership. +Opcore is the code-intelligence and robustness monorepo for graph context, edit planning, pre-write validation, repo robustness scanning/measurement, and the standalone ASP Core check provider for coding agents. The accepted runtime/CLI boundary is hybrid: Rust graph core with TypeScript contracts, CLI router, edit, validation, validation-typescript, validation-clone, ASP provider facade, npm/Opcore facade, and managed descriptor artifacts. See @docs/architecture/runtime-cli-ard.md and @docs/planning/opcore-alpha-roadmap.md before changing language, package, provider, product, or CLI ownership. ## Key Concepts @@ -11,9 +11,9 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit | Graph provider | Owns source extraction, persistent graph facts, freshness metadata, graph query contracts, and FTS search index artifacts. | | Edit planner | Owns symbol-aware rename, move, signature, patch, and tree edits; it must validate full edit plans, not isolated files. | | Validation engine | Owns mechanical checks, hypothetical validation, check manifests, and failure policy. | -| Opcore product facade | Thin user-facing robustness loop over graph, validation, edit, and ASP-provider packages: read-only scan/status/check/measure by default, approval-gated init, and no ASP-standard or old-tool replacement claims. | +| Opcore product facade | Thin user-facing robustness loop over graph, validation, edit, and ASP-provider packages: read-only scan/status/check/measure by default and approval-gated init. | | Command adapters | Package-owned graph, edit, check, and validate dispatch surfaces used by canonical `opcore` advanced routes. | -| Current-tool wrappers | Local agent tooling that invokes the already-installed ACE-managed tools; these wrappers are not Opcore implementation artifacts. | +| Repository self-validation | Opcore validates its own changed implementation surface through `npm run opcore:self-check` and `.opcore/config`. | ## Where To Look @@ -43,7 +43,6 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit | ASP warm inspect/edit session | `packages/opcore/src/advanced/asp-warm/`, @docs/architecture/asp-warm-session-ard.md | | ASP provider manifest generator | `scripts/write-asp-provider-manifest.mjs` writes canonical `asp-server.json` plus retained provisional install metadata. | | Golden fixtures and reference evidence | @packages/fixtures/ | -| Graph reference evidence manifest | `packages/fixtures/graph-reference-evidence/manifest.json` | | Graph release fixture | `packages/fixtures/graph-release/release-readiness-fixture.json` | | Graph release receipt | `docs/release/graph-release-receipt.json` | | Graph release payload checksum target | `docs/release/graph-release-receipt.payload.json` | @@ -54,7 +53,6 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit | Cutover receipt summary | @docs/release/cutover-receipt.summary.md | | ASP dogfood receipt | `docs/release/asp-dogfood-receipt.json` | | ASP dogfood receipt summary | @docs/release/asp-dogfood-receipt.summary.md | -| Retained guardrail matrix | @docs/release/retained-guardrail-matrix.md | | Secret scan allowlist | @docs/release/secret-scan-allowlist.json | | Release receipt generator | `scripts/generate-release-receipt.mjs` | | Cutover receipt generator | `scripts/generate-cutover-receipt.mjs` | @@ -62,30 +60,24 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit | Workspace checks | `scripts/check-workspace.mjs` | | Package dry-run checks | `scripts/check-packages.mjs` | | Provenance checks | `scripts/check-provenance.mjs` | -| Current ACE tool setup | `scripts/setup-current-tools.sh` | +| Opcore self-check | `scripts/run-opcore-self-check.mjs`, `.opcore/config` | | Local CI-equivalent gate | @scripts/ci/run-local-ci-equivalent.sh | | Zeroshot setup | @.zeroshot/settings.json | | GitHub Actions | @.github/workflows/ | | Tests | @tests/ | -## Current Tooling - -- Run `npm run setup:tools` after cloning or entering a fresh worktree. It writes `.ace/runtime/bin/{rox,crg,cix}` wrappers that exec the current external ACE-managed tools from `LATTICE_CURRENT_TOOLS_DIR`, sibling Covibes repos, or `PATH`. -- Run `npm run ace:install` when ACE provider state or bundled generic skills are missing. It writes ignored generated provider/runtime files under `.claude/`, `.agents/`, `.codex/`, `.gemini/`, `.opencode/`, and `.ace/`; do not hand-edit or commit those trees. Use `npm run ace:sync` after changing `CLAUDE.md`, `AGENTS.md`, or `ace.json`, and `npm run ace:validate` to verify generated ACE state. Repo-specific guidance belongs in this file, not in checked-in provider skills. -- The generated `rox` wrapper also prepends the current ACE-managed `rust-code-analysis-cli` native tool when one is discoverable - WHY: all-mode Rust function metrics must match scoped Rust Rox findings. -- NEVER point `.ace/runtime/bin/{rox,crg,cix}` at `packages/graph`, `packages/edit`, or `packages/validation` before the release/cutover issues say those packages are production-ready - WHY: agents must validate Opcore work with the stable current tools, not with the toolchain being rewritten. -- Source `scripts/dev-env.sh` when interactive shells should prefer the generated wrappers. It fails non-zero and leaves PATH/env untouched when `.ace/runtime/bin/{rox,crg,cix}` is incomplete - WHY: ACE, MCP, Zeroshot, and humans should resolve the same current tool surface. -- `ace.json` routes the code-review graph MCP through `.ace/runtime/bin/crg serve --repo "$repo_root"`; update `ace.json`, `scripts/setup-current-tools.sh`, and this file together when tool acquisition changes. -- Use current external `crg` for discovery before broad text scans, current external `cix` for cohesive symbol/edit deltas, and current external `rox` for staged/changed/repo validation. These are dev validation helpers, not Opcore release surfaces. -- `npm run current-tools:validate-changed` runs `scripts/ci/run-rox-clean-changed-gate.mjs`: it stops Rox, clears `.rox-cache` and `.robustness-engine-cache`, runs daemon-free changed-file Rox, and fails non-baseline findings while retaining legacy code-quality findings already present on the base tree. -- `scripts/ci/run-local-ci-equivalent.sh` uses a docs/agent-guidance fast path when the only changed files are launch docs or agent guidance: setup tools, shell syntax, release hygiene, workspace, provenance, and changed-file Rox. CI-wrapper, source, package, native artifact, release evidence, and other implementation changes still run `npm run ci`, `npm run current-tools:validate-all`, and `npm run current-tools:validate-rust-graph`. `cutover:check` may set `OPCORE_CUTOVER_REUSE_CURRENT_TOOL_GUARDRAILS=1` in root CI and release aggregate proof to reuse validated guardrail hashes from the checked-in cutover receipt while regenerating installed-artifact proof; `cutover:receipt` without the flag must run the retained current-tool commands - WHY: doc-only handoff proofs must fit the cmdproof timeout, aggregate installed-artifact CI must not depend on workstation-only current-tool wrappers, and maintainer receipts must still co-record real retained guardrail proof. +## Development Tooling + +- Run `npm run setup` after cloning or entering a fresh worktree. It installs repository dependencies only and must leave the worktree clean. +- Run `npm run opcore:self-check` after building. It invokes the built public Opcore CLI in introduced mode against the configured base ref and consumes the checked-in `.opcore/config` policy. +- `scripts/ci/run-local-ci-equivalent.sh` runs normal CI plus the Opcore self-check. Its docs/agent-guidance fast path uses only repository-native workspace, provenance, build, and self-validation commands. +- Zeroshot worktrees run `npm ci` and the same local CI-equivalent command proof - WHY: humans, agents, CI, and ship clusters must exercise one repository-owned validation surface. - Root `.npmrc` sets `loglevel=silent` - WHY: JSON-emitting npm scripts such as `npm run asp-dogfood:check -- --json` must write parseable JSON to redirected stdout without npm lifecycle preambles. - `npm run test:ci` routes through `scripts/run-test-ci.mjs`: it runs the parallel-safe Node test files first, runs `tests/validation-python.test.mjs` separately because its real subprocess fixtures can exhaust CI process slots under the parallel suite, then runs `tests/native-packaging-policy.test.mjs` separately with receipt gates skipped - WHY: Python compiler-truth tests need deterministic process availability, and the native packaging policy test intentionally mutates native package artifacts while exercising aggregate dry-run failures. - Graph-owned transitional `opcore graph serve --repo ` starts the graph package stdio/MCP bridge over graph-core JSONL; `--repo` defaults to cwd, supports ping/status/query/search/shutdown, injects missing nested query repos, and returns typed startup/frame/provider failures. -- #126 ships graph-core through bundled internal Opcore native packages `@the-open-engine/opcore-graph-core-darwin-arm64`, `@the-open-engine/opcore-graph-core-darwin-x64`, and `@the-open-engine/opcore-graph-core-linux-x64`; `packages/graph` resolves only matching package metadata and never probes `packages/graph/dist/native`, sibling checkouts, `.ace/runtime`, or PATH. +- #126 ships graph-core through bundled internal Opcore native packages `@the-open-engine/opcore-graph-core-darwin-arm64`, `@the-open-engine/opcore-graph-core-darwin-x64`, and `@the-open-engine/opcore-graph-core-linux-x64`; `packages/graph` resolves only matching package metadata and never probes `packages/graph/dist/native`, sibling checkouts, or PATH. - Release flow is `dev -> main`: CI runs on `dev` and `main`, PRs to `main` must come from `dev`, and `.github/workflows/release.yml` auto-publishes npm package version `0.2.1` with dist-tag `latest` after the `CI` workflow succeeds on `main`. Each release must provide readable notes at `docs/release/v.md`; the workflow uses that file verbatim for the GitHub release. The CI native jobs upload tarred native package directories so `opcore-graph-core` execute bits survive artifact transfer; aggregate CI and release publish must set `OPCORE_REQUIRE_ALL_NATIVE_PACKAGES=1` after extracting all three native artifacts, and `scripts/release-dry-run.mjs` then validates package-local executable binaries/checksums without rebuilding graph-core - WHY: aggregate and publish proof must consume runnable per-target artifacts produced by native jobs, not a local Linux rebuild or non-executable download. -- #19 keeps `OPCORE_GRAPH_WATCH_PATHS` as the only watch env default and ignores `CRG_WATCH_PATHS` - WHY: Opcore watch roots must not inherit old-tool scoping accidentally. -- #19 graph discovery excludes generated/private/dependency roots even without repo ignore files: `.git`, `node_modules`, `.pnpm`, `vendor`, `dist`, `target`, `.ace`, `.agents`, `.claude`, `.codex`, `.gemini`, `.lattice`, `.opencode`, `.rox-cache`, and `.robustness-engine-cache` - WHY: cache/vendor/provider mirror changes must not create graph facts, freshness changes, validation input, or FTS rows. +- #19 graph discovery excludes generated/private/dependency roots even without repo ignore files: `.git`, `node_modules`, `.pnpm`, `vendor`, `dist`, `target`, `.agents`, `.claude`, `.codex`, `.gemini`, `.lattice`, and `.opencode` - WHY: cache/vendor/provider mirror changes must not create graph facts, freshness changes, validation input, or FTS rows. - #17/#19/#21 source/coverage policy is reconciled across graph-core, validation, and Opcore status/metrics: graph-extractable TypeScript, JavaScript, Python `.py`/`.pyi`, and Rust `.rs`; validation-supported TS/JS variants, Python source/stubs, Rust source/includes, and `Cargo.toml`; retained `Cargo.lock`; unsupported/degraded stacks and missing Python tools are counted honestly. - #16 Python generated/private/dependency roots are excluded from discovery and status census: `.venv`, `venv`, `env`, `__pycache__`, `.eggs`, `build`, `.tox`, `.mypy_cache`, `.pytest_cache`, `.ruff_cache`, `site-packages`, `*.egg-info`, and `*.dist-info` - WHY: dependency/cache artifacts must not create graph freshness or coverage evidence. - #17 Python export metadata is best-effort: `__all__` wins when present, otherwise the leading-underscore convention marks module-level public names; file `exports[]` entries must include policy and supportedSymbol - WHY: Python has no enforced export boundary. @@ -96,13 +88,12 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - #197 makes hypothetical graph evaluation exact-state: validation creates one `ValidationFileView` per before/after state and owns one disposable graph session for that view; graph materializes the complete visible TS/TSX/JS/JSX, Python `.py`/`.pyi`, and Rust `.rs` universe into a bounded isolated root, builds graph-core once, shares the immutable session across checks, and removes it on every exit. Introduced mode must use distinct before/after snapshots, ASP listings must preserve host truncation, and exact-state construction/query failure is non-pass even when persistent graph mode is optional - WHY: a persistent target-repo graph or incomplete listing cannot describe hypothetical file contents and must never produce a false clean pre-write result. - #19 requires graph status to preserve real WAL checkpoint evidence from the latest pipeline summary and release gates to fail missing/fabricated WAL evidence - WHY: freshness and checkpoint pressure must remain host-visible provider facts. - #19 treats `opcore graph serve` as the stdio/MCP hot-query replacement, not a Unix socket, with parallel independent serve sessions as the supported concurrency evidence. -- #19 keeps current external CRG receipts as non-implementation compatibility evidence only - WHY: old CRG remains a guardrail until downstream cutover issues consume the Opcore proof. -- `opcore-asp-provider --stdio` is the transitional provider binary for the standalone ASP Core check provider; it uses host workspace callbacks and Opcore validation only, never ACE descriptors or current-tool wrappers. +- `opcore-asp-provider --stdio` is the transitional provider binary for the standalone ASP Core check provider; it uses host workspace callbacks and Opcore validation only. - `opcore asp serve --stdio` is a hidden host-launched warm ASP session for inspect/edit/check under `packages/opcore/src/advanced/asp-warm/`; it is intercepted before the public router, omitted from `opcore --help`, keeps lifecycle state under `.opcore/asp/`, never auto-spawns, never stays always-on beyond its idle timeout, and never mutates source files - WHY: agents may need warm inspect/edit latency without making ASP a public human command group or changing the cold check provider. -- #120 ASP dogfood uses `npm run asp-dogfood:check` with a temporary `ASP_HOME`, standalone sibling/private ASP manager bootstrap evidence, installed provider evidence, and retained current-tool guardrail receipts. `OPCORE_ASP_DOGFOOD_REUSE_CURRENT_TOOL_GUARDRAILS=1 npm run asp-dogfood:receipt` may refresh installed-artifact evidence from already-recorded retained guardrails, but the default dogfood path must still run those guardrails live - WHY: dogfood proves advisory/shadow host integration without making Opcore the host, manager, authority, or old-tool replacement. +- #120 ASP dogfood uses `npm run asp-dogfood:check` with a temporary `ASP_HOME`, a built adjacent `agent-server-protocol` checkout or explicit `ASP_DOGFOOD_ASP_REPO`, installed provider evidence, and provider/host authority receipts. Receipts redact the resolved manager root as `` - WHY: dogfood proves advisory/shadow host integration without making Opcore the host, manager, or authority or embedding workstation paths. - `opcore [--repo ] [--json]` is the public first-run scan. It emits `repoState` plus `validationResult`, prints Coverage before Findings, and writes only `.opcore/report.json`, `.opcore/history.jsonl`, and bounded `.opcore/telemetry.jsonl` capped at 500 records or 1 MiB. - `opcore --version`, `opcore -v`, and `opcore version` are read-only runtime provenance surfaces. JSON output carries `runtimeInfo` with package name/version, bin, artifact source (`source_checkout`, `installed_package`, or `unknown`), package root, and entrypoint - WHY: agents and humans must know which Opcore binary/artifact is actually running. -- `opcore status [--repo ] [--json]` is the runtime-owned activation/readiness entrypoint. It emits `repoState` and must stay read-only: no graph build/update/watch, validation checks, package installs, ASP setup, ACE setup, current-tool wrapper execution, or source writes. +- `opcore status [--repo ] [--json]` is the runtime-owned activation/readiness entrypoint. It emits `repoState` and must stay read-only: no graph build/update/watch, validation checks, package installs, ASP setup, or source writes. - `opcore doctor [--repo ] [--json]` is the runtime-owned diagnostic entrypoint. It emits `runtimeInfo`, `opcoreDoctor`, and transitional `validationStatus`, reporting version/provenance, config found/missing/unreadable state for `.opcore/config`, loaded check ids, graph freshness, generated-state ignore guidance, and next actions without building graphs, running checks, installing packages, setup, wrappers, or source writes. - `opcore check --changed --json` is the agent gate and defaults to `--base HEAD`. `opcore check --staged`, `opcore check --all`, and explicit file operands are native check scopes; explicit missing files and blank check ids must return structured `invalid_payload` JSON instead of passing zero checks or throwing plain text. - #31 latency telemetry contracts live in `packages/contracts`: `CommandTiming`, `RepoShapeFingerprint`, `CommandLatencyRecord`, `LatencyBudget`, and `LatencyBudgetResult` must stay source-safe, schema/validator/test aligned, and `.opcore/telemetry.jsonl` must remain ring-buffer bounded to 500 records or 1 MiB. Telemetry `bin` is the normalized public bin name and `canonicalCommand` is sanitized command identity, not raw argv or path operands. @@ -113,7 +104,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - #36 `opcore measure` reads bounded `.opcore/telemetry.jsonl` plus latency budgets and emits `opcoreMeasure.latency.findings[]` for slower or over-budget command/phase observations only; it must not write artifacts, run checks, or emit `ok` latency rows - WHY: slow actions need drillable evidence without turning measure into a runner or score. - #137 `opcore graph serve` writes one bounded `CommandLatencyRecord` per forwarded child frame through a product-CLI-injected telemetry writer, with canonical commands shaped as `opcore graph serve ` and per-op phase ids such as `serve_query`; `npm run latency:check` includes serve and inspect budgets in non-blocking trend mode - WHY: long-lived serve/inspect performance claims need before/after evidence without reintroducing a daemon or bypassing the existing telemetry contract. - `opcore install [--repo ] [--local|--global] [--yes] [--json]` is the recommended scan-first repo/agent setup path and interactive wizard. It runs read-only scan output before setup, prompts in an interactive Git repo to choose repo or global write-gate scope when neither scope nor `--yes`/`--json` is supplied, keeps JSON/non-TTY preview runs plan-only, and approved repo install writes additive `.opcore/config`, delimited guidance, Opcore agent skills, `.opcore/hooks/opcore-agent-gate.mjs`, Claude Code `.claude/settings.json` and Codex `.codex/hooks.json` PreToolUse wiring, a safe active `.git/hooks/pre-commit` when no existing hook is present, managed `.opcore/` `.gitignore` coverage, and `.opcore/init-undo.json`; approved global install writes `~/.opcore/hooks/opcore-agent-gate.mjs`, user-level skill files, merges `~/.claude/settings.json` and `~/.codex/hooks.json`, and records undo in `~/.opcore/init-undo.json`. `opcore uninstall [--repo ] [--local|--global] [--yes] [--json]` removes/restores only recorded Opcore-owned entries. `opcore init` remains the conservative compatibility setup path with explicit `--approve` semantics and the separate opt-in `--fail-closed-hook` script. -- `opcore try [--json]` is the launch demo loop. It creates local TS, Rust, mixed, and unsupported-file sample repos, runs scan/init/check/measure, returns `opcoreTry.published:false`, and must not publish anything or mention old-tool/current-tool names in human output. +- `opcore try [--json]` is the launch demo loop. It creates local TS, Rust, mixed, and unsupported-file sample repos, runs scan/init/check/measure, returns `opcoreTry.published:false`, and must not publish anything. - `opcore` is the only public npm package and intentionally exposes only Opcore-owned public bins: `opcore` plus the bundled ASP provider bin `opcore-asp-provider`. The opcoreGraph/opcoreValidation and cliGraph/validateCommand/editValidation trees remain parallel internal product/advanced-router surfaces, not duplicate drift. `opcore-graph` remains internal allowlisted naming. - Launch-facing naming gates must keep README, quickstart, concepts, examples, demo, agent integration, and `packages/opcore` copy branded as Opcore. Any remaining old-name hit in those surfaces must be explicitly allowlisted as internal/transitional implementation naming. @@ -135,30 +126,29 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - Repo-owned validation extensions live in `.opcore/config` `validation.checks.packs`, resolve from the target repo root, and must export current `ValidationCheckDefinition` objects; Opcore owns loading/registry validation, repos own policy content. - ALWAYS keep `packages/asp-provider` as a provider-process facade over ASP Core check/evaluate only - WHY: ASP hosts own decisions, authority, gate semantics, workspace grants, and apply behavior. - ALWAYS keep warm ASP inspect/edit composition inside `packages/opcore/src/advanced/asp-warm/` and out of `packages/asp-provider` - WHY: only the advanced Opcore router may combine ASP JSON-RPC with ts-morph inspect/edit state, while the standalone provider must remain cold and check-only. -- ALWAYS keep the Opcore product facade thin over public package adapters - WHY: `opcore` is first-run UX, not a second implementation of graph, validation, edit, ASP host authority, or old-tool behavior. +- ALWAYS keep the Opcore product facade thin over public package adapters - WHY: `opcore` is first-run UX, not a second implementation of graph, validation, edit, or ASP host authority. - ALWAYS make Opcore scan/status/check/measure read-only with respect to source files and require explicit approval before `opcore install` or compatibility `opcore init` writes guidance, hooks, or config - WHY: first-run trust depends on showing value before mutating a repo. - ALWAYS put coverage honesty before Opcore metrics - WHY: the graph engine currently supports TypeScript/JavaScript, Python source/stub files, and syn-backed Rust `.rs` extraction, while unsupported stacks must be counted instead of silently ignored. -- NEVER ship a blended quality score, security/SAST claim, all-stack claim, AI-authorship claim, automatic-fix claim, ASP-standard claim, or old-tool replacement claim from Opcore alpha - WHY: the alpha must survive skeptical drill-down and current receipts keep `oldToolReplacementClaimed: false`. +- NEVER ship a blended quality score, security/SAST claim, all-stack claim, AI-authorship claim, automatic-fix claim, or ASP-standard claim from Opcore alpha - WHY: every claim must survive skeptical drill-down through current receipts. - NEVER add new launch-facing old-name branding - WHY: the public/product name is Opcore. Existing old-name package/bin/repo references are transitional implementation debt to remove or hide before alpha. -- ALWAYS keep Rust validation in `packages/validation-rust` as provider assessment checks composed by the CLI, not host decisions or old-tool wrappers - WHY: Cargo, rustfmt, clippy, rustdoc, import/dead-code, unused dependency, and function-metric evidence must remain package-owned and overlay-safe. +- ALWAYS keep Rust validation in `packages/validation-rust` as provider assessment checks composed by the CLI, not host decisions - WHY: Cargo, rustfmt, clippy, rustdoc, import/dead-code, unused dependency, and function-metric evidence must remain package-owned and overlay-safe. - ALWAYS treat Cargo.lock-only changes as retained compatibility unless a later decision expands Rust adapter ownership - WHY: current parity covers `.rs`, `.inc`, and `Cargo.toml`; lockfile-only policy needs an explicit cutover decision before old guardrails move. - NEVER add public CLI behavior outside @docs/architecture/runtime-cli-ard.md canonical routing - WHY: early command shapes become accidental API promises. - ALWAYS keep `GraphProviderStatus.state` aligned with `failure.category` in TypeScript validators and JSON schema - WHY: consumers branch on both fields for required graph failure policy; contradictory pairs make provider handling ambiguous. - ALWAYS reject blank validation check names before normalization deduplicates or trims them - WHY: blank checks can otherwise become an empty no-check validation request and hide caller mistakes. -- ALWAYS update `rox.json`, CI, and this file in the same change when adding a new implementation language or Rust gate surface - WHY: language support without repo-wide and scoped validation lets agents ship unverified code paths. +- ALWAYS update `.opcore/config`, CI, and this file in the same change when adding a new implementation language or validation gate surface - WHY: language support without repo-wide and scoped self-validation lets agents ship unverified code paths. - NEVER add backward-compatibility shims for removed internal paths - WHY: this is a clean release line; migrate the caller or delete the old path. - ALWAYS keep generated provider/runtime trees out of Git - WHY: descriptors and scripts are source of truth; generated trees drift by machine. -- ALWAYS keep old-tool reference evidence under @docs/graph-reference-evidence/ and @packages/fixtures/graph-reference-evidence/ - WHY: reference data may mention old tools only under allowlisted evidence docs/fixtures, never as implementation package naming or source provenance. - ALWAYS keep staged graph optional-analysis classifications sourced from `graphReleaseOptionalAnalysisSurfaces` - WHY: #13 coverage, #14 flows, #15 communities, and #16 read-only suggestions are non-release-blocking #17 deferred/staged surfaces and must not drift across contracts, fixtures, receipts, or docs. -- ALWAYS update `packages/opcore/src/advanced/descriptor.ts`, `scripts/write-cli-descriptor.mjs`, descriptor fixtures, package packlists, and descriptor validation together when changing ACE acquisition metadata - WHY: ACE must consume installed Opcore release artifacts, not workspace-local paths. +- ALWAYS update `packages/opcore/src/advanced/descriptor.ts`, `scripts/write-cli-descriptor.mjs`, descriptor fixtures, package packlists, and descriptor validation together when changing managed artifact metadata - WHY: installed consumers must resolve package artifacts, not workspace-local paths. - ALWAYS update release receipt contracts, `scripts/generate-release-receipt.mjs`, docs/release receipts, CI, and package/provenance/secret negative tests together when changing release evidence ownership - WHY: #29 is the maintainer release proof gate for the alpha line. - ALWAYS update cutover receipt contracts, `scripts/generate-cutover-receipt.mjs`, docs/release cutover receipts, CI, and cutover negative tests together when changing installed-artifact release behavior - WHY: #30 proves canonical Opcore artifacts replace current external dev tools without fallback. - ALWAYS record installed package file paths and checksums in cutover receipts, including ASP provider manifests - WHY: cutover proof must show packaged artifacts survived installation, not only tarball and package.json evidence. -- ALWAYS keep ASP dogfood advisory/shadow and isolated to temp `ASP_HOME`; co-record `current-tools:validate-changed` and `current-tools:validate-rust-graph`, keep `oldToolReplacementClaimed: false`, and represent inspect/edit gaps as degraded or retained blockers - WHY: #120 proves standalone ASP manager integration without authorizing rollout or retiring Rox/CRG/CIX. +- ALWAYS keep ASP dogfood advisory/shadow and isolated to temp `ASP_HOME`, and represent inspect/edit gaps as degraded or parity blockers - WHY: #120 proves standalone ASP manager integration without authorizing rollout. - ALWAYS update `packages/asp-provider/src/manifest.ts`, `scripts/write-asp-provider-manifest.mjs`, package exports/packlists, release receipts, installed-bin tests, and claim scrub together when changing ASP provider manifest/install metadata - WHY: canonical `asp-server.json` and retained provisional metadata must not imply trust, authority, gate permission, or host apply permission. - ALWAYS treat `darwin-arm64`, `darwin-x64`, and `linux-x64` as the only supported Opcore alpha graph-core native targets until CI aggregate evidence expands the set - WHY: local single-platform builds cannot prove clean public installs for other targets. - ALWAYS require CI aggregate and release publish workflows to download all three Opcore native package artifacts before release receipts, cutover receipts, or npm publish claim cross-platform graph-core readiness - WHY: the public `opcore` package must bundle real per-target checksums, not local fallback or fabricated binaries. -- ALWAYS keep `opcore` launch-facing help, README snippets, smoke output, and JSON named Opcore while preserving transitional `opcore status` compatibility - WHY: first-run activation must not leak internal or old-tool names into the public readiness flow. +- ALWAYS keep `opcore` launch-facing help, README snippets, smoke output, and JSON named Opcore while preserving transitional `opcore status` compatibility - WHY: first-run activation must not leak internal implementation names into the public readiness flow. - ALWAYS keep Opcore metrics finding-only and evidence-backed, with no opaque score or blended quality number - WHY: `opcore measure` is a trend report over concrete counts, not a scoring system. - ALWAYS keep clone detection split between the existing graph-core native subcommand and the injected TypeScript validation-clone adapter - WHY: duplicate-code detection needs native indexing without a daemon, graph-provider dependency, security claim, or extra native package. - ALWAYS keep latency budget gates finding-only and non-blocking by default, with `--fail-on-over` reserved for explicit promotion - WHY: latency evidence should guide regression decisions without becoming an opaque score or live measurement runner. @@ -167,8 +157,8 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit ## Language And Runtime - Node >=22 and TypeScript are the current scaffold baseline. -- The accepted boundary is hybrid: Rust graph core owns extraction, persistence, watch refresh, hot graph queries, and clone index analysis; TypeScript owns contracts, router-core helpers, CLI composition, package command adapters, edit orchestration, validation policy, validation-typescript and validation-clone adapters, npm facade, and ACE descriptors. -- #21 adds the Cargo workspace, `crates/graph-core`, Rust sidecar protocol, installed native artifacts, checksums, schema-compatible `rox.json` Rust gate metadata, npm Rust gate scripts, workspace Rust/clippy lint policy, `rox.json` code-quality scope for `packages/`, `scripts/`, `tests/`, and `crates/`, repo-wide Rox extension coverage for scoped Rust graph function metrics under `crates/`, scoped `current-tools:validate-rust-graph`, and GitHub Actions Rust setup. +- The accepted boundary is hybrid: Rust graph core owns extraction, persistence, watch refresh, hot graph queries, and clone index analysis; TypeScript owns contracts, router-core helpers, CLI composition, package command adapters, edit orchestration, validation policy, validation-typescript and validation-clone adapters, npm facade, and managed descriptors. +- #21 adds the Cargo workspace, `crates/graph-core`, Rust sidecar protocol, installed native artifacts, checksums, npm Rust gate scripts, workspace Rust/clippy lint policy, and GitHub Actions Rust setup. - #8 adds Wave 1 Rust graph-core source extraction for `.ts`, `.tsx`, `.js`, and `.jsx` through OXC parser crates. #25 adds syn-backed `.rs` extraction through graph-core facts for File, Module, Struct, Enum, Trait, Impl, Function/Method, TypeAlias, Const/Static, Test, Macro, CONTAINS, IMPORTS_FROM, CALLS, IMPLEMENTS, and DEPENDS_ON with Rust language/signature/span/export metadata. #9 adds the SQLite GraphProvider store at `.opcore/graph/graph.db`, freshness metadata, and #19 direct-reader reference evidence. #10 implements `opcore graph build/update/watch/status`, incremental cached FileFacts updates, phase timings, daemon `ping`/`health`, and watch artifacts at `.opcore/graph/daemon/{pid,state.json,daemon.log}`. #11 implements read-only store-backed `impact`, named `query`, `review-context`, and `detect-changes` envelopes. #12 implements Rust graph-core FTS5 search with `nodes_fts`, signature projection, full and incremental index maintenance, typed failures, and canonical `opcore graph search` through the TypeScript graph adapter. Full build/update/status are unscoped unless `--paths` is passed; `OPCORE_GRAPH_WATCH_PATHS` scopes watch only. Pipeline failures return router status `error`/exit 1 without fabricated summaries; status, health, query, and search paths are read-only when the store is missing or stale. Long-tail parser coverage remains follow-up graph work. - #17 adds Python graph-core extraction for `.py` and `.pyi` through `tree-sitter-python`, emitting File/Module/Class/Function/Variable nodes plus CONTAINS/IMPORTS_FROM/CALLS/INHERITS/TESTED_BY edges. Python imports are repo-local best-effort over absolute, relative, package initializer, and stub candidates; unresolved relative or repo-local imports emit warning category `unresolved_import`; validation support remains absent. - The Rust graph fact-model foundation makes Rust node/edge kinds canonical and confirms SQLite store schema v1 already persists Rust fact columns used by #25 Rust extraction. @@ -180,25 +170,25 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - #56 adds the validation file view: `@the-open-engine/opcore-validation` composes normalized `ValidationRequest` overlays, resolved scope files, and injected workspace `readFile` access so `opcore validate` checks can read hypothetical after-state writes/deletes, before-state comparisons for introduced report mode, and before-state checksums without mutating the worktree. `ValidationFileView.defaultReadState` is check-visible so adapters with out-of-band readers can bind their before/after source mode to the runner pass. `checksumBefore` conflicts return refused/conflict before checks run. - #26 adds the validation-owned GraphProvider consumer boundary: `ValidationGraphProviderClient`, cached `ValidationGraphQuerySession`, graph requirement preloading, status/query failure mapping, and helper access for metadata, file checksums, IMPORTS_FROM, CALLS, and TESTED_BY facts. - #57 adds the TypeScript validation adapter: `@the-open-engine/opcore-validation-typescript` exports package-owned syntax, type, import-graph, dead-code, and relevant-tests check definitions. Syntax/type checks use the validation file view and an overlay-aware compiler host, including tsconfig path aliases for repo files and deterministic node_modules/package declaration resolution for external package imports; graph checks require #26 graph sessions and batched IMPORTS_FROM, CALLS, and TESTED_BY fact requirements. -- #20 adds the Rust validation adapter: `@the-open-engine/opcore-validation-rust` exports package-owned source hygiene, fmt, cargo-check, clippy, rustdoc, import-graph, dead-code, graph-signals, unused-deps, file-length, and function-metrics checks. `rust.graph-signals` is graph-provider-backed evidence from `ValidationGraphProviderClient` only (untested public Rust surface, dead public exports, module orphans/cycles); it does not replace mechanical cargo/rustdoc/clippy/Rox guardrails. #61 makes the five retained Rust rows native when supporting tools are available: rustdoc diagnostics block through `cargo doc`, import-graph reports unresolved modules/use paths/orphans/cycles from fileView, dead-code combines Cargo `dead_code` with orphan-source evidence, unused-deps parses cargo-udeps with workspace/package scoping, and function-metrics parses rust-code-analysis JSON object/array output. Missing `rustdoc`, `cargo-depgraph`, `cargo-udeps`, or `rust-code-analysis-cli` stays degraded or unsupported with `requiredTool` and retained `currentUsage`; no generic retained row remains for available tools. Rust checks materialize temporary workspaces from `ValidationCheckContext.fileView` after-state content for Cargo tools, add no validation daemon or hidden cache, never shell out to Rox, and keep current external Rust guardrails active until #29/#30 accept replacement receipts. #138 shares cargo-check JSON with dead-code, forbids a second dead-code cargo compile or `-Ddead_code` cache-busting flags, and injects a persistent `CARGO_TARGET_DIR` outside the worktree through `runTool`; cache keys must derive from generic repo, scope, overlay, platform, and toolchain inputs and cleanup must remain TTL/size bounded - WHY: Rust validation must be fast without leaking hypothetical after-state into real worktrees or tuning behavior to one repository. +- #20 adds the Rust validation adapter: `@the-open-engine/opcore-validation-rust` exports package-owned source hygiene, fmt, cargo-check, clippy, rustdoc, import-graph, dead-code, graph-signals, unused-deps, file-length, and function-metrics checks. `rust.graph-signals` is graph-provider-backed evidence from `ValidationGraphProviderClient` only (untested public Rust surface, dead public exports, module orphans/cycles), while mechanical evidence remains owned by Cargo and the configured native tools. Missing `rustdoc`, `cargo-depgraph`, `cargo-udeps`, or `rust-code-analysis-cli` stays degraded or unsupported with `requiredTool`. Rust checks materialize temporary workspaces from `ValidationCheckContext.fileView` after-state content for Cargo tools and add no validation daemon or hidden cache. #138 shares cargo-check JSON with dead-code, forbids a second dead-code cargo compile or `-Ddead_code` cache-busting flags, and injects a persistent `CARGO_TARGET_DIR` outside the worktree through `runTool`; cache keys must derive from generic repo, scope, overlay, platform, and toolchain inputs and cleanup must remain TTL/size bounded - WHY: Rust validation must be fast without leaking hypothetical after-state into real worktrees or tuning behavior to one repository. - Rust Cargo/native checks share one environment-keyed materialized workspace per validation file-view state through runner-owned disposable resources; the runner removes it on every exit - WHY: exact staged/tree/overlay semantics require state isolation, while per-check whole-repo copies create unbounded filesystem-event churn. - #151/#214 adds clone detection through the existing `opcore-graph-core` binary as a `clone` subcommand plus `@the-open-engine/opcore-validation-clone`. `CloneAnalysisRequest`/`CloneAnalysisResult` use `opcore.clone.v1`; committed analysis may refresh `.opcore/clone/clone.db`, while scoped or hypothetical analysis is ephemeral. The sparse request shape sends committed candidates as `paths`/`sourcePaths` plus `sourceReadMode`/`sourceTreeRef`, with full content only in write overlays. `clone.duplication` is a validation adapter check with injected native invocation, no graph requirement, no line-level identity, no daemon, and no SAST/security or score claim. - #27 adds canonical validation CLI surfaces: `opcore check` implements `files`, `staged`, `changed`, `tree`, `all`, and `manifest`; `tree` reads committed Git tree content from `--tree ` and scopes files from `--changed-from ` without consuming dirty worktree files. `opcore validate` implements `--request-file`, `hypothetical --request-file`, `pre-write --request-file --timeout-ms --json`, and `manifest`; runtime-owned `opcore status --json` includes `repoState`, while `opcore doctor --json` includes typed `runtimeInfo`, `opcoreDoctor`, and transitional `validationStatus` payloads with adapter routes, check ids, manifest entries, graph status, and daemon readiness metadata. #58 defines `opcore validate pre-write --request-file --timeout-ms 30000 --json` as the hook-safe, fail-closed pre-write contract with typed `PreWriteValidationReceipt` output. - #69 removes public runtime lifecycle command groups: top-level start and stop are unsupported unless a later architecture decision adopts them. Runtime readiness remains `opcore status` and `opcore doctor`; graph daemon lifecycle/status remains graph-owned under `opcore graph`. -- #118 adds bundled `@the-open-engine/opcore-asp-provider` internals and the public `opcore-asp-provider --stdio` bin from the `opcore` package as an independently launchable ASP Core check provider facade. It handles `initialize`, `initialized`, and `check/evaluate`, maps ASP create/modify/delete/rename changesets into validation overlays through host `workspace/listTree` and `workspace/readBlob` callbacks, runs the same TypeScript and Rust validation checks as Opcore validation composition, reports degraded/unsupported coverage for missing graph/toolchain/provider surfaces, emits provider-owned diagnostics, binds `validAsOf` to baseline/changeset/read blobs, and strips host-owned decision/authority/apply fields. It does not add a `opcore asp` router group and must not use ACE descriptors, `.ace/runtime`, `rox`, `crg`, or `cix` as implementation paths. +- #118 adds bundled `@the-open-engine/opcore-asp-provider` internals and the public `opcore-asp-provider --stdio` bin from the `opcore` package as an independently launchable ASP Core check provider facade. It handles `initialize`, `initialized`, and `check/evaluate`, maps ASP create/modify/delete/rename changesets into validation overlays through host `workspace/listTree` and `workspace/readBlob` callbacks, runs the same TypeScript and Rust validation checks as Opcore validation composition, reports degraded/unsupported coverage for missing graph/toolchain/provider surfaces, emits provider-owned diagnostics, binds `validAsOf` to baseline/changeset/read blobs, and strips host-owned decision/authority/apply fields. It does not add a public `opcore asp` router group. - #128 adds `opcore status` and `opcore status --json` as the read-only repo-aware activation command. It resolves repo/Git state, coverage, graph status/action, validation adapter/check availability, degraded Rust tools, cheap ASP enrollment hints, warnings, blockers, and next actions without running builds, checks, installs, setup, wrappers, or writes. Its JSON payload is `repoState`; `opcore status` keeps the transitional `validationStatus` payload. - #129 adds `opcore` and the standalone `opcore` bin. Zero-command scan uses `repoState` from #128, runs validation without source mutation, prints Coverage before Findings, writes only `.opcore/report.json`, `.opcore/history.jsonl`, and bounded `.opcore/telemetry.jsonl` capped at 500 records or 1 MiB, exposes `opcore check --changed|--staged|--all| --json` with stable agent exit codes, and exposes read-only `opcore --version`/`opcore version` runtime provenance. - #130 adds `packages/opcore/src/reporting.ts`, `OpcoreMetricReport`, `OpcoreMetricHistoryEntry`, and `OpcoreMeasureDelta`. Reports aggregate TS/JS syntax/type/test/dead-export diagnostics, graph structure/fan-in evidence when supplied, Rust source hygiene/file length/module/toolchain diagnostics, unsupported stack census, and honest degradations for unavailable checks/tools/facts. `writeOpcoreMetricArtifacts` writes only under `.opcore/`; `opcore status` excludes those generated artifacts from coverage; `opcore measure` reads them and returns deltas without validationResult, validationStatus, scans, graph builds, setup, or source writes. -- #131 adds `packages/opcore/src/init.ts` and the `opcoreInit` router payload. `opcore init` detects existing `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.github/copilot-instructions.md`, `.codex/AGENTS.md`, and `.opencode/AGENTS.md`, runs a read-only scan without `.opcore/report.json` or history writes, emits scan/settings/interaction/timing payloads, presents a plan before writing, prompts on TTY only, and now chooses repo/global write-gate scope by flag or interactive Git prompt. Approved repo init upserts a single `` block, writes additive `.opcore/config`, appends one managed `.opcore/` `.gitignore` line only in Git repos that do not already ignore it, installs `.opcore/hooks/opcore-agent-gate.mjs`, and merges Claude Code plus Codex PreToolUse hook settings without clobbering existing hooks. Approved global init installs the same adapter under `~/.opcore/hooks/`, merges `~/.claude/settings.json` and `~/.codex/hooks.json`, and records undo under `~/.opcore/init-undo.json`. The adapter maps Write/Edit/MultiEdit and Codex apply_patch payloads to hypothetical validation overlays, calls `opcore validate pre-write --request-file --timeout-ms 30000 --json`, exits 2 on non-ok receipts or validation command failure, and fail-opens only when the adapter cannot parse/map the harness payload. Approved writes refuse symlink targets or symlink ancestors for repo and global paths. Undo refuses metadata whose recorded root does not match or whose entries are outside Opcore-owned config/hooks/agent guidance paths plus the managed `.gitignore` line; `.gitignore` undo removes only that managed line and deletes an init-created `.gitignore` only when empty. The managed `.opcore/` ignore covers `.opcore/telemetry.jsonl`. The guidance must tell agents to run `opcore check --changed`, preserve existing guardrails, report unsupported/degraded coverage honestly, and not rely on ACE, Rox, CRG, CIX, or ASP host authority for direct Opcore. +- #131 adds `packages/opcore/src/init.ts` and the `opcoreInit` router payload. `opcore init` detects existing `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.github/copilot-instructions.md`, `.codex/AGENTS.md`, and `.opencode/AGENTS.md`, runs a read-only scan without `.opcore/report.json` or history writes, emits scan/settings/interaction/timing payloads, presents a plan before writing, prompts on TTY only, and now chooses repo/global write-gate scope by flag or interactive Git prompt. Approved repo init upserts a single `` block, writes additive `.opcore/config`, appends one managed `.opcore/` `.gitignore` line only in Git repos that do not already ignore it, installs `.opcore/hooks/opcore-agent-gate.mjs`, and merges Claude Code plus Codex PreToolUse hook settings without clobbering existing hooks. Approved global init installs the same adapter under `~/.opcore/hooks/`, merges `~/.claude/settings.json` and `~/.codex/hooks.json`, and records undo under `~/.opcore/init-undo.json`. The adapter maps Write/Edit/MultiEdit and Codex apply_patch payloads to hypothetical validation overlays, calls `opcore validate pre-write --request-file --timeout-ms 30000 --json`, exits 2 on non-ok receipts or validation command failure, and fail-opens only when the adapter cannot parse/map the harness payload. Approved writes refuse symlink targets or symlink ancestors for repo and global paths. Undo refuses metadata whose recorded root does not match or whose entries are outside Opcore-owned config/hooks/agent guidance paths plus the managed `.gitignore` line; `.gitignore` undo removes only that managed line and deletes an init-created `.gitignore` only when empty. The managed `.opcore/` ignore covers `.opcore/telemetry.jsonl`. The guidance must tell agents to run `opcore check --changed`, preserve existing safeguards, report unsupported/degraded coverage honestly, and not rely on ASP host authority for direct Opcore. - #133 adds `packages/opcore/src/try.ts` and the `opcoreTry` router payload. `opcore try` uses generated local sample repos only, keeps output coverage-first with named findings/deltas, includes unsupported-file census evidence, and records clean-room launch proof without public announcement or package publishing. -- #22 adds the edit-core library foundation: `@the-open-engine/opcore-edit` owns deterministic exact, multi-edit, and literal search-replace planners, edit checksums, plan hashes, validation overlay construction, preview mode, repo path policy, Node workspaces, and all-or-nothing atomic apply/rollback. #59 adds canonical `opcore edit exact`, `multi`, `search-replace`, `check`, and `apply` parsing inside the edit package, typed `editPlan`/`editResult` router payloads, no old `cix` aliases, and search-replace uniqueness unless `replaceAll` is true. #60 adds canonical `opcore edit patch` and `tree`: raw unified diff patch input through `--stdin`/`--request-file` or `{patch}` JSON, tree payloads `{repo?,validation?,fileContains?,files:[{path,content,checksumBefore?}|{path,delete:true,checksumBefore?}]}`, patch/tree-only forbidden target policy for absolute paths, parent traversal, UNC paths, symlink escapes, `.gitignore` targets, generated/private roots, and binary content, plus `editResult.rollback` state for atomic apply failures. #24 routes non-empty `opcore edit` apply/check plans through an injected validation runner before writes, rejects validation bypass plans, preserves full `ValidationResult` envelopes in edit results, and keeps `--dry-run` as a non-validating preview. #23 implements canonical `opcore edit rename`, `move`, and `signature` routes as graph-backed, validation-required edit plans: GraphProvider contract status/query/search evidence is required for targeting/freshness, TypeScript/JavaScript language-service materialization stays edit-owned, and apply/check refuses graph freshness changes before validation or writes. +- #22 adds the edit-core library foundation: `@the-open-engine/opcore-edit` owns deterministic exact, multi-edit, and literal search-replace planners, edit checksums, plan hashes, validation overlay construction, preview mode, repo path policy, Node workspaces, and all-or-nothing atomic apply/rollback. #59 adds canonical `opcore edit exact`, `multi`, `search-replace`, `check`, and `apply` parsing inside the edit package, typed `editPlan`/`editResult` router payloads, and search-replace uniqueness unless `replaceAll` is true. #60 adds canonical `opcore edit patch` and `tree`: raw unified diff patch input through `--stdin`/`--request-file` or `{patch}` JSON, tree payloads `{repo?,validation?,fileContains?,files:[{path,content,checksumBefore?}|{path,delete:true,checksumBefore?}]}`, patch/tree-only forbidden target policy for absolute paths, parent traversal, UNC paths, symlink escapes, `.gitignore` targets, generated/private roots, and binary content, plus `editResult.rollback` state for atomic apply failures. #24 routes non-empty `opcore edit` apply/check plans through an injected validation runner before writes, rejects validation bypass plans, preserves full `ValidationResult` envelopes in edit results, and keeps `--dry-run` as a non-validating preview. #23 implements canonical `opcore edit rename`, `move`, and `signature` routes as graph-backed, validation-required edit plans: GraphProvider contract status/query/search evidence is required for targeting/freshness, TypeScript/JavaScript language-service materialization stays edit-owned, and apply/check refuses graph freshness changes before validation or writes. - #149 adds the shared inspect/edit ts-morph Project construction seam: cold inspect signatures and graph-backed implementations default to target import-closure loading; references and symbol edits load the target import closure plus reverse importers instead of whole-repo Projects when that preserves correctness; graphless implementation paths keep whole-repo fallback where reverse dependents cannot be bounded safely; and both inspect/edit accept injected Project plus snapshot/revert hooks for future warm ASP reuse without introducing a daemon. - #153 adds the hidden `opcore asp serve --stdio` warm ASP session: host-launched only, process-local singleton/idle lifecycle under `.opcore/asp`, warm injected whole-repo ts-morph Project for `inspect/references` and `edit/rename` preview, unchanged delegated `check/evaluate`, `session/shutdown`, no source writes, no public help/manifest command group, no auto-spawned daemon, and no change to the cold `opcore-asp-provider --stdio` check-only capability. - #17 adds the graph release readiness receipt gate: `npm run graph-release:check` proves canonical graph commands, direct SQLite queries, serve transport, package inspection, provenance/license receipts, benchmark metrics, and handoff data for #7/#28/#29. -- #28 adds the aggregate Opcore ACE descriptor contract and artifact: `opcore` packages `dist/descriptors/opcore.managed-tool.json`, generated from `packages/opcore/src/advanced/descriptor.ts` after build. Descriptors must list only the canonical `opcore` bin, command groups graph/inspect/edit/check/validate/status/doctor, package-relative artifact/checksum paths, GraphProvider daemon/query/search/native capabilities, edit validation dependency, validation graph optional/required modes, and deferred #13-#16 optional surfaces as metadata. Descriptor validators must reject private runtime roots such as `.ace` on either slash style. +- #28 adds the aggregate Opcore managed descriptor contract and artifact: `opcore` packages `dist/descriptors/opcore.managed-tool.json`, generated from `packages/opcore/src/advanced/descriptor.ts` after build. Descriptors list only the canonical `opcore` bin, command groups graph/inspect/edit/check/validate/status/doctor, package-relative artifact/checksum paths, GraphProvider query/search/native capabilities, edit validation dependency, validation graph optional/required modes, and deferred optional surfaces as metadata. - #29 adds the repo-wide release receipt gate: `npm run release-receipt:check` proves the single public `opcore` tarball, bundled internal implementation/native/runtime dependencies, exact packlists, sha256 checksums, descriptor/provider manifest artifact resolution, canonical Opcore command groups, native graph artifact checksum evidence, production and bundled dependency licenses, provenance scans, release hygiene, graph #17 input evidence, and current-tree plus git-history secret scans. `npm run release-receipt:receipt` refreshes `docs/release/release-receipt.json`, `docs/release/release-receipt.summary.md`, license/provenance reports, and artifact attestation docs. Secret allowlist entries live only in `docs/release/secret-scan-allowlist.json` and must include reviewed path or commit scope, reviewer, reason, expiry, and optional fingerprint/kind narrowing; remove real findings instead of allowlisting them. -- #30 adds the installed-artifact cutover gate: `npm run cutover:check` packs and installs only the public `opcore` package into a clean temp project, clears current-tool env resolution, excludes local wrapper and sibling paths, verifies installed canonical bins (`opcore` and `opcore-asp-provider`), validates `ReleaseCutoverReceipt`, proves graph/inspect/edit/check/validate/status/doctor/pre-write plus scan/measure flows through `opcore`, and rejects old-tool/private-path markers or advertised `not_implemented` release commands. Each command receipt id is contract-bound to its expected canonical command/status/exit. Top-level `opcore inspect symbols|definition|references|signature|implementations|search` is read-only CLI behavior; signature and implementations are implemented read-only language-service parity. `opcore graph inspect` is not an advertised release route. -- #72 adds typed inspect reference results and `opcore inspect references --line [--column ]` for read-only CIX refs parity over fresh graph facts plus an inspect-owned TypeScript/JavaScript language-service seam. #100 adds shared read-only `InspectSignatureResult` and `InspectImplementationResult` contracts, fixture foundations, target parsing, graph freshness enforcement, and typed `unsupported_route` scaffolds. #101 implements `opcore inspect signature --line [--column ]` and node-id targeting for read-only CIX `sig` parity over fresh graph facts. #102 implements `opcore inspect implementations --line [--column ]` and class/type node-id targets for CIX `impls` parity over graph `IMPLEMENTS`/`INHERITS` facts plus TypeScript/TSX language-service materialization. +- #30 adds the installed-artifact cutover gate: `npm run cutover:check` packs and installs only the public `opcore` package into a clean temp project, sanitizes execution paths, verifies installed canonical bins (`opcore` and `opcore-asp-provider`), validates `ReleaseCutoverReceipt`, and proves graph/inspect/edit/check/validate/status/doctor/pre-write plus scan/measure flows through `opcore`. Each command receipt id is contract-bound to its expected canonical command/status/exit. Top-level `opcore inspect symbols|definition|references|signature|implementations|search` is read-only CLI behavior; signature and implementations are implemented read-only language-service parity. `opcore graph inspect` is not an advertised release route. +- #72 adds typed inspect reference results and `opcore inspect references --line [--column ]` over fresh graph facts plus an inspect-owned TypeScript/JavaScript language-service seam. #100 adds shared read-only `InspectSignatureResult` and `InspectImplementationResult` contracts, fixture foundations, target parsing, graph freshness enforcement, and typed `unsupported_route` scaffolds. #101 implements `opcore inspect signature --line [--column ]` and node-id targeting over fresh graph facts. #102 implements `opcore inspect implementations --line [--column ]` and class/type node-id targets over graph `IMPLEMENTS`/`INHERITS` facts plus TypeScript/TSX language-service materialization. - #141 keeps inspect file-symbol routes useful when graph facts are missing or stale: supported TS/JS `references` and `signature`, plus existing TS/TSX `implementations`, return read-only language-service payloads with `inspectResult.status: "degraded"` and `failure.category: "graph_unavailable"` instead of hard failure; unsupported paths and graph-only node-id targets remain hard failures or unsupported as before. - The provenance GitHub workflow must install stable Rust and run `npm run build` before `npm run release-receipt:check` - WHY: release receipts import ignored `dist/` contracts/descriptors and require native graph artifacts from a clean checkout. @@ -206,15 +196,17 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - ALL tests live in @tests/ until a package-specific test harness is explicitly introduced by an issue. - Add contract tests before implementation tests for GraphProvider, EditPlan, ValidationRequest, and canonical CLI behavior. -- Add golden/reference fixtures before replacing current external tool behavior. +- Add golden/reference fixtures before changing established behavior. - Keep release hygiene, conformance metadata, and package packlist gates executable when changing package or release surfaces - WHY: maintainer release receipts must fail before public alpha assumptions drift. -- Add #29 negative fixtures for release evidence regressions: Python code-review-graph provenance, high-confidence secrets, unexpected package files, old public bins, descriptor artifact drift, and missing native checksum evidence. -- Add #30 negative fixtures for cutover regressions: current-tool descriptor markers, advertised placeholder command receipts, missing cutover command receipts, and old bin fallback in installed projects. -- Local proof for agent work is `npm run ci:local`; for source/package/native/release changes it regenerates current-tool wrappers, runs `npm run ci`, runs repo-wide Rox, then runs the Rust graph function-metrics check. For docs/agent-guidance-only changes it uses the local CI fast path described above. GitHub Actions run Node and Rust gates while current external ACE tools remain local-worktree dependencies. +- Add #29 negative fixtures for release evidence regressions: high-confidence secrets, unexpected package files, + descriptor artifact drift, and missing native checksum evidence. +- Add #30 negative fixtures for cutover regressions: advertised placeholder command receipts, missing cutover command + receipts, and invalid installed package/bin surfaces. +- Local proof for agent work is `npm run ci:local`; for source/package/native/release changes it runs `npm run ci` and then `npm run opcore:self-check`. Docs/agent-guidance-only changes use the repository-native fast path described above. ## Commands -- Setup and wrappers: `npm run setup`, `npm run setup:tools`, `source scripts/dev-env.sh`, `npm run ace:install`, `npm run ace:sync`, `npm run ace:validate`. -- Proof: core `npm run ci`, targeted `node --test tests/...`, `npm run rust:check`, `npm run ci:local` or `npm run verify`; release `npm run graph:artifact`, `npm run descriptor:artifact`, `npm run asp-provider:manifest`, `npm run graph-release:check`, `npm run release-receipt:check`, `npm run cutover:check`, `npm run asp-dogfood:check`, `npm run pack:check`, `npm run release:hygiene`; retained guardrails `npm run current-tools:validate-changed`, `npm run current-tools:validate-all`, `npm run current-tools:validate-rust-graph`. +- Setup: `npm run setup`. +- Proof: core `npm run ci`, `npm run opcore:self-check`, targeted `node --test tests/...`, `npm run rust:check`, `npm run ci:local` or `npm run verify`; release `npm run graph:artifact`, `npm run descriptor:artifact`, `npm run asp-provider:manifest`, `npm run graph-release:check`, `npm run release-receipt:check`, `npm run cutover:check`, `npm run asp-dogfood:check`, `npm run pack:check`, `npm run release:hygiene`. - Public scan/readiness: `opcore --repo . --json`, `opcore status --repo . --json`, `opcore --version --json`, `opcore doctor --repo . --json`, `opcore measure --repo . --json`, `opcore try --json`. - Public setup/check/provider: `opcore install --repo . --json`, `opcore install --repo . --yes --json`, `opcore uninstall --repo . --yes --json`, `opcore check --changed --json`, `opcore-asp-provider --stdio`. diff --git a/CLAUDE.md b/CLAUDE.md index 9bedb52..45d49e4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,7 +2,7 @@ UPDATE THIS FILE when making architectural changes, adding patterns, or changing # Opcore -Opcore is the code-intelligence and robustness monorepo for graph context, edit planning, pre-write validation, repo robustness scanning/measurement, and the standalone ASP Core check provider for coding agents. Remaining old-name env vars, cache dirs, fixtures, receipts, and repo-path names are transitional implementation debt; do not introduce new public/product-facing old-name branding. The accepted runtime/CLI boundary is hybrid: Rust graph core with TypeScript contracts, CLI router, edit, validation, validation-typescript, validation-clone, ASP provider facade, npm/Opcore facade, and ACE descriptors. See @docs/architecture/runtime-cli-ard.md and @docs/planning/opcore-alpha-roadmap.md before changing language, package, provider, product, or CLI ownership. +Opcore is the code-intelligence and robustness monorepo for graph context, edit planning, pre-write validation, repo robustness scanning/measurement, and the standalone ASP Core check provider for coding agents. The accepted runtime/CLI boundary is hybrid: Rust graph core with TypeScript contracts, CLI router, edit, validation, validation-typescript, validation-clone, ASP provider facade, npm/Opcore facade, and managed descriptor artifacts. See @docs/architecture/runtime-cli-ard.md and @docs/planning/opcore-alpha-roadmap.md before changing language, package, provider, product, or CLI ownership. ## Key Concepts @@ -11,9 +11,9 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit | Graph provider | Owns source extraction, persistent graph facts, freshness metadata, graph query contracts, and FTS search index artifacts. | | Edit planner | Owns symbol-aware rename, move, signature, patch, and tree edits; it must validate full edit plans, not isolated files. | | Validation engine | Owns mechanical checks, hypothetical validation, check manifests, and failure policy. | -| Opcore product facade | Thin user-facing robustness loop over graph, validation, edit, and ASP-provider packages: read-only scan/status/check/measure by default, approval-gated init, and no ASP-standard or old-tool replacement claims. | +| Opcore product facade | Thin user-facing robustness loop over graph, validation, edit, and ASP-provider packages: read-only scan/status/check/measure by default and approval-gated init. | | Command adapters | Package-owned graph, edit, check, and validate dispatch surfaces used by canonical `opcore` advanced routes. | -| Current-tool wrappers | Local agent tooling that invokes the already-installed ACE-managed tools; these wrappers are not Opcore implementation artifacts. | +| Repository self-validation | Opcore validates its own changed implementation surface through `npm run opcore:self-check` and `.opcore/config`. | ## Where To Look @@ -43,7 +43,6 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit | ASP warm inspect/edit session | `packages/opcore/src/advanced/asp-warm/`, @docs/architecture/asp-warm-session-ard.md | | ASP provider manifest generator | `scripts/write-asp-provider-manifest.mjs` writes canonical `asp-server.json` plus retained provisional install metadata. | | Golden fixtures and reference evidence | @packages/fixtures/ | -| Graph reference evidence manifest | `packages/fixtures/graph-reference-evidence/manifest.json` | | Graph release fixture | `packages/fixtures/graph-release/release-readiness-fixture.json` | | Graph release receipt | `docs/release/graph-release-receipt.json` | | Graph release payload checksum target | `docs/release/graph-release-receipt.payload.json` | @@ -54,7 +53,6 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit | Cutover receipt summary | @docs/release/cutover-receipt.summary.md | | ASP dogfood receipt | `docs/release/asp-dogfood-receipt.json` | | ASP dogfood receipt summary | @docs/release/asp-dogfood-receipt.summary.md | -| Retained guardrail matrix | @docs/release/retained-guardrail-matrix.md | | Secret scan allowlist | @docs/release/secret-scan-allowlist.json | | Release receipt generator | `scripts/generate-release-receipt.mjs` | | Cutover receipt generator | `scripts/generate-cutover-receipt.mjs` | @@ -62,30 +60,24 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit | Workspace checks | `scripts/check-workspace.mjs` | | Package dry-run checks | `scripts/check-packages.mjs` | | Provenance checks | `scripts/check-provenance.mjs` | -| Current ACE tool setup | `scripts/setup-current-tools.sh` | +| Opcore self-check | `scripts/run-opcore-self-check.mjs`, `.opcore/config` | | Local CI-equivalent gate | @scripts/ci/run-local-ci-equivalent.sh | | Zeroshot setup | @.zeroshot/settings.json | | GitHub Actions | @.github/workflows/ | | Tests | @tests/ | -## Current Tooling - -- Run `npm run setup:tools` after cloning or entering a fresh worktree. It writes `.ace/runtime/bin/{rox,crg,cix}` wrappers that exec the current external ACE-managed tools from `LATTICE_CURRENT_TOOLS_DIR`, sibling Covibes repos, or `PATH`. -- Run `npm run ace:install` when ACE provider state or bundled generic skills are missing. It writes ignored generated provider/runtime files under `.claude/`, `.agents/`, `.codex/`, `.gemini/`, `.opencode/`, and `.ace/`; do not hand-edit or commit those trees. Use `npm run ace:sync` after changing `CLAUDE.md`, `AGENTS.md`, or `ace.json`, and `npm run ace:validate` to verify generated ACE state. Repo-specific guidance belongs in this file, not in checked-in provider skills. -- The generated `rox` wrapper also prepends the current ACE-managed `rust-code-analysis-cli` native tool when one is discoverable - WHY: all-mode Rust function metrics must match scoped Rust Rox findings. -- NEVER point `.ace/runtime/bin/{rox,crg,cix}` at `packages/graph`, `packages/edit`, or `packages/validation` before the release/cutover issues say those packages are production-ready - WHY: agents must validate Opcore work with the stable current tools, not with the toolchain being rewritten. -- Source `scripts/dev-env.sh` when interactive shells should prefer the generated wrappers. It fails non-zero and leaves PATH/env untouched when `.ace/runtime/bin/{rox,crg,cix}` is incomplete - WHY: ACE, MCP, Zeroshot, and humans should resolve the same current tool surface. -- `ace.json` routes the code-review graph MCP through `.ace/runtime/bin/crg serve --repo "$repo_root"`; update `ace.json`, `scripts/setup-current-tools.sh`, and this file together when tool acquisition changes. -- Use current external `crg` for discovery before broad text scans, current external `cix` for cohesive symbol/edit deltas, and current external `rox` for staged/changed/repo validation. These are dev validation helpers, not Opcore release surfaces. -- `npm run current-tools:validate-changed` runs `scripts/ci/run-rox-clean-changed-gate.mjs`: it stops Rox, clears `.rox-cache` and `.robustness-engine-cache`, runs daemon-free changed-file Rox, and fails non-baseline findings while retaining legacy code-quality findings already present on the base tree. -- `scripts/ci/run-local-ci-equivalent.sh` uses a docs/agent-guidance fast path when the only changed files are launch docs or agent guidance: setup tools, shell syntax, release hygiene, workspace, provenance, and changed-file Rox. CI-wrapper, source, package, native artifact, release evidence, and other implementation changes still run `npm run ci`, `npm run current-tools:validate-all`, and `npm run current-tools:validate-rust-graph`. `cutover:check` may set `OPCORE_CUTOVER_REUSE_CURRENT_TOOL_GUARDRAILS=1` in root CI and release aggregate proof to reuse validated guardrail hashes from the checked-in cutover receipt while regenerating installed-artifact proof; `cutover:receipt` without the flag must run the retained current-tool commands - WHY: doc-only handoff proofs must fit the cmdproof timeout, aggregate installed-artifact CI must not depend on workstation-only current-tool wrappers, and maintainer receipts must still co-record real retained guardrail proof. +## Development Tooling + +- Run `npm run setup` after cloning or entering a fresh worktree. It installs repository dependencies only and must leave the worktree clean. +- Run `npm run opcore:self-check` after building. It invokes the built public Opcore CLI in introduced mode against the configured base ref and consumes the checked-in `.opcore/config` policy. +- `scripts/ci/run-local-ci-equivalent.sh` runs normal CI plus the Opcore self-check. Its docs/agent-guidance fast path uses only repository-native workspace, provenance, build, and self-validation commands. +- Zeroshot worktrees run `npm ci` and the same local CI-equivalent command proof - WHY: humans, agents, CI, and ship clusters must exercise one repository-owned validation surface. - Root `.npmrc` sets `loglevel=silent` - WHY: JSON-emitting npm scripts such as `npm run asp-dogfood:check -- --json` must write parseable JSON to redirected stdout without npm lifecycle preambles. - `npm run test:ci` routes through `scripts/run-test-ci.mjs`: it runs the parallel-safe Node test files first, runs `tests/validation-python.test.mjs` separately because its real subprocess fixtures can exhaust CI process slots under the parallel suite, then runs `tests/native-packaging-policy.test.mjs` separately with receipt gates skipped - WHY: Python compiler-truth tests need deterministic process availability, and the native packaging policy test intentionally mutates native package artifacts while exercising aggregate dry-run failures. - Graph-owned transitional `opcore graph serve --repo ` starts the graph package stdio/MCP bridge over graph-core JSONL; `--repo` defaults to cwd, supports ping/status/query/search/shutdown, injects missing nested query repos, and returns typed startup/frame/provider failures. -- #126 ships graph-core through bundled internal Opcore native packages `@the-open-engine/opcore-graph-core-darwin-arm64`, `@the-open-engine/opcore-graph-core-darwin-x64`, and `@the-open-engine/opcore-graph-core-linux-x64`; `packages/graph` resolves only matching package metadata and never probes `packages/graph/dist/native`, sibling checkouts, `.ace/runtime`, or PATH. +- #126 ships graph-core through bundled internal Opcore native packages `@the-open-engine/opcore-graph-core-darwin-arm64`, `@the-open-engine/opcore-graph-core-darwin-x64`, and `@the-open-engine/opcore-graph-core-linux-x64`; `packages/graph` resolves only matching package metadata and never probes `packages/graph/dist/native`, sibling checkouts, or PATH. - Release flow is `dev -> main`: CI runs on `dev` and `main`, PRs to `main` must come from `dev`, and `.github/workflows/release.yml` auto-publishes npm package version `0.2.1` with dist-tag `latest` after the `CI` workflow succeeds on `main`. Each release must provide readable notes at `docs/release/v.md`; the workflow uses that file verbatim for the GitHub release. The CI native jobs upload tarred native package directories so `opcore-graph-core` execute bits survive artifact transfer; aggregate CI and release publish must set `OPCORE_REQUIRE_ALL_NATIVE_PACKAGES=1` after extracting all three native artifacts, and `scripts/release-dry-run.mjs` then validates package-local executable binaries/checksums without rebuilding graph-core - WHY: aggregate and publish proof must consume runnable per-target artifacts produced by native jobs, not a local Linux rebuild or non-executable download. -- #19 keeps `OPCORE_GRAPH_WATCH_PATHS` as the only watch env default and ignores `CRG_WATCH_PATHS` - WHY: Opcore watch roots must not inherit old-tool scoping accidentally. -- #19 graph discovery excludes generated/private/dependency roots even without repo ignore files: `.git`, `node_modules`, `.pnpm`, `vendor`, `dist`, `target`, `.ace`, `.agents`, `.claude`, `.codex`, `.gemini`, `.lattice`, `.opencode`, `.rox-cache`, and `.robustness-engine-cache` - WHY: cache/vendor/provider mirror changes must not create graph facts, freshness changes, validation input, or FTS rows. +- #19 graph discovery excludes generated/private/dependency roots even without repo ignore files: `.git`, `node_modules`, `.pnpm`, `vendor`, `dist`, `target`, `.agents`, `.claude`, `.codex`, `.gemini`, `.lattice`, and `.opencode` - WHY: cache/vendor/provider mirror changes must not create graph facts, freshness changes, validation input, or FTS rows. - #17/#19/#21 source/coverage policy is reconciled across graph-core, validation, and Opcore status/metrics: graph-extractable TypeScript, JavaScript, Python `.py`/`.pyi`, and Rust `.rs`; validation-supported TS/JS variants, Python source/stubs, Rust source/includes, and `Cargo.toml`; retained `Cargo.lock`; unsupported/degraded stacks and missing Python tools are counted honestly. - #16 Python generated/private/dependency roots are excluded from discovery and status census: `.venv`, `venv`, `env`, `__pycache__`, `.eggs`, `build`, `.tox`, `.mypy_cache`, `.pytest_cache`, `.ruff_cache`, `site-packages`, `*.egg-info`, and `*.dist-info` - WHY: dependency/cache artifacts must not create graph freshness or coverage evidence. - #17 Python export metadata is best-effort: `__all__` wins when present, otherwise the leading-underscore convention marks module-level public names; file `exports[]` entries must include policy and supportedSymbol - WHY: Python has no enforced export boundary. @@ -96,13 +88,12 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - #197 makes hypothetical graph evaluation exact-state: validation creates one `ValidationFileView` per before/after state and owns one disposable graph session for that view; graph materializes the complete visible TS/TSX/JS/JSX, Python `.py`/`.pyi`, and Rust `.rs` universe into a bounded isolated root, builds graph-core once, shares the immutable session across checks, and removes it on every exit. Introduced mode must use distinct before/after snapshots, ASP listings must preserve host truncation, and exact-state construction/query failure is non-pass even when persistent graph mode is optional - WHY: a persistent target-repo graph or incomplete listing cannot describe hypothetical file contents and must never produce a false clean pre-write result. - #19 requires graph status to preserve real WAL checkpoint evidence from the latest pipeline summary and release gates to fail missing/fabricated WAL evidence - WHY: freshness and checkpoint pressure must remain host-visible provider facts. - #19 treats `opcore graph serve` as the stdio/MCP hot-query replacement, not a Unix socket, with parallel independent serve sessions as the supported concurrency evidence. -- #19 keeps current external CRG receipts as non-implementation compatibility evidence only - WHY: old CRG remains a guardrail until downstream cutover issues consume the Opcore proof. -- `opcore-asp-provider --stdio` is the transitional provider binary for the standalone ASP Core check provider; it uses host workspace callbacks and Opcore validation only, never ACE descriptors or current-tool wrappers. +- `opcore-asp-provider --stdio` is the transitional provider binary for the standalone ASP Core check provider; it uses host workspace callbacks and Opcore validation only. - `opcore asp serve --stdio` is a hidden host-launched warm ASP session for inspect/edit/check under `packages/opcore/src/advanced/asp-warm/`; it is intercepted before the public router, omitted from `opcore --help`, keeps lifecycle state under `.opcore/asp/`, never auto-spawns, never stays always-on beyond its idle timeout, and never mutates source files - WHY: agents may need warm inspect/edit latency without making ASP a public human command group or changing the cold check provider. -- #120 ASP dogfood uses `npm run asp-dogfood:check` with a temporary `ASP_HOME`, standalone sibling/private ASP manager bootstrap evidence, installed provider evidence, and retained current-tool guardrail receipts. `OPCORE_ASP_DOGFOOD_REUSE_CURRENT_TOOL_GUARDRAILS=1 npm run asp-dogfood:receipt` may refresh installed-artifact evidence from already-recorded retained guardrails, but the default dogfood path must still run those guardrails live - WHY: dogfood proves advisory/shadow host integration without making Opcore the host, manager, authority, or old-tool replacement. +- #120 ASP dogfood uses `npm run asp-dogfood:check` with a temporary `ASP_HOME`, a built adjacent `agent-server-protocol` checkout or explicit `ASP_DOGFOOD_ASP_REPO`, installed provider evidence, and provider/host authority receipts. Receipts redact the resolved manager root as `` - WHY: dogfood proves advisory/shadow host integration without making Opcore the host, manager, or authority or embedding workstation paths. - `opcore [--repo ] [--json]` is the public first-run scan. It emits `repoState` plus `validationResult`, prints Coverage before Findings, and writes only `.opcore/report.json`, `.opcore/history.jsonl`, and bounded `.opcore/telemetry.jsonl` capped at 500 records or 1 MiB. - `opcore --version`, `opcore -v`, and `opcore version` are read-only runtime provenance surfaces. JSON output carries `runtimeInfo` with package name/version, bin, artifact source (`source_checkout`, `installed_package`, or `unknown`), package root, and entrypoint - WHY: agents and humans must know which Opcore binary/artifact is actually running. -- `opcore status [--repo ] [--json]` is the runtime-owned activation/readiness entrypoint. It emits `repoState` and must stay read-only: no graph build/update/watch, validation checks, package installs, ASP setup, ACE setup, current-tool wrapper execution, or source writes. +- `opcore status [--repo ] [--json]` is the runtime-owned activation/readiness entrypoint. It emits `repoState` and must stay read-only: no graph build/update/watch, validation checks, package installs, ASP setup, or source writes. - `opcore doctor [--repo ] [--json]` is the runtime-owned diagnostic entrypoint. It emits `runtimeInfo`, `opcoreDoctor`, and transitional `validationStatus`, reporting version/provenance, config found/missing/unreadable state for `.opcore/config`, loaded check ids, graph freshness, generated-state ignore guidance, and next actions without building graphs, running checks, installing packages, setup, wrappers, or source writes. - `opcore check --changed --json` is the agent gate and defaults to `--base HEAD`. `opcore check --staged`, `opcore check --all`, and explicit file operands are native check scopes; explicit missing files and blank check ids must return structured `invalid_payload` JSON instead of passing zero checks or throwing plain text. - #31 latency telemetry contracts live in `packages/contracts`: `CommandTiming`, `RepoShapeFingerprint`, `CommandLatencyRecord`, `LatencyBudget`, and `LatencyBudgetResult` must stay source-safe, schema/validator/test aligned, and `.opcore/telemetry.jsonl` must remain ring-buffer bounded to 500 records or 1 MiB. Telemetry `bin` is the normalized public bin name and `canonicalCommand` is sanitized command identity, not raw argv or path operands. @@ -113,7 +104,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - #36 `opcore measure` reads bounded `.opcore/telemetry.jsonl` plus latency budgets and emits `opcoreMeasure.latency.findings[]` for slower or over-budget command/phase observations only; it must not write artifacts, run checks, or emit `ok` latency rows - WHY: slow actions need drillable evidence without turning measure into a runner or score. - #137 `opcore graph serve` writes one bounded `CommandLatencyRecord` per forwarded child frame through a product-CLI-injected telemetry writer, with canonical commands shaped as `opcore graph serve ` and per-op phase ids such as `serve_query`; `npm run latency:check` includes serve and inspect budgets in non-blocking trend mode - WHY: long-lived serve/inspect performance claims need before/after evidence without reintroducing a daemon or bypassing the existing telemetry contract. - `opcore install [--repo ] [--local|--global] [--yes] [--json]` is the recommended scan-first repo/agent setup path and interactive wizard. It runs read-only scan output before setup, prompts in an interactive Git repo to choose repo or global write-gate scope when neither scope nor `--yes`/`--json` is supplied, keeps JSON/non-TTY preview runs plan-only, and approved repo install writes additive `.opcore/config`, delimited guidance, Opcore agent skills, `.opcore/hooks/opcore-agent-gate.mjs`, Claude Code `.claude/settings.json` and Codex `.codex/hooks.json` PreToolUse wiring, a safe active `.git/hooks/pre-commit` when no existing hook is present, managed `.opcore/` `.gitignore` coverage, and `.opcore/init-undo.json`; approved global install writes `~/.opcore/hooks/opcore-agent-gate.mjs`, user-level skill files, merges `~/.claude/settings.json` and `~/.codex/hooks.json`, and records undo in `~/.opcore/init-undo.json`. `opcore uninstall [--repo ] [--local|--global] [--yes] [--json]` removes/restores only recorded Opcore-owned entries. `opcore init` remains the conservative compatibility setup path with explicit `--approve` semantics and the separate opt-in `--fail-closed-hook` script. -- `opcore try [--json]` is the launch demo loop. It creates local TS, Rust, mixed, and unsupported-file sample repos, runs scan/init/check/measure, returns `opcoreTry.published:false`, and must not publish anything or mention old-tool/current-tool names in human output. +- `opcore try [--json]` is the launch demo loop. It creates local TS, Rust, mixed, and unsupported-file sample repos, runs scan/init/check/measure, returns `opcoreTry.published:false`, and must not publish anything. - `opcore` is the only public npm package and intentionally exposes only Opcore-owned public bins: `opcore` plus the bundled ASP provider bin `opcore-asp-provider`. The opcoreGraph/opcoreValidation and cliGraph/validateCommand/editValidation trees remain parallel internal product/advanced-router surfaces, not duplicate drift. `opcore-graph` remains internal allowlisted naming. - Launch-facing naming gates must keep README, quickstart, concepts, examples, demo, agent integration, and `packages/opcore` copy branded as Opcore. Any remaining old-name hit in those surfaces must be explicitly allowlisted as internal/transitional implementation naming. @@ -135,30 +126,29 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - Repo-owned validation extensions live in `.opcore/config` `validation.checks.packs`, resolve from the target repo root, and must export current `ValidationCheckDefinition` objects; Opcore owns loading/registry validation, repos own policy content. - ALWAYS keep `packages/asp-provider` as a provider-process facade over ASP Core check/evaluate only - WHY: ASP hosts own decisions, authority, gate semantics, workspace grants, and apply behavior. - ALWAYS keep warm ASP inspect/edit composition inside `packages/opcore/src/advanced/asp-warm/` and out of `packages/asp-provider` - WHY: only the advanced Opcore router may combine ASP JSON-RPC with ts-morph inspect/edit state, while the standalone provider must remain cold and check-only. -- ALWAYS keep the Opcore product facade thin over public package adapters - WHY: `opcore` is first-run UX, not a second implementation of graph, validation, edit, ASP host authority, or old-tool behavior. +- ALWAYS keep the Opcore product facade thin over public package adapters - WHY: `opcore` is first-run UX, not a second implementation of graph, validation, edit, or ASP host authority. - ALWAYS make Opcore scan/status/check/measure read-only with respect to source files and require explicit approval before `opcore install` or compatibility `opcore init` writes guidance, hooks, or config - WHY: first-run trust depends on showing value before mutating a repo. - ALWAYS put coverage honesty before Opcore metrics - WHY: the graph engine currently supports TypeScript/JavaScript, Python source/stub files, and syn-backed Rust `.rs` extraction, while unsupported stacks must be counted instead of silently ignored. -- NEVER ship a blended quality score, security/SAST claim, all-stack claim, AI-authorship claim, automatic-fix claim, ASP-standard claim, or old-tool replacement claim from Opcore alpha - WHY: the alpha must survive skeptical drill-down and current receipts keep `oldToolReplacementClaimed: false`. +- NEVER ship a blended quality score, security/SAST claim, all-stack claim, AI-authorship claim, automatic-fix claim, or ASP-standard claim from Opcore alpha - WHY: every claim must survive skeptical drill-down through current receipts. - NEVER add new launch-facing old-name branding - WHY: the public/product name is Opcore. Existing old-name package/bin/repo references are transitional implementation debt to remove or hide before alpha. -- ALWAYS keep Rust validation in `packages/validation-rust` as provider assessment checks composed by the CLI, not host decisions or old-tool wrappers - WHY: Cargo, rustfmt, clippy, rustdoc, import/dead-code, unused dependency, and function-metric evidence must remain package-owned and overlay-safe. +- ALWAYS keep Rust validation in `packages/validation-rust` as provider assessment checks composed by the CLI, not host decisions - WHY: Cargo, rustfmt, clippy, rustdoc, import/dead-code, unused dependency, and function-metric evidence must remain package-owned and overlay-safe. - ALWAYS treat Cargo.lock-only changes as retained compatibility unless a later decision expands Rust adapter ownership - WHY: current parity covers `.rs`, `.inc`, and `Cargo.toml`; lockfile-only policy needs an explicit cutover decision before old guardrails move. - NEVER add public CLI behavior outside @docs/architecture/runtime-cli-ard.md canonical routing - WHY: early command shapes become accidental API promises. - ALWAYS keep `GraphProviderStatus.state` aligned with `failure.category` in TypeScript validators and JSON schema - WHY: consumers branch on both fields for required graph failure policy; contradictory pairs make provider handling ambiguous. - ALWAYS reject blank validation check names before normalization deduplicates or trims them - WHY: blank checks can otherwise become an empty no-check validation request and hide caller mistakes. -- ALWAYS update `rox.json`, CI, and this file in the same change when adding a new implementation language or Rust gate surface - WHY: language support without repo-wide and scoped validation lets agents ship unverified code paths. +- ALWAYS update `.opcore/config`, CI, and this file in the same change when adding a new implementation language or validation gate surface - WHY: language support without repo-wide and scoped self-validation lets agents ship unverified code paths. - NEVER add backward-compatibility shims for removed internal paths - WHY: this is a clean release line; migrate the caller or delete the old path. - ALWAYS keep generated provider/runtime trees out of Git - WHY: descriptors and scripts are source of truth; generated trees drift by machine. -- ALWAYS keep old-tool reference evidence under @docs/graph-reference-evidence/ and @packages/fixtures/graph-reference-evidence/ - WHY: reference data may mention old tools only under allowlisted evidence docs/fixtures, never as implementation package naming or source provenance. - ALWAYS keep staged graph optional-analysis classifications sourced from `graphReleaseOptionalAnalysisSurfaces` - WHY: #13 coverage, #14 flows, #15 communities, and #16 read-only suggestions are non-release-blocking #17 deferred/staged surfaces and must not drift across contracts, fixtures, receipts, or docs. -- ALWAYS update `packages/opcore/src/advanced/descriptor.ts`, `scripts/write-cli-descriptor.mjs`, descriptor fixtures, package packlists, and descriptor validation together when changing ACE acquisition metadata - WHY: ACE must consume installed Opcore release artifacts, not workspace-local paths. +- ALWAYS update `packages/opcore/src/advanced/descriptor.ts`, `scripts/write-cli-descriptor.mjs`, descriptor fixtures, package packlists, and descriptor validation together when changing managed artifact metadata - WHY: installed consumers must resolve package artifacts, not workspace-local paths. - ALWAYS update release receipt contracts, `scripts/generate-release-receipt.mjs`, docs/release receipts, CI, and package/provenance/secret negative tests together when changing release evidence ownership - WHY: #29 is the maintainer release proof gate for the alpha line. - ALWAYS update cutover receipt contracts, `scripts/generate-cutover-receipt.mjs`, docs/release cutover receipts, CI, and cutover negative tests together when changing installed-artifact release behavior - WHY: #30 proves canonical Opcore artifacts replace current external dev tools without fallback. - ALWAYS record installed package file paths and checksums in cutover receipts, including ASP provider manifests - WHY: cutover proof must show packaged artifacts survived installation, not only tarball and package.json evidence. -- ALWAYS keep ASP dogfood advisory/shadow and isolated to temp `ASP_HOME`; co-record `current-tools:validate-changed` and `current-tools:validate-rust-graph`, keep `oldToolReplacementClaimed: false`, and represent inspect/edit gaps as degraded or retained blockers - WHY: #120 proves standalone ASP manager integration without authorizing rollout or retiring Rox/CRG/CIX. +- ALWAYS keep ASP dogfood advisory/shadow and isolated to temp `ASP_HOME`, and represent inspect/edit gaps as degraded or parity blockers - WHY: #120 proves standalone ASP manager integration without authorizing rollout. - ALWAYS update `packages/asp-provider/src/manifest.ts`, `scripts/write-asp-provider-manifest.mjs`, package exports/packlists, release receipts, installed-bin tests, and claim scrub together when changing ASP provider manifest/install metadata - WHY: canonical `asp-server.json` and retained provisional metadata must not imply trust, authority, gate permission, or host apply permission. - ALWAYS treat `darwin-arm64`, `darwin-x64`, and `linux-x64` as the only supported Opcore alpha graph-core native targets until CI aggregate evidence expands the set - WHY: local single-platform builds cannot prove clean public installs for other targets. - ALWAYS require CI aggregate and release publish workflows to download all three Opcore native package artifacts before release receipts, cutover receipts, or npm publish claim cross-platform graph-core readiness - WHY: the public `opcore` package must bundle real per-target checksums, not local fallback or fabricated binaries. -- ALWAYS keep `opcore` launch-facing help, README snippets, smoke output, and JSON named Opcore while preserving transitional `opcore status` compatibility - WHY: first-run activation must not leak internal or old-tool names into the public readiness flow. +- ALWAYS keep `opcore` launch-facing help, README snippets, smoke output, and JSON named Opcore while preserving transitional `opcore status` compatibility - WHY: first-run activation must not leak internal implementation names into the public readiness flow. - ALWAYS keep Opcore metrics finding-only and evidence-backed, with no opaque score or blended quality number - WHY: `opcore measure` is a trend report over concrete counts, not a scoring system. - ALWAYS keep clone detection split between the existing graph-core native subcommand and the injected TypeScript validation-clone adapter - WHY: duplicate-code detection needs native indexing without a daemon, graph-provider dependency, security claim, or extra native package. - ALWAYS keep latency budget gates finding-only and non-blocking by default, with `--fail-on-over` reserved for explicit promotion - WHY: latency evidence should guide regression decisions without becoming an opaque score or live measurement runner. @@ -167,8 +157,8 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit ## Language And Runtime - Node >=22 and TypeScript are the current scaffold baseline. -- The accepted boundary is hybrid: Rust graph core owns extraction, persistence, watch refresh, hot graph queries, and clone index analysis; TypeScript owns contracts, router-core helpers, CLI composition, package command adapters, edit orchestration, validation policy, validation-typescript and validation-clone adapters, npm facade, and ACE descriptors. -- #21 adds the Cargo workspace, `crates/graph-core`, Rust sidecar protocol, installed native artifacts, checksums, schema-compatible `rox.json` Rust gate metadata, npm Rust gate scripts, workspace Rust/clippy lint policy, `rox.json` code-quality scope for `packages/`, `scripts/`, `tests/`, and `crates/`, repo-wide Rox extension coverage for scoped Rust graph function metrics under `crates/`, scoped `current-tools:validate-rust-graph`, and GitHub Actions Rust setup. +- The accepted boundary is hybrid: Rust graph core owns extraction, persistence, watch refresh, hot graph queries, and clone index analysis; TypeScript owns contracts, router-core helpers, CLI composition, package command adapters, edit orchestration, validation policy, validation-typescript and validation-clone adapters, npm facade, and managed descriptors. +- #21 adds the Cargo workspace, `crates/graph-core`, Rust sidecar protocol, installed native artifacts, checksums, npm Rust gate scripts, workspace Rust/clippy lint policy, and GitHub Actions Rust setup. - #8 adds Wave 1 Rust graph-core source extraction for `.ts`, `.tsx`, `.js`, and `.jsx` through OXC parser crates. #25 adds syn-backed `.rs` extraction through graph-core facts for File, Module, Struct, Enum, Trait, Impl, Function/Method, TypeAlias, Const/Static, Test, Macro, CONTAINS, IMPORTS_FROM, CALLS, IMPLEMENTS, and DEPENDS_ON with Rust language/signature/span/export metadata. #9 adds the SQLite GraphProvider store at `.opcore/graph/graph.db`, freshness metadata, and #19 direct-reader reference evidence. #10 implements `opcore graph build/update/watch/status`, incremental cached FileFacts updates, phase timings, daemon `ping`/`health`, and watch artifacts at `.opcore/graph/daemon/{pid,state.json,daemon.log}`. #11 implements read-only store-backed `impact`, named `query`, `review-context`, and `detect-changes` envelopes. #12 implements Rust graph-core FTS5 search with `nodes_fts`, signature projection, full and incremental index maintenance, typed failures, and canonical `opcore graph search` through the TypeScript graph adapter. Full build/update/status are unscoped unless `--paths` is passed; `OPCORE_GRAPH_WATCH_PATHS` scopes watch only. Pipeline failures return router status `error`/exit 1 without fabricated summaries; status, health, query, and search paths are read-only when the store is missing or stale. Long-tail parser coverage remains follow-up graph work. - #17 adds Python graph-core extraction for `.py` and `.pyi` through `tree-sitter-python`, emitting File/Module/Class/Function/Variable nodes plus CONTAINS/IMPORTS_FROM/CALLS/INHERITS/TESTED_BY edges. Python imports are repo-local best-effort over absolute, relative, package initializer, and stub candidates; unresolved relative or repo-local imports emit warning category `unresolved_import`; validation support remains absent. - The Rust graph fact-model foundation makes Rust node/edge kinds canonical and confirms SQLite store schema v1 already persists Rust fact columns used by #25 Rust extraction. @@ -180,25 +170,25 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - #56 adds the validation file view: `@the-open-engine/opcore-validation` composes normalized `ValidationRequest` overlays, resolved scope files, and injected workspace `readFile` access so `opcore validate` checks can read hypothetical after-state writes/deletes, before-state comparisons for introduced report mode, and before-state checksums without mutating the worktree. `ValidationFileView.defaultReadState` is check-visible so adapters with out-of-band readers can bind their before/after source mode to the runner pass. `checksumBefore` conflicts return refused/conflict before checks run. - #26 adds the validation-owned GraphProvider consumer boundary: `ValidationGraphProviderClient`, cached `ValidationGraphQuerySession`, graph requirement preloading, status/query failure mapping, and helper access for metadata, file checksums, IMPORTS_FROM, CALLS, and TESTED_BY facts. - #57 adds the TypeScript validation adapter: `@the-open-engine/opcore-validation-typescript` exports package-owned syntax, type, import-graph, dead-code, and relevant-tests check definitions. Syntax/type checks use the validation file view and an overlay-aware compiler host, including tsconfig path aliases for repo files and deterministic node_modules/package declaration resolution for external package imports; graph checks require #26 graph sessions and batched IMPORTS_FROM, CALLS, and TESTED_BY fact requirements. -- #20 adds the Rust validation adapter: `@the-open-engine/opcore-validation-rust` exports package-owned source hygiene, fmt, cargo-check, clippy, rustdoc, import-graph, dead-code, graph-signals, unused-deps, file-length, and function-metrics checks. `rust.graph-signals` is graph-provider-backed evidence from `ValidationGraphProviderClient` only (untested public Rust surface, dead public exports, module orphans/cycles); it does not replace mechanical cargo/rustdoc/clippy/Rox guardrails. #61 makes the five retained Rust rows native when supporting tools are available: rustdoc diagnostics block through `cargo doc`, import-graph reports unresolved modules/use paths/orphans/cycles from fileView, dead-code combines Cargo `dead_code` with orphan-source evidence, unused-deps parses cargo-udeps with workspace/package scoping, and function-metrics parses rust-code-analysis JSON object/array output. Missing `rustdoc`, `cargo-depgraph`, `cargo-udeps`, or `rust-code-analysis-cli` stays degraded or unsupported with `requiredTool` and retained `currentUsage`; no generic retained row remains for available tools. Rust checks materialize temporary workspaces from `ValidationCheckContext.fileView` after-state content for Cargo tools, add no validation daemon or hidden cache, never shell out to Rox, and keep current external Rust guardrails active until #29/#30 accept replacement receipts. #138 shares cargo-check JSON with dead-code, forbids a second dead-code cargo compile or `-Ddead_code` cache-busting flags, and injects a persistent `CARGO_TARGET_DIR` outside the worktree through `runTool`; cache keys must derive from generic repo, scope, overlay, platform, and toolchain inputs and cleanup must remain TTL/size bounded - WHY: Rust validation must be fast without leaking hypothetical after-state into real worktrees or tuning behavior to one repository. +- #20 adds the Rust validation adapter: `@the-open-engine/opcore-validation-rust` exports package-owned source hygiene, fmt, cargo-check, clippy, rustdoc, import-graph, dead-code, graph-signals, unused-deps, file-length, and function-metrics checks. `rust.graph-signals` is graph-provider-backed evidence from `ValidationGraphProviderClient` only (untested public Rust surface, dead public exports, module orphans/cycles), while mechanical evidence remains owned by Cargo and the configured native tools. Missing `rustdoc`, `cargo-depgraph`, `cargo-udeps`, or `rust-code-analysis-cli` stays degraded or unsupported with `requiredTool`. Rust checks materialize temporary workspaces from `ValidationCheckContext.fileView` after-state content for Cargo tools and add no validation daemon or hidden cache. #138 shares cargo-check JSON with dead-code, forbids a second dead-code cargo compile or `-Ddead_code` cache-busting flags, and injects a persistent `CARGO_TARGET_DIR` outside the worktree through `runTool`; cache keys must derive from generic repo, scope, overlay, platform, and toolchain inputs and cleanup must remain TTL/size bounded - WHY: Rust validation must be fast without leaking hypothetical after-state into real worktrees or tuning behavior to one repository. - Rust Cargo/native checks share one environment-keyed materialized workspace per validation file-view state through runner-owned disposable resources; the runner removes it on every exit - WHY: exact staged/tree/overlay semantics require state isolation, while per-check whole-repo copies create unbounded filesystem-event churn. - #151/#214 adds clone detection through the existing `opcore-graph-core` binary as a `clone` subcommand plus `@the-open-engine/opcore-validation-clone`. `CloneAnalysisRequest`/`CloneAnalysisResult` use `opcore.clone.v1`; committed analysis may refresh `.opcore/clone/clone.db`, while scoped or hypothetical analysis is ephemeral. The sparse request shape sends committed candidates as `paths`/`sourcePaths` plus `sourceReadMode`/`sourceTreeRef`, with full content only in write overlays. `clone.duplication` is a validation adapter check with injected native invocation, no graph requirement, no line-level identity, no daemon, and no SAST/security or score claim. - #27 adds canonical validation CLI surfaces: `opcore check` implements `files`, `staged`, `changed`, `tree`, `all`, and `manifest`; `tree` reads committed Git tree content from `--tree ` and scopes files from `--changed-from ` without consuming dirty worktree files. `opcore validate` implements `--request-file`, `hypothetical --request-file`, `pre-write --request-file --timeout-ms --json`, and `manifest`; runtime-owned `opcore status --json` includes `repoState`, while `opcore doctor --json` includes typed `runtimeInfo`, `opcoreDoctor`, and transitional `validationStatus` payloads with adapter routes, check ids, manifest entries, graph status, and daemon readiness metadata. #58 defines `opcore validate pre-write --request-file --timeout-ms 30000 --json` as the hook-safe, fail-closed pre-write contract with typed `PreWriteValidationReceipt` output. - #69 removes public runtime lifecycle command groups: top-level start and stop are unsupported unless a later architecture decision adopts them. Runtime readiness remains `opcore status` and `opcore doctor`; graph daemon lifecycle/status remains graph-owned under `opcore graph`. -- #118 adds bundled `@the-open-engine/opcore-asp-provider` internals and the public `opcore-asp-provider --stdio` bin from the `opcore` package as an independently launchable ASP Core check provider facade. It handles `initialize`, `initialized`, and `check/evaluate`, maps ASP create/modify/delete/rename changesets into validation overlays through host `workspace/listTree` and `workspace/readBlob` callbacks, runs the same TypeScript and Rust validation checks as Opcore validation composition, reports degraded/unsupported coverage for missing graph/toolchain/provider surfaces, emits provider-owned diagnostics, binds `validAsOf` to baseline/changeset/read blobs, and strips host-owned decision/authority/apply fields. It does not add a `opcore asp` router group and must not use ACE descriptors, `.ace/runtime`, `rox`, `crg`, or `cix` as implementation paths. +- #118 adds bundled `@the-open-engine/opcore-asp-provider` internals and the public `opcore-asp-provider --stdio` bin from the `opcore` package as an independently launchable ASP Core check provider facade. It handles `initialize`, `initialized`, and `check/evaluate`, maps ASP create/modify/delete/rename changesets into validation overlays through host `workspace/listTree` and `workspace/readBlob` callbacks, runs the same TypeScript and Rust validation checks as Opcore validation composition, reports degraded/unsupported coverage for missing graph/toolchain/provider surfaces, emits provider-owned diagnostics, binds `validAsOf` to baseline/changeset/read blobs, and strips host-owned decision/authority/apply fields. It does not add a public `opcore asp` router group. - #128 adds `opcore status` and `opcore status --json` as the read-only repo-aware activation command. It resolves repo/Git state, coverage, graph status/action, validation adapter/check availability, degraded Rust tools, cheap ASP enrollment hints, warnings, blockers, and next actions without running builds, checks, installs, setup, wrappers, or writes. Its JSON payload is `repoState`; `opcore status` keeps the transitional `validationStatus` payload. - #129 adds `opcore` and the standalone `opcore` bin. Zero-command scan uses `repoState` from #128, runs validation without source mutation, prints Coverage before Findings, writes only `.opcore/report.json`, `.opcore/history.jsonl`, and bounded `.opcore/telemetry.jsonl` capped at 500 records or 1 MiB, exposes `opcore check --changed|--staged|--all| --json` with stable agent exit codes, and exposes read-only `opcore --version`/`opcore version` runtime provenance. - #130 adds `packages/opcore/src/reporting.ts`, `OpcoreMetricReport`, `OpcoreMetricHistoryEntry`, and `OpcoreMeasureDelta`. Reports aggregate TS/JS syntax/type/test/dead-export diagnostics, graph structure/fan-in evidence when supplied, Rust source hygiene/file length/module/toolchain diagnostics, unsupported stack census, and honest degradations for unavailable checks/tools/facts. `writeOpcoreMetricArtifacts` writes only under `.opcore/`; `opcore status` excludes those generated artifacts from coverage; `opcore measure` reads them and returns deltas without validationResult, validationStatus, scans, graph builds, setup, or source writes. -- #131 adds `packages/opcore/src/init.ts` and the `opcoreInit` router payload. `opcore init` detects existing `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.github/copilot-instructions.md`, `.codex/AGENTS.md`, and `.opencode/AGENTS.md`, runs a read-only scan without `.opcore/report.json` or history writes, emits scan/settings/interaction/timing payloads, presents a plan before writing, prompts on TTY only, and now chooses repo/global write-gate scope by flag or interactive Git prompt. Approved repo init upserts a single `` block, writes additive `.opcore/config`, appends one managed `.opcore/` `.gitignore` line only in Git repos that do not already ignore it, installs `.opcore/hooks/opcore-agent-gate.mjs`, and merges Claude Code plus Codex PreToolUse hook settings without clobbering existing hooks. Approved global init installs the same adapter under `~/.opcore/hooks/`, merges `~/.claude/settings.json` and `~/.codex/hooks.json`, and records undo under `~/.opcore/init-undo.json`. The adapter maps Write/Edit/MultiEdit and Codex apply_patch payloads to hypothetical validation overlays, calls `opcore validate pre-write --request-file --timeout-ms 30000 --json`, exits 2 on non-ok receipts or validation command failure, and fail-opens only when the adapter cannot parse/map the harness payload. Approved writes refuse symlink targets or symlink ancestors for repo and global paths. Undo refuses metadata whose recorded root does not match or whose entries are outside Opcore-owned config/hooks/agent guidance paths plus the managed `.gitignore` line; `.gitignore` undo removes only that managed line and deletes an init-created `.gitignore` only when empty. The managed `.opcore/` ignore covers `.opcore/telemetry.jsonl`. The guidance must tell agents to run `opcore check --changed`, preserve existing guardrails, report unsupported/degraded coverage honestly, and not rely on ACE, Rox, CRG, CIX, or ASP host authority for direct Opcore. +- #131 adds `packages/opcore/src/init.ts` and the `opcoreInit` router payload. `opcore init` detects existing `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.github/copilot-instructions.md`, `.codex/AGENTS.md`, and `.opencode/AGENTS.md`, runs a read-only scan without `.opcore/report.json` or history writes, emits scan/settings/interaction/timing payloads, presents a plan before writing, prompts on TTY only, and now chooses repo/global write-gate scope by flag or interactive Git prompt. Approved repo init upserts a single `` block, writes additive `.opcore/config`, appends one managed `.opcore/` `.gitignore` line only in Git repos that do not already ignore it, installs `.opcore/hooks/opcore-agent-gate.mjs`, and merges Claude Code plus Codex PreToolUse hook settings without clobbering existing hooks. Approved global init installs the same adapter under `~/.opcore/hooks/`, merges `~/.claude/settings.json` and `~/.codex/hooks.json`, and records undo under `~/.opcore/init-undo.json`. The adapter maps Write/Edit/MultiEdit and Codex apply_patch payloads to hypothetical validation overlays, calls `opcore validate pre-write --request-file --timeout-ms 30000 --json`, exits 2 on non-ok receipts or validation command failure, and fail-opens only when the adapter cannot parse/map the harness payload. Approved writes refuse symlink targets or symlink ancestors for repo and global paths. Undo refuses metadata whose recorded root does not match or whose entries are outside Opcore-owned config/hooks/agent guidance paths plus the managed `.gitignore` line; `.gitignore` undo removes only that managed line and deletes an init-created `.gitignore` only when empty. The managed `.opcore/` ignore covers `.opcore/telemetry.jsonl`. The guidance must tell agents to run `opcore check --changed`, preserve existing safeguards, report unsupported/degraded coverage honestly, and not rely on ASP host authority for direct Opcore. - #133 adds `packages/opcore/src/try.ts` and the `opcoreTry` router payload. `opcore try` uses generated local sample repos only, keeps output coverage-first with named findings/deltas, includes unsupported-file census evidence, and records clean-room launch proof without public announcement or package publishing. -- #22 adds the edit-core library foundation: `@the-open-engine/opcore-edit` owns deterministic exact, multi-edit, and literal search-replace planners, edit checksums, plan hashes, validation overlay construction, preview mode, repo path policy, Node workspaces, and all-or-nothing atomic apply/rollback. #59 adds canonical `opcore edit exact`, `multi`, `search-replace`, `check`, and `apply` parsing inside the edit package, typed `editPlan`/`editResult` router payloads, no old `cix` aliases, and search-replace uniqueness unless `replaceAll` is true. #60 adds canonical `opcore edit patch` and `tree`: raw unified diff patch input through `--stdin`/`--request-file` or `{patch}` JSON, tree payloads `{repo?,validation?,fileContains?,files:[{path,content,checksumBefore?}|{path,delete:true,checksumBefore?}]}`, patch/tree-only forbidden target policy for absolute paths, parent traversal, UNC paths, symlink escapes, `.gitignore` targets, generated/private roots, and binary content, plus `editResult.rollback` state for atomic apply failures. #24 routes non-empty `opcore edit` apply/check plans through an injected validation runner before writes, rejects validation bypass plans, preserves full `ValidationResult` envelopes in edit results, and keeps `--dry-run` as a non-validating preview. #23 implements canonical `opcore edit rename`, `move`, and `signature` routes as graph-backed, validation-required edit plans: GraphProvider contract status/query/search evidence is required for targeting/freshness, TypeScript/JavaScript language-service materialization stays edit-owned, and apply/check refuses graph freshness changes before validation or writes. +- #22 adds the edit-core library foundation: `@the-open-engine/opcore-edit` owns deterministic exact, multi-edit, and literal search-replace planners, edit checksums, plan hashes, validation overlay construction, preview mode, repo path policy, Node workspaces, and all-or-nothing atomic apply/rollback. #59 adds canonical `opcore edit exact`, `multi`, `search-replace`, `check`, and `apply` parsing inside the edit package, typed `editPlan`/`editResult` router payloads, and search-replace uniqueness unless `replaceAll` is true. #60 adds canonical `opcore edit patch` and `tree`: raw unified diff patch input through `--stdin`/`--request-file` or `{patch}` JSON, tree payloads `{repo?,validation?,fileContains?,files:[{path,content,checksumBefore?}|{path,delete:true,checksumBefore?}]}`, patch/tree-only forbidden target policy for absolute paths, parent traversal, UNC paths, symlink escapes, `.gitignore` targets, generated/private roots, and binary content, plus `editResult.rollback` state for atomic apply failures. #24 routes non-empty `opcore edit` apply/check plans through an injected validation runner before writes, rejects validation bypass plans, preserves full `ValidationResult` envelopes in edit results, and keeps `--dry-run` as a non-validating preview. #23 implements canonical `opcore edit rename`, `move`, and `signature` routes as graph-backed, validation-required edit plans: GraphProvider contract status/query/search evidence is required for targeting/freshness, TypeScript/JavaScript language-service materialization stays edit-owned, and apply/check refuses graph freshness changes before validation or writes. - #149 adds the shared inspect/edit ts-morph Project construction seam: cold inspect signatures and graph-backed implementations default to target import-closure loading; references and symbol edits load the target import closure plus reverse importers instead of whole-repo Projects when that preserves correctness; graphless implementation paths keep whole-repo fallback where reverse dependents cannot be bounded safely; and both inspect/edit accept injected Project plus snapshot/revert hooks for future warm ASP reuse without introducing a daemon. - #153 adds the hidden `opcore asp serve --stdio` warm ASP session: host-launched only, process-local singleton/idle lifecycle under `.opcore/asp`, warm injected whole-repo ts-morph Project for `inspect/references` and `edit/rename` preview, unchanged delegated `check/evaluate`, `session/shutdown`, no source writes, no public help/manifest command group, no auto-spawned daemon, and no change to the cold `opcore-asp-provider --stdio` check-only capability. - #17 adds the graph release readiness receipt gate: `npm run graph-release:check` proves canonical graph commands, direct SQLite queries, serve transport, package inspection, provenance/license receipts, benchmark metrics, and handoff data for #7/#28/#29. -- #28 adds the aggregate Opcore ACE descriptor contract and artifact: `opcore` packages `dist/descriptors/opcore.managed-tool.json`, generated from `packages/opcore/src/advanced/descriptor.ts` after build. Descriptors must list only the canonical `opcore` bin, command groups graph/inspect/edit/check/validate/status/doctor, package-relative artifact/checksum paths, GraphProvider daemon/query/search/native capabilities, edit validation dependency, validation graph optional/required modes, and deferred #13-#16 optional surfaces as metadata. Descriptor validators must reject private runtime roots such as `.ace` on either slash style. +- #28 adds the aggregate Opcore managed descriptor contract and artifact: `opcore` packages `dist/descriptors/opcore.managed-tool.json`, generated from `packages/opcore/src/advanced/descriptor.ts` after build. Descriptors list only the canonical `opcore` bin, command groups graph/inspect/edit/check/validate/status/doctor, package-relative artifact/checksum paths, GraphProvider query/search/native capabilities, edit validation dependency, validation graph optional/required modes, and deferred optional surfaces as metadata. - #29 adds the repo-wide release receipt gate: `npm run release-receipt:check` proves the single public `opcore` tarball, bundled internal implementation/native/runtime dependencies, exact packlists, sha256 checksums, descriptor/provider manifest artifact resolution, canonical Opcore command groups, native graph artifact checksum evidence, production and bundled dependency licenses, provenance scans, release hygiene, graph #17 input evidence, and current-tree plus git-history secret scans. `npm run release-receipt:receipt` refreshes `docs/release/release-receipt.json`, `docs/release/release-receipt.summary.md`, license/provenance reports, and artifact attestation docs. Secret allowlist entries live only in `docs/release/secret-scan-allowlist.json` and must include reviewed path or commit scope, reviewer, reason, expiry, and optional fingerprint/kind narrowing; remove real findings instead of allowlisting them. -- #30 adds the installed-artifact cutover gate: `npm run cutover:check` packs and installs only the public `opcore` package into a clean temp project, clears current-tool env resolution, excludes local wrapper and sibling paths, verifies installed canonical bins (`opcore` and `opcore-asp-provider`), validates `ReleaseCutoverReceipt`, proves graph/inspect/edit/check/validate/status/doctor/pre-write plus scan/measure flows through `opcore`, and rejects old-tool/private-path markers or advertised `not_implemented` release commands. Each command receipt id is contract-bound to its expected canonical command/status/exit. Top-level `opcore inspect symbols|definition|references|signature|implementations|search` is read-only CLI behavior; signature and implementations are implemented read-only language-service parity. `opcore graph inspect` is not an advertised release route. -- #72 adds typed inspect reference results and `opcore inspect references --line [--column ]` for read-only CIX refs parity over fresh graph facts plus an inspect-owned TypeScript/JavaScript language-service seam. #100 adds shared read-only `InspectSignatureResult` and `InspectImplementationResult` contracts, fixture foundations, target parsing, graph freshness enforcement, and typed `unsupported_route` scaffolds. #101 implements `opcore inspect signature --line [--column ]` and node-id targeting for read-only CIX `sig` parity over fresh graph facts. #102 implements `opcore inspect implementations --line [--column ]` and class/type node-id targets for CIX `impls` parity over graph `IMPLEMENTS`/`INHERITS` facts plus TypeScript/TSX language-service materialization. +- #30 adds the installed-artifact cutover gate: `npm run cutover:check` packs and installs only the public `opcore` package into a clean temp project, sanitizes execution paths, verifies installed canonical bins (`opcore` and `opcore-asp-provider`), validates `ReleaseCutoverReceipt`, and proves graph/inspect/edit/check/validate/status/doctor/pre-write plus scan/measure flows through `opcore`. Each command receipt id is contract-bound to its expected canonical command/status/exit. Top-level `opcore inspect symbols|definition|references|signature|implementations|search` is read-only CLI behavior; signature and implementations are implemented read-only language-service parity. `opcore graph inspect` is not an advertised release route. +- #72 adds typed inspect reference results and `opcore inspect references --line [--column ]` over fresh graph facts plus an inspect-owned TypeScript/JavaScript language-service seam. #100 adds shared read-only `InspectSignatureResult` and `InspectImplementationResult` contracts, fixture foundations, target parsing, graph freshness enforcement, and typed `unsupported_route` scaffolds. #101 implements `opcore inspect signature --line [--column ]` and node-id targeting over fresh graph facts. #102 implements `opcore inspect implementations --line [--column ]` and class/type node-id targets over graph `IMPLEMENTS`/`INHERITS` facts plus TypeScript/TSX language-service materialization. - #141 keeps inspect file-symbol routes useful when graph facts are missing or stale: supported TS/JS `references` and `signature`, plus existing TS/TSX `implementations`, return read-only language-service payloads with `inspectResult.status: "degraded"` and `failure.category: "graph_unavailable"` instead of hard failure; unsupported paths and graph-only node-id targets remain hard failures or unsupported as before. - The provenance GitHub workflow must install stable Rust and run `npm run build` before `npm run release-receipt:check` - WHY: release receipts import ignored `dist/` contracts/descriptors and require native graph artifacts from a clean checkout. @@ -206,15 +196,17 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - ALL tests live in @tests/ until a package-specific test harness is explicitly introduced by an issue. - Add contract tests before implementation tests for GraphProvider, EditPlan, ValidationRequest, and canonical CLI behavior. -- Add golden/reference fixtures before replacing current external tool behavior. +- Add golden/reference fixtures before changing established behavior. - Keep release hygiene, conformance metadata, and package packlist gates executable when changing package or release surfaces - WHY: maintainer release receipts must fail before public alpha assumptions drift. -- Add #29 negative fixtures for release evidence regressions: Python code-review-graph provenance, high-confidence secrets, unexpected package files, old public bins, descriptor artifact drift, and missing native checksum evidence. -- Add #30 negative fixtures for cutover regressions: current-tool descriptor markers, advertised placeholder command receipts, missing cutover command receipts, and old bin fallback in installed projects. -- Local proof for agent work is `npm run ci:local`; for source/package/native/release changes it regenerates current-tool wrappers, runs `npm run ci`, runs repo-wide Rox, then runs the Rust graph function-metrics check. For docs/agent-guidance-only changes it uses the local CI fast path described above. GitHub Actions run Node and Rust gates while current external ACE tools remain local-worktree dependencies. +- Add #29 negative fixtures for release evidence regressions: high-confidence secrets, unexpected package files, + descriptor artifact drift, and missing native checksum evidence. +- Add #30 negative fixtures for cutover regressions: advertised placeholder command receipts, missing cutover command + receipts, and invalid installed package/bin surfaces. +- Local proof for agent work is `npm run ci:local`; for source/package/native/release changes it runs `npm run ci` and then `npm run opcore:self-check`. Docs/agent-guidance-only changes use the repository-native fast path described above. ## Commands -- Setup and wrappers: `npm run setup`, `npm run setup:tools`, `source scripts/dev-env.sh`, `npm run ace:install`, `npm run ace:sync`, `npm run ace:validate`. -- Proof: core `npm run ci`, targeted `node --test tests/...`, `npm run rust:check`, `npm run ci:local` or `npm run verify`; release `npm run graph:artifact`, `npm run descriptor:artifact`, `npm run asp-provider:manifest`, `npm run graph-release:check`, `npm run release-receipt:check`, `npm run cutover:check`, `npm run asp-dogfood:check`, `npm run pack:check`, `npm run release:hygiene`; retained guardrails `npm run current-tools:validate-changed`, `npm run current-tools:validate-all`, `npm run current-tools:validate-rust-graph`. +- Setup: `npm run setup`. +- Proof: core `npm run ci`, `npm run opcore:self-check`, targeted `node --test tests/...`, `npm run rust:check`, `npm run ci:local` or `npm run verify`; release `npm run graph:artifact`, `npm run descriptor:artifact`, `npm run asp-provider:manifest`, `npm run graph-release:check`, `npm run release-receipt:check`, `npm run cutover:check`, `npm run asp-dogfood:check`, `npm run pack:check`, `npm run release:hygiene`. - Public scan/readiness: `opcore --repo . --json`, `opcore status --repo . --json`, `opcore --version --json`, `opcore doctor --repo . --json`, `opcore measure --repo . --json`, `opcore try --json`. - Public setup/check/provider: `opcore install --repo . --json`, `opcore install --repo . --yes --json`, `opcore uninstall --repo . --yes --json`, `opcore check --changed --json`, `opcore-asp-provider --stdio`. diff --git a/ace.json b/ace.json deleted file mode 100644 index 5137aa2..0000000 --- a/ace.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "canonical": ".claude", - "providers": [ - ".agents", - ".codex", - ".gemini", - ".opencode" - ], - "policy": "standard", - "sync": { - "skills": true, - "commands": false, - "hooks": true, - "contextDocs": true - }, - "mcpServers": { - "code-review-graph": { - "command": "bash", - "args": [ - "-lc", - "set -euo pipefail; repo_root=$(git rev-parse --show-toplevel 2>/dev/null || pwd); exec \"$repo_root/.ace/runtime/bin/crg\" serve --repo \"$repo_root\"" - ] - } - }, - "contextDocs": { - "source": "CLAUDE.md", - "excludedPaths": [ - "dist", - "node_modules", - "coverage", - ".changeset", - ".ace", - ".agents", - ".claude", - ".codex", - ".gemini", - ".opencode", - ".zeroshot/bin", - ".zeroshot/logs", - ".zeroshot/run", - ".code-review-graph", - ".rox-cache", - ".robustness-engine-cache" - ] - }, - "gitSafe": false, - "preWriteQuality": { - "bash": false - } -} diff --git a/crates/graph-core/src/extraction/ignore.rs b/crates/graph-core/src/extraction/ignore.rs index 1763bc9..e01c10a 100644 --- a/crates/graph-core/src/extraction/ignore.rs +++ b/crates/graph-core/src/extraction/ignore.rs @@ -45,7 +45,6 @@ const BUILT_IN_IGNORE_GLOBS: &[&str] = &[ "**/vendor/**", "**/dist/**", "**/target/**", - "**/.ace/**", "**/.agents/**", "**/.claude/**", "**/.codex/**", @@ -53,9 +52,6 @@ const BUILT_IN_IGNORE_GLOBS: &[&str] = &[ "**/.lattice/**", "**/.opencode/**", "**/.opcore/**", - "**/.rox-cache/**", - ".robustness-engine-cache/**", - "**/.robustness-engine-cache/**", ".venv/**", "**/.venv/**", "venv/**", @@ -105,7 +101,6 @@ pub(super) fn ignore_matcher(repo_root: &Path) -> Result TestResult { - with_watch_env(Some("./src:tests\\unit"), None, || { + with_watch_env(Some("./src:tests\\unit"), || { let env_options = parse(&["watch", "--repo", "."])?; assert_eq!(env_options.watch_paths, vec!["src", "tests/unit"]); let cli_options = parse(&["watch", "--repo", ".", "--paths", "./src//,tests\\unit"])?; @@ -242,18 +242,9 @@ mod tests { }) } - #[test] - fn crg_watch_paths_do_not_scope_lattice_watch() -> TestResult { - with_watch_env(None, Some("src"), || { - let options = parse(&["watch", "--repo", "."])?; - assert!(options.watch_paths.is_empty()); - Ok(()) - }) - } - #[test] fn unsafe_watch_paths_are_rejected() -> TestResult { - with_watch_env(None, None, || { + with_watch_env(None, || { for path in [ "/tmp/src", "C:\\tmp\\src", @@ -300,29 +291,19 @@ mod tests { fn with_watch_env( opcore_value: Option<&str>, - crg_value: Option<&str>, run: impl FnOnce() -> Result, ) -> Result { let _guard = ENV_LOCK.lock().map_err(|error| error.to_string())?; let old_opcore = std::env::var("OPCORE_GRAPH_WATCH_PATHS").ok(); - let old_crg = std::env::var("CRG_WATCH_PATHS").ok(); match opcore_value { Some(value) => std::env::set_var("OPCORE_GRAPH_WATCH_PATHS", value), None => std::env::remove_var("OPCORE_GRAPH_WATCH_PATHS"), } - match crg_value { - Some(value) => std::env::set_var("CRG_WATCH_PATHS", value), - None => std::env::remove_var("CRG_WATCH_PATHS"), - } let result = run(); match old_opcore { Some(value) => std::env::set_var("OPCORE_GRAPH_WATCH_PATHS", value), None => std::env::remove_var("OPCORE_GRAPH_WATCH_PATHS"), } - match old_crg { - Some(value) => std::env::set_var("CRG_WATCH_PATHS", value), - None => std::env::remove_var("CRG_WATCH_PATHS"), - } result } } diff --git a/docs/architecture/runtime-cli-ard.md b/docs/architecture/runtime-cli-ard.md index 55a8cfa..b00665d 100644 --- a/docs/architecture/runtime-cli-ard.md +++ b/docs/architecture/runtime-cli-ard.md @@ -6,7 +6,7 @@ Decision: hybrid ## Context -The internal Opcore implementation line is the release line for graph, edit, validation, and the standalone ASP Core check provider facade behind Opcore. The bootstrap repository is TypeScript/npm, but the final runtime must not be chosen by scaffold inertia. The graph provider needs fast source extraction, persistent graph facts, SQLite/WAL indexing, watch daemons, and hot query paths. The edit and validation tracks need TypeScript compiler APIs, ts-morph-compatible orchestration, ESLint-style checks, JSON CLI output, npm packaging, and ACE/Zeroshot integration. The ASP provider facade needs a small stdio JSON-RPC process over host-owned workspace callbacks and Opcore validation checks. +The internal Opcore implementation line is the release line for graph, edit, validation, and the standalone ASP Core check provider facade behind Opcore. The bootstrap repository is TypeScript/npm, but the final runtime must not be chosen by scaffold inertia. The graph provider needs fast source extraction, persistent graph facts, SQLite/WAL indexing, watch daemons, and hot query paths. The edit and validation tracks need TypeScript compiler APIs, ts-morph-compatible orchestration, ESLint-style checks, JSON CLI output, npm packaging, and Zeroshot and CI integration. The ASP provider facade needs a small stdio JSON-RPC process over host-owned workspace callbacks and Opcore validation checks. ## Decision @@ -21,7 +21,7 @@ Opcore's internal Opcore implementation line uses a hybrid runtime: - TypeScript validation-typescript owns TypeScript-specific rules and compiler-backed adapters. - TypeScript validation-clone owns duplicate-code validation adapter wiring over the injected graph-core clone subcommand, without a graph-provider requirement or SAST/security claim. - TypeScript asp-provider owns the standalone ASP Core `check/evaluate` provider-process facade and provisional install manifest. -- TypeScript npm facade, ACE descriptors, and ASP provider install manifest own install metadata, runtime discovery inputs, wrapper generation, and release integration. +- TypeScript npm facade, managed descriptors, and ASP provider install manifest own install metadata, runtime discovery inputs, wrapper generation, and release integration. The ownership invariant is: do not collapse graph, edit, and policy ownership into one muddled abstraction. @@ -30,8 +30,8 @@ The ownership invariant is: do not collapse graph, edit, and policy ownership in | Option | Outcome | Reason | |---|---|---| | TS-only | Rejected | Simpler npm distribution, but graph performance, indexing, watch behavior, SQLite/WAL control, and hot query latency would depend on the wrong runtime boundary. | -| Rust-first | Rejected | Strong graph/runtime performance, but it would fight TypeScript compiler APIs, ESLint-style rule authoring, npm-first install, JSON CLI ergonomics, and ACE hook integration. | -| Hybrid | Accepted | Keeps the graph core in Rust where performance and persistence matter, while TypeScript owns contracts, CLI routing, edit, validation, npm facades, and ACE descriptors. | +| Rust-first | Rejected | Strong graph/runtime performance, but it would fight TypeScript compiler APIs, ESLint-style rule authoring, npm-first install, JSON CLI ergonomics, and agent hook integration. | +| Hybrid | Accepted | Keeps the graph core in Rust where performance and persistence matter, while TypeScript owns contracts, CLI routing, edit, validation, npm facades, and managed descriptors. | ## CLI Router @@ -45,7 +45,7 @@ The ownership invariant is: do not collapse graph, edit, and policy ownership in | `opcore check` | validation | Run mechanical checks and graph-aware checks. | | `opcore validate` | validation | Validate proposed or hypothetical edits against policy and manifests. | | `opcore status` | runtime | Report runtime, wrapper, graph, and validation readiness. | -| `opcore doctor` | runtime | Diagnose install, native artifact, wrapper, and ACE/Zeroshot integration problems. | +| `opcore doctor` | runtime | Diagnose install, native artifact, wrapper, and Zeroshot and CI integration problems. | | `opcore` | runtime | Run zero-command source-read-only scan, print coverage before findings, and write `.opcore/report.json`, `.opcore/history.jsonl`, plus bounded `.opcore/telemetry.jsonl` capped at 500 records or 1 MiB. | | `opcore status` | runtime | Report read-only repo activation, graph readiness, coverage, degraded toolchains, ASP enrollment hints, and next commands. | | `opcore check` | validation | Run the universal agent validation gate over changed, staged, or explicit files. | @@ -54,7 +54,7 @@ The ownership invariant is: do not collapse graph, edit, and policy ownership in | `opcore measure` | runtime | Read `.opcore/report.json` and `.opcore/history.jsonl` and return read-only metric deltas. | | `opcore try` | runtime | Generate local TS, Rust, mixed, and unsupported-file sample repos, run the launch loop, and publish nothing. | -Canonical `opcore graph`, `opcore edit`, `opcore check`, and `opcore validate` commands dispatch directly to public adapters owned by `packages/graph`, `packages/edit`, and `packages/validation`. Top-level `opcore inspect symbols|definition|references|signature|implementations|search` is CLI-owned read-only routing backed by public GraphProvider query/search APIs; `opcore inspect references --line [--column ]` adds #72 inspect-owned TypeScript/JavaScript language-service resolution for CIX refs parity. `opcore inspect signature --line [--column ]` and `opcore inspect signature ` add #101 inspect-owned TypeScript/JavaScript language-service signature parity for functions, methods, constructors, classes, interfaces, type aliases, overloads, imported/aliased symbols, path aliases, TS/TSX, JS, and JSX after fresh GraphProvider evidence is available. `opcore inspect implementations --line [--column ]` and node-id targets add #102 read-only implementation evidence for TypeScript/TSX class implements, class extends, and interface extends relationships over fresh graph facts plus language-service materialization. Unimplemented graph inspect routes are not advertised as release behavior. `opcore edit` implements `exact`, `multi`, `search-replace`, `patch`, `tree`, `rename`, `move`, `signature`, `check`, and `apply` over edit-core, edit-owned patch/tree APIs, and graph-backed symbol planning. `opcore edit patch` accepts unified diffs and Codex `apply_patch` documents; unified `--3way` is explicitly de-scoped for this release and returns a typed refusal instead of attempting dirty-file merges. Symbol routes consume GraphProvider contract status/query/search evidence plus edit-owned TypeScript/JavaScript language-service materialization, then emit normal validation-required edit plans; apply/check refuses graph freshness changes before validation or writes. Search-replace must reject duplicate matches unless `replaceAll` is true. `opcore check` implements `files`, `staged`, `changed`, `tree`, `all`, and `manifest`; tree checks read committed Git tree content from `--tree ` and scope files from `--changed-from `. `opcore validate` implements request-file validation, `hypothetical`, `pre-write`, and `manifest`. Runtime-owned `opcore status` and `opcore doctor` include typed validation status payloads but do not own validation checks. `opcore status` emits a stable `repoState` payload and must remain read-only: no graph build/update/watch, validation check execution, package install, ASP setup, ACE setup, current-tool wrapper execution, or source writes. `opcore` scan and `opcore check` must avoid source edits, hooks, setup, ASP setup, ACE setup, old-tool wrapper execution, sibling checkouts, and package installs; scan may write only `.opcore/report.json`, `.opcore/history.jsonl`, and bounded `.opcore/telemetry.jsonl` capped at 500 records or 1 MiB. `opcore install` is the recommended setup writer in the public product facade; it runs the same read-only scan without report/history writes, emits scan/settings/interaction/timing in `opcoreInit`, prompts in an interactive Git repo to choose repo or global write-gate scope when neither scope nor `--json`/`--yes` is supplied, keeps JSON and non-TTY no-flag runs plan-only, and approved mode is additive/idempotent. Repo scope appends `.opcore/` to `.gitignore` only in Git repos that do not already ignore it, skips `.gitignore` in non-Git repos, writes `.opcore/config`, agent guidance, Opcore agent skill files, `.opcore/hooks/opcore-agent-gate.mjs`, active `.git/hooks/pre-commit` when safe, and merges Claude Code `.claude/settings.json` plus Codex `.codex/hooks.json`; undo uses repo `.opcore/init-undo.json` through `opcore uninstall`. Global scope writes `~/.opcore/hooks/opcore-agent-gate.mjs`, user-level skill files, merges `~/.claude/settings.json` and `~/.codex/hooks.json`, and records undo in `~/.opcore/init-undo.json`. `opcore init` remains a compatibility setup route with explicit `--approve` semantics. The adapter maps harness write/edit payloads to validation overlays, calls `opcore validate pre-write --request-file --timeout-ms 30000 --json`, exits 2 for non-ok receipts and validation command failures, and fail-opens only for adapter parse/mapping errors. Claude Code PreToolUse blocks matching Edit/MultiEdit/Write calls through exit 2. Codex PreToolUse is wired as the strongest current guardrail for supported edit aliases, but Codex hook trust and interception scope remain harness behavior, so public copy must not claim broader enforcement. The separate fail-closed pre-commit hook script remains opt-in through `--fail-closed-hook`. The managed `.opcore/` ignore covers `.opcore/telemetry.jsonl`. No package may expose old-tool public bins, old `cix` aliases, or old-tool package identities as Opcore release surface. +Canonical `opcore graph`, `opcore edit`, `opcore check`, and `opcore validate` commands dispatch directly to public adapters owned by `packages/graph`, `packages/edit`, and `packages/validation`. Top-level `opcore inspect symbols|definition|references|signature|implementations|search` is CLI-owned read-only routing backed by public GraphProvider query/search APIs; `opcore inspect references --line [--column ]` adds #72 inspect-owned TypeScript/JavaScript language-service resolution for semantic reference evidence. `opcore inspect signature --line [--column ]` and `opcore inspect signature ` add #101 inspect-owned TypeScript/JavaScript language-service signature parity for functions, methods, constructors, classes, interfaces, type aliases, overloads, imported/aliased symbols, path aliases, TS/TSX, JS, and JSX after fresh GraphProvider evidence is available. `opcore inspect implementations --line [--column ]` and node-id targets add #102 read-only implementation evidence for TypeScript/TSX class implements, class extends, and interface extends relationships over fresh graph facts plus language-service materialization. Unimplemented graph inspect routes are not advertised as release behavior. `opcore edit` implements `exact`, `multi`, `search-replace`, `patch`, `tree`, `rename`, `move`, `signature`, `check`, and `apply` over edit-core, edit-owned patch/tree APIs, and graph-backed symbol planning. `opcore edit patch` accepts unified diffs and Codex `apply_patch` documents; unified `--3way` is explicitly de-scoped for this release and returns a typed refusal instead of attempting dirty-file merges. Symbol routes consume GraphProvider contract status/query/search evidence plus edit-owned TypeScript/JavaScript language-service materialization, then emit normal validation-required edit plans; apply/check refuses graph freshness changes before validation or writes. Search-replace must reject duplicate matches unless `replaceAll` is true. `opcore check` implements `files`, `staged`, `changed`, `tree`, `all`, and `manifest`; tree checks read committed Git tree content from `--tree ` and scope files from `--changed-from `. `opcore validate` implements request-file validation, `hypothetical`, `pre-write`, and `manifest`. Runtime-owned `opcore status` and `opcore doctor` include typed validation status payloads but do not own validation checks. `opcore status` emits a stable `repoState` payload and must remain read-only: no graph build/update/watch, validation check execution, package install, ASP setup, external development-tool execution, or source writes. `opcore` scan and `opcore check` must avoid source edits, hooks, setup, ASP setup, external development-tool execution, sibling checkouts, and package installs; scan may write only `.opcore/report.json`, `.opcore/history.jsonl`, and bounded `.opcore/telemetry.jsonl` capped at 500 records or 1 MiB. `opcore install` is the recommended setup writer in the public product facade; it runs the same read-only scan without report/history writes, emits scan/settings/interaction/timing in `opcoreInit`, prompts in an interactive Git repo to choose repo or global write-gate scope when neither scope nor `--json`/`--yes` is supplied, keeps JSON and non-TTY no-flag runs plan-only, and approved mode is additive/idempotent. Repo scope appends `.opcore/` to `.gitignore` only in Git repos that do not already ignore it, skips `.gitignore` in non-Git repos, writes `.opcore/config`, agent guidance, Opcore agent skill files, `.opcore/hooks/opcore-agent-gate.mjs`, active `.git/hooks/pre-commit` when safe, and merges Claude Code `.claude/settings.json` plus Codex `.codex/hooks.json`; undo uses repo `.opcore/init-undo.json` through `opcore uninstall`. Global scope writes `~/.opcore/hooks/opcore-agent-gate.mjs`, user-level skill files, merges `~/.claude/settings.json` and `~/.codex/hooks.json`, and records undo in `~/.opcore/init-undo.json`. `opcore init` remains a compatibility setup route with explicit `--approve` semantics. The adapter maps harness write/edit payloads to validation overlays, calls `opcore validate pre-write --request-file --timeout-ms 30000 --json`, exits 2 for non-ok receipts and validation command failures, and fail-opens only for adapter parse/mapping errors. Claude Code PreToolUse blocks matching Edit/MultiEdit/Write calls through exit 2. Codex PreToolUse is wired as the strongest current guardrail for supported edit aliases, but Codex hook trust and interception scope remain harness behavior, so public copy must not claim broader enforcement. The separate fail-closed pre-commit hook script remains opt-in through `--fail-closed-hook`. The managed `.opcore/` ignore covers `.opcore/telemetry.jsonl`. The public package exposes exactly the `opcore` and `opcore-asp-provider` bins. #58 adds `opcore validate pre-write --request-file --timeout-ms 30000 --json` for hook integration. The route is validation-owned, file-based, fail-closed, overlay-only, and emits a typed `PreWriteValidationReceipt` with timing, repo, scope, checks, graph, overlay, status, and failure summary data. @@ -62,11 +62,11 @@ Canonical `opcore graph`, `opcore edit`, `opcore check`, and `opcore validate` c #30 adds `ReleaseCutoverReceipt` and `npm run cutover:check` as the installed-artifact cutover proof. The gate packs the public release packages, installs them into a clean temporary project, clears current-tool environment resolution, excludes local wrapper/sibling paths, verifies installed canonical bins (`opcore` and `opcore-asp-provider`), binds every command receipt id to its expected canonical command/status/exit, and fails on old-tool/private-path markers or advertised `not_implemented` release commands. -#120 adds `AspDogfoodReceipt` and `npm run asp-dogfood:check` as advisory/shadow evidence that the independent ASP manager can install/enroll Opcore as a Core check provider and record host-owned decisions separately from provider assessments. The receipt uses temporary `ASP_HOME` state, records `opcore-asp-provider --stdio` manifest/bin evidence, co-records retained current-tool guardrails, and keeps `oldToolReplacementClaimed: false`; it is not a cutover, host authority claim, public standard-readiness claim, or `opcore asp` route. +#120 adds `AspDogfoodReceipt` and `npm run asp-dogfood:check` as advisory/shadow evidence that the independent ASP manager can install/enroll Opcore as a Core check provider and record host-owned decisions separately from provider assessments. The receipt uses temporary `ASP_HOME` state, records `opcore-asp-provider --stdio` manifest/bin evidence, co-records `opcore:self-check`; it is not a cutover, host authority claim, public standard-readiness claim, or `opcore asp` route. Top-level runtime lifecycle helpers are not public command groups. If shared lifecycle commands are adopted later, they need a new architecture decision and release acceptance criteria; graph daemon lifecycle remains graph-owned under `opcore graph`. -There is no public `opcore asp` router group in this release. ASP Core check integration is launched as the package-owned `opcore-asp-provider --stdio` provider process from `@the-open-engine/opcore-asp-provider`; it is not an ACE descriptor route and it must not execute current-tool wrappers or old `rox`, `crg`, or `cix` binaries. Issue #153 adds a hidden host-launched warm stdio route under the Opcore advanced entrypoint for inspect/edit/check sessions; it stays out of public help and manifests, keeps bounded singleton/idle state under `.opcore/asp/`, delegates check/evaluate to the cold provider mapping, never auto-spawns, and never mutates source files. +There is no public `opcore asp` router group in this release. ASP Core check integration is launched as the package-owned `opcore-asp-provider --stdio` provider process from `@the-open-engine/opcore-asp-provider`; it does not invoke external development toolchains. Issue #153 adds a hidden host-launched warm stdio route under the Opcore advanced entrypoint for inspect/edit/check sessions; it stays out of public help and manifests, keeps bounded singleton/idle state under `.opcore/asp/`, delegates check/evaluate to the cold provider mapping, never auto-spawns, and never mutates source files. ## Ownership Boundaries @@ -80,10 +80,9 @@ There is no public `opcore asp` router group in this release. ASP Core check int | `packages/validation-rust` | Rust validation adapter rules, temporary workspace materialization from validation file views, and Cargo/native-tool-backed checks exposed to validation through contracts. It does not own host decisions or current external guardrail replacement policy. | | `packages/validation-typescript` | TypeScript adapter rules, compiler-backed checks, and TypeScript graph-aware validations exposed to validation through contracts. | | `packages/validation-clone` | Duplicate-code validation adapter rules over `ValidationCheckContext.fileView`, with an injected native clone invoker supplied by CLI composition. It does not own graph-provider status, daemon lifecycle, host decisions, SAST/security policy, or scoring. | -| `packages/asp-provider` | Standalone ASP Core check provider facade, stdio JSON-RPC lifecycle, ASP changeset-to-validation-overlay mapping, provider-owned diagnostics/coverage, read-set freshness binding, and provisional manifest metadata. It does not own host decisions, authority, gates, apply behavior, ACE launch, or current-tool execution. | +| `packages/asp-provider` | Standalone ASP Core check provider facade, stdio JSON-RPC lifecycle, ASP changeset-to-validation-overlay mapping, provider-owned diagnostics/coverage, read-set freshness binding, and provisional manifest metadata. It does not own host decisions, authority, gates, apply behavior, or external development tooling. | | `packages/fixtures` | Golden repos, graph snapshots, reference evidence, canonical command conformance cases, and release/cutover fixtures. | -| Release descriptors and manifests | npm package metadata, ACE descriptor metadata, ASP provider install metadata, native platform package declarations, provenance, and checksums. | -| `scripts/setup-current-tools.sh` | Current-tool bootstrap only. It must keep wrappers pointed at external ACE-managed tools until release/cutover issues say Opcore packages are production-ready. | +| Release descriptors and manifests | npm package metadata, managed descriptor metadata, ASP provider install metadata, native platform package declarations, provenance, and checksums. | No package may import implementation internals across graph, edit, or policy tracks. Shared shapes move to `packages/contracts` before use across boundaries. @@ -91,7 +90,7 @@ No package may import implementation internals across graph, edit, or policy tra #21 adds the Cargo workspace, `crates/graph-core`, JS/npm wrapper package work, platform artifacts, checksums, Rust validation adapter coverage, and Rust CI gates. -The Rust graph-core sidecar is packaged through optional Opcore native packages, not through local graph-package native output. The supported Opcore alpha targets are exactly `darwin-arm64`, `darwin-x64`, and `linux-x64`, provided by `@the-open-engine/opcore-graph-core-darwin-arm64`, `@the-open-engine/opcore-graph-core-darwin-x64`, and `@the-open-engine/opcore-graph-core-linux-x64`. The graph package facade resolves only the matching optional package metadata, validates `metadata.json` and `opcore-graph-core.sha256`, invokes the sidecar with schema-versioned JSON/NDJSON `GraphDaemonRequest` envelopes, and maps native/process/protocol failures to typed GraphProvider statuses instead of empty graph data. It must not fall back to workspace-local builds, sibling checkouts, `.ace/runtime`, PATH tools, or PATH-discovered tools; unsupported platforms, including Windows, fail with a clear unsupported-platform status. +The Rust graph-core sidecar is packaged through optional Opcore native packages, not through local graph-package native output. The supported Opcore alpha targets are exactly `darwin-arm64`, `darwin-x64`, and `linux-x64`, provided by `@the-open-engine/opcore-graph-core-darwin-arm64`, `@the-open-engine/opcore-graph-core-darwin-x64`, and `@the-open-engine/opcore-graph-core-linux-x64`. The graph package facade resolves only the matching optional package metadata, validates `metadata.json` and `opcore-graph-core.sha256`, invokes the sidecar with schema-versioned JSON/NDJSON `GraphDaemonRequest` envelopes, and maps native/process/protocol failures to typed GraphProvider statuses instead of empty graph data. It must not fall back to workspace-local builds, sibling checkouts, or PATH-discovered tools; unsupported platforms, including Windows, fail with a clear unsupported-platform status. Release artifacts must include: @@ -99,7 +98,7 @@ Release artifacts must include: - platform-specific native graph-core package artifacts for `darwin-arm64`, `darwin-x64`, and `linux-x64`. - checksum/provenance data for each native artifact. - CI gates for Node, TypeScript contracts, Rust build/test, native wrapper loading, fixture conformance, and a release-dry-run aggregate job that downloads all supported native package artifacts before release or cutover receipts claim cross-platform readiness. -- ACE/Zeroshot consumption through generated wrappers and descriptors, not direct source-tree paths. +- Zeroshot consumption through clean npm setup and repository-owned command proofs. - ASP provider provisional manifest metadata as install metadata only; it must not grant authority, trust, or gate permission. #8 adds Wave 1 staged source extraction in Rust graph-core for TS, TSX, JS, and JSX using OXC parser crates. Query responses may now return GraphProvider-compatible File, Class, Function, Type, and Test facts with typed extraction diagnostics. #9 adds the GraphProvider SQLite/WAL store at `.opcore/graph/graph.db`, freshness metadata, deterministic full-snapshot replacement, store-backed nodes/edges/neighbors/symbols selectors, and the #19 direct-reader reference evidence projection. @@ -118,7 +117,7 @@ Before replacing current external tool behavior, add golden/reference fixtures a ## Rust Validation Adapter -#20 adds `@the-open-engine/opcore-validation-rust` as a validation adapter package composed by the CLI beside TypeScript checks. Stable check ids are `rust.source-hygiene`, `rust.fmt`, `rust.cargo-check`, `rust.clippy`, `rust.rustdoc`, `rust.import-graph`, `rust.dead-code`, `rust.graph-signals`, `rust.unused-deps`, and `rust.function-metrics`. These are provider assessment checks; ASP hosts decide allow, deny, or degraded coverage later. `rust.graph-signals` is graph-provider-backed evidence over public contracts only; it does not replace the retained mechanical cargo/rustdoc/clippy/Rox guardrails. +#20 adds `@the-open-engine/opcore-validation-rust` as a validation adapter package composed by the CLI beside TypeScript checks. Stable check ids are `rust.source-hygiene`, `rust.fmt`, `rust.cargo-check`, `rust.clippy`, `rust.rustdoc`, `rust.import-graph`, `rust.dead-code`, `rust.graph-signals`, `rust.unused-deps`, and `rust.function-metrics`. These are provider assessment checks; ASP hosts decide allow, deny, or degraded coverage later. `rust.graph-signals` is graph-provider-backed evidence over public contracts only; mechanical evidence remains owned by Cargo and configured native tools. Rust checks read candidate files through `ValidationCheckContext.fileView`. Checks that need Cargo or native tools materialize one temporary workspace per validation file-view state from after-state content, apply write/delete overlays there, share that immutable workspace across selected checks with the same materialization environment, and dispose it when the state exits. Distinct environments remain distinct resources because Git index/tree environment can change staged or tree snapshots. The real worktree is never mutated. `.rs`, `.inc`, and `Cargo.toml` are adapter-owned inputs. Cargo.lock-only changes remain retained compatibility until a later decision expands Rust adapter ownership. diff --git a/docs/integration/pre-write-validation.md b/docs/integration/pre-write-validation.md index 4967f81..9ee7f7b 100644 --- a/docs/integration/pre-write-validation.md +++ b/docs/integration/pre-write-validation.md @@ -1,6 +1,6 @@ # Pre-Write Validation Integration -Issue #58 defines the hook-safe validation route for ACE and Codex cutover: +Issue #58 defines the hook-safe validation route for coding-agent integrations: ```bash opcore validate pre-write --request-file --timeout-ms 30000 --json diff --git a/docs/planning/opcore-alpha-roadmap.md b/docs/planning/opcore-alpha-roadmap.md index f689762..8308c77 100644 --- a/docs/planning/opcore-alpha-roadmap.md +++ b/docs/planning/opcore-alpha-roadmap.md @@ -55,7 +55,7 @@ Opcore alpha must provide value quickly and honestly: - Reports must state coverage before findings: deep TypeScript/JavaScript graph support, Rust validation/toolchain support, experimental Python validation (degraded-honest), and unsupported-language counts. - Init JSON includes scan, per-language onboarding settings, interaction state, and timing fields for time-to-first-output checks. - Metrics are named, drillable counts and deltas, not a blended quality score. -- Current Rox/CRG/CIX guardrails remain retained until explicit replacement evidence says otherwise. +- Opcore validates its own changes through repository-owned checks and `npm run opcore:self-check`. ## Honest Day-One Signals @@ -71,7 +71,8 @@ Ship only signals the engine can defend: - Python `.py`/`.pyi` graph-backed structure, untested modules, dead exports, syntax, source-hygiene, import graph, relevant-test signals, and configured-authority `python.types`; exactly one mypy or Pyright authority runs per project, while absent/conflicting/unavailable authority remains degraded. Opt-in `python.ruff-lint`/`python.ruff-format` add receipt-backed findings and activation-aware degraded status. Python readiness and parity remain gated on later release evidence. - Unsupported language census with no fake findings. -Do not ship headline claims for generic complexity, TS complexity, Python code analysis, Go/Java analysis, security, cross-repo percentiles, automatic fixes, or old-tool replacement. +Do not ship headline claims for generic complexity, TS complexity, Python code analysis, Go/Java analysis, security, +cross-repo percentiles, or automatic fixes. ## Release Gate @@ -80,18 +81,19 @@ Do not call the alpha ready until current evidence proves: - `npm run build` succeeds from a clean checkout. - `npm run release:hygiene` passes with launch-facing docs branded as Opcore. - `npm run provenance:check` passes and finds no forbidden public-surface claims. -- `npm run cutover:check` proves installed `opcore` scan/status/check/measure flows and keeps `oldToolReplacementClaimed: false`. +- `npm run cutover:check` proves installed `opcore` scan/status/check/measure flows and repository self-validation. - `opcore try --json` returns `opcoreTry.published:false`. - `opcore --repo . --json` emits the scan artifact allowlist: `.opcore/report.json`; `.opcore/history.jsonl`; bounded `.opcore/telemetry.jsonl` capped at 500 records or 1 MiB. -- `opcore status --repo . --json` does not build graphs, run checks, run setup, install packages, use ACE/current-tool wrappers, or write files. +- `opcore status --repo . --json` does not build graphs, run checks, run setup, install packages, or write files. - `opcore init --repo . --json` previews setup without writing. - TTY `opcore init --repo .` prompts after the scan and setup plan; declining writes nothing. - `opcore init --repo . --approve --json` applies additive setup without prompting and still avoids scan artifact writes. - `opcore check --changed --json` has stable agent exit codes. -- Public docs and package output contain no ASP-standard, old-tool replacement, security/SAST, all-stack, AI-authorship, automatic-fix, or blended-score overclaims. +- Public docs and package output contain no ASP-standard, generic replacement, security/SAST, all-stack, + AI-authorship, automatic-fix, or blended-score overclaims. ## Public Wording Rules @@ -105,7 +107,7 @@ Avoid: - "ASP is the standard." - "Opcore proves the standard." -- "Opcore replaces Rox/CRG/CIX." +- "Opcore replaces your existing engineering policy." - "Works with every stack." - "Python code analysis" as a headline. - "Detects AI authorship." @@ -116,8 +118,7 @@ Avoid: ## Non-Goals - Public ASP standard launch. -- Old-tool retirement. -- ACE-managed distribution. +- External development-tool integration. - Source-editing or automatic-fix product claims. - A SaaS dashboard. - Windows support in `0.2.0`. diff --git a/docs/release/artifact-attestation.md b/docs/release/artifact-attestation.md index 54b479d..c2649ae 100644 --- a/docs/release/artifact-attestation.md +++ b/docs/release/artifact-attestation.md @@ -21,9 +21,8 @@ No package publishing happens in this gate. ## Cutover Gate Issue #30 receipt: docs/release/cutover-receipt.json -Cutover receipt SHA-256: c72be0d79199505770c631b08201be09f8602e239e80c1ffa974e7f24d1cf32b +Cutover receipt SHA-256: 1bdb6d984ad52243be972abb5b061631f4eca65e7d5c5a9eac525569898ef97f Installed command receipts: 28 Rust command receipts: 7 Python command receipts: 8 -Current-tool guardrails retained: 2 -Old-tool replacement claimed: false +Self-validation: passed diff --git a/docs/release/asp-dogfood-receipt.json b/docs/release/asp-dogfood-receipt.json index a2d2d6d..d9543ae 100644 --- a/docs/release/asp-dogfood-receipt.json +++ b/docs/release/asp-dogfood-receipt.json @@ -4261,8 +4261,7 @@ "temp": true, "isolated": true, "sharedStateMutated": false, - "pathSanitized": true, - "aceRuntimeBinExcluded": true + "pathSanitized": true }, "hostFixture": { "repo": "/private/var/folders/cd/q981l5pn1l3gylf5_gddbkbw0000gn/T/opcore-asp-dogfood-nKYzZH/asp-host-fixture", @@ -6889,143 +6888,21 @@ "diagnosticsCount": 1, "hostOwnedFieldLeak": false }, - "currentToolGuardrails": [ - { - "id": "current-tools-validate-changed", - "command": [ - "npm", - "run", - "current-tools:validate-changed" - ], - "status": "passed", - "exitCode": 0, - "stdoutSha256": "c841ab97cd599893cd51807789d41d37c67133d4abd1315aba222fca4c3beae8", - "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "assertion": "current-tools:validate-changed remains active", - "retained": true - }, - { - "id": "current-tools-validate-rust-graph", - "command": [ - "npm", - "run", - "current-tools:validate-rust-graph" - ], - "status": "passed", - "exitCode": 0, - "stdoutSha256": "37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570", - "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "output": [], - "assertion": "current-tools:validate-rust-graph remains active", - "retained": true - }, - { - "id": "current-tools-validate-all", - "command": [ - "npm", - "run", - "current-tools:validate-all" - ], - "status": "retained-not-run", - "exitCode": null, - "stdoutSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "retained": true, - "assertion": "Retained old-tool guardrail; omitted unless --include-current-tools-all is passed" - } - ], "unsupportedSurfaces": [ { "surface": "inspect", "status": "parity-blocker", "cleanCoverage": false, - "blocker": "ASP dogfood covers Core check/evaluate only; inspect request/response mapping remains outside #120." + "blocker": "ASP dogfood covers Core check/evaluate only; inspect mapping remains outside this receipt." }, { "surface": "edit", - "status": "retained-old-tool-gate", + "status": "parity-blocker", "cleanCoverage": false, - "blocker": "ASP dogfood does not authorize edits or apply behavior; edit parity remains covered by current old-tool and cutover gates." - } - ], - "parityBlockers": [ - { - "source": "docs/validation/rust-adapter-parity.md:26", - "detail": "command receipts, but it does not include maintainer approval to retire Rust retained-tool rows." - }, - { - "source": "docs/validation/rust-adapter-parity.md:28", - "detail": "| Surface | Decision | Evidence | Why retirement is not accepted | Current guardrail action |" - }, - { - "source": "docs/validation/rust-adapter-parity.md:29", - "detail": "|---|---|---|---|---|" - }, - { - "source": "docs/validation/rust-adapter-parity.md:30", - "detail": "| `rust.rustdoc` | retained | #29 receipt records no graph replacement evidence. | Rustdoc diagnostics, broken intra-doc links, and documentation-policy failures remain unique current-tool evidence. No installed-artifact receipt plus maintainer approval proves exact replacement. | Keep current external Rust guardrails active for rustdoc coverage. |" - }, - { - "source": "docs/validation/rust-adapter-parity.md:31", - "detail": "| `rust.import-graph` | deferred | #29 records Rust graph `IMPORTS_FROM`/`DEPENDS_ON` facts; #30 records installed Rust graph build/query/impact/review-context/detect-changes/search receipts. | Graph facts are useful parity evidence, but rustdoc and cargo-depgraph-enriched import checks remain retained where graph facts are not sufficient. No maintainer approval flips this row. | Keep current external import-graph guardrails active while native graph evidence complements them. |" - }, - { - "source": "docs/validation/rust-adapter-parity.md:32", - "detail": "| `rust.dead-code` | retained | #29 records exported symbol metadata and graph-backed dead-public-export signals. | Cargo `dead_code` diagnostics and compiler reachability remain uniquely provided by current tools. Graph dead-public-export evidence is not exact replacement evidence. | Keep current external dead-code guardrails active. |" - }, - { - "source": "docs/validation/rust-adapter-parity.md:33", - "detail": "| `rust.unused-deps` | retained | #29 records no graph replacement evidence. | Cargo-udeps unused dependency analysis remains the unique evidence source. | Keep current external unused-dependency guardrails active. |" - }, - { - "source": "docs/validation/rust-adapter-parity.md:34", - "detail": "| `rust.function-metrics` | retained | #29 records Rust function/method spans and signatures; #30 records installed Rust graph receipts. | Rust-code-analysis complexity, line-count, and parameter-threshold metrics remain unique current-tool evidence. Spans/signatures are not exact metric replacement evidence. | Keep current external function-metric guardrails active. |" - }, - { - "source": "docs/validation/rust-adapter-parity.md:35", - "detail": "| `current-tools:validate-rust-graph` | retained | #29 records the aggregate Rust graph guardrail as retained; #30 Rust receipts are graph-owned installed command receipts. | No receipt proves an exact aggregate replacement for the current-tools Rust graph gate, and no maintainer approval retires it. | Continue running `npm run current-tools:validate-rust-graph`. |" - }, - { - "source": "docs/validation/rust-adapter-parity.md:36", - "detail": "| Rust portion of `current-tools:validate-changed` | retained | #30 installed `opcore check changed` receipt uses `--checks typescript.syntax`; #29 carries only Rust comparison evidence with `oldToolReplacementClaimed: false`. | The installed changed-check receipt does not exercise Rust retained-tool coverage, and no Tom approval flips Rust changed-file guardrails. | Continue running `npm run current-tools:validate-changed` for changed Rust-owned inputs and mixed changes. |" - }, - { - "source": "docs/validation/rust-adapter-parity.md:38", - "detail": "## Native Rust Checks" - }, - { - "source": "docs/validation/rust-adapter-parity.md:40", - "detail": "`@the-open-engine/opcore-validation-rust` exports these provider assessment checks:" - }, - { - "source": "docs/validation/rust-adapter-parity.md:42", - "detail": "| Check | Native behavior | Retained compatibility |" - }, - { - "source": "docs/validation/rust-retained-tools-receipts-2026-06-23.md:59", - "detail": "## Guardrails" - }, - { - "source": "docs/validation/rust-retained-tools-receipts-2026-06-23.md:61", - "detail": "- No validation daemon." - }, - { - "source": "docs/validation/rust-retained-tools-receipts-2026-06-23.md:62", - "detail": "- No hidden validation cache." - }, - { - "source": "docs/validation/rust-retained-tools-receipts-2026-06-23.md:63", - "detail": "- No Rox imports, Rox cache reads, or Rox shellouts from native Opcore checks." - }, - { - "source": "docs/validation/rust-retained-tools-receipts-2026-06-23.md:64", - "detail": "- Current external Rox gates stay active until downstream #27/#28/#29 accept replacement evidence." - }, - { - "source": "docs/validation/rust-retained-tools-receipts-2026-06-23.md:65", - "detail": "- Results are provider assessments only, not ASP host decisions or release authority." + "blocker": "ASP dogfood does not authorize edits or apply behavior; installed Opcore evidence covers edit behavior." } ], + "parityBlockers": [], "authority": { "hostOwnsDecisions": true, "providerOutputIsHostDecision": false, @@ -7035,15 +6912,25 @@ } }, "publicReleaseActions": [], - "oldToolReplacementClaimed": false, "forbiddenMarkerScan": { "scannedTextCount": 2, "findingCount": 0, "markersBlocked": [ "opcore asp serve", - "opcore asp", - "dist/bin/lattice", - ".ace/runtime" + "opcore asp" ] + }, + "selfValidation": { + "id": "opcore-self-check", + "command": [ + "npm", + "run", + "opcore:self-check" + ], + "status": "passed", + "exitCode": 0, + "stdoutSha256": "98006e4c11c39bfe79e15f629ed3e2f063b9c16f0c7ad5708d7d6e855614c01d", + "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "assertion": "Opcore validated its own changed implementation surface" } } diff --git a/docs/release/asp-dogfood-receipt.summary.md b/docs/release/asp-dogfood-receipt.summary.md index 53ab95b..d57b46e 100644 --- a/docs/release/asp-dogfood-receipt.summary.md +++ b/docs/release/asp-dogfood-receipt.summary.md @@ -1,26 +1,7 @@ # ASP Dogfood Receipt Summary -Issue #120 receipt for advisory standalone ASP manager dogfood. +Issue #120 records advisory standalone ASP manager dogfood over the installed `opcore-asp-provider --stdio` process. -Machine receipt: docs/release/asp-dogfood-receipt.json -Machine receipt SHA-256: cf3808f44372b2ba4a3180717ea50f107a1c20c7d32772818ea25e524b68d514 -Bootstrap source: local-sibling -Repo enrollment mode: advisory -Host fixture repo: temporary -Host fixture changed paths: src/dogfood.ts -Source repo mutated: false -Provider command: opcore-asp-provider --stdio -Host assurance: gated -Transaction guarantee: none -Old-tool replacement claimed: false - -| Guardrail | Status | Exit | Evidence | -|-----------|--------|------|----------| -| current-tools-validate-changed | passed | 0 | current-tools:validate-changed remains active | -| current-tools-validate-rust-graph | passed | 0 | current-tools:validate-rust-graph remains active | -| current-tools-validate-all | retained-not-run | not-run | Retained old-tool guardrail; omitted unless --include-current-tools-all is passed | - -## Deferred Coverage - -- inspect: parity-blocker; ASP dogfood covers Core check/evaluate only; inspect request/response mapping remains outside #120. -- edit: retained-old-tool-gate; ASP dogfood does not authorize edits or apply behavior; edit parity remains covered by current old-tool and cutover gates. +The receipt proves temporary isolated host state, installed provider identity, host-owned decisions, provider +assessment provenance, and `opcore:self-check`. Inspect and edit remain explicit parity blockers for this check-only +dogfood receipt. diff --git a/docs/release/crg-graph-parity-ledger.md b/docs/release/crg-graph-parity-ledger.md deleted file mode 100644 index 984aeb9..0000000 --- a/docs/release/crg-graph-parity-ledger.md +++ /dev/null @@ -1,71 +0,0 @@ -# CRG Graph Parity Ledger - -Issue: #53 - -Status: CRG graph parity is demonstrated for `opcore graph`; replacement remains deferred. - -## Scope - -This ledger enumerates CRG-to-`opcore graph` parity from retained receipts only. It is release evidence, not a public retirement claim, npm publish action, ASP authority claim, or ACE wrapper cutover. - -Receipt sources: - -- `docs/release/graph-release-receipt.json` issue `#17`, regenerated with in-repo fixture `packages/fixtures/source-extraction/wave1`. -- `docs/release/cutover-receipt.json` issue `#30`, generated `2026-06-27T10:22:13.799Z`, installed `node_modules/.bin/opcore` proof with `environmentIsolation.opcoreBinOnly: true` meaning only Opcore-owned bins are exposed and `environmentIsolation.oldBinsAbsent.{crg,cix,rox}: true`. -- `docs/release/asp-dogfood-receipt.json` issue `#120`, generated `2026-06-26T19:04:19.583Z`, with `oldToolReplacementClaimed: false` and retained current-tool guardrails. - -## Issue Namespace - -The graph fixtures and receipts use covibes-tree issue numbers for older graph-release work. In that namespace, `#13`, `#14`, `#15`, and `#16` are graph optional-surface children of graph parent `#4` and graph-release gate `#17`: coverage, flows, communities, and read-only suggestions. - -Those numbers collide with the Opcore GitHub issue namespace. This document is Opcore issue `#53`, under Opcore epic `#13`; do not map covibes-tree `#13`-`#16` to Opcore-tree epics. - -## Implemented Surface Ledger - -| Surface | Status | Receipt evidence | -|---|---|---| -| `opcore graph build` | implemented | `graph-release-receipt.json` `commandCoverage.id=opcore-graph-build` on `wave1`; `cutover-receipt.json` `commandReceipts.id=graph-build`, status `ok`, exit 0 from `node_modules/.bin/opcore`, assertion `graph build completed with native artifact`. | -| `opcore graph update` | implemented | `graph-release-receipt.json` `commandCoverage.id=opcore-graph-update`, passed exit 0 in 664ms on `wave1`. The cutover receipt does not duplicate update. | -| `opcore graph watch` | implemented | `graph-release-receipt.json` `commandCoverage.id=opcore-graph-watch`, passed exit 0 in 609ms on `wave1`. The cutover receipt does not duplicate watch. | -| `opcore graph status` | implemented | `graph-release-receipt.json` `commandCoverage.id=opcore-graph-status`, passed exit 0 in 619ms on `wave1`; `cutover-receipt.json` `commandReceipts.id=graph-status`, status `ok`, exit 0, assertion `graph status available after build`. | -| `opcore graph query` | implemented | `graph-release-receipt.json` `commandCoverage.id=opcore-graph-query`, passed exit 0 in 603ms on `wave1`; `cutover-receipt.json` `commandReceipts.id=graph-query`, status `ok`, exit 0, assertion `graph query returned facts`. | -| `opcore graph impact` | implemented | `graph-release-receipt.json` `commandCoverage.id=opcore-graph-impact`, passed exit 0 in 617ms on `wave1`; `cutover-receipt.json` `commandReceipts.id=graph-impact`, status `ok`, exit 0, command includes `--files src/components/GreetingCard.tsx`, assertion `graph impact returned file impact`. | -| `opcore graph review-context` | implemented | `cutover-receipt.json` `commandReceipts.id=graph-review-context`, status `ok`, exit 0 from `node_modules/.bin/opcore`, command includes `--files src/components/GreetingCard.tsx`, assertion `graph review-context returned related facts`. The graph-release receipt does not include review-context. | -| `opcore graph detect-changes` | implemented | `cutover-receipt.json` `commandReceipts.id=graph-detect-changes`, status `ok`, exit 0 from `node_modules/.bin/opcore`, command includes `--files src/components/GreetingCard.tsx`, assertion `graph detect-changes returned typed change data`. The graph-release receipt does not include detect-changes. | -| `opcore graph search` | implemented | `graph-release-receipt.json` `commandCoverage.id=opcore-graph-search`, passed exit 0 in 644ms on `wave1`; `cutover-receipt.json` `commandReceipts.id=graph-search`, status `ok`, exit 0, command `graph search Greeting --limit 5`, assertion `graph search returned ranked results`. | -| `opcore graph serve` | implemented | `graph-release-receipt.json` `commandCoverage.id=opcore-graph-serve`, passed exit 0 in 555ms on `wave1`; `graph-release-receipt.json` `serveTransport` passed ping/status/query/search/shutdown over `opcore.graph.daemon`; `cutover-receipt.json` `commandReceipts.id=graph-serve`, status `ok`, exit 0, assertion `graph serve status route is ready`. | - -Combined receipts cover all 10 required surfaces at least once. `graph-release-receipt.json` covers build, update, watch, status, query, impact, search, and serve. `cutover-receipt.json` covers build, status, query, impact, review-context, detect-changes, search, and serve from installed artifacts with old bins absent. - -## Deferred Optional Analyses - -Optional graph analyses remain non-blocking and deferred in `packages/fixtures/graph-reference-evidence/manifest.json` and `docs/release/graph-release-receipt.json`: - -| Covibes-tree issue | Surface | Classification | Status | -|---|---|---|---| -| `#13` | coverage | deferred | deferred | -| `#14` | flows | optional | deferred | -| `#15` | communities | optional | deferred | -| `#16` | read_only_suggestions | supporting | deferred | - -These rows are not required for CRG graph parity and must not be read as Opcore epic dependencies. - -## Fact Model Evidence - -`packages/fixtures/graph-reference-evidence/golden-corpus.json` exercises the TypeScript graph corpus with 7 nodes and 6 edges: node kinds `File`, `Function`, and `Test`; edge kinds `CALLS`, `CONTAINS`, and `TESTED_BY`. `IMPORTS_FROM` is declared by the SQLite fixture but not exercised by the golden corpus. The current SQLite fixture also declares Rust-ready graph kinds and edges, but does not declare `Class` or `Type` as node kinds. - -Export metadata is represented by the SQLite `nodes.is_exported` column and `idx_nodes_exported_name` index in `packages/fixtures/graph-reference-evidence/sqlite-fixtures.json`. The reference fixtures do not require `attributes.exported` as fixture evidence. - -The graph-release receipt records direct SQLite reader evidence for status counts, edge counts, impact edges from file, search-by-name, and freshness metadata against `packages/fixtures/source-extraction/wave1/.opcore/graph/graph.db`. - -## Replacement Claim - -CRG graph parity is installed-receipt-backed: `cutover-receipt.json` runs `opcore graph` through `node_modules/.bin/opcore` while `environmentIsolation.opcoreBinOnly` confirms only Opcore-owned bins are exposed and `environmentIsolation.oldBinsAbsent.{crg,cix,rox}` is `true`. - -The formal old-tool replacement claim remains withheld. `asp-dogfood-receipt.json` pins `oldToolReplacementClaimed: false`, records retained `current-tools:validate-changed` and `current-tools:validate-rust-graph` guardrails, and keeps inspect/edit gaps outside ASP dogfood authority. ACE wrappers remain on current tools until explicit downstream cutover work changes that. - -Ledger state: CRG graph is `parity-demonstrated`; replacement claim is `replacement-claim-deferred`. - -## Cross Dependencies - -Rust graph parity rows are tracked by Opcore epic `#10` children: `#26` build/update/watch/status, `#27` query/search/impact/review-context/detect-changes, `#28` graph-backed validation, `#29` old-Rox comparison receipts, and `#30` retained-Rox retirement. This ledger references those rows and does not duplicate their acceptance evidence. diff --git a/docs/release/cutover-receipt.json b/docs/release/cutover-receipt.json index 06eea20..d8048d5 100644 --- a/docs/release/cutover-receipt.json +++ b/docs/release/cutover-receipt.json @@ -2,8 +2,8 @@ "schemaVersion": 1, "issue": "#30", "origin": "covibes-authored-cutover-proof", - "generatedAt": "2026-07-18T06:36:28.550Z", - "commitSha": "2d4e920e0b08a989953486b261327ed7496c328b", + "generatedAt": "2026-08-07T17:45:44.810Z", + "commitSha": "6ed9e0e5b48acdc298af1a2e1af1a59aae32ac78", "privateRepo": true, "packageNames": [ "opcore" @@ -14,7 +14,7 @@ "version": "0.2.1", "tarball": { "filename": "opcore-0.2.1.tgz", - "sha256": "f3c4478eca2a9a1118b10bdabdfa009dfed0bdd8903312f06c42a8b60515b128" + "sha256": "4c8962af2364d101587162ca56b4797eab5bea8a87738b8721a2734fa26cb483" }, "installedManifest": { "path": "node_modules/opcore/package.json", @@ -95,7 +95,7 @@ }, { "path": "node_modules/opcore/dist/advanced/asp-warm/warm-project-registry.js", - "sha256": "9a77612096d3e22a7ac8bebd52224bf5924409998bc0a264c9da8df02684bbee" + "sha256": "9cb81d931a7bd2052b7f99c22bec2fbfa988a8c29793dd75ac6094894040bc3f" }, { "path": "node_modules/opcore/dist/advanced/descriptor.d.ts", @@ -163,11 +163,11 @@ }, { "path": "node_modules/opcore/dist/advanced/inspect-language-service.d.ts.map", - "sha256": "9474ad4e5880a163ab7b6bbdffe51a0905b417e3575debc47199b20231ec5fe5" + "sha256": "4063a317bf3c82e1af2b74178de7d2129bf2f181c78b7f4542a6129163672684" }, { "path": "node_modules/opcore/dist/advanced/inspect-language-service.js", - "sha256": "8fe5e18238e0f0dfc005953dca723dc88cda350bb0d4cfb4b1283644ec5788f2" + "sha256": "5bb5d0d5db70859d0d236448045ea32cbc3f5eb719c7f256b48cd9010e0f42ba" }, { "path": "node_modules/opcore/dist/advanced/manifest.d.ts", @@ -195,15 +195,15 @@ }, { "path": "node_modules/opcore/dist/advanced/validation-composition.d.ts", - "sha256": "b7c28a6e21cbd3be1460bfed8f7df125eca47bbc4c1b4ddbefc311ab108ac370" + "sha256": "19b789ea43d428cf956da86979f61fbd34eca814122f698e21df8c3a5b807d0d" }, { "path": "node_modules/opcore/dist/advanced/validation-composition.d.ts.map", - "sha256": "ad06345d267c837f10334addb62d30389670129b49c9dcfbdd07b1ecb6398f87" + "sha256": "58c641c54f25fa5851c858466edea4bf4453e3307176a4d3d01e36ba07891572" }, { "path": "node_modules/opcore/dist/advanced/validation-composition.js", - "sha256": "c054f94d89a88cb0bb4ae8ef0440f766cb1f17c81a72d03ae156e4a7c907696b" + "sha256": "c32698ecac5863bbe7350dce480da7b7304b445fd8e815b208fe0ed064db2e7a" }, { "path": "node_modules/opcore/dist/agent-gate.d.ts", @@ -263,11 +263,11 @@ }, { "path": "node_modules/opcore/dist/doctor.d.ts.map", - "sha256": "110d344818ad031b1de8ccd69380671881cf80ba310df8b9f2ae85110eabaf8c" + "sha256": "e6d68cd0338091a9a8fb5c049265b2517efa618bbd8e90d10e5240514b9552c8" }, { "path": "node_modules/opcore/dist/doctor.js", - "sha256": "a5f8c2f24d4ac799a96b73bb0c43d52d1f287d969ee0081f25682b7b5a23c825" + "sha256": "8b3803eb6457f005e480ea5d7d4cd4dcf4a97c742ad1baa8714c570f8964a2ed" }, { "path": "node_modules/opcore/dist/graph-provider-client.d.ts", @@ -303,7 +303,7 @@ }, { "path": "node_modules/opcore/dist/init.js", - "sha256": "b0d1a394fd7f0cfd142a9cf5f0245becc6fb3baa687e6dbacc83046ff05dc7e7" + "sha256": "3274aa8778ab425d5fb84421bc9746d79a6ba393a0a3194446db7b124a0b93e0" }, { "path": "node_modules/opcore/dist/install-wizard-screen.d.ts", @@ -339,7 +339,7 @@ }, { "path": "node_modules/opcore/dist/json-output.js", - "sha256": "87f9c35eaafe9c3f917f18c3fb083acf5ac3a7a2cef42b78e8904bd4cfef9852" + "sha256": "003a14cede6abaa521ad271ca7c78c526778cf09c4975ce1f3f6cb15369ec5d9" }, { "path": "node_modules/opcore/dist/path-policy.d.ts", @@ -419,11 +419,11 @@ }, { "path": "node_modules/opcore/dist/reporting.d.ts.map", - "sha256": "f9ff1b0f1cab4481adaf18822d3fe23c949d00fc209f65e333da1967a381e227" + "sha256": "450ee9b50c4201bc6183152f916b3b2464961f11f5075c2936d9d149c813d4f3" }, { "path": "node_modules/opcore/dist/reporting.js", - "sha256": "2d879ead468ed0c95cd6d7c7a62d8fe0e11290af300c137d5ff3459659abaec6" + "sha256": "7f624cb51afeb9b2e25627ccbe7be898167e42502dacba5b2ee893cc3b025157" }, { "path": "node_modules/opcore/dist/router.d.ts", @@ -499,15 +499,15 @@ }, { "path": "node_modules/opcore/dist/source-policy.d.ts", - "sha256": "2a2c097a2df95010ad184898a1dc78b52821e155943573d4aabf0e5f43e81ed2" + "sha256": "8c3d16c4d6a851a964be5e40186449f6d6a11a76bb71503cce3e7e75f63a7694" }, { "path": "node_modules/opcore/dist/source-policy.d.ts.map", - "sha256": "3aa5b007c23983b575d02871ec4fef71bc87c57ef0e30c1ce44d07593f4223ac" + "sha256": "1089259f39d3c5e2d6404a4bfc5ce51a35136acefc1b81a48069d82ecbb9ce8d" }, { "path": "node_modules/opcore/dist/source-policy.js", - "sha256": "f66c2416b961cdc546a6813f521fe98876f195b21efb9053a8206962a7c4873e" + "sha256": "9fc5220cb1660071c2c199eeb336a98e7a3e6cdc85816eb807dbfda9253cc2df" }, { "path": "node_modules/opcore/dist/status-args.d.ts", @@ -599,11 +599,11 @@ }, { "path": "node_modules/opcore/dist/status-state.d.ts.map", - "sha256": "f82dec0779e45cc9b95d605dfbaf3ca353dcf0f5a663dfb9a3127b386f6eaaf0" + "sha256": "1ac635de74d0be13bf50c300312ad6baa45aee73feffd88d7f2f75cdd7729bdb" }, { "path": "node_modules/opcore/dist/status-state.js", - "sha256": "b4532b946efb07e53a00da79a0747d2db7907141b12b2a485a5faa47b82c08c2" + "sha256": "d797acfb868c457c702e65372c135616d30c9fdf161b28a9c766ca2d3f9fd744" }, { "path": "node_modules/opcore/dist/status-validation.d.ts", @@ -611,11 +611,11 @@ }, { "path": "node_modules/opcore/dist/status-validation.d.ts.map", - "sha256": "da0652f715ccd0963f66ac5e3fcb097ad014c6583c638b76f9e2c15dc0e32fd8" + "sha256": "4be041522e1b64e0e269811392232788cc24918b86adb7a9441f82551ec1e213" }, { "path": "node_modules/opcore/dist/status-validation.js", - "sha256": "c067d6433a5e1ea96df0ffcbfdb4f1aa82264505c8aed395239cabe63e4ec5ac" + "sha256": "e90b2bbe6bdf1855c55ebb5f73a0b4e61912edb89c091a910dba7c574679df47" }, { "path": "node_modules/opcore/dist/status.d.ts", @@ -667,15 +667,15 @@ }, { "path": "node_modules/opcore/dist/validation-composition.d.ts", - "sha256": "64becc3c5f557aba3672e006b6e060dac62f6f88fe2371ac62fc2947cf1cd14e" + "sha256": "5989aa61c266566b7ef4957e47f52419fdbc49c9c17f0a97c325b17ce1446f92" }, { "path": "node_modules/opcore/dist/validation-composition.d.ts.map", - "sha256": "8e5cd3d5b27a4d3c0b2ecfd730f29fd8ead5e23b3d89dde15960ece728281a95" + "sha256": "c0982d3aabf7329e861e86eef7920727a166b2d5be17af0f06134710e35b8901" }, { "path": "node_modules/opcore/dist/validation-composition.js", - "sha256": "6dbde57aa4eb9db2a859279648a1785428b27329d8e222a70428172a62eaa912" + "sha256": "ec437daecd199278e75f3263aac171a6b4b7cbd8fbef0895a669fa48ca959cf8" }, { "path": "node_modules/opcore/dist/validation-graph-session.d.ts", @@ -751,11 +751,11 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-asp-provider/dist/mapping.d.ts.map", - "sha256": "81081b573cd9498756c5048399ce6cc60189d6a4ba7aac8c58d7a87559f0b077" + "sha256": "0550ca9636befc772eb002e03597bc303ff23e173778edceb9db5707b66ef09d" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-asp-provider/dist/mapping.js", - "sha256": "bbc7e9b39d9dfd0b309442584fbd7895ee1f5942878e1fd160ae7dc507c6daed" + "sha256": "d742e133c01bc029da401f30bf2a4edca2d6f04335e7248b751f735148285a60" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-asp-provider/dist/protocol.d.ts", @@ -771,15 +771,15 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-asp-provider/dist/validation-composition.d.ts", - "sha256": "adab11e92e36d1f34d9b37757181b1527fbe9a571ba683c1aae62d8faf78b9d0" + "sha256": "6346cc8fd2ad7cb5e0cccd103d04d5c5fbe84a7e78eaefbd2127d5315d241fdf" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-asp-provider/dist/validation-composition.d.ts.map", - "sha256": "3124e89e18f9c5370e5f1fe2e81b8a45a11fa1433c8ae4019e265100fe9bad01" + "sha256": "91976c441366599b275ec2798a4dde56e1e3356b6cb08c87702130d056fd2696" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-asp-provider/dist/validation-composition.js", - "sha256": "197af4d677cee31ead9c7803788bf39f82f9e3e46db382dc927f4701ec62003b" + "sha256": "5ba81fb375317201181446063914a48902578333fb5980008cbc092ff4b5a6a8" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-asp-provider/dist/workspace.d.ts", @@ -799,19 +799,19 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-asp-provider/README.md", - "sha256": "22f9bc36b92f6640267ae5ac7354831dbbe8f9771feed2e05ac9399e7357cc03" + "sha256": "71f4c594bb69c53369b75d8424de283ab9f26e55ba4f0bdeab857a8cb0055071" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-contracts/dist/index.d.ts", - "sha256": "04c3529642808722491a610d95594d0a9d0f5c7bd480c86aec30c10da7ec4e60" + "sha256": "f1bea3ef560016e90fa0f7d6341faa07242c4ea7e900a9a2a9bf5fb00a3947ac" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-contracts/dist/index.d.ts.map", - "sha256": "19ef90f6f5171dbfd0fa0131642a15bdf0b2b8b9060d2876d1c7312edb036790" + "sha256": "9f3f3814e6c596f1b9ddb3a0c4967ca70b8607274f81ebca30924443dbd2b96e" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-contracts/dist/index.js", - "sha256": "c9d59ede565c92e4488c185223c96575ff8fa3adf9e34b99959e4d41a4949a25" + "sha256": "3a0a59407189ef2a1edfb6c2c697d493eaadee83e9e33ae0652d934dc94b24a2" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-contracts/package.json", @@ -823,7 +823,7 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-contracts/schemas/opcore-contracts.schema.json", - "sha256": "0db053e2dec21f3143dcff59c7aa26ad5a8c4f9eadb5e34f110dcfffcb32eba7" + "sha256": "6358fe4ec5a4c37b13679460fa6d91ff582f75ac36eadd30b22d8261cc925000" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-edit/dist/atomic-writer.d.ts", @@ -975,11 +975,11 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-edit/dist/path-policy.d.ts.map", - "sha256": "5fb6eefd43d8ae9e92549242f465e6d959075e9fa8751719bd9b9affa9c906a2" + "sha256": "1b4813762b5969ca45d761fd9881c2d25e1272f1af5f0f039b0e8a6687c737a4" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-edit/dist/path-policy.js", - "sha256": "230639081d9b2a4acf54f5e883f1f2dc8ec9fc330a57ba9189080959c075cf5b" + "sha256": "244b2fbc4d2ec4af40c4cdd10e301c8b2eddee5e5916472daa769a0b8c221191" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-edit/dist/planner.d.ts", @@ -1459,15 +1459,15 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-policy/dist/factory.d.ts", - "sha256": "5233652fd7c9ac59a9ffdada309e76d451dd3a73d6dadf7dc53977596808120a" + "sha256": "d18c4014f16b748582921a21b668ad74249cf6386545928bfe81e32cdfc510a3" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-policy/dist/factory.d.ts.map", - "sha256": "03192673e6cba4665c2b6beaa4613bc81996273f044bfef56cd1760f9f3f75d6" + "sha256": "9f92bca9342f3f540d97700aec679ccc7196bfdcc016ddfd63ab107b1604bad6" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-policy/dist/factory.js", - "sha256": "070be0a2f4a0c50271424eff9fff29768440d2f151f04de582fe57a083895493" + "sha256": "4dcbe0e426d01fd1cd1b3922b2832e1366b46ecf32992fff95f5447de451c2a3" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-policy/dist/index.d.ts", @@ -1527,15 +1527,15 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/check-ids.d.ts", - "sha256": "ae2f204719f2bd127a0ef22b2c0f9581fea6cc033a26ee360c994ee42bcbaf50" + "sha256": "22a13912355e6b958b67873c813b28c6b06f9c1c0eb30792f6b49495eb8227e0" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/check-ids.d.ts.map", - "sha256": "2413c3099dcb5f56aef778c45a1f07ffe65acd79eeabbdafb0726f2092eae16b" + "sha256": "dce4c35f5460ce9ee6495a45f0bf67fdd5f63df98927431ffc4835552b14b03f" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/check-ids.js", - "sha256": "5e1aef29466da1e49a13fa0f04c63eede39d6da3f686f946ad5c11afd90c8b35" + "sha256": "21bc0dfabc1211abb3c7d1842497105b0070f0d0ff4d757ef18f5392a9b9364a" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/compiler-protocol.d.ts", @@ -1575,15 +1575,15 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/environment-resolution.d.ts", - "sha256": "d93493b07da4e4a2325a0838e19077a9ae534487821573b78540b03780858a84" + "sha256": "79aa9267d234f0bf71633700f9788158dc9b7c73d53b17d65a9cc1062a408a7b" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/environment-resolution.d.ts.map", - "sha256": "7667de8584b4fb91bbcba8722e793f13ce8f95be2c02f8565703bbeba702701f" + "sha256": "336880a2159aa62bfec303ac24e8fe403ed50d902611277e4ae28a094fb2de37" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/environment-resolution.js", - "sha256": "46f548c9c8ab37ef21f734b8016dd95491bd749b5ea2e5ba7e4ee62768898cc7" + "sha256": "bfb558cb41b0eb796b44e1eae62914894a0dbdda536cbb10d6d92352eaced97b" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/graph-requirements.d.ts", @@ -1623,15 +1623,15 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/index.d.ts", - "sha256": "2297dce38a1d39b7bffd466a31060066ddb79c4ccfc5752d4ab191a29770575f" + "sha256": "96fcd62da36d9128a802e5e7e784964211a91ba2d370e889f6bd6e0952aefe7a" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/index.d.ts.map", - "sha256": "b688ca7b040abb33d556b247d611a127b4e75207665abf4408f63bd2c33caefd" + "sha256": "03af26deadbad7582237cfda4e925d66afe4852d15bd85242fd1fbb2c9cc5f79" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/index.js", - "sha256": "fffab3bfa9eb10a5ca8675a896a85c8c7a7f946d771be5caa306ffc5205e4a08" + "sha256": "84124a91b8ed7f1e6b6d104436e4aa50e6b2dc461ce9a4ebc2f33a49fbfd86e6" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ini-config.d.ts", @@ -1645,6 +1645,18 @@ "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ini-config.js", "sha256": "c05d3fcbe7f85594f3aeaa6436ba8b120afea6f6ab1f7e3d76c161a668a36c75" }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/materialized-workspace.d.ts", + "sha256": "85c2e34ca8f5f7776def9eb9332299b0b619ae5fa979c8d861a395c0a1b210bb" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/materialized-workspace.d.ts.map", + "sha256": "d48ef9f5abdb7514be07ce9d688d6624f8c2d2a3a6796bb958c888a040f6a48c" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/materialized-workspace.js", + "sha256": "243d370f16625cf3473f906b098d91059e7d8b2f1467cf9c1b62d8c368b817e0" + }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/mypy-config-values.d.ts", "sha256": "67d28edcf2f8e696a577c9afd417d3fd41b41de46388f70cda8498315e92d8bc" @@ -1719,39 +1731,39 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/process.d.ts", - "sha256": "e4f9b2956b4dd6c50206eefa8440ed0e2f1e5d9cab455439478da4753f99287f" + "sha256": "74aa56b2cbb2b1f430c6fd81d998537396c943f7ee025a2f3c0c286f92a7b579" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/process.d.ts.map", - "sha256": "69efd4c6155f1dd23c0d04aeaf60d4e28affe4ba12db983285685cca7b17cee5" + "sha256": "c549ddef9eec50fa3c1baaf1d0cd0291322a2efe8943ca49b78b8fa805210458" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/process.js", - "sha256": "7cb40898ec8dae8c7beab0f4170278b10edcd159095eef964941e03597cd5976" + "sha256": "5d632b5ae123a1da4f41b25651cb493dabffe2d229349a6aecc5cac967ba4229" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/project-config-files.d.ts", - "sha256": "497823250ecde82b9e744adf7689819179e161f1f1bbdcd831b925c4236e3f55" + "sha256": "5e7f925e3b1634b171afd2e2822a37996a13f95d9f5ca2d576bea302118eee2b" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/project-config-files.d.ts.map", - "sha256": "754de277453cf78a68991966b34b4e63f6b298376bee40281e04f86e4a17e880" + "sha256": "7d0fff39d043626edcd76d2d21c8ab3a3f89c76fef1fadff27df94adb10aaeab" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/project-config-files.js", - "sha256": "f9712fcf234ac93a6490d2b97281608c2333b68b45f9c5c27d93aade61105b51" + "sha256": "af05beeb33f98ad54aa979a7067bd2b34309fa3f371e9023201a5c2647b6161f" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/project-context.d.ts", - "sha256": "773241c9fd9b80e352d7ce53ac908d3ccd021dbae17bd0f3da5346aed08b7575" + "sha256": "809be7d86d5edc022c663aff9f7e0ffbdb7807ef5e338fb7f7b62fa515f389e9" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/project-context.d.ts.map", - "sha256": "c8a313303530b707f6c47e77ab091d0a459ef7f3c17c4e83229871710319c6d1" + "sha256": "41a77e90bd7717ab83e50a97e5d48220ba52c6b66b7d8ba50643e0a484cc8b93" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/project-context.js", - "sha256": "766dc99fd6999254f0970f27c5e6f553c67eed34f911adc9e849def397606162" + "sha256": "c2c59c8850f296a0928ba7176523d36ad5804e5465c7cfa72a720977cd2343ba" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/project-discovery.d.ts", @@ -1777,17 +1789,29 @@ "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/project-fingerprint.js", "sha256": "0faa887e07a73134622ab29989322637e85af760cf1877ccb90fcd024b93e59a" }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/project-groups.d.ts", + "sha256": "924a96f6038d9699c5b8b50957c31140fc10d67a21a5762e87ea7ef6eab679d6" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/project-groups.d.ts.map", + "sha256": "4c494cdd76b38ae628fab3e04106c426b2f52e6327edb344b5e7e6fe155b772e" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/project-groups.js", + "sha256": "da86dd25edf4033e9b7cf47c9c99eca17b700253e1b3896b326ec3e9a800ac0b" + }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/project-workspace.d.ts", - "sha256": "84c0b4e365d65451ed5300b2164c92b2116f60eef38521a542a20a0587d024ee" + "sha256": "80b3f817945d2b28c16c766aebd13d7d1ec7a4525c476e6ae1d6f49d8835034d" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/project-workspace.d.ts.map", - "sha256": "811647d3ceb411ca07805a0d306d2117d37744a69d80d17036db3e51f7d2f7cb" + "sha256": "9d756997e9aeb77816ec2b1d0a144e88130ea737c2f8500e7ed0fbac2134d224" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/project-workspace.js", - "sha256": "e15bdae3b1ac852273ed31bbc4bf4436d8c9c309e8b7ff1743563a4dfd7e3308" + "sha256": "5a00fab587da5ce725361bd6261f5ac93d21e7bddb7567d9acde861d8fd130aa" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/protocol-validation.d.ts", @@ -1849,6 +1873,138 @@ "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pyright-runner.js", "sha256": "0943383eefacbd48d8b31328947f71d321a59811ea4e506ed916f7801f78690c" }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-check.d.ts", + "sha256": "cb50d05c72f1e7d8d13ab79ce93be1b17e189626f9a510b3de14e2fb8313a392" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-check.d.ts.map", + "sha256": "98e3cce78f4aca1e2426e9a215e46dc70918ea5199fca4273c8ba03077eea146" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-check.js", + "sha256": "f06288de3d8c3c86474b7655bf31772aa1ba4e2aed2942281524a5cdb3b674f2" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-hook-source.d.ts", + "sha256": "36a13f0754952b01eef01ab3c238a5da3d2a82fbdf566390fbad76ebabe354da" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-hook-source.d.ts.map", + "sha256": "79210dc5f5f2a805edbfeecd1fb07faa53f5a105445eafab32e2f96e7204ddf7" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-hook-source.js", + "sha256": "d043590b479d0c93edfd241f475231eda76158a91063d7d4c9c2c4112b1e400e" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-process.d.ts", + "sha256": "37599fdde8e0a9b4b2264cc325a2ff36dd17c9a91cec50f2add49313ebdf5dca" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-process.d.ts.map", + "sha256": "0b7ce2111a6d44e1fd3918212c468517876bbd72610c80dd53b2fd66794a33c9" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-process.js", + "sha256": "cdba85f809bd0e5cf915b3a7cd34afbd7c1b40d669f7d7539774e4be6977d231" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-project-runner.d.ts", + "sha256": "0b1c537d63c99175a700b083f158c0a76baf1099b9056c879a7c130c32375587" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-project-runner.d.ts.map", + "sha256": "64f319b4be6018223991c69c71d38484cf622786054d4f717c29a083b20a062f" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-project-runner.js", + "sha256": "11e9472c9fec2a74a2657ebd3c0ea045c94f669d7bd74e5a47a2b79382765c4b" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-protocol.d.ts", + "sha256": "96e3afd4f1b82b38351d1aa9693bccc1c354fb9a1d1ee9edf2ac557641fd9eba" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-protocol.d.ts.map", + "sha256": "23bd626fedb1a3d282670306751fe7ecac1a8cc87032816c6cb24db6ef8ac3eb" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-protocol.js", + "sha256": "106366b2de8af92390272e96b4b32cda9c69fcae080ed6b93adcf265e240a9a4" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-result.d.ts", + "sha256": "c730374bd414f2abdd598fb393076bcef8a1c86f702531a962f6f2a54bde4ec3" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-result.d.ts.map", + "sha256": "b8db95aaf743a7a2eb68f2de98fe66add4dbe353322447415f9fb1fad883c422" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-result.js", + "sha256": "6c2f149cba17fe48170e2717c5a55017c841f08b36f596b4ada9c7571a0163f3" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-types.d.ts", + "sha256": "a2352c0db570885b2535bde03ae56db23a7db53f06e7e603405964e2856d9885" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-types.d.ts.map", + "sha256": "20414cc649b7340c593a4efe247f439cc00a3cc8684b7d9ea17c631b5198c006" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-types.js", + "sha256": "8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-workspace.d.ts", + "sha256": "85e30d92ca1d044d6643b695e76464afdbf8e6de70fdb3b069e8ab659877ebce" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-workspace.d.ts.map", + "sha256": "3fa43a0016c84b3b3c19a9e67d6d622c3fd949b3c4ade1144f79abb1484907f2" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/pytest-workspace.js", + "sha256": "cdb1574e39892a087c0f99a078e6103fea29cfcd5e4d0f74ddae741d4638c957" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/python-check-result.d.ts", + "sha256": "2993ae8fa2864fb5d055c02f17ac8abf44a040f01437b9e4fd1ebad5102888fe" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/python-check-result.d.ts.map", + "sha256": "e352e216b30a7fc6e62bc02c8527c2aa6e5a5c2c8949bee607b6b088ebb541fa" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/python-check-result.js", + "sha256": "ddc7360c4aa632a4b3f21309b94b8fce98f1cebd27546546c620df323c6fd336" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/python-context-result.d.ts", + "sha256": "066b73b7d936f9d818fc46c75fc557af975c7afa34e9f50e20049b1915c81a71" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/python-context-result.d.ts.map", + "sha256": "1cf3098f3643481bea362045ec15a57a0a5eaf864231dfa6ec4533df0a47e362" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/python-context-result.js", + "sha256": "ab9d98df5aa0eaf7c24d26c2f2defc87848a4048d53a156e8b4af39d977f2ffb" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/python-execution-workspace.d.ts", + "sha256": "5f056d705d63215f19c8660959552851255a8af2c06566abcc8a3bbf882d0783" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/python-execution-workspace.d.ts.map", + "sha256": "0c4c421516eb6e176be28a3b584d1b0e4eac85c21091acaa5ae7c32b8180bdfe" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/python-execution-workspace.js", + "sha256": "005eddfbc422fd62d92c646b598b2e9b317e40300e76e5a8d862ec77393c3a9f" + }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/relevant-tests-check.d.ts", "sha256": "997ccad55a27d3b8db02837b0cb3ab87aae977490a5a6010fb972c8767f1447c" @@ -1859,7 +2015,151 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/relevant-tests-check.js", - "sha256": "79568d4598b33b78ed388499b11038e3004518454d675fd4e95adc72462640a0" + "sha256": "4cb5a5aa64e8fc3a55e762140b5685c50aad9af53d48e5a7353dd42f3b825828" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-capability-run.d.ts", + "sha256": "ea50389d268b8d4a978cf7dfb033141046b77a49248ea9e02d3fd6ab4614f963" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-capability-run.d.ts.map", + "sha256": "43ca61bddb14b1278a70e7e1b6252193314feaf51622c9fbf53c9a603c8573a0" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-capability-run.js", + "sha256": "ccb62c91c0ebabd692a8c380aea9918054f9e29ffc46a3af8c643948afb6b11c" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-check-definition.d.ts", + "sha256": "720d616966a15934743ce37183fb84539d232e8b9add2eb274afc2ba73b7c88e" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-check-definition.d.ts.map", + "sha256": "05f9d3444cd30e74b96c42e02b4ce49efea0175241ccea11828bf6f32054b3b9" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-check-definition.js", + "sha256": "676cd30bea4395a40c691365f69423988b63d53cbcc81b830a817b2d4b9c69f0" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-check-shared.d.ts", + "sha256": "648c987049c01116a35fc34b08105915bcdb62134953c32fdc942989d1ddba7c" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-check-shared.d.ts.map", + "sha256": "e613c8a622a926cb7de90b1ea0805423b14e62ee7455b98e7cd3bc23b8af0ab3" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-check-shared.js", + "sha256": "d08c30bc4c6c5da0dc8a3983ac6c39c5aadc543ca050243c8467e458ad193368" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-config-paths.d.ts", + "sha256": "a63d4b4cf7afff1c8e361a83ea4c6bca163d0cc94cd5c911aab4ecf7a0401801" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-config-paths.d.ts.map", + "sha256": "d4442d66ebc93fd13d003125f4c0a1a4c2eb360e54717c8368ce912d935967b6" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-config-paths.js", + "sha256": "ea3cd5484c0e25e620d87649d49383766ab2773665d90bdc29708202f3614fe7" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-config-proof.d.ts", + "sha256": "2b2a9804325752d770ab920abf0fd237ebcc6cebcba384a586ed9f0c7e52aa2a" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-config-proof.d.ts.map", + "sha256": "49396dc15490606433180582e125efc588b5b7c956b349fdef027677af24c68f" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-config-proof.js", + "sha256": "cbd6d00ebab311ddc997c738b02e98770f5be65d30a73f74d923ce6113b1c95c" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-execution-workspace.d.ts", + "sha256": "f619280169af7e475e96a5630ef4b39c9e61a0fc6fecf57bec9cb8e18dd4bc51" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-execution-workspace.d.ts.map", + "sha256": "62f839b1a18fbf4208c9d3175fe837216dea027b2bd77e5506fc025ac1e16869" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-execution-workspace.js", + "sha256": "874818997de5db6f0627b3e268ca267ecfa14a5b2404f53f415df6b0736c9a7e" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-execution.d.ts", + "sha256": "79217274ef1c2affe31112c4a4eebb9464f0c16d81e099c4cd0e622dc33da8f6" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-execution.d.ts.map", + "sha256": "01d7302400c81743f2a03edcb710cb5be96a80a33da5382f440a9c56049f1dc4" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-execution.js", + "sha256": "2c4eb300bda685f0d7390d1a786875d744fe4e404c2e55eb30f3a94ddec73695" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-format-check.d.ts", + "sha256": "78df5b2778ef29ab4675446d72dd48e8e7d9aa777487d99fb4d00ce21ba95bbd" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-format-check.d.ts.map", + "sha256": "174d46b3e4a40005206ee19bafb886da4eb49e74827ad1c186df916c635df72f" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-format-check.js", + "sha256": "8a12d6c5312c9cb923cbaf5ae7d50bf638eab61d479e39ded75d7b3f099b5058" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-format-refinement.d.ts", + "sha256": "cc0b58f6caec8010a536c17353ef8c4b2c1665c156f2e6a01fafc658d04980ce" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-format-refinement.d.ts.map", + "sha256": "607e99dd7d8d7bfc53001101bbb2bb4bcd21cbf401e539ca8e0da0c59768b074" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-format-refinement.js", + "sha256": "0f3a8da1669162685a594bc385f3a3e9c2cc563085a5182cb8749dbe08a6b130" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-invocation-failure.d.ts", + "sha256": "46913a33c505d73987a225bb48c45c5ba4c191788b80d80919cdff5b89e75c61" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-invocation-failure.d.ts.map", + "sha256": "5dbb2fbe6eafdcb3a58cfa6e95df15f261e91b2cc73935e77be5df0c798a7d4c" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-invocation-failure.js", + "sha256": "45d9f26231260f37e030207ada0b7c6ed96e4a241e3a3108d32f9d9f62ade6d8" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-lint-check.d.ts", + "sha256": "45658d88cdcab83eddc27a9cc524f5a90cd83f301242294c2c76b252e24e7adb" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-lint-check.d.ts.map", + "sha256": "843f8ab8b12175b1e7ceffd753ff8984f43bffb9f9657a32a002c50fc3fea2ea" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-lint-check.js", + "sha256": "0bf9b1e0b192315b211f739faecacef58b233109b33dd305e11f63fb7c482f9a" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-lint-output.d.ts", + "sha256": "ad027daf8c03f0bef0dfd3d680df5e56236fbe9dacfd1cafcea9652a79f90832" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-lint-output.d.ts.map", + "sha256": "f1f63de27c00955e8e50f03cabed4a3952aa8f2a283b31c606d2857e3735ee67" + }, + { + "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/ruff-lint-output.js", + "sha256": "cf8c290f1283a5ad30d4762b202e1e0e90d4b6527a6895359570198eb48d6ca7" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/source-closure.d.ts", @@ -1867,23 +2167,23 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/source-closure.d.ts.map", - "sha256": "71ed3366c6f58aec9fae8bbd6723bb5f908ca2ffb2e284accee7f35ab26418f8" + "sha256": "3709393485a427a04c655070dac4d8b4c3111974a320f733af0c37359029348d" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/source-closure.js", - "sha256": "eae99dc9a3c633b4f42194cad429b6d32a23efc99b7eff1fd23ffca019b18171" + "sha256": "22398188ced614dd1207efdb0a1d2863b52dea87d179babaadd166ff6550b09d" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/source-files.d.ts", - "sha256": "dfce5a4343e78ec47df186161a9dab524f58e694cb4cfe57dfca42037da78d60" + "sha256": "fb156543f935d685116686f0ae0283066e54d396225677d3dbd0f4df783a447e" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/source-files.d.ts.map", - "sha256": "ce06e10a525f1505d57fd2bee542a2c4981a0b838da66f3dc97644935d41d310" + "sha256": "e65d921a4a223d4d6761f283813d5213ceef779d0cba1a847ec1fefeccb464e1" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/source-files.js", - "sha256": "0eebb0d5e1dccc7fef2b361c8bf7e9bc8e33ba951c04ef2996ea861c62c20350" + "sha256": "0fd5d0784276f059fdc830e430b6faece8710f82910391c309b882c58312d755" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/source-hygiene-check.d.ts", @@ -1899,11 +2199,11 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/source-types.d.ts", - "sha256": "936f7ac1e09f675979270813162658eafe32e83cb7027df8c2bf33b69cff0f09" + "sha256": "c4e30d37532a2583473f7c05a6c3aed1191e2626477390e6de4095abf5059805" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/source-types.d.ts.map", - "sha256": "0fa14033fe5b2eda30e761a9b2c2f56765e1d1c0d5fa176fd9b0362f50964090" + "sha256": "3511af97c05c27ec011dfa285cc009a510da24bb6711121d52a2c19088b53167" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/source-types.js", @@ -1911,15 +2211,15 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/static-config.d.ts", - "sha256": "ae01347dc436be0d276a667c405c25e524edbd683857ce56305abc62fd86a490" + "sha256": "a357dc733ddb5a188e0acc6a798d38fa89023ff66a3f52d874b9c5d8268918d3" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/static-config.d.ts.map", - "sha256": "5a073d331beda8a776bd23f5b34373d1aae6b5faed2916348bd2804257c9a432" + "sha256": "3bb91038153c08ab669b5606845df4fd4db8996ded8789d8cf3ccddde774f486" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/static-config.js", - "sha256": "97460315c77b2fb5dcc1e01a123ff57db2a939cd7aefa82acfccf1ce9f486e7b" + "sha256": "1463934785c9fe6695ad8e60321fbe57a2b770411451a91e94af3b1be4db13d3" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/strict-json.d.ts", @@ -1939,11 +2239,11 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/syntax-check.d.ts.map", - "sha256": "dc9907f32137fd34f8d60a5f236307e4b905a2d58d3adbb42bc28fdabc515402" + "sha256": "a329d2e19829af92b0d4bed0abe528ab719e31b2b22b56271e9af4a04525ea94" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/syntax-check.js", - "sha256": "f5f3822c3b62e8be40c8dd142aedee02ccf5222863daac785dae9d743e47acaa" + "sha256": "21d54ebffb8563c134db1ea9762ff883ca51b1bed2d4da7cd761900ecfaeceb1" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/toml-config.d.ts", @@ -1959,15 +2259,15 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/toolchain.d.ts", - "sha256": "632e02df935c1f62fc0fa93eafa0707352173be6f759b46d62c37915912e7a6c" + "sha256": "d0f6b22cdd8c53964be3ec208a5ae15f59581e7c45ec436a83dca46d4962c92b" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/toolchain.d.ts.map", - "sha256": "b1f1418bdf728706d1a0699f14803288290c8b1c097038a3e0cbb666aac52a1f" + "sha256": "6c2ea813702b040408cd8e1ded956129b98c9f3dd839580e63d15c62f193d1ce" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/toolchain.js", - "sha256": "2a6f80bda3f4013d958b448306503713ac8db21ce506d3d46910a723cd254ceb" + "sha256": "96050dbae7525f1a499a4cd9964de76f6f75785991e533bd6615a1c5dbe24f79" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/type-authority.d.ts", @@ -1983,15 +2283,15 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/type-capability-run.d.ts", - "sha256": "c82b36087b1339e130b5da9a37a6fdbec38dada50da513d745750617488fbfbb" + "sha256": "e7bbb8377c19243856976c45b8bf953b670e563e319a86b9a3027b09da3fd77c" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/type-capability-run.d.ts.map", - "sha256": "2ebe915fa8fdc02113bab3616b18b802ea623e0fdd1ab9267fec57e7551fa556" + "sha256": "57777ee8154679dc6a839f2794a5d0b71f451730769b29676a52f18e8c298666" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/type-capability-run.js", - "sha256": "23088d94769aca2dba534aa2e17b0ee105c45c1971274e7f3d69db81c7cf7014" + "sha256": "3c7d1e6a382a17c9a5c4c22e0b5e091d631df457cda4a2d45d469abe49f3e4ab" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/type-check.d.ts", @@ -1999,11 +2299,11 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/type-check.d.ts.map", - "sha256": "871a330554c156f9055b550c356041e5b4cf64e9ad054fc99c520fe22aea0fa7" + "sha256": "e4683237ef37244ff8d7d020c1d1f3c178265d76b4702c423a0d7f565ce4c5bf" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/type-check.js", - "sha256": "ff167e49fe942cb9addfc83a421533d69d0d7667a9684bc3e7a276115307f544" + "sha256": "646f73a824443c60b13c9647394ec8855dec09d8713b36080a64b8cf66e59534" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/type-result.d.ts", @@ -2011,19 +2311,19 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/type-result.d.ts.map", - "sha256": "f03c987bae4fa9cc6a0207ee0a9be3493ba8170f7a69fc6b05e3cd2c7f34faa7" + "sha256": "aab3c680e99504a5022211c5bd8eb8cf3ac1470d00045c9efa35f0c02be5f1a8" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/type-result.js", - "sha256": "dd0f6587ad116b71589aee2f16a69be7aea4590a935c60d9d2c5f4666885692d" + "sha256": "38b186ff2703d1a06cf8f1b395bc478c72c878e79ca8f5e16259beed79c1d962" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/type-runner-runtime.d.ts", - "sha256": "29e046656843be57a082d0375d491bea58f2de2e31bb8bc46912820c4e7aaa3c" + "sha256": "d1a41eb965b73f5e8a587bbc35aae79e4a07b6b3eed532f605a827bb20cfd107" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/type-runner-runtime.d.ts.map", - "sha256": "346a94cc921488a0eaab7d7f4f3d9ea9c28e0998b2a6f3fe16d3b15479cce8a7" + "sha256": "5bea6bcb87fcd3f2d14b155a3f04d27dd58243e20c6d982e43f4398bac0d0f43" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/type-runner-runtime.js", @@ -2031,11 +2331,11 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/type-runner-types.d.ts", - "sha256": "1e64ae862d330656cffa2fe63062f270b9ba2608e963fd7220840d65f2bf8f2d" + "sha256": "b4aeda3c500f3fa9966720886651a85ba4469b931bcbe1e838c859ae21e06d10" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/type-runner-types.d.ts.map", - "sha256": "f316ed6daad0c7598e1975863c1c6e1e4768325922b46da54912d50a4424d25c" + "sha256": "30432e3ede165f83d89603d934dc364bf1eb3d139f7b22bcdb966d63dda0330a" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-python/dist/type-runner-types.js", @@ -2223,11 +2523,11 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-rust/dist/materialize.d.ts.map", - "sha256": "a4a3f8ba61e38e2895d598060aab9152e569f22aa2201471cfc43ba9dd9097ec" + "sha256": "5c8c703e382b6063cdc34ffade5f5d13a9bef42f1af27fa92f428c4e338562ce" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-rust/dist/materialize.js", - "sha256": "b941f8d84b7638fd3c2081013349e4dfafb4d1dc5510b057be6d837c9820ad64" + "sha256": "66055ea2ec7123d5938d1df1982c35b64a61734694d2e28562d4cbde12bebb78" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-rust/dist/process.d.ts", @@ -2543,11 +2843,11 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-typescript/dist/lint-plugin-cache.d.ts.map", - "sha256": "5bc9a67d30a9fcd10ac533ccf6722c7b292211df702879938ae37def8492b37a" + "sha256": "8da4015ba99ba5ac218b0fcbf936fee18dd24ffc9be50c03374ac92bb45b1a31" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-typescript/dist/lint-plugin-cache.js", - "sha256": "c407b0c0ce2e74430af733c11282039686bc0fa7ecd5f748e4d374a4d81eaa8d" + "sha256": "65bb191dffff0ccc328bd78cee0b358656c37b62a0d3fbd6b726258d36fdfc33" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation-typescript/dist/lint-plugin-check.d.ts", @@ -2639,7 +2939,7 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation/dist/aggregation.js", - "sha256": "eccddc1c56476df4006b8caf30057c27117851f9c0cd170847678bfc51245204" + "sha256": "c8f1b43339f75d112737074d7f9435396d0a3a7a43389bb1ee819dac4b6c81e5" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation/dist/command-adapter.d.ts", @@ -2647,11 +2947,11 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation/dist/command-adapter.d.ts.map", - "sha256": "886e5ff3a6b1c23477e7cb5de2d81e6eebfdbdf7c094537b91f7f75d72dd8559" + "sha256": "73ceb8c78d63b30289bd3eb78518ca23587e85aaf0f6a5a5ada42bd4a19c9dcd" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation/dist/command-adapter.js", - "sha256": "912810eba24593a0f5439347b62dc67a1ed1aec62f3b7cba960e273523f9e87d" + "sha256": "73120520edd732597a647a8503799044d22405ec1cc96efb4abd912f051b28e9" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation/dist/command-options.d.ts", @@ -2703,15 +3003,15 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation/dist/registry.d.ts", - "sha256": "0480cdb8df7b95f68f14f2eba16d273e9fb517cf7e0de0eaa01fedecdadf4563" + "sha256": "8a16a2d6b7e89c0bf3f0bb848e6500ee91c9f5961a83f1d8e2f6a2f9210e6a6f" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation/dist/registry.d.ts.map", - "sha256": "1e95cbafb5c73ffef6860fd36bd1f0e1784742c0ecbaf5fa8d4904f25429fa84" + "sha256": "c987a7aa035a7f181a679bcc95c6911c8396a75581cbcef4f36c0624c9fcdc72" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation/dist/registry.js", - "sha256": "7d5b44429cdc512d8dbcdd5fc49169acd855175656ad915864c31b6173c3b5a4" + "sha256": "738d9efd141ca9a42db7346d904d6317d780bd18935bee253e5e690381b784dc" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation/dist/request.d.ts", @@ -2743,11 +3043,11 @@ }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation/dist/runner.d.ts.map", - "sha256": "ffdf7e8ee511ef92e1162c871e3c4d0b8463b976accda851193539afa4d186b3" + "sha256": "01f599c040069cb7a125f8259ad838d1956fc9a9e795ef23a8c288b5f79b58e9" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation/dist/runner.js", - "sha256": "76395cb451d9989cfa710fbc9ca7f577e17dd6a754fb4fb61c7d097859ecb928" + "sha256": "289989a6a0604de76a394f4dc33d68388f9dd0702d8f6e87054ed27925e50c13" }, { "path": "node_modules/opcore/node_modules/@the-open-engine/opcore-validation/dist/scope.d.ts", @@ -5739,18 +6039,9 @@ ] }, "environmentIsolation": { - "currentToolEnvCleared": true, - "clearedEnvVarCount": 5, "pathSanitized": true, - "aceRuntimeBinExcluded": true, - "siblingCovibesExcluded": true, - "opcoreBinOnly": true, - "oldBinsAbsent": { - "lattice": true, - "crg": true, - "cix": true, - "rox": true - } + "siblingRepositoriesExcluded": true, + "opcoreBinsVerified": true }, "commandReceipts": [ { @@ -5767,7 +6058,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "36113707a91309f5c999cd90122e3da19faa21c67ca2112acce48e2f6be0c46c", + "stdoutSha256": "3c2af8a6115b3d451db3cd1556865c83506181130f80397cdff0c7331ca6d984", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "opcore scan wrote read-only report artifacts" }, @@ -5785,7 +6076,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "423b6b401ac37b261a29e4d87ab163b372e2b4b52b3a4edb4944d931e3a694e8", + "stdoutSha256": "36b59af1489bd5d1adc8c502434e5eef143cb7a738193e6c6643097600c2b336", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "opcore status returned repoState" }, @@ -5817,7 +6108,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "f5131c4730d2d9b7d81fe551589c6d13c21d761467f5a8ff155d762f6901a96c", + "stdoutSha256": "11325fc37e8097b59ef83226f60cdf833f4009a626a11dcdfd890e684f24a28c", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "opcore check changed defaulted base to HEAD" }, @@ -5835,7 +6126,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "c4a21817782a216e4e03ca17b8209e9b154edb1af4a0f181ea18fbe5d5eaf5f2", + "stdoutSha256": "64b81e6c577f416f52d34417fcf228bcb412c9995365ec562c7dae0c5ecdccca", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "opcore measure returned read-only report deltas" }, @@ -5853,7 +6144,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "bfa8573238f3fb5eca7b4bca7cf7ed0c0de2f7f451f7e2d6e43e4021b071c06e", + "stdoutSha256": "62259730c4fe2aeac952350b45906cf3c0e38931c0a177415be54137abdfc198", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "opcore try generated local sample repos without publishing" }, @@ -5871,7 +6162,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "6e3f62a9c661ea71fa4da1491ca21a38fe00b342a420f0b04e651990afe2878a", + "stdoutSha256": "f5338a2ee4bc1bf6732a66974dab391dbe836933da81ad698f784342d3529962", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "runtime status reports validation readiness" }, @@ -5889,7 +6180,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "1145413cee682702c85758a9c6b9f6066c49065e9e67b49b68240b06e6170197", + "stdoutSha256": "85f13337e842a6e14184d3466d66ee34dbe941df11a49d35da85cf60b0158f24", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "runtime doctor reports validation readiness" }, @@ -5909,7 +6200,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "b22a7a73954479eaaf805bbb8d340f95dfda68553e613f73f6d510333885896a", + "stdoutSha256": "5f5717082535d188bc301379ac01bdaf332f835fa72348009025990b7d3356db", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "graph build completed with native artifact" }, @@ -5929,7 +6220,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "324e6d40a039c2aeeac7908ba5abdc46ddd31c849d8aba6a651b885b9f976368", + "stdoutSha256": "76aeb3daa3e632620f2142164b45f4c630e1d5c310fc8350af58f8d27bfaa90f", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "graph status available after build" }, @@ -5949,7 +6240,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "f1ce978b3c92240ac499db40a704a6a57db662a0dc8e577696c265fa9a1848cb", + "stdoutSha256": "57d34113c2c7e2f9c28862b63d90dd014bf433f36071cd0b8b5a7fb8640e0028", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "graph query returned facts" }, @@ -5973,7 +6264,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "70f01ffe2aa7ecfcfe2f23e92c9c0b662c2b6e4dbac1f81b579b3852c8950171", + "stdoutSha256": "5794383bd46e14ab5a6af4892c51ee0c9bb8a0c440fe7f439c4d1731166b1d29", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "graph impact returned file impact" }, @@ -5997,7 +6288,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "1448277aafc98d14b7e01279b841143ba0f12fbfd60eb81766e7f0479e01e619", + "stdoutSha256": "681210e9195fa824c4df7ca8bd554dd5671e6406f13dd05884adb814c01bb035", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "graph review-context returned related facts" }, @@ -6021,7 +6312,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "d41bb844d1cd4569c7dcd96dbdac4608f468cc58e82a12b1114c6f482b4856c0", + "stdoutSha256": "55f37b72791b9941acaf20d30dfdbe7a38c1ed0800e193ba5fba2146b26d9b5a", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "graph detect-changes returned typed change data" }, @@ -6047,7 +6338,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "2762b69f03689f5b98c1534dd7365bcac5e0f0057299da6d1d71fd2f0f2e7681", + "stdoutSha256": "f4e071af4aa3b5d55c171a66d744084690034ed1f7481b1d25e03c40ae178f19", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "graph search returned ranked results" }, @@ -6067,7 +6358,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "8e1b81680031ec996c1911c294f1717201865971eb19aa2f13015f2567a0a6a2", + "stdoutSha256": "e4960bdf835dbce256bf724c00a06d840ca87e74e2412257a9c7dbdd347fa73f", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "graph serve status route is ready" }, @@ -6093,7 +6384,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "57a75201b8e0c6e880d685f4acff2bc4c30e1eecb383d7078baf7b2856e555a9", + "stdoutSha256": "22cb73667744ba1af71f1ebb69ba18d64987db25dca2b9d616d19a8e0527f1bd", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "inspect symbols returned graph symbols" }, @@ -6115,7 +6406,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "07b9447821670d4cda06f1f9b3e035c51ce4a3a21f60ce97eaf2e851f7b9f478", + "stdoutSha256": "5cace6ae4f1e76dd43ea706b43b0d6514f443b6537851ad8631b1f0275dbda50", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "inspect definition returned a symbol" }, @@ -6141,7 +6432,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "9920c09424d083fd9811fb017a4f76df18ec1af1e039f87bc5d82b66d3f3cca8", + "stdoutSha256": "be58acf38764b0ffd8364551b94693117a3ec2b821834fb97c52a7bc6b6f12af", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "inspect references returned callers" }, @@ -6163,7 +6454,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "04f90b01005ea95d988cfa410408eb0374213a38539132c5345352625fa7c0cc", + "stdoutSha256": "386c4983ccb718fff3ad690cbde663598294e774ff63d3481675c763c9d1c55f", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "inspect signature returned read-only language-service signatures" }, @@ -6185,7 +6476,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "fbe1a9195487b304c47693aba906639989ed2a642c0d9dcb0a2339c590ce287f", + "stdoutSha256": "58416b6b1a529b93acced616153c8e0e30c4ce169ebad2eca4da2c07af5ca724", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "inspect implementations returned implementation evidence" }, @@ -6211,7 +6502,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "9ff50bf0cc2eb9c9ddaa20e3018a4c2b87ae09583b87db535738dbb16971bcee", + "stdoutSha256": "1c1eea32c5eca91ae788359df4b60f69d29598790c7c02bdc5a18b1215d66f13", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "inspect search returned graph search results" }, @@ -6243,7 +6534,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "6dd2f83ee9d36f94410e1260b05bf0064657d75e46711d44834c9a598cd24e8f", + "stdoutSha256": "6aba96b0a6ce9c1727aa855e0657e86513ba7e68640b212062c6ab9f4d96e518", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "safe edit preview produced a plan without writing" }, @@ -6277,7 +6568,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "c408d7cb1c30e5fb770edc0e3731e5effd723f84fd147c85e9f4d8f2c7c97255", + "stdoutSha256": "e04566959f046e57cba27f557cf80b9303b4df1b77b06f41cfb9660258fddf8b", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "safe edit apply wrote after validation" }, @@ -6311,7 +6602,7 @@ "status": "error", "exitCode": 1, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "74011e1a54fc7a299458e645697258a3ab77f175fe1bbacd0b053287eec246f4", + "stdoutSha256": "c339beb8ba8827d85ede805ba7d490b8fb69095987b2a8970c4178a1fd6ae952", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "validation-refused edit left file unchanged" }, @@ -6337,7 +6628,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "9ac0f91133a7bf56a2c999ddf84a7e2c69ac18694e7388f4cef7fafd97113062", + "stdoutSha256": "e371b7481bcb6a8f96d528c87ac7c1a519e1970fd707e3efb0fa5c52cecf4e2d", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "check files passed syntax and type checks" }, @@ -6361,7 +6652,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "41f83a81b6fd39cd5e9ed539e232df040508fe8f8595d4cd3b42863711111620", + "stdoutSha256": "e46ac5c60df2a0ebb34482b8d1b12417345e9127c755532a25d75cff7d700175", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "validate request passed" }, @@ -6389,7 +6680,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "d7a5f71ea1b84d12bd34a88586b2b3565b88cdcfa4f3574d6f3ff890182ca1b1", + "stdoutSha256": "d2bc0e05c496e77d6a602ce34057ff42a33bd7637c9fcfe4847dab9e42a16e34", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "pre-write pass receipt was ok" }, @@ -6417,7 +6708,7 @@ "status": "error", "exitCode": 1, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "0a8bb14c58e704b6145597c78a67a5d0a1a85e160b19866051fb0f1f33b65e82", + "stdoutSha256": "de9d07436b36f5f8450470017b20ab468837ed4d8a0f1d6067089c9a47c6e50d", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "pre-write failure receipt failed closed" } @@ -6439,7 +6730,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "35c4f156114926183039c3486e2aea9d62c6dd22ff30103dd88bd78dd91fa292", + "stdoutSha256": "c0501725c2ae853be3f6c9889c0c0ac7a7223d69c9753a9713a2f92dc3ce09ae", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "Rust graph build completed with installed native artifact" }, @@ -6459,7 +6750,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "870318ab1d0a49d120b45f2d061b52b75a21761b8eb1c91408c66b769f085296", + "stdoutSha256": "5e0e86dd93fcdae6d9598193dc6a4a130ad119ea874f9b3c466888f6e26362a2", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "Rust graph status available after build" }, @@ -6479,7 +6770,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "b427e613f1c30404883435b27864b1f370a319ff9ee836a92ef7414e1842bbc6", + "stdoutSha256": "93f1744c2bd7c2d575f0fe50ef04dc7515101087a19d5eee1f056c7815d29301", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "Rust graph query returned Rust facts" }, @@ -6503,7 +6794,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "cdd0f55fb1fee6a2fbba25a66712af47e17b3f984d20651c5c52e876e04cf76f", + "stdoutSha256": "913f9c2ec7248ede8d4ded155a056ae117b12efc97114a5847d35e4860959b53", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "Rust graph impact returned related Rust facts" }, @@ -6527,7 +6818,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "671aca39436cb322d14cdcd7c332e2bf4e4aa8d206b282b8e17a23116a3e4fd7", + "stdoutSha256": "1958ab70b6104e1b8f38ea46ed33e61f364970aba9f42afbba4387ea224c5dac", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "Rust graph review-context returned related Rust facts" }, @@ -6551,7 +6842,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "0b14409a5abd2020928b38ec3c70f4b287a1bd5dea2856605c5e604973c41deb", + "stdoutSha256": "2bd48ed0d16f222d1502a0863b7344726cf5e4715eabdde4717b1af1981b3677", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "Rust graph detect-changes returned typed Rust change data" }, @@ -6577,7 +6868,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "9c1145f77611b954bc8e0cfc0d68551e2a211486e78e90d3cb301ddb60860b48", + "stdoutSha256": "c061e8ad9a9896509cd3e92116111aa2253342758d927807b2ca78697c92af5b", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "Rust graph search returned ranked Rust symbols" } @@ -6602,7 +6893,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "1cbeccedf0104e8677d2d48708d488f422ef4e9d82407659ce81bc0b578cd860", + "stdoutSha256": "3cc5669beadd60a5851dcf0162eec726decb43817e1c0b552738af14805bffed", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "opcore scan returned Python repoState and validation evidence from installed artifacts" }, @@ -6624,7 +6915,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "60d6826a0650af05780b4f1b7e55317b8090cc441ba3417186b101fa20f63085", + "stdoutSha256": "9ea18e96672848c7394072b99465b91033093382e1c5b63b302a32e1b26a223b", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "opcore status returned Python repoState from installed artifacts" }, @@ -6660,7 +6951,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "7c1b37f8d613cc3bef3911fcdd881fb29b177de77516890953d68788b56c580b", + "stdoutSha256": "b0a4266f4752dc7982fe5f05b5a9f096931a218967fd1b425ede199e912c9697", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "opcore check changed validated Python syntax and hygiene from installed artifacts" }, @@ -6681,7 +6972,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "b8e91f03cbe6fe815fbb6246dec11ed7738630ef91d83caae60fba3e6a92e63a", + "stdoutSha256": "40e3413eadd66071b72d9e96d931a4a0ea0585c5ade330e9c5c494b8314c5f4d", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "opcore measure returned Python metric deltas from installed artifacts" }, @@ -6704,7 +6995,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "a55bd3df8241031cd60a4728aa37d5acb0a65f0b1a88f8edeb823c85dbc6d0e1", + "stdoutSha256": "40d27cfd4050e38b42edf9623340ce0655035fbb63296357210636076aa8a240", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "Python graph build completed with installed native artifact" }, @@ -6727,7 +7018,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "3a249ceca524bf44091ce805fd452861139d0b70048fb20e27c422f73c48801f", + "stdoutSha256": "27757fa1c644f97f7cb17f6c01d1bb5ccdc2e288b56ea561e0f4f997b702bf80", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "Python graph status available after installed-artifact build" }, @@ -6752,7 +7043,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "9a79b95d07b8f6e931e04c27ce154e2aac787044ef49d53fa28bc0e1ed75974a", + "stdoutSha256": "d0a132ac76558051eae345b7231d7d139e95e95eacd01b263570d62e17272767", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "Python graph query returned installed-artifact Python facts" }, @@ -6782,7 +7073,7 @@ "status": "ok", "exitCode": 0, "binPath": "node_modules/.bin/opcore", - "stdoutSha256": "bd422996a6afe5fa4eb1da11a715457e950d272caf83bc2829ba9211ecd23bef", + "stdoutSha256": "2dcf525a2fe366332595332f310ab06a32d49e146be41668ee2cc949e0daa254", "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "assertion": "Python graph search returned ranked installed-artifact Python symbols" } @@ -6845,7 +7136,7 @@ ], "status": "passed", "exitCode": 0, - "assertion": "source-hygiene check ran built-in policy while status reported ruff absent" + "assertion": "source hygiene stayed honest without ruff" }, { "id": "python-relevant-tests-no-pytest", @@ -6869,59 +7160,35 @@ ], "status": "passed", "exitCode": 0, - "assertion": "read-only status reported absent mypy, pyright, ruff, and pytest as degraded" + "assertion": "missing Python toolchain stayed degraded" } ], - "currentToolGuardrails": [ - { - "id": "current-tools-validate-changed", - "command": [ - "npm", - "run", - "current-tools:validate-changed" - ], - "status": "passed", - "exitCode": 0, - "stdoutSha256": "c841ab97cd599893cd51807789d41d37c67133d4abd1315aba222fca4c3beae8", - "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "retained": true, - "assertion": "retained external changed-file guardrail passed during installed-artifact cutover proof", - "oldToolReplacementClaimed": false - }, - { - "id": "current-tools-validate-rust-graph", - "command": [ - "npm", - "run", - "current-tools:validate-rust-graph" - ], - "status": "passed", - "exitCode": 0, - "stdoutSha256": "37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570", - "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "retained": true, - "assertion": "retained external Rust graph guardrail passed during installed-artifact cutover proof", - "oldToolReplacementClaimed": false - } - ], - "oldToolReplacementClaimed": false, + "selfValidation": { + "id": "opcore-self-check", + "command": [ + "npm", + "run", + "opcore:self-check" + ], + "status": "passed", + "exitCode": 0, + "stdoutSha256": "98006e4c11c39bfe79e15f629ed3e2f063b9c16f0c7ad5708d7d6e855614c01d", + "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "assertion": "Opcore validated its own changed implementation surface" + }, "forbiddenMarkerScan": { - "scannedTextCount": 2005, + "scannedTextCount": 2105, "findingCount": 0, "markersBlocked": [ - "private-runtime", - "current-tool-env", "private-home", - "old-tool-bins", - "old-product-name", - "doubled-token" + "launch-claim" ] }, "inputEvidence": [ { "issue": "#17", "path": "docs/release/graph-release-receipt.json", - "checksumSha256": "fee81c57608f2c63ddbfc23d53490760d03a5df49fe69e3849860db0599e852f" + "checksumSha256": "dd92c74c58a5f4e039b36de397352e05fecd0a19a127f6a770a299e05bd1832f" }, { "issue": "#29", @@ -6931,7 +7198,7 @@ { "issue": "#58", "path": "docs/integration/pre-write-validation.md", - "checksumSha256": "ec111c8a4c8b3c41aec245082f2e700173b8b54c50c471ded0003862abe9e2ac" + "checksumSha256": "71d326f6c3b0eac3b1dde1b08142b8d6751f91469bc477f952f5ee858f8b165c" } ] } diff --git a/docs/release/cutover-receipt.summary.md b/docs/release/cutover-receipt.summary.md index 400f54c..7b6b333 100644 --- a/docs/release/cutover-receipt.summary.md +++ b/docs/release/cutover-receipt.summary.md @@ -3,14 +3,13 @@ Maintainer cutover gate proves installed Opcore artifacts handle canonical release commands without dev-tool fallback. Machine receipt: docs/release/cutover-receipt.json -Machine receipt SHA-256: c72be0d79199505770c631b08201be09f8602e239e80c1ffa974e7f24d1cf32b +Machine receipt SHA-256: 1bdb6d984ad52243be972abb5b061631f4eca65e7d5c5a9eac525569898ef97f Installed packages: 1 Command receipts: 28 Rust command receipts: 7 Python command receipts: 8 -Current-tool guardrails retained: 2 -Old-tool replacement claimed: false +Self-validation: passed Forbidden marker findings: 0 Input evidence: #17, #29, #58 diff --git a/docs/release/graph-release-handoff.md b/docs/release/graph-release-handoff.md index 1e16cde..0b0009b 100644 --- a/docs/release/graph-release-handoff.md +++ b/docs/release/graph-release-handoff.md @@ -3,13 +3,13 @@ Issue #17 graph-release gate receipt for #7, #28, and #29. Full receipt: docs/release/graph-release-receipt.json -Full receipt SHA-256: fee81c57608f2c63ddbfc23d53490760d03a5df49fe69e3849860db0599e852f +Full receipt SHA-256: dd92c74c58a5f4e039b36de397352e05fecd0a19a127f6a770a299e05bd1832f | Issue | Checksummed Receipt Path | SHA-256 | |-------|--------------------------|---------| -| #7 | docs/release/graph-release-receipt.payload.json | 2ae48d67a1a908c5418f274c8d6d07843c131c9fc7f11ee1d0bd1f89fbcc628b | -| #28 | docs/release/graph-release-receipt.payload.json | 2ae48d67a1a908c5418f274c8d6d07843c131c9fc7f11ee1d0bd1f89fbcc628b | -| #29 | docs/release/graph-release-receipt.payload.json | 2ae48d67a1a908c5418f274c8d6d07843c131c9fc7f11ee1d0bd1f89fbcc628b | +| #7 | docs/release/graph-release-receipt.payload.json | f6d68bdecc65a83e4e34e2d208f9158c5c83d06957e1da6ef90ab2444dd2891d | +| #28 | docs/release/graph-release-receipt.payload.json | f6d68bdecc65a83e4e34e2d208f9158c5c83d06957e1da6ef90ab2444dd2891d | +| #29 | docs/release/graph-release-receipt.payload.json | f6d68bdecc65a83e4e34e2d208f9158c5c83d06957e1da6ef90ab2444dd2891d | ## Parent #4 Graph Scope @@ -30,5 +30,5 @@ Full receipt SHA-256: fee81c57608f2c63ddbfc23d53490760d03a5df49fe69e3849860db059 License report: docs/release/license-report.md Provenance receipt: docs/release/provenance-receipts.md -Rollback: keep ACE wrappers on current external tools if receipt regresses. +Rollback: block release and repair Opcore self-validation if this receipt regresses. Maintainer note: these graph release checks must pass before publishing alpha artifacts. diff --git a/docs/release/graph-release-receipt.json b/docs/release/graph-release-receipt.json index efc66de..18d8184 100644 --- a/docs/release/graph-release-receipt.json +++ b/docs/release/graph-release-receipt.json @@ -2,8 +2,8 @@ "schemaVersion": 1, "issue": "#17", "origin": "covibes-authored-synthetic", - "generatedAt": "2026-07-18T06:29:58.350Z", - "commitSha": "2d4e920e0b08a989953486b261327ed7496c328b", + "generatedAt": "2026-08-07T17:35:07.549Z", + "commitSha": "6ed9e0e5b48acdc298af1a2e1af1a59aae32ac78", "graphPackageVersions": [ { "packageName": "@the-open-engine/opcore-graph", @@ -59,7 +59,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/wave1", - "durationMs": 997 + "durationMs": 541 }, { "id": "opcore-graph-update", @@ -76,7 +76,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/wave1", - "durationMs": 681 + "durationMs": 543 }, { "id": "opcore-graph-watch", @@ -93,7 +93,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/wave1", - "durationMs": 662 + "durationMs": 533 }, { "id": "opcore-graph-status", @@ -110,7 +110,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/wave1", - "durationMs": 743 + "durationMs": 550 }, { "id": "opcore-graph-query", @@ -127,7 +127,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/wave1", - "durationMs": 691 + "durationMs": 531 }, { "id": "opcore-graph-impact", @@ -144,7 +144,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/wave1", - "durationMs": 734 + "durationMs": 529 }, { "id": "opcore-graph-search", @@ -161,7 +161,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/wave1", - "durationMs": 734 + "durationMs": 535 }, { "id": "opcore-graph-serve", @@ -178,7 +178,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/wave1", - "durationMs": 745 + "durationMs": 531 } ], "rustCommandCoverage": [ @@ -197,7 +197,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/rust-only", - "durationMs": 743 + "durationMs": 535 }, { "id": "opcore-graph-rust-update", @@ -214,7 +214,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/rust-only", - "durationMs": 723 + "durationMs": 538 }, { "id": "opcore-graph-rust-watch", @@ -231,7 +231,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/rust-only", - "durationMs": 733 + "durationMs": 539 }, { "id": "opcore-graph-rust-status", @@ -248,7 +248,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/rust-only", - "durationMs": 735 + "durationMs": 541 }, { "id": "opcore-graph-rust-query", @@ -265,7 +265,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/rust-only", - "durationMs": 784 + "durationMs": 532 }, { "id": "opcore-graph-rust-impact", @@ -282,7 +282,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/rust-only", - "durationMs": 691 + "durationMs": 541 }, { "id": "opcore-graph-rust-search", @@ -299,7 +299,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/rust-only", - "durationMs": 724 + "durationMs": 530 }, { "id": "opcore-graph-rust-serve", @@ -316,7 +316,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/rust-only", - "durationMs": 719 + "durationMs": 527 } ], "directSqliteQueries": [ @@ -396,82 +396,82 @@ "benchmarks": [ { "metric": "install_setup_ms", - "value": 6, + "value": 1, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { "metric": "cold_build_ms", - "value": 997, + "value": 541, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { "metric": "incremental_update_ms", - "value": 681, + "value": 543, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { "metric": "impact_cold_ms", - "value": 734, + "value": 529, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { "metric": "impact_hot_ms", - "value": 756, + "value": 538, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { "metric": "search_ms", - "value": 734, + "value": 535, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { "metric": "daemon_startup_ms", - "value": 1130, + "value": 759, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { "metric": "daemon_query_ms", - "value": 1130, + "value": 758, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { "metric": "db_size_bytes", - "value": 245760, + "value": 241664, "unit": "bytes", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { "metric": "wal_size_bytes", - "value": 403792, + "value": 399672, "unit": "bytes", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" } ], @@ -507,10 +507,10 @@ "forbiddenMarkersAbsent": true, "generatedBuildMetadataAbsent": true, "privatePathsAbsent": true, - "pythonCrgSourceAbsent": true, - "pythonGraphPackageMetadataAbsent": true, - "pythonCrgGitHistoryAbsent": true, - "forbiddenImplementationPackageNamesAbsent": true, + "sourceProvenanceAbsent": true, + "packageMetadataAbsent": true, + "gitHistoryAbsent": true, + "foreignImplementationNamesAbsent": true, "inspections": [ "npm-pack-dry-run", "package-file-scan", @@ -652,10 +652,10 @@ "graphArtifact": { "artifactName": "opcore-graph-core", "artifactVersion": "0.2.1", - "targetPlatform": "darwin-arm64", + "targetPlatform": "linux-x64", "binaryPath": "opcore-graph-core", "checksumPath": "opcore-graph-core.sha256", - "checksumSha256": "72acf10de0ece619c3896943addfb0cc1b09dab22000b48a653b033224217bc8", + "checksumSha256": "c3a771435a7a8172a9e5f0bb20b0d111453efc70901e95308094664f77e7bdf1", "buildProfile": "release" }, "optionalSurfaces": [ @@ -688,20 +688,20 @@ { "issue": "#7", "receiptPath": "docs/release/graph-release-receipt.payload.json", - "checksumSha256": "2ae48d67a1a908c5418f274c8d6d07843c131c9fc7f11ee1d0bd1f89fbcc628b", - "rollbackNote": "Keep ACE wrappers on current external tools if receipt regresses." + "checksumSha256": "f6d68bdecc65a83e4e34e2d208f9158c5c83d06957e1da6ef90ab2444dd2891d", + "rollbackNote": "Block release and repair Opcore self-validation if this receipt regresses." }, { "issue": "#28", "receiptPath": "docs/release/graph-release-receipt.payload.json", - "checksumSha256": "2ae48d67a1a908c5418f274c8d6d07843c131c9fc7f11ee1d0bd1f89fbcc628b", - "rollbackNote": "Keep ACE wrappers on current external tools if receipt regresses." + "checksumSha256": "f6d68bdecc65a83e4e34e2d208f9158c5c83d06957e1da6ef90ab2444dd2891d", + "rollbackNote": "Block release and repair Opcore self-validation if this receipt regresses." }, { "issue": "#29", "receiptPath": "docs/release/graph-release-receipt.payload.json", - "checksumSha256": "2ae48d67a1a908c5418f274c8d6d07843c131c9fc7f11ee1d0bd1f89fbcc628b", - "rollbackNote": "Keep ACE wrappers on current external tools if receipt regresses." + "checksumSha256": "f6d68bdecc65a83e4e34e2d208f9158c5c83d06957e1da6ef90ab2444dd2891d", + "rollbackNote": "Block release and repair Opcore self-validation if this receipt regresses." } ] } diff --git a/docs/release/graph-release-receipt.payload.json b/docs/release/graph-release-receipt.payload.json index 92f69ab..48c1f98 100644 --- a/docs/release/graph-release-receipt.payload.json +++ b/docs/release/graph-release-receipt.payload.json @@ -2,8 +2,8 @@ "schemaVersion": 1, "issue": "#17", "origin": "covibes-authored-synthetic", - "generatedAt": "2026-07-18T06:29:58.350Z", - "commitSha": "2d4e920e0b08a989953486b261327ed7496c328b", + "generatedAt": "2026-08-07T17:35:07.549Z", + "commitSha": "6ed9e0e5b48acdc298af1a2e1af1a59aae32ac78", "graphPackageVersions": [ { "packageName": "@the-open-engine/opcore-graph", @@ -59,7 +59,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/wave1", - "durationMs": 997 + "durationMs": 541 }, { "id": "opcore-graph-update", @@ -76,7 +76,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/wave1", - "durationMs": 681 + "durationMs": 543 }, { "id": "opcore-graph-watch", @@ -93,7 +93,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/wave1", - "durationMs": 662 + "durationMs": 533 }, { "id": "opcore-graph-status", @@ -110,7 +110,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/wave1", - "durationMs": 743 + "durationMs": 550 }, { "id": "opcore-graph-query", @@ -127,7 +127,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/wave1", - "durationMs": 691 + "durationMs": 531 }, { "id": "opcore-graph-impact", @@ -144,7 +144,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/wave1", - "durationMs": 734 + "durationMs": 529 }, { "id": "opcore-graph-search", @@ -161,7 +161,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/wave1", - "durationMs": 734 + "durationMs": 535 }, { "id": "opcore-graph-serve", @@ -178,7 +178,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/wave1", - "durationMs": 745 + "durationMs": 531 } ], "rustCommandCoverage": [ @@ -197,7 +197,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/rust-only", - "durationMs": 743 + "durationMs": 535 }, { "id": "opcore-graph-rust-update", @@ -214,7 +214,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/rust-only", - "durationMs": 723 + "durationMs": 538 }, { "id": "opcore-graph-rust-watch", @@ -231,7 +231,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/rust-only", - "durationMs": 733 + "durationMs": 539 }, { "id": "opcore-graph-rust-status", @@ -248,7 +248,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/rust-only", - "durationMs": 735 + "durationMs": 541 }, { "id": "opcore-graph-rust-query", @@ -265,7 +265,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/rust-only", - "durationMs": 784 + "durationMs": 532 }, { "id": "opcore-graph-rust-impact", @@ -282,7 +282,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/rust-only", - "durationMs": 691 + "durationMs": 541 }, { "id": "opcore-graph-rust-search", @@ -299,7 +299,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/rust-only", - "durationMs": 724 + "durationMs": 530 }, { "id": "opcore-graph-rust-serve", @@ -316,7 +316,7 @@ "status": "passed", "exitCode": 0, "fixture": "packages/fixtures/source-extraction/rust-only", - "durationMs": 719 + "durationMs": 527 } ], "directSqliteQueries": [ @@ -396,82 +396,82 @@ "benchmarks": [ { "metric": "install_setup_ms", - "value": 6, + "value": 1, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { "metric": "cold_build_ms", - "value": 997, + "value": 541, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { "metric": "incremental_update_ms", - "value": 681, + "value": 543, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { "metric": "impact_cold_ms", - "value": 734, + "value": 529, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { "metric": "impact_hot_ms", - "value": 756, + "value": 538, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { "metric": "search_ms", - "value": 734, + "value": 535, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { "metric": "daemon_startup_ms", - "value": 1130, + "value": 759, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { "metric": "daemon_query_ms", - "value": 1130, + "value": 758, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { "metric": "db_size_bytes", - "value": 245760, + "value": 241664, "unit": "bytes", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { "metric": "wal_size_bytes", - "value": 403792, + "value": 399672, "unit": "bytes", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" } ], @@ -507,10 +507,10 @@ "forbiddenMarkersAbsent": true, "generatedBuildMetadataAbsent": true, "privatePathsAbsent": true, - "pythonCrgSourceAbsent": true, - "pythonGraphPackageMetadataAbsent": true, - "pythonCrgGitHistoryAbsent": true, - "forbiddenImplementationPackageNamesAbsent": true, + "sourceProvenanceAbsent": true, + "packageMetadataAbsent": true, + "gitHistoryAbsent": true, + "foreignImplementationNamesAbsent": true, "inspections": [ "npm-pack-dry-run", "package-file-scan", @@ -652,10 +652,10 @@ "graphArtifact": { "artifactName": "opcore-graph-core", "artifactVersion": "0.2.1", - "targetPlatform": "darwin-arm64", + "targetPlatform": "linux-x64", "binaryPath": "opcore-graph-core", "checksumPath": "opcore-graph-core.sha256", - "checksumSha256": "72acf10de0ece619c3896943addfb0cc1b09dab22000b48a653b033224217bc8", + "checksumSha256": "c3a771435a7a8172a9e5f0bb20b0d111453efc70901e95308094664f77e7bdf1", "buildProfile": "release" }, "optionalSurfaces": [ diff --git a/docs/release/inspect-implementations-parity.md b/docs/release/inspect-implementations-parity.md index d6d7d27..119cc34 100644 --- a/docs/release/inspect-implementations-parity.md +++ b/docs/release/inspect-implementations-parity.md @@ -1,33 +1,12 @@ -# Inspect Implementations Parity +# Inspect Implementations Evidence Issue: #102 -`opcore inspect implementations` is read-only inspect evidence. It requires fresh graph provider status, uses graph `IMPLEMENTS` and `INHERITS` facts as mandatory input evidence, and materializes TypeScript/TSX locations through the inspect language-service path. It does not return edit plans, apply receipts, validation results, ASP host decisions, or gate authority. +`opcore inspect implementations` is read-only evidence over fresh graph `IMPLEMENTS` and `INHERITS` facts plus +TypeScript/TSX language-service locations. Results include implementation file, line, column, span, symbol, relation +kind, target identity, graph node ids, and resolver provenance. -## CIX Field Mapping +Covered behavior includes class implementation and inheritance, interface inheritance, TSX declarations, path aliases, +same-name disambiguation, node-id targets, and typed unavailable/stale/missing/ambiguous/unsupported failures. -| Old `cix impls` field | Opcore field | -|-----------------------|---------------| -| implementation file | `inspectResult.implementations[].file` | -| line and column | `line`, `column`, `span` | -| implementation name | `symbol.name` | -| implementation kind | `kind`: `implements`, `inherited_implements`, `extends`, `interface_extends` | -| queried target | `target.id`, `target.name`, `target.kind` | -| source proof | `evidence.graphNodeIds`, `evidence.resolver` | - -## Covered Evidence - -- Classes implementing interfaces, including inherited interface satisfaction. -- Classes extending classes, including imported and aliased base names. -- Interface inheritance. -- TSX component/model declarations. -- `tsconfig` path aliases through TypeScript project materialization. -- Same-name symbol disambiguation with `--line` and optional `--column`. -- Node-id class/type targets. -- Typed failures for unavailable/stale graph, missing symbols, malformed targets, ambiguous targets, and unsupported JS/JSX/other files. - -## Retained Gap - -Constructor parameter usage from old `cix impls` output is retained as a compatibility gap. Opcore classifies constructor usage as reference evidence, not implementation evidence, so it is covered by inspect references rather than implementation results. - -This evidence does not claim old-tool retirement, public certification, ASP host authority, or ACE-managed Opcore replacement. +Constructor parameter usage remains reference evidence rather than implementation evidence. diff --git a/docs/release/inspect-signature-parity.md b/docs/release/inspect-signature-parity.md index 9f6ba21..9cdc776 100644 --- a/docs/release/inspect-signature-parity.md +++ b/docs/release/inspect-signature-parity.md @@ -1,159 +1,10 @@ -# Inspect Signature Parity Evidence +# Inspect Signature Evidence Issue: #101 -Status: `opcore inspect signature` is implemented as read-only inspect-owned language-service evidence after mandatory fresh GraphProvider status. This does not retire CIX, change ACE guidance, or make ASP host decisions. +`opcore inspect signature` is read-only inspect-owned language-service evidence after graph freshness evaluation. +It returns symbol identity, source location/span, rendered signature, kind, parameters, type parameters, return type, +export/async state, overload index, graph node ids, and resolver provenance. -## Field Mapping - -| CIX `sig` field | Opcore field | -|-----------------|---------------| -| `name` | `inspectResult.signatures[].symbol.name` and `signature` text | -| `kind` | `inspectResult.signatures[].kind` | -| `file` | `inspectResult.signatures[].file` | -| `line` | `inspectResult.signatures[].line` | -| `isExported` | `inspectResult.signatures[].exported` | -| `isAsync` | `inspectResult.signatures[].async` | -| `typeParameters` | `inspectResult.signatures[].typeParameters` | -| `parameters` | `inspectResult.signatures[].parameters` | -| `returnType` | `inspectResult.signatures[].returnType` | -| not present | `providerStatus`, `target`, `span`, `symbol.id`, `evidence.graphNodeIds`, `evidence.resolver`, `overloadIndex` | - -## Commands Run - -```sh -npm run setup:tools -``` - -Output: - -```text -current tool wrappers ready at /Users/tom/.zeroshot/worktrees/cobalt-falcon-81/.ace/runtime/bin -``` - -```sh -./.ace/runtime/bin/cix sig packages/fixtures/inspect-symbol-parity/src/models.ts formatGreeting --line 20 --json -``` - -Output: - -```json -{ - "success": true, - "signatures": [ - { - "name": "formatGreeting", - "kind": "function", - "file": "src/models.ts", - "line": 20, - "isExported": true, - "isAsync": false, - "typeParameters": [], - "parameters": [ - { - "name": "message", - "type": "GreetingMessage", - "optional": false - } - ], - "returnType": "string" - } - ], - "timing": "141ms" -} -``` - -```sh -node packages/cli/dist/index.js graph build --repo packages/fixtures/inspect-symbol-parity --json -node packages/cli/dist/index.js inspect signature src/models.ts formatGreeting --line 20 --repo packages/fixtures/inspect-symbol-parity --json -``` - -Opcore output excerpt: - -```json -{ - "owner": "inspect", - "status": "ok", - "providerStatus": { - "state": "available" - }, - "inspectResult": { - "route": "signature", - "status": "ok", - "target": { - "kind": "file_symbol", - "path": "src/models.ts", - "symbolName": "formatGreeting", - "line": 20, - "nodeId": "function:src/models.ts#formatGreeting" - }, - "signatures": [ - { - "file": "src/models.ts", - "line": 20, - "column": 1, - "signature": "formatGreeting(message: GreetingMessage): string", - "kind": "function", - "parameters": [ - { - "name": "message", - "type": "GreetingMessage", - "optional": false - } - ], - "typeParameters": [], - "exported": true, - "async": false, - "returnType": "string", - "symbol": { - "id": "function:src/models.ts#formatGreeting", - "name": "formatGreeting", - "kind": "Function" - }, - "evidence": { - "graphNodeIds": [ - "function:src/models.ts#formatGreeting" - ], - "resolver": "language_service" - } - } - ] - } -} -``` - -Overload comparison: - -```sh -./.ace/runtime/bin/cix sig packages/fixtures/inspect-symbol-parity/src/overloads.ts describeGreeting --line 3 --json -``` - -Output: - -```json -{ - "success": false, - "message": "No signatures found for \"describeGreeting\"" -} -``` - -```sh -node packages/cli/dist/index.js inspect signature src/overloads.ts describeGreeting --line 3 --repo packages/fixtures/inspect-symbol-parity --json -``` - -Opcore returns two signatures with `overloadIndex` `0` and `1`: - -```json -[ - { - "signature": "describeGreeting(model: GreetingModel): string", - "overloadIndex": 0 - }, - { - "signature": "describeGreeting(message: GreetingMessage): string", - "overloadIndex": 1 - } -] -``` - -Retained gap: `opcore inspect implementations` remains typed unsupported. CIX stays as the retained guardrail until the implementations parity issue lands and #17/#4 evidence is updated. +The fixture suite covers functions, methods, constructors, classes, interfaces, type aliases, overloads, +imported/aliased symbols, path aliases, TS/TSX, JS, JSX, file-symbol targets, and node-id targets. diff --git a/docs/release/provenance-receipts.md b/docs/release/provenance-receipts.md index 09a6af0..201c67c 100644 --- a/docs/release/provenance-receipts.md +++ b/docs/release/provenance-receipts.md @@ -1,11 +1,11 @@ # Provenance Receipts -Maintainer provenance evidence for the Opcore alpha release gate. +Maintainer provenance evidence for the Opcore release gate. -- Current-tree files scanned: 695 -- Git-history commits scanned: 305 -- Python code-review-graph source findings: 0 -- Python package metadata findings: 0 -- Copied git-history marker findings: 0 +- Current repository and package outputs are scanned. +- Generated provider/build state is forbidden from tracked source. +- Package dependencies and TypeScript outputs must remain repository-confined. +- Native artifact metadata must use package-relative paths. +- Copied Git-history markers are rejected. -Allowed old-tool mentions are limited to dev current-tool setup, ACE routing, and graph reference evidence fixtures. +Run `npm run provenance:check` to refresh the executable proof. diff --git a/docs/release/retained-guardrail-matrix.md b/docs/release/retained-guardrail-matrix.md deleted file mode 100644 index 4636d39..0000000 --- a/docs/release/retained-guardrail-matrix.md +++ /dev/null @@ -1,64 +0,0 @@ -# Retained Guardrail Matrix - -Issue: #54 - -Status: master retained-guardrail matrix for Opcore epic #13. - -This matrix records current Rox, CRG, and CIX guardrail status after the #13 parity-ledger work. It is private release-readiness evidence only. It is not a public release, npm publish, registry or certification claim, ASP authority claim, ACE wrapper cutover, or old-tool retirement announcement. - -## Invariant - -No surface is marked `replaced` unless an installed Opcore receipt proves exact replacement for that surface and the release/cutover issue explicitly accepts the replacement. Until then, current external Rox, CRG, and CIX guardrails remain retained or replacement-deferred. - -`docs/release/asp-dogfood-receipt.json` pins `oldToolReplacementClaimed: false`. That value must stay false until a later installed-artifact receipt authorizes a surface-specific replacement claim. - -## Receipt Sources - -- `docs/release/crg-graph-parity-ledger.md` (#53): CRG graph parity ledger and issue-number collision note. -- `docs/release/graph-release-receipt.json` (#17): graph release command coverage and serve transport evidence. -- `docs/release/cutover-receipt.json` (#30): installed `node_modules/.bin/opcore` command receipts, with `opcoreBinOnly:true` meaning only Opcore-owned bins are exposed and old bins are absent from the cutover environment. -- `docs/release/asp-dogfood-receipt.json` (#120): advisory ASP dogfood, retained current-tool guardrails, inspect/edit deferred coverage, and `oldToolReplacementClaimed: false`. -- `docs/validation/rust-adapter-parity.md`, `docs/validation/rust-retained-tools-receipts-2026-06-23.md`, and `docs/validation/rust-old-rox-comparison-receipt-2026-06-27.json` (#29, PR #104 merge `ab0362d339ec2c41b0cc71ae5bb400c4b8254e36`): Rust adapter parity, retained Rox compatibility evidence, and the #29 comparison evidence consumed by #30 per-surface Rust decisions with `oldToolReplacementClaimed: false`. -- `docs/release/inspect-signature-parity.md` and `docs/release/inspect-implementations-parity.md`: CIX inspect parity evidence and retained gaps. - -## Matrix - -| Current guardrail surface | Opcore/lattice evidence | Matrix state | Guardrail decision | -|---|---|---|---| -| CRG graph: build/update/watch/status/query/impact/review-context/detect-changes/search/serve | `crg-graph-parity-ledger.md`; `graph-release-receipt.json` command coverage; `cutover-receipt.json` graph command receipts from installed artifacts with `environmentIsolation.oldBinsAbsent.{crg,cix,rox}: true`. | `deferred` | Parity is demonstrated, but replacement claim is deferred. CRG remains the retained graph guardrail until downstream cutover work accepts retirement. | -| Rox validation - TS/JS: `typescript.syntax`, `typescript.types`, `typescript.import-graph`, `typescript.dead-code`, `typescript.relevant-tests` | `release-receipt.json` validation check ids; `cutover-receipt.json` validation receipts: `opcore-check-changed`, `check-files`, `validate-request`, `validate-pre-write-pass`, and fail-closed `validate-pre-write-fail`. | `deferred` | Installed validation receipts exist, but Rox changed/repo guardrails remain active through `current-tools:validate-changed` and `current-tools:validate-all`. No old-tool replacement claim. | -| Rox validation - Rust foundation: `rust.source-hygiene`, `rust.fmt`, `rust.cargo-check`, `rust.clippy`, `rust.file-length` | `rust-adapter-parity.md` native Rust checks; `rust-retained-tools-receipts-2026-06-23.md`; `asp-dogfood-receipt.json` retained current-tool guardrails. | `retained` | Keep Rox Rust gates active. Runtime, cache, and retirement decisions feed #10/#26-#30 rather than #13. | -| Rox validation - Rust retained tool: `rust.rustdoc` | `rust-adapter-parity.md` records rustdoc diagnostics as blocking policy evidence and keeps retained guardrails active when `rustdoc` is missing; `rust-old-rox-comparison-receipt-2026-06-27.json` records no graph fact replacing rustdoc diagnostics and `replacementStatus: "retained"`. | `retained` | #30 decision: retain. No installed-artifact receipt or maintainer approval flips this surface. Rustdoc diagnostics, broken intra-doc links, and documentation-policy failures remain current-tool evidence. | -| Rox validation - Rust retained tool: `rust.import-graph` | `rust-adapter-parity.md` records native unresolved module/use path, orphan source, and module-cycle checks with optional `cargo-depgraph` enrichment; `rust-old-rox-comparison-receipt-2026-06-27.json` records Rust graph evidence exists but `replacementStatus: "deferred"`. | `deferred` | #30 decision: defer replacement. Rust graph facts are useful evidence, but cargo-depgraph-enriched import checks and retained current-tool behavior are not exactly replaced, and no maintainer approval authorizes retirement. | -| Rox validation - Rust retained tool: `rust.dead-code` | `rust-adapter-parity.md` records native `cargo check` `dead_code` denial plus orphan-source evidence; `rust-old-rox-comparison-receipt-2026-06-27.json` records graph-backed dead-public-export evidence but `replacementStatus: "retained"`. | `retained` | #30 decision: retain. Graph signals do not replace Cargo compiler reachability or current-tool gate behavior, and `oldToolReplacementClaimed` stays false. | -| Rox validation - Rust retained tool: `rust.unused-deps` | `rust-adapter-parity.md` records `cargo-udeps` parsing and degraded/unsupported behavior when the tool is missing; `rust-old-rox-comparison-receipt-2026-06-27.json` records no graph fact replacing cargo-udeps unused dependency analysis and `replacementStatus: "retained"`. | `retained` | #30 decision: retain. Unused dependency evidence remains uniquely provided by cargo-udeps/current tools until an installed-artifact receipt and approval accept replacement. | -| Rox validation - Rust retained tool: `rust.function-metrics` | `rust-adapter-parity.md` records `rust-code-analysis-cli` metrics for line count, complexity, and parameter thresholds; `rust-old-rox-comparison-receipt-2026-06-27.json` records graph spans/signatures exist but `replacementStatus: "retained"`. | `retained` | #30 decision: retain. Rust graph symbol spans do not exactly replace rust-code-analysis metrics or threshold behavior. | -| Rox validation - Rust aggregate gate: `current-tools:validate-rust-graph` | `rust-adapter-parity.md` lists `npm run current-tools:validate-rust-graph` as a retained guardrail; `rust-old-rox-comparison-receipt-2026-06-27.json` records this aggregate gate with `replacementStatus: "retained"` and `oldToolReplacementClaimed: false`. | `retained` | #30 decision: retain the aggregate Rust graph current-tool gate. The #29 receipt is comparison evidence only and does not retire the command. | -| Rox validation - Rust changed-file current-tool gate: `current-tools:validate-changed` | `rust-adapter-parity.md` lists `npm run current-tools:validate-changed` as a retained guardrail for Rust parity work; `asp-dogfood-receipt.json` records the changed-file current-tool guardrail as passed retained evidence. | `retained` | #30 decision: retain the Rust-relevant changed-file current-tool gate. No #29/#30 installed-artifact receipt or approval authorizes replacing this command. | -| CIX inspect: symbols/definition/references/signature/implementations/search | `cutover-receipt.json` inspect receipts: `inspect-symbols`, `inspect-definition`, `inspect-references`, `inspect-signature`, `inspect-implementations`, and `inspect-search`; `inspect-signature-parity.md`; `inspect-implementations-parity.md`; #49 node-id references fix. | `deferred` | Installed inspect receipts exist, but ASP dogfood still records inspect as `parity-blocker` because ASP inspect request/response mapping is outside #120. CIX remains retained until an inspect-specific cutover accepts replacement. | -| CIX edit: exact/multi/search-replace/patch/tree/rename/move/signature | `cutover-receipt.json` edit receipts: `edit-preview`, `edit-apply`, and fail-closed `edit-refused`; edit behavior remains under edit-owned validation plans. | `retained` | `asp-dogfood-receipt.json` marks edit `retained-old-tool-gate`: ASP dogfood does not authorize edits or apply behavior. CIX edit remains retained until edit-specific installed receipts and ASP/host authority decisions accept replacement. | - -No row is currently `replaced`. - -## Current Retained Gates - -These commands remain active retained guardrails: - -```sh -npm run current-tools:validate-changed -npm run current-tools:validate-rust-graph -npm run current-tools:validate-all -``` - -`rust-old-rox-comparison-receipt-2026-06-27.json` records `current-tools:validate-rust-graph` as retained for #30 comparison purposes. `rust-adapter-parity.md` keeps both `current-tools:validate-rust-graph` and `current-tools:validate-changed` active for Rust parity work. `asp-dogfood-receipt.json` records `current-tools-validate-changed` and `current-tools-validate-rust-graph` as passed retained guardrails, while `current-tools-validate-all` remains `retained-not-run` unless explicitly requested. - -## Issue Truth-Up Queue - -After this matrix lands, update these coordination issues with the matrix link and the same current truth: - -- Opcore #13: #54 closes the retained old-tool guardrail matrix; no row is replaced and `oldToolReplacementClaimed:false` remains pinned. -- Opcore #30: old-tool retirement remains deferred; CRG graph has installed parity receipts, but the #29 Rust comparison receipt keeps `rust.rustdoc`, `rust.dead-code`, `rust.unused-deps`, `rust.function-metrics`, `current-tools:validate-rust-graph`, and Rust `current-tools:validate-changed` retained, keeps `rust.import-graph` deferred, and keeps `oldToolReplacementClaimed:false`. -- Opcore #10 / #26-#30: Rust parity rows remain owned by the Rust graph/Rox-retirement lane; #54 does not retire Rox. -- `the-open-engine/agent-server-protocol#26`: ASP #18 has closed with Option A accepted: no edit/inspect daemon and installed cold-start is acceptable. ASP coordination should now treat Opcore as one enrolled provider/server, not the host or authority. ACE remains an optional downstream host client. Inspect remains a parity blocker and edit remains a retained old-tool gate until ASP request/response and host-authority work accepts replacement. - -Do not update public docs, publish packages, announce retirement, or change repository visibility from this matrix. diff --git a/docs/superpowers/plans/2026-07-09-opcore-legacy-policy-parity.md b/docs/superpowers/plans/2026-07-09-opcore-legacy-policy-parity.md deleted file mode 100644 index 76f554e..0000000 --- a/docs/superpowers/plans/2026-07-09-opcore-legacy-policy-parity.md +++ /dev/null @@ -1,319 +0,0 @@ -# Opcore Native Policy Parity Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Make Opcore natively represent and enforce the same repo-local robustness policy capabilities the team depends on today, without importing, translating, loading, or depending on external policy config or runtime. - -**Non-negotiable boundary:** Active behavior must come only from Opcore-owned `.opcore/config` policy, Opcore packages, and Opcore check packs. Do not add config importers, translation shims, public fields that describe imported policy, or active runtime branches that inspect external policy config. - -**Architecture:** Add a first-class repo validation policy model under `.opcore/config`, then route every Opcore validation entrypoint through one policy-aware check factory. Existing validation packages already implement many primitives; this work wires configurable thresholds, selection, docs, clone, path policy, TypeScript architecture rules, dead-code entrypoints, repo lint plugins, Rust command gates, and custom check packs through supported Opcore-owned APIs. - -**Tech Stack:** TypeScript, Node >=22, existing `@the-open-engine/opcore-validation*` packages, Rust graph-core clone subcommand, Node test runner. - ---- - -## Clean Target - -Opcore must support this native config shape: - -```json -{ - "schemaVersion": 1, - "kind": "opcore_init_config", - "validation": { - "adapters": ["typescript", "rust", "docs", "clone"], - "timeoutMs": 120000, - "pathPolicy": { - "include": ["packages/", "scripts/"], - "exclude": ["node_modules", "dist", ".ace", ".agents", ".claude", ".codex", ".opcore"] - }, - "checks": { - "packs": ["./.opcore/checks/covibes-policy.cjs"], - "disabled": ["typescript.types"], - "defaults": ["docs.existence", "docs.freshness"], - "typescript": { - "fileLength": { "maxFileLines": 600 }, - "functionMetrics": { "maxFunctionLines": 120, "maxComplexity": 10, "maxParams": 4 }, - "lint": { "repoPlugin": "./eslint-local-rules/index.cjs", "cacheDependencyGlobs": ["AGENTS.md", "eslint-local-rules/**/*.cjs"] }, - "importGraph": { - "ignoreTypeOnlyImports": true, - "layerRules": [ - { "name": "no-client-to-server", "from": "%/client/src/%", "to": "%/server/%" } - ] - }, - "deadCode": { "entrypoints": ["server/shared/types/preview-surface.ts"] } - }, - "rust": { - "fileLength": { "maxFileLines": 500 }, - "functionMetrics": { "maxFunctionLines": 80, "maxComplexity": 10, "maxParams": 4 }, - "commandGates": [ - { "id": "rust-gate.cargo-test", "command": "cargo", "args": ["test"], "cwd": ".", "timeoutMs": 120000 } - ] - }, - "docs": { - "enabled": { "existence": true, "freshness": true, "staleness": false, "length": true, "hubCoverage": true, "subtreeCoverage": true }, - "policy": { - "filenames": ["AGENTS.md", "CLAUDE.md"], - "requiredPaths": ["."], - "requireRoot": true, - "minimumContentLength": 1, - "maxLines": 220, - "maxSectionLines": 80 - }, - "history": { "maxStaleDays": 90 }, - "hubCoverage": { "minFanIn": 5, "minFanOut": 5, "requireExplicitMention": true }, - "subtreeCoverage": { "minLoc": 20000 } - }, - "clone": { - "windowSize": 16, - "minLines": 16, - "threshold": 5, - "partitions": [["server", "shared"], ["client"], ["platform-cli"]], - "exclude": ["docs/**", "generated/**"], - "modes": ["staged", "changed", "files"] - } - } - } -} -``` - -Root-level fields outside `validation` may remain as install metadata, but they are not active validation behavior. In particular, do not read or translate a root-level `checks` object. - -## Current Gaps - -- Repo config parsing needs a normalized native `validation` policy model. -- All validation entrypoints need to construct checks through the same policy-aware factory. -- Built-in check enable/disable and default-scope controls need to apply consistently. -- Thresholds need to flow into TypeScript and Rust file/function checks. -- Path policy needs to filter file views before checks run. -- Clone detection needs native policy knobs for window size, threshold, partitions, excludes, and scope modes. -- Docs checks need native existence/freshness/staleness/length/hub/subtree configuration. -- TypeScript checks need native import-layer rules, type-only handling, dead-code entrypoints, and repo lint plugin rules. -- Rust checks need native command gates for repo-specific commands. -- Status, doctor, reports, package wiring, and packlists need to expose the native policy without implying replacement claims. - -## Files - -Create or keep: - -- `packages/validation-policy/` - - Shared native policy parser, path-policy helper, check-pack loader, and policy-aware check factory. -- `packages/opcore/src/repo-validation-config.ts` - - Thin public re-export for Opcore facade consumers. -- `packages/opcore/src/repo-validation-policy.ts` - - Thin public re-export plus clone invoker injection for the Opcore facade. -- `packages/opcore/src/path-policy.ts` - - Thin public re-export for tests and downstream use. -- `packages/opcore/src/repo-check-packs.ts` - - Thin public re-export for check-pack helpers. -- `packages/validation-typescript/src/import-layer-rules-check.ts` -- `packages/validation-typescript/src/dead-code-entrypoints.ts` -- `packages/validation-typescript/src/lint-plugin-check.ts` -- `packages/validation-rust/src/command-gate-check.ts` - -Modify: - -- `packages/opcore/src/validation-composition.ts` -- `packages/opcore/src/advanced/validation-composition.ts` -- `packages/asp-provider/src/validation-composition.ts` -- `packages/opcore/src/scan.ts` -- `packages/opcore/src/status.ts` -- `packages/opcore/src/doctor.ts` -- `packages/opcore/src/reporting.ts` -- `packages/validation-typescript/src/index.ts` -- `packages/validation-rust/src/index.ts` -- `packages/validation-docs/src/index.ts` -- `packages/validation-clone/src/clone-check.ts` -- `packages/contracts` -- package metadata, workspace checks, release package dirs, packlists, and AGENTS.md where architecture changes require it. - -## Task 1: Native Repo Validation Config - -- [x] Add normalized `.opcore/config.validation` parser. -- [x] Validate adapters, arrays, positive integers, thresholds, path policy, docs policy, clone policy, and TypeScript policy. -- [x] Keep unknown top-level config fields inert. -- [x] Keep check-pack loading repo-relative and package-resolvable through native `validation.checks.packs`. -- [x] Remove root-level `checks` translation and tests. - -Verification: - -```bash -node --test --test-name-pattern "repo validation config|native check pack config|native disabled|native docs" tests/validation-cli.test.mjs -``` - -## Task 2: Native Path Policy - -- [x] Add repo-relative include/exclude matching. -- [x] Filter validation file-view scope files, visible files, overlays, reads, existence checks, and overlay lookup. -- [x] Confirm graph-backed checks do not bypass the filtered file view through unfiltered graph requirements. - -Verification: - -```bash -node --test --test-name-pattern "path policy" tests/validation-cli.test.mjs -``` - -## Task 3: Policy-Aware Check Construction - -- [x] Add shared `@the-open-engine/opcore-validation-policy` package. -- [x] Route public `opcore check`, advanced check/validate, scan, and ASP provider validation through the same policy-aware factory. -- [x] Validate unknown check ids after built-ins and packs are assembled. -- [x] Apply adapter selection, disabled checks, default-scope promotion, and path policy wrappers. -- [x] Add policy evidence to status, doctor, and reports. - -Verification: - -```bash -node --test tests/validation-cli.test.mjs tests/asp-provider.test.mjs -``` - -## Task 4: Built-In Thresholds And Check Selection - -- [x] Wire TypeScript file/function thresholds. -- [x] Wire Rust file/function thresholds. -- [x] Wire docs enabled flags to docs default/disabled selection. -- [x] Reject unknown native check ids. - -Verification: - -```bash -node --test --test-name-pattern "configured TypeScript thresholds|configured Rust thresholds|unknown check id" tests/validation-cli.test.mjs -``` - -## Task 5: Clone Policy - -- [x] Extend clone contract request fields for `windowSize`, `threshold`, `partitions`, `exclude`, and `modes`. -- [x] Pass clone policy through TypeScript validation adapter to graph-core clone analysis. -- [x] Implement native graph-core clone filtering for window, threshold, partitions, excludes, and scope modes. - -Verification: - -```bash -node --test tests/validation-clone.test.mjs -cargo test -p opcore-graph-core clone:: -``` - -## Task 6: Docs Policy - -- [x] Add docs length max-line and max-section checks. -- [x] Add hub fan-out and explicit mention policy. -- [x] Add subtree coverage policy. -- [x] Wire docs config through the shared policy factory. - -Verification: - -```bash -node --test tests/validation-docs.test.mjs -``` - -## Task 7: TypeScript Import-Layer Rules - -- [x] Add native `typescript.import-layer-rules` check. -- [x] Support `ignoreTypeOnlyImports`, `layerRules`, and `fromNot`. -- [x] Wire TypeScript import graph policy into check construction. - -Verification: - -```bash -node --test --test-name-pattern "import layer|type-only imports" tests/validation-typescript.test.mjs -``` - -## Task 8: TypeScript Dead-Code Entrypoints - -- [x] Add native dead-code `entrypoints` option. -- [x] Treat configured entrypoint files as graph reachability roots. -- [x] Keep unsupported graph coverage visible instead of claiming clean coverage when evidence is missing. -- [x] Wire entrypoints through `.opcore/config.validation.checks.typescript.deadCode`. - -Verification: - -```bash -node --test --test-name-pattern "configured TypeScript dead-code entrypoints" tests/validation-typescript.test.mjs -node --test --test-name-pattern "configured TypeScript dead-code entrypoints" tests/validation-cli.test.mjs -``` - -## Task 9: TypeScript Repo Lint Plugin - -- [x] Add native `typescript.lint-plugin` check only when `validation.checks.typescript.lint.repoPlugin` is configured. -- [x] Use repo-relative plugin paths only; reject absolute paths, parent traversal, and resolved paths outside the repo. -- [x] Load plugin rules with `createRequire(join(repoRoot, "package.json"))`. -- [x] Cache plugin loading by plugin path and configured dependency mtimes. -- [x] Preserve the default `typescript.lint` check. - -Verification: - -```bash -node --test --test-name-pattern "repo lint plugin" tests/validation-typescript.test.mjs -``` - -## Task 10: Rust Command Gates - -- [x] Add native Rust command-gate check definitions from `validation.checks.rust.commandGates`. -- [x] Restrict commands to repo-contained cwd and explicit command/args arrays. -- [x] Honor per-gate timeout and repo-level timeout. -- [x] Return command stdout/stderr/status evidence without mutating source files. -- [x] Wire command gates through the shared policy factory. - -Verification: - -```bash -node --test --test-name-pattern "Rust command gate" tests/validation-rust.test.mjs tests/validation-cli.test.mjs -``` - -## Task 11: Status, Doctor, Reports - -- [x] `opcore status --json` reports whether native validation policy is loaded, check count, disabled ids, default ids, packs, and degraded policy fields without running checks. -- [x] `opcore doctor --json` reports config parse errors, loaded packs, native policy readiness, and next actions. -- [x] Scan/reporting includes configured/disabled check evidence and policy degradations. -- [x] No status, doctor, report, or install output suggests config import or translation mode. - -Verification: - -```bash -node --test --test-name-pattern "policy readiness|doctor|report" tests/opcore-facade.test.mjs tests/validation-cli.test.mjs -``` - -## Task 12: Packaging, Docs, And Guardrails - -- [x] Update package exports, workspace checks, release package dirs, lockfile, and packlists for new package/files. -- [x] Update AGENTS.md for the new `packages/validation-policy` ownership boundary and native policy rules. -- [x] Ensure launch-facing docs and package metadata say Opcore and do not claim replacement of external tools. -- [x] Ensure no new active code path imports, translates, or loads external policy config. - -Verification: - -```bash -npm run build -npm run workspace:check -npm run pack:check -``` - -## Task 13: Final Verification - -- [x] Run targeted test suites for changed packages. -- [x] Run local CI-equivalent or the strongest feasible repo gate. -- [x] Run current external retained guardrail comparison only as evidence that no guardrail coverage was lost; do not make Opcore depend on those tools. - - Attempted `npm run current-tools:validate-changed`; it fails before running retained analysis with `Cannot read properties of undefined (reading 'trim')`. -- [x] Record exact commands and outcomes in the final handoff. - -Minimum verification: - -```bash -npm run build -node --test tests/validation-cli.test.mjs -node --test tests/validation-typescript.test.mjs -node --test tests/validation-docs.test.mjs -node --test tests/validation-clone.test.mjs -node --test tests/validation-rust.test.mjs -node --test tests/asp-provider.test.mjs -cargo test -p opcore-graph-core clone:: -``` - -## Completion Criteria - -- Native `.opcore/config.validation` can express the depended-on policy surface. -- Every validation entrypoint uses the native policy-aware check factory. -- TypeScript, Rust, docs, clone, path policy, check packs, and ASP provider paths honor native policy. -- Unsupported or degraded evidence is explicit. -- No config importer, external-policy loader, or active translation layer exists. -- No public/product surface claims replacement of external tools. diff --git a/docs/validation/rust-adapter-parity.md b/docs/validation/rust-adapter-parity.md index 21b2dbf..5767394 100644 --- a/docs/validation/rust-adapter-parity.md +++ b/docs/validation/rust-adapter-parity.md @@ -1,135 +1,12 @@ -# Rust Adapter Parity Evidence +# Rust Validation Evidence -Status: private dogfood and release-readiness evidence for #20, #21, #28, and the #30 validation-doc slice. +`@the-open-engine/opcore-validation-rust` owns Rust provider assessment checks for source hygiene, formatting, +Cargo compilation, clippy, rustdoc, import structure, dead code, graph signals, unused dependencies, file length, +and function metrics. -Source rows: `agent-server-protocol/docs/planning/old-tool-compatibility-matrix.{md,json}` Rust validation rows -`lattice-rox-rust-adapter-and-function-metrics`, `orchestra-rox-rust-gate`, `orchestra-rox-native-dependencies`, -`covibes-gateway-rox-rust-gate`, `robustness-engine-rust-adapter-source`, and -`robustness-engine-cargo-manifest-handling`. +Cargo-backed checks materialize one isolated after-state workspace per validation file view. Missing optional tools +produce typed degraded or unsupported outcomes with `requiredTool`; they never become silent passes. Graph-backed +checks consume `ValidationGraphProviderClient` facts through public contracts only. -## #30 Validation Retirement Decisions - -This is private release-readiness documentation only. It is not a publish action, visibility change, public -announcement, public replacement claim, or old-tool retirement approval. - -Decision policy for #30 validation docs: no surface is `replaced` unless installed-artifact receipt evidence proves the -exact replacement and maintainer approval accepts retirement for that surface. The landed #29 comparison receipt does -not make a retirement decision, pins `oldToolReplacementClaimed: false`, and records no public release actions. It -landed through PR #104 at merge `ab0362d339ec2c41b0cc71ae5bb400c4b8254e36`. - -Evidence anchors: - -- [#29 Rust old-Rox comparison receipt](rust-old-rox-comparison-receipt-2026-06-27.json) and - [summary](rust-old-rox-comparison-receipt-2026-06-27.md). -- [#30 cutover receipt](../release/cutover-receipt.json) and - [summary](../release/cutover-receipt.summary.md). The receipt proves installed command coverage, including Rust graph - command receipts, but it does not include maintainer approval to retire Rust retained-tool rows. - -| Surface | Decision | Evidence | Why retirement is not accepted | Current guardrail action | -|---|---|---|---|---| -| `rust.rustdoc` | retained | #29 receipt records no graph replacement evidence. | Rustdoc diagnostics, broken intra-doc links, and documentation-policy failures remain unique current-tool evidence. No installed-artifact receipt plus maintainer approval proves exact replacement. | Keep current external Rust guardrails active for rustdoc coverage. | -| `rust.import-graph` | deferred | #29 records Rust graph `IMPORTS_FROM`/`DEPENDS_ON` facts; #30 records installed Rust graph build/query/impact/review-context/detect-changes/search receipts. | Graph facts are useful parity evidence, but rustdoc and cargo-depgraph-enriched import checks remain retained where graph facts are not sufficient. No maintainer approval flips this row. | Keep current external import-graph guardrails active while native graph evidence complements them. | -| `rust.dead-code` | retained | #29 records exported symbol metadata and graph-backed dead-public-export signals. | Cargo `dead_code` diagnostics and compiler reachability remain uniquely provided by current tools. Graph dead-public-export evidence is not exact replacement evidence. | Keep current external dead-code guardrails active. | -| `rust.unused-deps` | retained | #29 records no graph replacement evidence. | Cargo-udeps unused dependency analysis remains the unique evidence source. | Keep current external unused-dependency guardrails active. | -| `rust.function-metrics` | retained | #29 records Rust function/method spans and signatures; #30 records installed Rust graph receipts. | Rust-code-analysis complexity, line-count, and parameter-threshold metrics remain unique current-tool evidence. Spans/signatures are not exact metric replacement evidence. | Keep current external function-metric guardrails active. | -| `current-tools:validate-rust-graph` | retained | #29 records the aggregate Rust graph guardrail as retained; #30 Rust receipts are graph-owned installed command receipts. | No receipt proves an exact aggregate replacement for the current-tools Rust graph gate, and no maintainer approval retires it. | Continue running `npm run current-tools:validate-rust-graph`. | -| Rust portion of `current-tools:validate-changed` | retained | #30 installed `opcore check changed` receipt uses `--checks typescript.syntax`; #29 carries only Rust comparison evidence with `oldToolReplacementClaimed: false`. | The installed changed-check receipt does not exercise Rust retained-tool coverage, and no Tom approval flips Rust changed-file guardrails. | Continue running `npm run current-tools:validate-changed` for changed Rust-owned inputs and mixed changes. | - -## Native Rust Checks - -`@the-open-engine/opcore-validation-rust` exports these provider assessment checks: - -| Check | Native behavior | Retained compatibility | -|---|---|---| -| `rust.source-hygiene` | Rejects `.inc`, `include!(...)`, `rustfmt::skip`, `allow(dead_code)`, broad `allow`/`expect`, and owned lint suppressions. | none for covered inputs | -| `rust.fmt` | Runs rustfmt or cargo fmt in a temporary workspace. | current external gate stays until #27 self-dogfood proof | -| `rust.cargo-check` | Runs structured Cargo metadata and `cargo check --message-format=json`. | current external gate stays until #21 runtime/cache decision | -| `rust.clippy` | Runs `cargo clippy` with Opcore-owned lint set. | current external gate stays until #21 runtime/cache decision | -| `rust.rustdoc` | Runs `cargo doc --no-deps --all-features --message-format=json`; rustdoc diagnostics are blocking policy evidence. | unsupported when rustdoc is missing; retain old gate | -| `rust.import-graph` | Reports unresolved `mod`, unresolved `crate`/`self`/`super` use paths, orphan source files, and module cycles from fileView after-state content. | cargo-depgraph enrichment remains degraded when unavailable | -| `rust.dead-code` | Reuses structured `cargo check --message-format=json` dead-code diagnostics from `rust.cargo-check` and adds native orphan-source dead-code evidence, without a second `-Ddead_code` compile. | core Cargo absence makes adapter unavailable; old gates stay active | -| `rust.graph-signals` | Reports graph-backed untested public Rust surface, dead public exports, module orphans, and module cycles through `ValidationGraphProviderClient`. | requires available graph facts; does not replace cargo/rustdoc/clippy/Rox guardrails | -| `rust.unused-deps` | Runs cargo-udeps with workspace or package scoping and parses unused dependency names into deterministic diagnostics. | unsupported when cargo-udeps is missing; retain old gate | -| `rust.function-metrics` | Runs rust-code-analysis-cli JSON object/array output and enforces 80 lines, complexity 10, params 4. | unsupported when tool is missing; retain old gate | - -## Retained Compatibility Ledger - -`opcore status --json` and `opcore doctor --json` must report retained blockers in `degradedChecks` only when a -supporting retained tool is missing. With `rustdoc`, `cargo-depgraph`, `cargo-udeps`, and `rust-code-analysis-cli` -available, the Rust adapter is `available` with `degradedChecks: []`. Missing-tool entries are not passing no-op checks; -they are machine-readable cutover blockers for #27/#28/#29. - -| Check | Opcore | Orchestra | CoVibes | Gateway | Required tool when degraded | Follow-up | -|---|---:|---:|---:|---:|---|---| -| `rust.rustdoc` | no | yes | no | yes | `rustdoc` | #27/#28/#29 | -| `rust.import-graph` | no | yes | no | yes | `cargo-depgraph` | #27/#28/#29 | -| `rust.dead-code` | no | yes | no | yes | core `cargo` only | #27/#28/#29 | -| `rust.graph-signals` | yes | no | yes | yes | graph provider | #28/#29 | -| `rust.unused-deps` | no | yes | no | yes | `cargo-udeps` | #27/#28/#29 | -| `rust.function-metrics` | yes | yes | no | yes | `rust-code-analysis-cli` | #27/#28/#29 | - -Cargo.lock-only changes are retained compatibility too. Current native ownership covers `.rs`, `.inc`, and -`Cargo.toml`; lockfile-only policy remains under #21 until an explicit cutover decision expands ownership. - -## Scope And Overlay Evidence - -Rust-owned inputs are `.rs`, `.inc`, and `Cargo.toml`. Cargo.lock-only changes are explicitly skipped as retained compatibility. Tree scope uses committed Git tree content through the validation workspace. Pre-write and hypothetical requests use fileView after-state overlays and one run-scoped temporary materialization for Cargo tools. Selected checks with the same materialization environment share the workspace; the runner removes it on normal, fail-fast, streaming, or failed exits. - -Representative Opcore diffs: - -```diff -diff --git a/crates/graph-core/src/lib.rs b/crates/graph-core/src/lib.rs -+#[allow(dead_code)] -+pub fn hidden_regression() {} -``` - -Expected native result: `rust.source-hygiene` returns `policy_failure` with `RUST_SOURCE_ALLOW_DEAD_CODE`. - -```diff -diff --git a/Cargo.toml b/Cargo.toml -@@ --members = ["crates/graph-core"] -+members = ["crates/graph-core", "crates/new-member"] -``` - -Expected native result: Rust checks run because Cargo.toml is adapter-owned. Missing package/toolchain failures are typed as `policy_failure`, `unsupported_request`, or `infrastructure_failure`, not silent skips. - -Representative Orchestra comparison diffs: - -```diff -diff --git a/crates/orchestra-core/src/lib.rs b/crates/orchestra-core/src/lib.rs -+pub fn unchecked(values: Vec) -> i32 { values[3] } -``` - -Expected native result: `rust.clippy` reports owned lint diagnostics when run beside Orchestra current gates. Orchestra must keep `npm run rox:ci`, `npm run rox:repo`, and `npm run rox:check` until #28 records replacement evidence from the same diff. - -## #21 Runtime Facts - -- Temporary workspace materialization is required for Cargo-backed checks, but it is bounded to one workspace per - validation file-view state and materialization environment. -- Missing `cargo`, `rustfmt`, or `clippy` makes the Rust adapter unavailable. Missing `rustdoc`, `cargo-udeps`, - `cargo-depgraph`, or `rust-code-analysis-cli` keeps the Rust adapter degraded and annotates the retained blocker - entry with `requiredTool`; no generic retained entries remain when those tools are available. -- Retained blocker entries include `currentUsage` booleans for Opcore, Orchestra, CoVibes, and gateway consumers. -- cargo-depgraph is optional enrichment for `rust.import-graph`; missing state is degraded, not a policy failure. -- cargo-udeps and rust-code-analysis-cli are required for their selected checks; missing state returns `unsupported_request`. -- #61 adds no validation daemon, hidden validation cache, Rox import, Rox cache read, or Rox shellout from native checks. - -## Retained Guardrails - -Keep Opcore current external Rust guardrails until #21/#27 accept parity and runtime behavior: - -```sh -npm run current-tools:validate-rust-graph -npm run current-tools:validate-changed -npm run current-tools:validate-all -``` - -Keep Orchestra current gates until #28 records safe comparison evidence: - -```sh -cd ../orchestra -npm run rox:ci -npm run rox:repo -npm run rox:check -``` +Opcore validates Rust changes through `npm run opcore:self-check`, targeted `opcore check` scopes, and the normal +Rust/CI gates. No external development toolchain is part of this repository's validation path. diff --git a/docs/validation/rust-old-rox-comparison-receipt-2026-06-27.json b/docs/validation/rust-old-rox-comparison-receipt-2026-06-27.json deleted file mode 100644 index 328503a..0000000 --- a/docs/validation/rust-old-rox-comparison-receipt-2026-06-27.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "schemaVersion": 1, - "issue": "#29", - "origin": "covibes-authored-old-rox-comparison", - "generatedAt": "2026-06-27T00:00:00.000Z", - "privateRepo": true, - "oldToolReplacementClaimed": false, - "publicReleaseActions": [], - "surfaces": [ - { - "id": "rust.rustdoc", - "graphEvidenceExists": false, - "graphEvidence": [ - "No graph fact replaces rustdoc diagnostics." - ], - "stillUniquelyProvidedByCurrentTools": [ - "rustdoc diagnostics, broken intra-doc links, and documentation-policy failures remain current-tool evidence." - ], - "replacementStatus": "retained" - }, - { - "id": "rust.import-graph", - "graphEvidenceExists": true, - "graphEvidence": [ - "Rust graph extraction emits IMPORTS_FROM and DEPENDS_ON edges for crate module files.", - "Rust graph query, impact, review-context, detect-changes, and search receipts exercise those facts on a Rust fixture." - ], - "stillUniquelyProvidedByCurrentTools": [ - "rustdoc and cargo-depgraph-enriched import checks remain retained current-tool evidence where graph facts are not sufficient." - ], - "replacementStatus": "deferred" - }, - { - "id": "rust.dead-code", - "graphEvidenceExists": true, - "graphEvidence": [ - "Rust graph extraction emits exported symbol metadata and graph-backed dead-public-export signals." - ], - "stillUniquelyProvidedByCurrentTools": [ - "Cargo dead_code diagnostics and retained current-tool gate behavior still uniquely cover compiler reachability." - ], - "replacementStatus": "retained" - }, - { - "id": "rust.unused-deps", - "graphEvidenceExists": false, - "graphEvidence": [ - "No graph fact replaces cargo-udeps unused dependency analysis." - ], - "stillUniquelyProvidedByCurrentTools": [ - "cargo-udeps and retained current-tool unused dependency detection remain the unique evidence source." - ], - "replacementStatus": "retained" - }, - { - "id": "rust.function-metrics", - "graphEvidenceExists": true, - "graphEvidence": [ - "Rust graph extraction emits function and method symbols with source spans and signatures." - ], - "stillUniquelyProvidedByCurrentTools": [ - "rust-code-analysis complexity, line-count, and parameter-threshold metrics remain retained current-tool evidence." - ], - "replacementStatus": "retained" - }, - { - "id": "current-tools:validate-rust-graph", - "graphEvidenceExists": false, - "graphEvidence": [ - "Graph receipts do not replace the aggregate current-tools Rust graph gate." - ], - "stillUniquelyProvidedByCurrentTools": [ - "npm run current-tools:validate-rust-graph remains the retained aggregate guardrail until #30 makes a retirement decision." - ], - "replacementStatus": "retained" - } - ], - "guardrails": [ - { - "id": "current-tools:validate-rust-graph", - "command": [ - "npm", - "run", - "current-tools:validate-rust-graph" - ], - "replacementStatus": "retained", - "oldToolReplacementClaimed": false - } - ] -} diff --git a/docs/validation/rust-old-rox-comparison-receipt-2026-06-27.md b/docs/validation/rust-old-rox-comparison-receipt-2026-06-27.md deleted file mode 100644 index b5b346b..0000000 --- a/docs/validation/rust-old-rox-comparison-receipt-2026-06-27.md +++ /dev/null @@ -1,18 +0,0 @@ -# Rust Old-Rox Comparison Receipt - 2026-06-27 - -Issue: #29. - -Machine receipt: `docs/validation/rust-old-rox-comparison-receipt-2026-06-27.json` - -This receipt records Rust graph evidence without making a retirement decision. `oldToolReplacementClaimed` is pinned to `false`, and no public release or publish action is recorded. - -| Surface | Graph Evidence | Still Unique To Current Tools | Status | -|---|---|---|---| -| `rust.rustdoc` | none | rustdoc diagnostics and documentation-policy failures | retained | -| `rust.import-graph` | Rust module/import graph facts plus query and impact receipts | rustdoc/cargo-depgraph-enriched import checks | deferred | -| `rust.dead-code` | exported symbol metadata and graph-backed dead-public-export signals | Cargo `dead_code` compiler reachability | retained | -| `rust.unused-deps` | none | cargo-udeps unused dependency analysis | retained | -| `rust.function-metrics` | Rust function/method spans and signatures | rust-code-analysis complexity and threshold metrics | retained | -| `current-tools:validate-rust-graph` | none | aggregate retained Rust graph guardrail | retained | - -`npm run current-tools:validate-rust-graph` remains active until #30 explicitly changes retained-tool policy. diff --git a/docs/validation/rust-retained-tools-receipts-2026-06-23.json b/docs/validation/rust-retained-tools-receipts-2026-06-23.json deleted file mode 100644 index 55dfdd2..0000000 --- a/docs/validation/rust-retained-tools-receipts-2026-06-23.json +++ /dev/null @@ -1,235 +0,0 @@ -{ - "schemaVersion": 1, - "issue": "#61", - "origin": "covibes-authored-retained-rust-parity-proof", - "generatedAt": "2026-06-23T22:18:30.000Z", - "runtimeDecision": { - "issue": "#21", - "decision": "no validation daemon and no hidden validation cache for initial Opcore cutover", - "providerOnly": true - }, - "nativeRetainedChecks": [ - "rust.rustdoc", - "rust.import-graph", - "rust.dead-code", - "rust.unused-deps", - "rust.function-metrics" - ], - "opcore": { - "launchGate": { - "fileLengthBaselinePresent": true, - "evidence": [ - "packages/validation-rust/src/file-length-check.ts exists", - "packages/validation-rust/src/check-ids.ts exports rust.file-length", - "check manifest, validate manifest, status, and doctor include rust.file-length" - ] - }, - "statusReceipts": [ - { - "command": "node packages/cli/dist/index.js status --json", - "exitCode": 0, - "rustAdapterStatus": "degraded", - "degradedChecks": [ - { - "checkId": "rust.import-graph", - "requiredTool": "cargo-depgraph", - "reason": "optional_tool_unavailable" - } - ] - }, - { - "command": "node packages/cli/dist/index.js doctor --json", - "exitCode": 0, - "rustAdapterStatus": "degraded", - "degradedChecks": [ - { - "checkId": "rust.import-graph", - "requiredTool": "cargo-depgraph", - "reason": "optional_tool_unavailable" - } - ] - } - ], - "nativeToolAvailableReceipt": { - "command": "node --test tests/validation-rust.test.mjs tests/validation-cli.test.mjs", - "assertion": "fake full Rust toolchain reports Rust adapter available with degradedChecks []", - "exitCode": 0 - }, - "nativeCheckReceipts": [ - { - "checkId": "rust.rustdoc", - "coverage": [ - "cargo doc JSON diagnostics become blocking policy failures", - "missing rustdoc returns unsupported_request" - ] - }, - { - "checkId": "rust.import-graph", - "coverage": [ - "unresolved mod declarations", - "unresolved crate/self/super use paths", - "orphan Rust source files", - "module cycles", - "overlay write/delete after-state reads" - ] - }, - { - "checkId": "rust.dead-code", - "coverage": [ - "cargo check dead_code JSON diagnostics", - "native orphan-source dead-code evidence" - ] - }, - { - "checkId": "rust.unused-deps", - "coverage": [ - "cargo-udeps workspace/package scoping", - "deterministic RUST_UNUSED_DEPENDENCY diagnostics", - "missing cargo-udeps unsupported_request" - ] - }, - { - "checkId": "rust.function-metrics", - "coverage": [ - "rust-code-analysis object and array JSON", - "function line, complexity, and parameter thresholds", - "repo and package scopes", - "missing rust-code-analysis-cli unsupported_request" - ] - } - ], - "verification": [ - { - "command": "npm run build", - "exitCode": 0 - }, - { - "command": "node --test tests/validation-rust.test.mjs tests/validation-rust-package-scope.test.mjs tests/validation-rust-regressions.test.mjs tests/validation-cli.test.mjs tests/validation-command-adapter.test.mjs tests/contracts.test.mjs tests/schema-contracts.test.mjs tests/gate-negative-fixtures.test.mjs", - "exitCode": 0, - "tests": 127 - }, - { - "command": "npm run rust:check", - "exitCode": 0 - }, - { - "command": "npm run ci", - "exitCode": 0, - "tests": 423 - }, - { - "command": "npm run setup:tools", - "exitCode": 0 - }, - { - "command": "npm run current-tools:validate-rust-graph", - "exitCode": 0, - "summary": "[]" - }, - { - "command": "npm run current-tools:validate-changed", - "exitCode": 0, - "summary": "Rox changed gate passed with 6 baseline-equivalent legacy code-quality findings retained." - }, - { - "command": "npm run current-tools:validate-all", - "exitCode": 0, - "summary": "No issues found" - } - ] - }, - "orchestra": { - "repo": "covibes/orchestra", - "oldToolReceipts": [ - { - "command": "npm run rox:ci", - "exitCode": 0, - "summary": "No issues found" - }, - { - "command": "npm run rox:repo", - "exitCode": 0, - "summary": "No issues found" - }, - { - "command": "npm run rox:check", - "exitCode": 0, - "summary": "No issues found" - } - ] - }, - "gateway": { - "repo": "covibes/covibes/gateway", - "oldToolReceipt": { - "command": "NODE_NO_WARNINGS=1 ../.ace/runtime/bin/rox check --all --no-daemon", - "exitCode": 2, - "retainedFindingCount": 11, - "retainedFindings": [ - { - "path": "crates/preview-router/src/ext_proc.rs", - "code": "code-quality/file-length", - "message": "File too long (1591 lines, max 600)" - }, - { - "path": "crates/preview-router/src/proto.rs", - "code": "rust-adapter/no-broad-lint-suppression", - "message": "Broad Rust lint suppression is forbidden" - }, - { - "path": "crates/preview-router/tests/golden_vectors.rs", - "code": "rust-adapter/no-rox-owned-lint-suppression", - "message": "Suppressing Rust lints owned by rox is forbidden" - }, - { - "path": null, - "code": "rust-adapter/import-graph-error", - "message": "rustdoc import graph failed for preview-router test cache_load" - }, - { - "path": "crates/preview-router/src/config.rs:136", - "code": "rust-adapter/cyclomatic-complexity", - "message": "Function from_lookup complexity 11 exceeds max 10" - }, - { - "path": "crates/preview-router/src/identity.rs:97", - "code": "rust-adapter/cyclomatic-complexity", - "message": "Function parse_preview_host_identity complexity 16 exceeds max 10" - }, - { - "path": "crates/preview-router/src/metadata.rs:58", - "code": "rust-adapter/cyclomatic-complexity", - "message": "Function parse_deployment_metadata complexity 11 exceeds max 10" - }, - { - "path": "crates/preview-router/src/postgres_provider.rs:167", - "code": "rust-adapter/cyclomatic-complexity", - "message": "Function decode_row complexity 15 exceeds max 10" - }, - { - "path": "crates/preview-router/src/router.rs:86", - "code": "rust-adapter/cyclomatic-complexity", - "message": "Function route complexity 11 exceeds max 10" - }, - { - "path": "crates/preview-router/tests/golden_vectors.rs:170", - "code": "rust-adapter/cyclomatic-complexity", - "message": "Function all_golden_vectors_pass complexity 13 exceeds max 10" - }, - { - "path": null, - "code": "rust-adapter/dead-code-error", - "message": "rustdoc orphan-source reachability failed for preview-router test cache_load" - } - ] - } - }, - "guardrails": { - "noValidationDaemon": true, - "noHiddenValidationCache": true, - "noRoxImports": true, - "noRoxCacheReads": true, - "noRoxShelloutsFromNativeChecks": true, - "oldRoxGatesRemainActive": true, - "providerAssessmentsOnly": true - } -} diff --git a/docs/validation/rust-retained-tools-receipts-2026-06-23.md b/docs/validation/rust-retained-tools-receipts-2026-06-23.md deleted file mode 100644 index 032ec96..0000000 --- a/docs/validation/rust-retained-tools-receipts-2026-06-23.md +++ /dev/null @@ -1,65 +0,0 @@ -# Rust Retained Tools Receipts - 2026-06-23 - -Issue: #61. - -Runtime decision: #21 remains Option A: no validation daemon and no hidden validation cache. Opcore emits provider -assessment evidence only; ASP hosts keep decision authority. - -## Native Parity Closed - -The five retained Rust rows now have native Opcore behavior when supporting tools are available: - -| Check | Native receipt | -|---|---| -| `rust.rustdoc` | `cargo doc --no-deps --all-features --message-format=json`; rustdoc diagnostics block, missing rustdoc is `unsupported_request`. | -| `rust.import-graph` | fileView-based unresolved `mod`, unresolved `crate`/`self`/`super` use paths, orphan sources, and cycles. | -| `rust.dead-code` | Cargo `dead_code` diagnostics plus native orphan-source dead-code evidence. | -| `rust.unused-deps` | cargo-udeps workspace/package scoping and sorted `RUST_UNUSED_DEPENDENCY` diagnostics. | -| `rust.function-metrics` | rust-code-analysis object/array JSON parsing with lines, complexity, and parameter thresholds. | - -With a full fake Rust toolchain, status and doctor tests prove the Rust adapter is `available` with -`degradedChecks: []`. In this local environment `cargo-depgraph` is absent, so live status/doctor correctly show only: - -```json -{"checkId":"rust.import-graph","requiredTool":"cargo-depgraph","reason":"optional_tool_unavailable"} -``` - -`rust.dead-code` has no generic retained row when core Cargo is available. - -## Opcore Receipts - -| Command | Result | -|---|---| -| `npm run build` | pass | -| targeted validation Rust/CLI/contracts/schema/gate tests | pass, 127 tests | -| `node packages/cli/dist/index.js check manifest --json` | pass, includes `rust.file-length` | -| `node packages/cli/dist/index.js validate manifest --json` | pass, includes `rust.file-length` | -| `node packages/cli/dist/index.js status --json` | pass, Rust adapter degraded only for missing `cargo-depgraph` | -| `node packages/cli/dist/index.js doctor --json` | pass, Rust adapter degraded only for missing `cargo-depgraph` | -| `npm run rust:check` | pass | -| `npm run ci` | pass, 423 tests | -| `npm run setup:tools` | pass | -| `npm run current-tools:validate-rust-graph` | pass, `[]` | -| `npm run current-tools:validate-changed` | pass, 6 baseline-equivalent legacy findings retained | -| `npm run current-tools:validate-all` | pass, no issues found | - -## External Old-Tool Receipts - -Orchestra remains strict and green: - -| Command | Result | -|---|---| -| `npm run rox:ci` | pass, no issues found | -| `npm run rox:repo` | pass, no issues found | -| `npm run rox:check` | pass, no issues found | - -Gateway remains retained evidence with exit 2 and 11 existing findings. The finding list is preserved in -`docs/validation/rust-retained-tools-receipts-2026-06-23.json`; no thresholds were relaxed. - -## Guardrails - -- No validation daemon. -- No hidden validation cache. -- No Rox imports, Rox cache reads, or Rox shellouts from native Opcore checks. -- Current external Rox gates stay active until downstream #27/#28/#29 accept replacement evidence. -- Results are provider assessments only, not ASP host decisions or release authority. diff --git a/eslint.config.mjs b/eslint.config.mjs index 0b9b881..be31dd3 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -4,9 +4,9 @@ import tseslint from "typescript-eslint"; // Advisory ESLint config for opcore (TypeScript + JS). // // Run with `npm run lint:eslint`. This is intentionally NOT wired into `npm run ci` -// yet: the existing Rox, tsc, clippy `-D warnings`, and `cargo fmt --check` gates remain -// the blocking guardrails. ESLint is added advisory-first so violations can be measured -// and cleaned up before any enforcement decision (matches the covibes/orchestra baseline). +// yet: TypeScript, Rust, workspace, provenance, and Opcore self-validation remain +// the blocking guardrails. ESLint is advisory-first so violations can be measured +// and cleaned up before any enforcement decision. export default tseslint.config( { ignores: [ @@ -15,7 +15,6 @@ export default tseslint.config( "**/target/**", "**/*.d.ts", ".claude/**", - ".ace/**", ], }, js.configs.recommended, diff --git a/package.json b/package.json index 184e75e..798b778 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,7 @@ "asp-dogfood:check": "node scripts/generate-asp-dogfood-receipt.mjs --json", "asp-dogfood:receipt": "node scripts/generate-asp-dogfood-receipt.mjs --write --json", "asp-provider:manifest": "node scripts/write-asp-provider-manifest.mjs", - "baseline:graph-reference": "node scripts/measure-graph-reference-baselines.mjs", - "ci": "npm run lint && npm run rust:check && npm run build && npm run test:ci && npm run graph-release:check && npm run release-receipt:check && OPCORE_CUTOVER_REUSE_RELEASE_PACKAGES=1 OPCORE_CUTOVER_REUSE_CURRENT_TOOL_GUARDRAILS=1 npm run cutover:check", + "ci": "npm run lint && npm run rust:check && npm run build && npm run test:ci && npm run graph-release:check && npm run release-receipt:check && OPCORE_CUTOVER_REUSE_RELEASE_PACKAGES=1 npm run cutover:check", "ci:local": "bash ./scripts/ci/run-local-ci-equivalent.sh", "conformance:check": "node --test tests/conformance.test.mjs", "descriptor:artifact": "node scripts/write-cli-descriptor.mjs", @@ -41,6 +40,7 @@ "license:report": "node scripts/license-report.mjs", "latency:check": "node scripts/check-latency-budgets.mjs --records tests/fixtures/latency/telemetry-pass.jsonl", "lint": "node scripts/check-workspace.mjs", + "opcore:self-check": "node scripts/run-opcore-self-check.mjs", "pack:check": "node scripts/check-packages.mjs", "provenance:check": "node scripts/check-provenance.mjs", "python:resolver-matrix": "node scripts/check-python-resolver-matrix.mjs", @@ -51,21 +51,11 @@ "rust:clippy": "cargo clippy --all-targets --all-features -- -D warnings", "rust:fmt": "cargo fmt --check", "rust:test": "cargo test", - "ace:check": "bash ./scripts/run-ace.sh check", - "ace:install": "bash ./scripts/run-ace.sh install", - "ace:status": "bash ./scripts/run-ace.sh status", - "ace:sync": "bash ./scripts/run-ace.sh sync", - "ace:validate": "bash ./scripts/run-ace.sh validate", - "setup": "npm ci && npm run setup:tools", + "setup": "npm ci", "setup:check-clean": "bash ./scripts/ci/check-setup-clean-worktree.sh", - "setup:tools": "bash ./scripts/setup-current-tools.sh", "test": "node --test tests/*.test.mjs", "test:ci": "node scripts/run-test-ci.mjs", "verify": "bash ./scripts/ci/run-local-ci-equivalent.sh", - "current-tools:validate-all": "./.ace/runtime/bin/rox check --all --no-daemon", - "current-tools:validate-rust-graph": "node scripts/check-rust-graph-function-metrics.mjs", - "current-tools:validate-changed": "node scripts/ci/run-rox-clean-changed-gate.mjs", - "current-tools:graph-status": "./.ace/runtime/bin/crg status --repo .", "lint:eslint": "eslint ." }, "repository": { diff --git a/packages/asp-provider/README.md b/packages/asp-provider/README.md index 8910263..f6a2db9 100644 --- a/packages/asp-provider/README.md +++ b/packages/asp-provider/README.md @@ -18,8 +18,7 @@ and honest coverage; the ASP host owns the allow/deny/transaction outcome. - It is **not** an ASP host, manager, catalog, or authority. - It is **not** exposed through aggregate CLI ASP subcommands; there is no ASP router command. The provider is launched as its own process. -- It does **not** use ACE as a carrier or provisioner, does **not** read or write - `.ace/runtime`, and does **not** execute `rox`, `crg`, or `cix`. +- It does not invoke external development toolchains. - It makes no ASP-standard, old-tool-replacement, security-scanner, all-stack, AI-authorship, automatic-fix, or opaque score-style claim. diff --git a/packages/contracts/schemas/opcore-contracts.schema.json b/packages/contracts/schemas/opcore-contracts.schema.json index 4aefbac..908bfaf 100644 --- a/packages/contracts/schemas/opcore-contracts.schema.json +++ b/packages/contracts/schemas/opcore-contracts.schema.json @@ -5374,15 +5374,7 @@ "minItems": 1, "items": { "type": "string", - "minLength": 1, - "pattern": "^(?!.*(?:^|[\\\\/])\\.ace(?:[\\\\/]|$))(?!.*LATTICE_CURRENT_TOOLS_DIR).+", - "not": { - "enum": [ - "crg", - "cix", - "rox" - ] - } + "minLength": 1 } }, "expectedExitCode": { @@ -6533,15 +6525,7 @@ "minItems": 1, "items": { "type": "string", - "minLength": 1, - "pattern": "^(?!.*(?:^|[\\\\/])\\.ace(?:[\\\\/]|$))(?!.*LATTICE_CURRENT_TOOLS_DIR).+", - "not": { - "enum": [ - "crg", - "cix", - "rox" - ] - } + "minLength": 1 } }, "expectedExitCode": { @@ -6679,7 +6663,7 @@ "ManagedToolPackagePath": { "type": "string", "minLength": 1, - "pattern": "^(?![\\\\/])(?![A-Za-z]:[\\\\/])(?!~(?:[\\\\/]|$))(?!\\.\\.)(?!.*(?:^|[\\\\/])\\.\\.(?:[\\\\/]|$))(?!.*(?:^|[\\\\/])(?:\\.ace|\\.agents|\\.claude|\\.codex|\\.gemini|\\.opencode)(?:[\\\\/]|$))(?!.*LATTICE_CURRENT_TOOLS_DIR).+" + "pattern": "^(?![\\\\/])(?![A-Za-z]:[\\\\/])(?!~(?:[\\\\/]|$))(?!\\.\\.)(?!.*(?:^|[\\\\/])\\.\\.(?:[\\\\/]|$))(?!.*(?:^|[\\\\/])(?:\\.agents|\\.claude|\\.codex|\\.gemini|\\.opencode)(?:[\\\\/]|$)).+" }, "InspectReferenceTarget": { "$ref": "#/$defs/InspectSymbolTarget" @@ -11239,10 +11223,10 @@ "forbiddenMarkersAbsent", "generatedBuildMetadataAbsent", "privatePathsAbsent", - "pythonCrgSourceAbsent", - "pythonGraphPackageMetadataAbsent", - "pythonCrgGitHistoryAbsent", - "forbiddenImplementationPackageNamesAbsent", + "sourceProvenanceAbsent", + "packageMetadataAbsent", + "gitHistoryAbsent", + "foreignImplementationNamesAbsent", "inspections" ], "properties": { @@ -11268,20 +11252,20 @@ "privatePathsAbsent": { "const": true }, - "pythonCrgSourceAbsent": { - "const": true + "inspections": { + "$ref": "#/$defs/GraphReleaseStringArray" }, - "pythonGraphPackageMetadataAbsent": { + "sourceProvenanceAbsent": { "const": true }, - "pythonCrgGitHistoryAbsent": { + "packageMetadataAbsent": { "const": true }, - "forbiddenImplementationPackageNamesAbsent": { + "gitHistoryAbsent": { "const": true }, - "inspections": { - "$ref": "#/$defs/GraphReleaseStringArray" + "foreignImplementationNamesAbsent": { + "const": true } } }, @@ -11347,7 +11331,8 @@ "supporting", "optional", "deferred" - ] + ], + "$ref": "#/$defs/GraphReleaseSurfaceClassification" }, "status": { "const": "deferred" @@ -13558,85 +13543,96 @@ } ] }, - "GraphReferenceEvidenceBaselineReceipt": { + "Sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "ReleaseReceiptPackageName": { + "enum": [ + "opcore" + ] + }, + "ReleaseReceiptCommandGroupName": { + "enum": [ + "graph", + "inspect", + "edit", + "check", + "validate", + "status", + "doctor" + ] + }, + "ReleaseReceiptReportId": { + "enum": [ + "package-inspection", + "license", + "provenance", + "release-hygiene", + "graph-release", + "secret-history" + ] + }, + "ReleaseReceiptTarballEvidence": { "type": "object", "additionalProperties": false, - "allOf": [ - { - "$ref": "#/$defs/GraphReferenceEvidenceRequiredFixtureCoverage" - } - ], "required": [ - "id", - "metric", - "classification", - "receipt", - "label", - "sourceAvailability", - "nonImplementationInput", - "fixtures" + "filename", + "path", + "sha256" ], "properties": { - "id": { + "filename": { "type": "string", "minLength": 1 }, - "metric": { - "type": "string", - "minLength": 1 + "path": { + "$ref": "#/$defs/RepoRelativePath" }, - "classification": { - "$ref": "#/$defs/GraphReferenceEvidenceClassification" + "sha256": { + "$ref": "#/$defs/Sha256" }, - "receipt": { + "integrity": { "type": "string", "minLength": 1 }, - "label": { - "const": "reference_evidence_non_implementation_input" - }, - "sourceAvailability": { - "enum": [ - "available", - "unavailable" - ] - }, - "nonImplementationInput": { - "const": true - }, - "fixtures": { - "$ref": "#/$defs/GraphReferenceEvidenceCoverageFixtures" + "shasum": { + "type": "string", + "minLength": 1 } } }, - "GraphReferenceEvidenceCommandSurface": { + "ReleaseReceiptPackageManifestMetadata": { "type": "object", "additionalProperties": false, - "allOf": [ - { - "$ref": "#/$defs/GraphReferenceEvidenceRequiredFixtureCoverage" - } - ], "required": [ - "id", - "classification", - "referenceTool", - "referenceCommand", - "canonicalCommand", - "flags", - "positionals", - "fixtures", - "exitSemantics" + "name", + "version", + "license", + "files", + "bins", + "dependencies", + "bundledDependencies" ], "properties": { - "id": { + "name": { + "$ref": "#/$defs/ReleaseReceiptPackageName" + }, + "version": { "type": "string", "minLength": 1 }, - "classification": { - "$ref": "#/$defs/GraphReferenceEvidenceClassification" + "license": { + "type": "string", + "minLength": 1 }, - "canonicalCommand": { + "main": { + "$ref": "#/$defs/RepoRelativePath" + }, + "types": { + "$ref": "#/$defs/RepoRelativePath" + }, + "files": { "type": "array", "minItems": 1, "items": { @@ -13644,794 +13640,140 @@ "minLength": 1 } }, - "flags": { - "type": "array", - "items": { + "bins": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/RepoRelativePath" + } + }, + "dependencies": { + "type": "object", + "additionalProperties": { "type": "string", "minLength": 1 } }, - "positionals": { + "bundledDependencies": { "type": "array", "items": { "type": "string", "minLength": 1 } }, - "fixtures": { - "$ref": "#/$defs/GraphReferenceEvidenceCoverageFixtures" - }, - "exitSemantics": { - "$ref": "#/$defs/GraphReferenceEvidenceExitSemantics" + "optionalDependencies": { + "type": "object", + "additionalProperties": { + "type": "string", + "minLength": 1 + } }, - "referenceTool": { - "type": "string", - "minLength": 1 + "os": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 1 + } }, - "referenceCommand": { + "cpu": { "type": "array", + "minItems": 1, "items": { "type": "string", "minLength": 1 } } - } - }, - "GraphReferenceEvidenceDaemonFixture": { - "type": "object", - "additionalProperties": false, + }, "allOf": [ { - "$ref": "#/$defs/GraphReferenceEvidenceRequiredFixtureCoverage" + "if": { + "properties": { + "name": { + "const": "opcore" + } + }, + "required": [ + "name" + ] + }, + "then": { + "required": [ + "main", + "types" + ], + "properties": { + "bins": { + "required": [ + "opcore", + "opcore-asp-provider" + ] + }, + "bundledDependencies": { + "contains": { + "const": "@the-open-engine/opcore-asp-provider" + }, + "minContains": 1 + } + } + } } - ], + ] + }, + "ReleaseReceiptNativeArtifactEvidence": { + "type": "object", + "additionalProperties": false, "required": [ - "id", - "classification", - "fixture", - "protocol", - "envelopes", - "fixtures" + "packageName", + "bundledPackageName", + "targetPlatform", + "metadata", + "binaryPath", + "checksumPath", + "metadataPath", + "binarySha256", + "checksumFileSha256", + "metadataSha256", + "descriptorArtifactId", + "descriptorChecksumId" ], "properties": { - "id": { - "type": "string", - "minLength": 1 + "packageName": { + "const": "opcore" }, - "classification": { - "$ref": "#/$defs/GraphReferenceEvidenceClassification" + "targetPlatform": { + "$ref": "#/$defs/GraphCoreNativeSupportedTarget" }, - "fixture": { - "type": "string", - "minLength": 1 + "metadata": { + "$ref": "#/$defs/GraphProviderArtifactMetadata" }, - "protocol": { - "type": "string", - "minLength": 1 + "binaryPath": { + "$ref": "#/$defs/RepoRelativePath" }, - "envelopes": { - "$ref": "#/$defs/GraphReferenceEvidenceStringArray" + "checksumPath": { + "$ref": "#/$defs/RepoRelativePath" }, - "fixtures": { - "$ref": "#/$defs/GraphReferenceEvidenceCoverageFixtures" - } - } - }, - "GraphReferenceEvidenceExitSemantics": { - "type": "object", - "additionalProperties": false, - "required": [ - "success", - "failure" - ], - "properties": { - "success": { - "const": 0 + "metadataPath": { + "$ref": "#/$defs/RepoRelativePath" }, - "failure": { + "binarySha256": { + "$ref": "#/$defs/Sha256" + }, + "checksumFileSha256": { + "$ref": "#/$defs/Sha256" + }, + "metadataSha256": { + "$ref": "#/$defs/Sha256" + }, + "descriptorArtifactId": { + "type": "string", + "minLength": 1 + }, + "descriptorChecksumId": { "type": "string", "minLength": 1 - } - } - }, - "GraphReferenceEvidenceJsonOutputSurface": { - "type": "object", - "additionalProperties": false, - "allOf": [ - { - "$ref": "#/$defs/GraphReferenceEvidenceRequiredFixtureCoverage" - } - ], - "required": [ - "id", - "command", - "classification", - "requiredFields", - "fixtures", - "exitSemantics" - ], - "properties": { - "id": { - "type": "string", - "minLength": 1 - }, - "command": { - "type": "string", - "minLength": 1 - }, - "classification": { - "$ref": "#/$defs/GraphReferenceEvidenceClassification" - }, - "requiredFields": { - "type": "array", - "minItems": 1, - "items": { - "type": "string", - "minLength": 1 - } - }, - "fixtures": { - "$ref": "#/$defs/GraphReferenceEvidenceCoverageFixtures" - }, - "exitSemantics": { - "$ref": "#/$defs/GraphReferenceEvidenceExitSemantics" - } - } - }, - "GraphReferenceEvidenceManifest": { - "type": "object", - "additionalProperties": false, - "required": [ - "schemaVersion", - "issue", - "origin", - "fixtureRefs", - "commandSurfaces", - "jsonOutputSurfaces", - "sqliteFixtures", - "daemonFixtures", - "baselineReceipts", - "optionalAnalysisSurfaces", - "goldenCorpus", - "provenance" - ], - "properties": { - "schemaVersion": { - "const": 1 - }, - "issue": { - "const": "#19" - }, - "origin": { - "const": "covibes-authored-synthetic" - }, - "fixtureRefs": { - "$ref": "#/$defs/GraphReferenceEvidenceStringArray" - }, - "commandSurfaces": { - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/$defs/GraphReferenceEvidenceCommandSurface" - } - }, - "jsonOutputSurfaces": { - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/$defs/GraphReferenceEvidenceJsonOutputSurface" - } - }, - "sqliteFixtures": { - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/$defs/GraphReferenceEvidenceSqliteFixture" - } - }, - "daemonFixtures": { - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/$defs/GraphReferenceEvidenceDaemonFixture" - } - }, - "baselineReceipts": { - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/$defs/GraphReferenceEvidenceBaselineReceipt" - } - }, - "optionalAnalysisSurfaces": { - "type": "array", - "minItems": 4, - "maxItems": 4, - "allOf": [ - { - "contains": { - "type": "object", - "required": [ - "issue", - "id", - "classification", - "status" - ], - "properties": { - "issue": { - "const": "#13" - }, - "id": { - "const": "coverage" - }, - "classification": { - "const": "deferred" - }, - "status": { - "const": "deferred" - } - } - }, - "minContains": 1, - "maxContains": 1 - }, - { - "contains": { - "type": "object", - "required": [ - "issue", - "id", - "classification", - "status" - ], - "properties": { - "issue": { - "const": "#14" - }, - "id": { - "const": "flows" - }, - "classification": { - "const": "optional" - }, - "status": { - "const": "deferred" - } - } - }, - "minContains": 1, - "maxContains": 1 - }, - { - "contains": { - "type": "object", - "required": [ - "issue", - "id", - "classification", - "status" - ], - "properties": { - "issue": { - "const": "#15" - }, - "id": { - "const": "communities" - }, - "classification": { - "const": "optional" - }, - "status": { - "const": "deferred" - } - } - }, - "minContains": 1, - "maxContains": 1 - }, - { - "contains": { - "type": "object", - "required": [ - "issue", - "id", - "classification", - "status" - ], - "properties": { - "issue": { - "const": "#16" - }, - "id": { - "const": "read_only_suggestions" - }, - "classification": { - "const": "supporting" - }, - "status": { - "const": "deferred" - } - } - }, - "minContains": 1, - "maxContains": 1 - } - ], - "items": { - "type": "object", - "additionalProperties": false, - "allOf": [ - { - "$ref": "#/$defs/GraphReferenceEvidenceRequiredFixtureCoverage" - } - ], - "required": [ - "issue", - "id", - "classification", - "status", - "fixtures" - ], - "properties": { - "issue": { - "enum": [ - "#13", - "#14", - "#15", - "#16" - ] - }, - "id": { - "type": "string", - "minLength": 1 - }, - "classification": { - "enum": [ - "supporting", - "optional", - "deferred" - ] - }, - "status": { - "const": "deferred" - }, - "fixtures": { - "$ref": "#/$defs/GraphReferenceEvidenceCoverageFixtures" - } - } - } - }, - "goldenCorpus": { - "type": "object", - "additionalProperties": false, - "allOf": [ - { - "$ref": "#/$defs/GraphReferenceEvidenceRequiredFixtureCoverage" - } - ], - "required": [ - "id", - "classification", - "fixture", - "covers", - "fixtures" - ], - "properties": { - "id": { - "type": "string", - "minLength": 1 - }, - "classification": { - "$ref": "#/$defs/GraphReferenceEvidenceClassification" - }, - "fixture": { - "type": "string", - "minLength": 1 - }, - "covers": { - "$ref": "#/$defs/GraphReferenceEvidenceStringArray" - }, - "fixtures": { - "$ref": "#/$defs/GraphReferenceEvidenceCoverageFixtures" - } - } - }, - "provenance": { - "type": "object", - "additionalProperties": false, - "required": [ - "containsPythonCrgSource", - "containsPackageMetadata", - "containsGitHistory", - "referenceReceiptsAreImplementationInput", - "implementationPackageNames", - "allowedMentionPaths" - ], - "properties": { - "containsPythonCrgSource": { - "const": false - }, - "containsPackageMetadata": { - "const": false - }, - "containsGitHistory": { - "const": false - }, - "implementationPackageNames": { - "$ref": "#/$defs/GraphReferenceEvidenceStringArray" - }, - "allowedMentionPaths": { - "$ref": "#/$defs/GraphReferenceEvidenceStringArray" - }, - "referenceReceiptsAreImplementationInput": { - "const": false - } - } - } - } - }, - "GraphReferenceEvidenceRequiredFixtureCoverage": { - "if": { - "required": [ - "classification" - ], - "properties": { - "classification": { - "const": "required" - } - } - }, - "then": { - "properties": { - "fixtures": { - "allOf": [ - { - "$ref": "#/$defs/GraphReferenceEvidenceCoverageFixtures" - }, - { - "minItems": 1 - } - ] - } - } - } - }, - "GraphReferenceEvidenceSqliteFixture": { - "type": "object", - "additionalProperties": false, - "allOf": [ - { - "$ref": "#/$defs/GraphReferenceEvidenceRequiredFixtureCoverage" - } - ], - "required": [ - "id", - "classification", - "fixture", - "tables", - "indexes", - "metadataKeys", - "nodeKinds", - "edgeKinds", - "directReaderQueries", - "fixtures" - ], - "properties": { - "id": { - "type": "string", - "minLength": 1 - }, - "classification": { - "$ref": "#/$defs/GraphReferenceEvidenceClassification" - }, - "fixture": { - "type": "string", - "minLength": 1 - }, - "tables": { - "$ref": "#/$defs/GraphReferenceEvidenceStringArray" - }, - "indexes": { - "$ref": "#/$defs/GraphReferenceEvidenceStringArray" - }, - "metadataKeys": { - "$ref": "#/$defs/GraphReferenceEvidenceStringArray" - }, - "nodeKinds": { - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/$defs/GraphNodeKind" - } - }, - "edgeKinds": { - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/$defs/GraphEdgeKind" - } - }, - "directReaderQueries": { - "$ref": "#/$defs/GraphReferenceEvidenceStringArray" - }, - "fixtures": { - "$ref": "#/$defs/GraphReferenceEvidenceCoverageFixtures" - } - } - }, - "GraphReferenceEvidenceClassification": { - "enum": [ - "required", - "supporting", - "optional", - "deferred" - ] - }, - "GraphReferenceEvidenceCoverageFixtures": { - "type": "array", - "items": { - "type": "string", - "minLength": 1 - } - }, - "GraphReferenceEvidenceStringArray": { - "type": "array", - "minItems": 1, - "items": { - "type": "string", - "minLength": 1 - } - }, - "Sha256": { - "type": "string", - "pattern": "^[a-f0-9]{64}$" - }, - "ReleaseReceiptPackageName": { - "enum": [ - "opcore" - ] - }, - "ReleaseReceiptCommandGroupName": { - "enum": [ - "graph", - "inspect", - "edit", - "check", - "validate", - "status", - "doctor" - ] - }, - "ReleaseReceiptReportId": { - "enum": [ - "package-inspection", - "license", - "provenance", - "release-hygiene", - "graph-release", - "secret-history" - ] - }, - "ReleaseReceiptTarballEvidence": { - "type": "object", - "additionalProperties": false, - "required": [ - "filename", - "path", - "sha256" - ], - "properties": { - "filename": { - "type": "string", - "minLength": 1 - }, - "path": { - "$ref": "#/$defs/RepoRelativePath" - }, - "sha256": { - "$ref": "#/$defs/Sha256" - }, - "integrity": { - "type": "string", - "minLength": 1 - }, - "shasum": { - "type": "string", - "minLength": 1 - } - } - }, - "ReleaseReceiptPackageManifestMetadata": { - "type": "object", - "additionalProperties": false, - "required": [ - "name", - "version", - "license", - "files", - "bins", - "dependencies", - "bundledDependencies" - ], - "properties": { - "name": { - "$ref": "#/$defs/ReleaseReceiptPackageName" - }, - "version": { - "type": "string", - "minLength": 1 - }, - "license": { - "type": "string", - "minLength": 1 - }, - "main": { - "$ref": "#/$defs/RepoRelativePath" - }, - "types": { - "$ref": "#/$defs/RepoRelativePath" - }, - "files": { - "type": "array", - "minItems": 1, - "items": { - "type": "string", - "minLength": 1 - } - }, - "bins": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/RepoRelativePath" - }, - "not": { - "anyOf": [ - { - "required": [ - "crg" - ] - }, - { - "required": [ - "cix" - ] - }, - { - "required": [ - "rox" - ] - } - ] - } - }, - "dependencies": { - "type": "object", - "additionalProperties": { - "type": "string", - "minLength": 1 - } - }, - "bundledDependencies": { - "type": "array", - "items": { - "type": "string", - "minLength": 1 - } - }, - "optionalDependencies": { - "type": "object", - "additionalProperties": { - "type": "string", - "minLength": 1 - } - }, - "os": { - "type": "array", - "minItems": 1, - "items": { - "type": "string", - "minLength": 1 - } - }, - "cpu": { - "type": "array", - "minItems": 1, - "items": { - "type": "string", - "minLength": 1 - } - } - }, - "allOf": [ - { - "if": { - "properties": { - "name": { - "const": "opcore" - } - }, - "required": [ - "name" - ] - }, - "then": { - "required": [ - "main", - "types" - ], - "properties": { - "bins": { - "required": [ - "opcore", - "opcore-asp-provider" - ] - }, - "bundledDependencies": { - "contains": { - "const": "@the-open-engine/opcore-asp-provider" - }, - "minContains": 1 - } - } - } - } - ] - }, - "ReleaseReceiptNativeArtifactEvidence": { - "type": "object", - "additionalProperties": false, - "required": [ - "packageName", - "bundledPackageName", - "targetPlatform", - "metadata", - "binaryPath", - "checksumPath", - "metadataPath", - "binarySha256", - "checksumFileSha256", - "metadataSha256", - "descriptorArtifactId", - "descriptorChecksumId" - ], - "properties": { - "packageName": { - "const": "opcore" - }, - "targetPlatform": { - "$ref": "#/$defs/GraphCoreNativeSupportedTarget" - }, - "metadata": { - "$ref": "#/$defs/GraphProviderArtifactMetadata" - }, - "binaryPath": { - "$ref": "#/$defs/RepoRelativePath" - }, - "checksumPath": { - "$ref": "#/$defs/RepoRelativePath" - }, - "metadataPath": { - "$ref": "#/$defs/RepoRelativePath" - }, - "binarySha256": { - "$ref": "#/$defs/Sha256" - }, - "checksumFileSha256": { - "$ref": "#/$defs/Sha256" - }, - "metadataSha256": { - "$ref": "#/$defs/Sha256" - }, - "descriptorArtifactId": { - "type": "string", - "minLength": 1 - }, - "descriptorChecksumId": { - "type": "string", - "minLength": 1 - }, - "bundledPackageName": { - "$ref": "#/$defs/GraphCoreNativePackageName" + }, + "bundledPackageName": { + "$ref": "#/$defs/GraphCoreNativePackageName" } }, "allOf": [ @@ -14579,29 +13921,10 @@ "type": "integer", "minimum": 1 }, - "bins": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/RepoRelativePath" - }, - "not": { - "anyOf": [ - { - "required": [ - "crg" - ] - }, - { - "required": [ - "cix" - ] - }, - { - "required": [ - "rox" - ] - } - ] + "bins": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/RepoRelativePath" } }, "descriptorReferences": { @@ -15526,25 +14849,6 @@ "type": "object", "additionalProperties": { "$ref": "#/$defs/RepoRelativePath" - }, - "not": { - "anyOf": [ - { - "required": [ - "crg" - ] - }, - { - "required": [ - "cix" - ] - }, - { - "required": [ - "rox" - ] - } - ] } } } @@ -15719,57 +15023,19 @@ "type": "object", "additionalProperties": false, "required": [ - "currentToolEnvCleared", - "clearedEnvVarCount", "pathSanitized", - "aceRuntimeBinExcluded", - "siblingCovibesExcluded", - "opcoreBinOnly", - "oldBinsAbsent" + "siblingRepositoriesExcluded", + "opcoreBinsVerified" ], "properties": { - "currentToolEnvCleared": { - "const": true - }, - "clearedEnvVarCount": { - "type": "integer", - "minimum": 5 - }, "pathSanitized": { "const": true }, - "aceRuntimeBinExcluded": { - "const": true - }, - "siblingCovibesExcluded": { + "siblingRepositoriesExcluded": { "const": true }, - "opcoreBinOnly": { + "opcoreBinsVerified": { "const": true - }, - "oldBinsAbsent": { - "type": "object", - "additionalProperties": false, - "required": [ - "lattice", - "crg", - "cix", - "rox" - ], - "properties": { - "lattice": { - "const": true - }, - "crg": { - "const": true - }, - "cix": { - "const": true - }, - "rox": { - "const": true - } - } } } }, @@ -18351,240 +17617,6 @@ } ] }, - "RustOldRoxComparisonSurfaceReceipt": { - "type": "object", - "additionalProperties": false, - "required": [ - "id", - "graphEvidenceExists", - "graphEvidence", - "stillUniquelyProvidedByCurrentTools", - "replacementStatus" - ], - "properties": { - "id": { - "enum": [ - "rust.rustdoc", - "rust.import-graph", - "rust.dead-code", - "rust.unused-deps", - "rust.function-metrics", - "current-tools:validate-rust-graph" - ] - }, - "graphEvidenceExists": { - "type": "boolean" - }, - "graphEvidence": { - "type": "array", - "minItems": 1, - "items": { - "type": "string", - "minLength": 1 - } - }, - "stillUniquelyProvidedByCurrentTools": { - "type": "array", - "minItems": 1, - "items": { - "type": "string", - "minLength": 1 - } - }, - "replacementStatus": { - "enum": [ - "retained", - "deferred" - ] - } - } - }, - "RustOldRoxComparisonGuardrailReceipt": { - "type": "object", - "additionalProperties": false, - "required": [ - "id", - "command", - "replacementStatus", - "oldToolReplacementClaimed" - ], - "properties": { - "id": { - "const": "current-tools:validate-rust-graph" - }, - "command": { - "type": "array", - "minItems": 3, - "maxItems": 3, - "prefixItems": [ - { - "const": "npm" - }, - { - "const": "run" - }, - { - "const": "current-tools:validate-rust-graph" - } - ], - "items": false - }, - "replacementStatus": { - "const": "retained" - }, - "oldToolReplacementClaimed": { - "const": false - } - } - }, - "RustOldRoxComparisonReceipt": { - "type": "object", - "additionalProperties": false, - "required": [ - "schemaVersion", - "issue", - "origin", - "generatedAt", - "privateRepo", - "oldToolReplacementClaimed", - "publicReleaseActions", - "surfaces", - "guardrails" - ], - "properties": { - "schemaVersion": { - "const": 1 - }, - "issue": { - "const": "#29" - }, - "origin": { - "const": "covibes-authored-old-rox-comparison" - }, - "generatedAt": { - "type": "string", - "minLength": 1 - }, - "privateRepo": { - "const": true - }, - "oldToolReplacementClaimed": { - "const": false - }, - "publicReleaseActions": { - "type": "array", - "maxItems": 0 - }, - "surfaces": { - "type": "array", - "minItems": 6, - "maxItems": 6, - "allOf": [ - { - "contains": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "id": { - "const": "rust.rustdoc" - } - } - }, - "minContains": 1, - "maxContains": 1 - }, - { - "contains": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "id": { - "const": "rust.import-graph" - } - } - }, - "minContains": 1, - "maxContains": 1 - }, - { - "contains": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "id": { - "const": "rust.dead-code" - } - } - }, - "minContains": 1, - "maxContains": 1 - }, - { - "contains": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "id": { - "const": "rust.unused-deps" - } - } - }, - "minContains": 1, - "maxContains": 1 - }, - { - "contains": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "id": { - "const": "rust.function-metrics" - } - } - }, - "minContains": 1, - "maxContains": 1 - }, - { - "contains": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "id": { - "const": "current-tools:validate-rust-graph" - } - } - }, - "minContains": 1, - "maxContains": 1 - } - ], - "items": { - "$ref": "#/$defs/RustOldRoxComparisonSurfaceReceipt" - } - }, - "guardrails": { - "type": "array", - "minItems": 1, - "maxItems": 1, - "items": { - "$ref": "#/$defs/RustOldRoxComparisonGuardrailReceipt" - } - } - } - }, "AspDogfoodCommandRunReceipt": { "type": "object", "allOf": [ @@ -18698,31 +17730,9 @@ "type": "string", "minLength": 1 } - } - }, - "AspDogfoodPassedCommandRunReceipt": { - "allOf": [ - { - "$ref": "#/$defs/AspDogfoodCommandRunReceipt" - }, - { - "type": "object", - "required": [ - "status", - "exitCode" - ], - "properties": { - "status": { - "const": "passed" - }, - "exitCode": { - "const": 0 - } - } - } - ] + } }, - "AspDogfoodGuardrailReceipt": { + "AspDogfoodPassedCommandRunReceipt": { "allOf": [ { "$ref": "#/$defs/AspDogfoodCommandRunReceipt" @@ -18730,81 +17740,16 @@ { "type": "object", "required": [ - "id", - "retained" + "status", + "exitCode" ], "properties": { - "id": { - "enum": [ - "current-tools-validate-changed", - "current-tools-validate-rust-graph", - "current-tools-validate-all" - ] + "status": { + "const": "passed" }, - "retained": { - "const": true - } - } - }, - { - "if": { - "required": [ - "id" - ], - "properties": { - "id": { - "enum": [ - "current-tools-validate-changed", - "current-tools-validate-rust-graph" - ] - } - } - }, - "then": { - "properties": { - "status": { - "const": "passed" - }, - "exitCode": { - "const": 0 - } - } - } - }, - { - "if": { - "required": [ - "id" - ], - "properties": { - "id": { - "const": "current-tools-validate-all" - } + "exitCode": { + "const": 0 } - }, - "then": { - "anyOf": [ - { - "properties": { - "status": { - "const": "passed" - }, - "exitCode": { - "const": 0 - } - } - }, - { - "properties": { - "status": { - "const": "retained-not-run" - }, - "exitCode": { - "const": null - } - } - } - ] } } ] @@ -18828,7 +17773,6 @@ "status": { "enum": [ "degraded", - "retained-old-tool-gate", "parity-blocker" ] }, @@ -18880,13 +17824,12 @@ "repoEnrollment", "hostEvaluation", "providerProbe", - "currentToolGuardrails", "unsupportedSurfaces", "parityBlockers", "authority", "publicReleaseActions", - "oldToolReplacementClaimed", - "forbiddenMarkerScan" + "forbiddenMarkerScan", + "selfValidation" ], "properties": { "schemaVersion": { @@ -18994,8 +17937,7 @@ "temp", "isolated", "sharedStateMutated", - "pathSanitized", - "aceRuntimeBinExcluded" + "pathSanitized" ], "properties": { "path": { @@ -19013,9 +17955,6 @@ }, "pathSanitized": { "const": true - }, - "aceRuntimeBinExcluded": { - "const": true } } }, @@ -19326,58 +18265,6 @@ } ] }, - "currentToolGuardrails": { - "type": "array", - "minItems": 3, - "maxItems": 3, - "items": { - "$ref": "#/$defs/AspDogfoodGuardrailReceipt" - }, - "allOf": [ - { - "contains": { - "type": "object", - "properties": { - "id": { - "const": "current-tools-validate-changed" - }, - "status": { - "const": "passed" - } - } - }, - "minContains": 1, - "maxContains": 1 - }, - { - "contains": { - "type": "object", - "properties": { - "id": { - "const": "current-tools-validate-rust-graph" - }, - "status": { - "const": "passed" - } - } - }, - "minContains": 1, - "maxContains": 1 - }, - { - "contains": { - "type": "object", - "properties": { - "id": { - "const": "current-tools-validate-all" - } - } - }, - "minContains": 1, - "maxContains": 1 - } - ] - }, "unsupportedSurfaces": { "type": "array", "minItems": 2, @@ -19414,7 +18301,7 @@ }, "parityBlockers": { "type": "array", - "minItems": 1, + "minItems": 0, "items": { "$ref": "#/$defs/AspDogfoodParityBlocker" } @@ -19456,9 +18343,6 @@ "type": "array", "maxItems": 0 }, - "oldToolReplacementClaimed": { - "const": false - }, "forbiddenMarkerScan": { "type": "object", "additionalProperties": false, @@ -19477,14 +18361,12 @@ }, "markersBlocked": { "type": "array", - "minItems": 4, - "maxItems": 4, + "minItems": 2, + "maxItems": 2, "items": { "enum": [ "opcore asp serve", - "opcore asp", - "dist/bin/lattice", - ".ace/runtime" + "opcore asp" ] }, "allOf": [ @@ -19501,24 +18383,13 @@ }, "minContains": 1, "maxContains": 1 - }, - { - "contains": { - "const": "dist/bin/lattice" - }, - "minContains": 1, - "maxContains": 1 - }, - { - "contains": { - "const": ".ace/runtime" - }, - "minContains": 1, - "maxContains": 1 } ] } } + }, + "selfValidation": { + "$ref": "#/$defs/OpcoreSelfValidationReceipt" } } }, @@ -19540,10 +18411,9 @@ "rustCommandReceipts", "pythonCommandReceipts", "negativeChecks", - "currentToolGuardrails", - "oldToolReplacementClaimed", "forbiddenMarkerScan", - "inputEvidence" + "inputEvidence", + "selfValidation" ], "properties": { "schemaVersion": { @@ -20453,42 +19323,8 @@ } ] }, - "currentToolGuardrails": { - "type": "array", - "minItems": 2, - "maxItems": 2, - "items": { - "$ref": "#/$defs/ReleaseCutoverCurrentToolGuardrailReceipt" - }, - "allOf": [ - { - "contains": { - "properties": { - "id": { - "const": "current-tools-validate-changed" - } - }, - "required": [ - "id" - ] - } - }, - { - "contains": { - "properties": { - "id": { - "const": "current-tools-validate-rust-graph" - } - }, - "required": [ - "id" - ] - } - } - ] - }, - "oldToolReplacementClaimed": { - "const": false + "selfValidation": { + "$ref": "#/$defs/OpcoreSelfValidationReceipt" } } }, @@ -22336,7 +21172,7 @@ } ] }, - "ReleaseCutoverCurrentToolGuardrailReceipt": { + "OpcoreSelfValidationReceipt": { "type": "object", "additionalProperties": false, "required": [ @@ -22346,16 +21182,11 @@ "exitCode", "stdoutSha256", "stderrSha256", - "retained", - "assertion", - "oldToolReplacementClaimed" + "assertion" ], "properties": { "id": { - "enum": [ - "current-tools-validate-changed", - "current-tools-validate-rust-graph" - ] + "const": "opcore-self-check" }, "command": { "type": "array", @@ -22369,10 +21200,7 @@ "const": "run" }, { - "enum": [ - "current-tools:validate-changed", - "current-tools:validate-rust-graph" - ] + "const": "opcore:self-check" } ], "items": false @@ -22389,103 +21217,18 @@ "stderrSha256": { "$ref": "#/$defs/Sha256" }, - "retained": { - "const": true - }, "assertion": { "type": "string", "minLength": 1 - }, - "oldToolReplacementClaimed": { - "const": false - } - }, - "allOf": [ - { - "if": { - "required": [ - "id" - ], - "properties": { - "id": { - "const": "current-tools-validate-changed" - } - } - }, - "then": { - "properties": { - "command": { - "type": "array", - "minItems": 3, - "maxItems": 3, - "prefixItems": [ - { - "const": "npm" - }, - { - "const": "run" - }, - { - "const": "current-tools:validate-changed" - } - ], - "items": false - } - } - } - }, - { - "if": { - "required": [ - "id" - ], - "properties": { - "id": { - "const": "current-tools-validate-rust-graph" - } - } - }, - "then": { - "properties": { - "command": { - "type": "array", - "minItems": 3, - "maxItems": 3, - "prefixItems": [ - { - "const": "npm" - }, - { - "const": "run" - }, - { - "const": "current-tools:validate-rust-graph" - } - ], - "items": false - } - } - } - }, - { - "if": { - "required": [ - "status" - ], - "properties": { - "status": { - "const": "passed" - } - } - }, - "then": { - "properties": { - "exitCode": { - "const": 0 - } - } - } } + } + }, + "GraphReleaseSurfaceClassification": { + "enum": [ + "required", + "supporting", + "optional", + "deferred" ] } } diff --git a/packages/contracts/src/index.ts b/packages/contracts/src/index.ts index 027509b..79503d3 100644 --- a/packages/contracts/src/index.ts +++ b/packages/contracts/src/index.ts @@ -1815,8 +1815,8 @@ export const commandLatencyTelemetryArtifactPolicy = { rotation: "ring_buffer" } as const; -export const graphReferenceEvidenceClassifications = ["required", "supporting", "optional", "deferred"] as const; -export type GraphReferenceEvidenceClassification = (typeof graphReferenceEvidenceClassifications)[number]; +export const graphReleaseSurfaceClassifications = ["required", "supporting", "optional", "deferred"] as const; +export type GraphReleaseSurfaceClassification = (typeof graphReleaseSurfaceClassifications)[number]; export const graphReleaseCoreCommandIds = [ "opcore-graph-build", @@ -2032,43 +2032,14 @@ export const releaseCutoverNegativeCheckIds = [ ] as const; export type ReleaseCutoverNegativeCheckId = (typeof releaseCutoverNegativeCheckIds)[number]; -export const releaseCutoverCurrentToolGuardrailIds = [ - "current-tools-validate-changed", - "current-tools-validate-rust-graph" -] as const; -export type ReleaseCutoverCurrentToolGuardrailId = (typeof releaseCutoverCurrentToolGuardrailIds)[number]; - export const releaseCutoverInputIssues = ["#17", "#29", "#58"] as const; export type ReleaseCutoverInputIssue = (typeof releaseCutoverInputIssues)[number]; -export const rustOldRoxComparisonSurfaceIds = [ - "rust.rustdoc", - "rust.import-graph", - "rust.dead-code", - "rust.unused-deps", - "rust.function-metrics", - "current-tools:validate-rust-graph" -] as const; -export type RustOldRoxComparisonSurfaceId = (typeof rustOldRoxComparisonSurfaceIds)[number]; - -export const rustOldRoxComparisonReplacementStatuses = ["retained", "deferred"] as const; -export type RustOldRoxComparisonReplacementStatus = (typeof rustOldRoxComparisonReplacementStatuses)[number]; - -export const aspDogfoodRequiredGuardrailIds = ["current-tools-validate-changed", "current-tools-validate-rust-graph"] as const; -export type AspDogfoodRequiredGuardrailId = (typeof aspDogfoodRequiredGuardrailIds)[number]; - -export const aspDogfoodOptionalGuardrailIds = ["current-tools-validate-all"] as const; -export type AspDogfoodOptionalGuardrailId = (typeof aspDogfoodOptionalGuardrailIds)[number]; - -export const aspDogfoodGuardrailIds = [...aspDogfoodRequiredGuardrailIds, ...aspDogfoodOptionalGuardrailIds] as const; -export type AspDogfoodGuardrailId = (typeof aspDogfoodGuardrailIds)[number]; - export const aspDogfoodUnsupportedSurfaceIds = ["inspect", "edit"] as const; export type AspDogfoodUnsupportedSurfaceId = (typeof aspDogfoodUnsupportedSurfaceIds)[number]; -export const aspDogfoodForbiddenProviderMarkers = ["opcore asp serve", "opcore asp", "dist/bin/lattice", ".ace/runtime"] as const; +export const aspDogfoodForbiddenProviderMarkers = ["opcore asp serve", "opcore asp"] as const; export type AspDogfoodForbiddenProviderMarker = (typeof aspDogfoodForbiddenProviderMarkers)[number]; -const legacyAspProviderBinMarker = ["lattice", "asp", "provider"].join("-"); const releaseCutoverRequestFilePlaceholder = ""; const releaseCutoverMissingGraphRepoPlaceholder = ""; @@ -3095,91 +3066,6 @@ export const commandRouterManifest: CommandRouterManifest = { ] }; -export interface GraphReferenceEvidenceSurfaceBase { - id: string; - classification: GraphReferenceEvidenceClassification; - fixtures: readonly string[]; -} - -export interface GraphReferenceEvidenceExitSemantics { - success: 0; - failure: string; -} - -export interface GraphReferenceEvidenceCommandSurface extends GraphReferenceEvidenceSurfaceBase { - referenceTool: string; - referenceCommand: readonly string[]; - canonicalCommand: readonly string[]; - flags: readonly string[]; - positionals: readonly string[]; - exitSemantics: GraphReferenceEvidenceExitSemantics; -} - -export interface GraphReferenceEvidenceJsonOutputSurface extends GraphReferenceEvidenceSurfaceBase { - command: string; - requiredFields: readonly string[]; - exitSemantics: GraphReferenceEvidenceExitSemantics; -} - -export interface GraphReferenceEvidenceSqliteFixture extends GraphReferenceEvidenceSurfaceBase { - fixture: string; - tables: readonly string[]; - indexes: readonly string[]; - metadataKeys: readonly string[]; - nodeKinds: readonly string[]; - edgeKinds: readonly string[]; - directReaderQueries: readonly string[]; -} - -export interface GraphReferenceEvidenceDaemonFixture extends GraphReferenceEvidenceSurfaceBase { - fixture: string; - protocol: "opcore.graph.daemon" | "reference-mcp-stdio-baseline-only" | (string & {}); - envelopes: readonly string[]; -} - -export interface GraphReferenceEvidenceBaselineReceipt extends GraphReferenceEvidenceSurfaceBase { - metric: string; - receipt: string; - label: "reference_evidence_non_implementation_input"; - sourceAvailability: "available" | "unavailable"; - nonImplementationInput: true; -} - -export interface GraphReferenceEvidenceOptionalAnalysisSurface extends GraphReferenceEvidenceSurfaceBase { - issue: GraphReleaseDeferredChild; - id: GraphReleaseOptionalAnalysisSurface["id"] | (string & {}); - status: "deferred"; -} - -export interface GraphReferenceEvidenceGoldenCorpusRef extends GraphReferenceEvidenceSurfaceBase { - fixture: string; - covers: readonly string[]; -} - -export interface GraphReferenceEvidenceProvenance { - containsPythonCrgSource: false; - containsPackageMetadata: false; - containsGitHistory: false; - referenceReceiptsAreImplementationInput: false; - implementationPackageNames: readonly string[]; - allowedMentionPaths: readonly string[]; -} - -export interface GraphReferenceEvidenceManifest { - schemaVersion: 1; - issue: "#19"; - origin: "covibes-authored-synthetic"; - fixtureRefs: readonly string[]; - commandSurfaces: readonly GraphReferenceEvidenceCommandSurface[]; - jsonOutputSurfaces: readonly GraphReferenceEvidenceJsonOutputSurface[]; - sqliteFixtures: readonly GraphReferenceEvidenceSqliteFixture[]; - daemonFixtures: readonly GraphReferenceEvidenceDaemonFixture[]; - baselineReceipts: readonly GraphReferenceEvidenceBaselineReceipt[]; - optionalAnalysisSurfaces: readonly GraphReferenceEvidenceOptionalAnalysisSurface[]; - goldenCorpus: GraphReferenceEvidenceGoldenCorpusRef; - provenance: GraphReferenceEvidenceProvenance; -} - export interface GraphReleaseCommandCoverage { id: GraphReleaseCoreCommandId; bin: "opcore"; @@ -3235,10 +3121,10 @@ export interface GraphReleasePackageInspection { forbiddenMarkersAbsent: true; generatedBuildMetadataAbsent: true; privatePathsAbsent: true; - pythonCrgSourceAbsent: true; - pythonGraphPackageMetadataAbsent: true; - pythonCrgGitHistoryAbsent: true; - forbiddenImplementationPackageNamesAbsent: true; + sourceProvenanceAbsent: true; + packageMetadataAbsent: true; + gitHistoryAbsent: true; + foreignImplementationNamesAbsent: true; inspections: readonly string[]; } @@ -3267,7 +3153,7 @@ export interface GraphReleaseReportReceipt { export interface GraphReleaseOptionalSurfaceReceipt { issue: GraphReleaseDeferredChild; id: GraphReleaseOptionalAnalysisSurface["id"] | (string & {}); - classification: GraphReferenceEvidenceClassification; + classification: GraphReleaseSurfaceClassification; status: "unsupported" | "deferred"; } @@ -3520,18 +3406,9 @@ export interface ReleaseCutoverDescriptorEvidence { } export interface ReleaseCutoverEnvironmentIsolationEvidence { - currentToolEnvCleared: true; - clearedEnvVarCount: number; pathSanitized: true; - aceRuntimeBinExcluded: true; - siblingCovibesExcluded: true; - opcoreBinOnly: true; - oldBinsAbsent: { - lattice: true; - crg: true; - cix: true; - rox: true; - }; + siblingRepositoriesExcluded: true; + opcoreBinsVerified: true; } export interface ReleaseCutoverCommandReceipt { @@ -3582,16 +3459,14 @@ export interface ReleaseCutoverNegativeCheck { assertion: string; } -export interface ReleaseCutoverCurrentToolGuardrailReceipt { - id: ReleaseCutoverCurrentToolGuardrailId; - command: readonly ["npm", "run", "current-tools:validate-changed"] | readonly ["npm", "run", "current-tools:validate-rust-graph"]; +export interface OpcoreSelfValidationReceipt { + id: "opcore-self-check"; + command: readonly ["npm", "run", "opcore:self-check"]; status: "passed"; exitCode: 0; stdoutSha256: string; stderrSha256: string; - retained: true; assertion: string; - oldToolReplacementClaimed: false; } export interface ReleaseCutoverForbiddenMarkerScan { @@ -3621,39 +3496,11 @@ export interface ReleaseCutoverReceipt { rustCommandReceipts: readonly ReleaseCutoverRustCommandReceipt[]; pythonCommandReceipts: readonly ReleaseCutoverPythonCommandReceipt[]; negativeChecks: readonly ReleaseCutoverNegativeCheck[]; - currentToolGuardrails: readonly ReleaseCutoverCurrentToolGuardrailReceipt[]; - oldToolReplacementClaimed: false; + selfValidation: OpcoreSelfValidationReceipt; forbiddenMarkerScan: ReleaseCutoverForbiddenMarkerScan; inputEvidence: readonly ReleaseCutoverInputEvidence[]; } -export interface RustOldRoxComparisonSurfaceReceipt { - id: RustOldRoxComparisonSurfaceId; - graphEvidenceExists: boolean; - graphEvidence: readonly string[]; - stillUniquelyProvidedByCurrentTools: readonly string[]; - replacementStatus: RustOldRoxComparisonReplacementStatus; -} - -export interface RustOldRoxComparisonGuardrailReceipt { - id: "current-tools:validate-rust-graph"; - command: readonly ["npm", "run", "current-tools:validate-rust-graph"]; - replacementStatus: "retained"; - oldToolReplacementClaimed: false; -} - -export interface RustOldRoxComparisonReceipt { - schemaVersion: 1; - issue: "#29"; - origin: "covibes-authored-old-rox-comparison"; - generatedAt: string; - privateRepo: true; - oldToolReplacementClaimed: false; - publicReleaseActions: readonly []; - surfaces: readonly RustOldRoxComparisonSurfaceReceipt[]; - guardrails: readonly RustOldRoxComparisonGuardrailReceipt[]; -} - export interface AspDogfoodManagerEvidence { bootstrapSource: "local-sibling"; aspRepoPath: string; @@ -3668,7 +3515,6 @@ export interface AspDogfoodAspHomeEvidence { isolated: true; sharedStateMutated: false; pathSanitized: true; - aceRuntimeBinExcluded: true; } export interface AspDogfoodHostFixtureEvidence { @@ -3747,14 +3593,9 @@ export interface AspDogfoodProviderProbeEvidence extends AspDogfoodCommandRunRec hostOwnedFieldLeak: false; } -export interface AspDogfoodGuardrailReceipt extends AspDogfoodCommandRunReceipt { - id: AspDogfoodGuardrailId; - retained: true; -} - export interface AspDogfoodUnsupportedSurfaceEvidence { surface: AspDogfoodUnsupportedSurfaceId; - status: "degraded" | "retained-old-tool-gate" | "parity-blocker"; + status: "degraded" | "parity-blocker"; cleanCoverage: false; blocker: string; } @@ -3797,12 +3638,11 @@ export interface AspDogfoodReceipt { repoEnrollment: AspDogfoodRepoEnrollmentEvidence; hostEvaluation: AspDogfoodHostEvaluationEvidence; providerProbe: AspDogfoodProviderProbeEvidence; - currentToolGuardrails: readonly AspDogfoodGuardrailReceipt[]; + selfValidation: OpcoreSelfValidationReceipt; unsupportedSurfaces: readonly AspDogfoodUnsupportedSurfaceEvidence[]; parityBlockers: readonly AspDogfoodParityBlocker[]; authority: AspDogfoodAuthorityEvidence; publicReleaseActions: readonly []; - oldToolReplacementClaimed: false; forbiddenMarkerScan: AspDogfoodForbiddenMarkerScan; } @@ -4135,11 +3975,6 @@ function validateManagedToolIdentity(descriptor: ManagedToolDescriptor): void { function validateManagedToolEntrypoints(entrypoints: readonly ManagedToolDescriptorEntrypoint[]): void { validateNonEmptyArray(entrypoints, "Managed tool descriptor entrypoints"); - for (const entrypoint of entrypoints) { - if (entrypoint?.bin !== "opcore" && ["lattice", "crg", "cix", "rox"].includes(String(entrypoint?.bin))) { - throw new Error("Managed tool descriptor must not reference old public aliases"); - } - } validateExactStringSet( entrypoints.map((entrypoint) => entrypoint.bin), ["opcore"], @@ -4461,7 +4296,7 @@ function validateManagedToolProvenanceHooks(provenanceHooks: readonly ManagedToo } } -const managedToolPrivateRuntimePathPattern = /(?:^|\/)(?:\.ace|\.agents|\.claude|\.codex|\.gemini|\.opencode)(?:\/|$)/; +const managedToolPrivateRuntimePathPattern = /(?:^|\/)(?:\.agents|\.claude|\.codex|\.gemini|\.opencode)(?:\/|$)/; function normalizeManagedToolDescriptorString(value: string): string { return value.replaceAll("\\", "/"); @@ -4475,30 +4310,24 @@ function validateManagedToolPackagePath(path: string, label: string): string { if (managedToolPrivateRuntimePathPattern.test(normalized)) { throw new Error(`${label} must not reference private runtime paths`); } - if (normalized.includes("LATTICE_CURRENT_TOOLS_DIR")) { - throw new Error(`${label} must not reference current-tool environment variables`); - } return normalized; } function validateManagedToolCommandTokens(tokens: readonly string[], label: string): void { for (const token of tokens) { validateNonEmptyString(token, label); - if (["lattice", "crg", "cix", "rox"].includes(token)) throw new Error("Managed tool descriptor must not reference old public aliases"); const normalizedToken = normalizeManagedToolDescriptorString(token); - if (managedToolPrivateRuntimePathPattern.test(normalizedToken) || normalizedToken.includes("LATTICE_CURRENT_TOOLS_DIR")) { - throw new Error("Managed tool descriptor must not reference current-tool runtime paths"); + if (managedToolPrivateRuntimePathPattern.test(normalizedToken)) { + throw new Error("Managed tool descriptor must not reference private runtime paths"); } } } function validateManagedToolDescriptorForbiddenStrings(value: unknown): void { - const oldAliasPattern = /(^|[\\/\s])(?:lattice|crg|cix|rox)(?:$|[\\/\s])/i; for (const text of collectStrings(value)) { - if (oldAliasPattern.test(text)) throw new Error("Managed tool descriptor must not reference old public aliases"); const normalizedText = normalizeManagedToolDescriptorString(text); - if (managedToolPrivateRuntimePathPattern.test(normalizedText) || normalizedText.includes("LATTICE_CURRENT_TOOLS_DIR")) { - throw new Error("Managed tool descriptor must not reference current-tool runtime paths"); + if (managedToolPrivateRuntimePathPattern.test(normalizedText)) { + throw new Error("Managed tool descriptor must not reference private runtime paths"); } if (normalizedText.includes("/Users/tom")) { throw new Error("Managed tool descriptor must not reference private paths"); @@ -5779,34 +5608,6 @@ export function validateCommandAdapterRequest(request: CommandAdapterRequest): C return request; } -export function validateGraphReferenceEvidenceManifest(manifest: GraphReferenceEvidenceManifest): GraphReferenceEvidenceManifest { - if (!manifest || typeof manifest !== "object") { - throw new Error("Graph reference evidence manifest is required"); - } - if (manifest.schemaVersion !== 1) { - throw new Error("Graph reference evidence manifest schemaVersion must be 1"); - } - if (manifest.issue !== "#19") { - throw new Error("Graph reference evidence manifest issue must be #19"); - } - if (manifest.origin !== "covibes-authored-synthetic") { - throw new Error("Graph reference evidence manifest origin must be covibes-authored-synthetic"); - } - - validateStringArray(manifest.fixtureRefs, "Graph reference evidence manifest fixtureRefs", { allowEmpty: false }); - validateGraphReferenceEvidenceCommandSurfaces(manifest.commandSurfaces); - validateGraphReferenceEvidenceJsonOutputSurfaces(manifest.jsonOutputSurfaces); - validateGraphReferenceEvidenceSqliteFixtures(manifest.sqliteFixtures); - validateGraphReferenceEvidenceDaemonFixtures(manifest.daemonFixtures); - validateGraphReferenceEvidenceBaselineReceipts(manifest.baselineReceipts); - validateGraphReferenceEvidenceOptionalSurfaces(manifest.optionalAnalysisSurfaces); - validateGraphReferenceEvidenceGoldenCorpus(manifest.goldenCorpus); - validateGraphReferenceEvidenceProvenance(manifest.provenance); - validateGraphReferenceEvidenceSourceFreeStrings(manifest); - - return manifest; -} - export function validateGraphReleaseReceipt(receipt: GraphReleaseReceipt): GraphReleaseReceipt { if (!receipt || typeof receipt !== "object") { throw new Error("Graph release receipt is required"); @@ -5869,6 +5670,17 @@ export function validateReleaseReceipt(receipt: ReleaseReceipt): ReleaseReceipt return receipt; } +const validateOpcoreSelfValidation = (receipt: OpcoreSelfValidationReceipt, label: string): void => { + if (!receipt || typeof receipt !== "object") throw new Error(`${label} receipt is required`); + if (receipt.id !== "opcore-self-check") throw new Error(`${label} id must be opcore-self-check`); + validateExactStringSequence(receipt.command, ["npm", "run", "opcore:self-check"], `${label} command`); + if (receipt.status !== "passed") throw new Error(`${label} status must be passed`); + if (receipt.exitCode !== 0) throw new Error(`${label} exitCode must be 0`); + validateSha256(receipt.stdoutSha256, `${label} stdoutSha256`); + validateSha256(receipt.stderrSha256, `${label} stderrSha256`); + validateNonEmptyString(receipt.assertion, `${label} assertion`); +}; + export function validateReleaseCutoverReceipt(receipt: ReleaseCutoverReceipt): ReleaseCutoverReceipt { if (!receipt || typeof receipt !== "object") throw new Error("Release cutover receipt is required"); if (receipt.schemaVersion !== 1) throw new Error("Release cutover receipt schemaVersion must be 1"); @@ -5887,33 +5699,12 @@ export function validateReleaseCutoverReceipt(receipt: ReleaseCutoverReceipt): R validateReleaseCutoverRustCommandReceipts(receipt.rustCommandReceipts); validateReleaseCutoverPythonCommandReceipts(receipt.pythonCommandReceipts); validateReleaseCutoverNegativeChecks(receipt.negativeChecks); - validateReleaseCutoverCurrentToolGuardrails(receipt.currentToolGuardrails); - if (receipt.oldToolReplacementClaimed !== false) { - throw new Error("Release cutover receipt must not claim old-tool replacement"); - } + validateOpcoreSelfValidation(receipt.selfValidation, "Release cutover self-validation"); validateReleaseCutoverForbiddenMarkerScan(receipt.forbiddenMarkerScan); validateReleaseCutoverInputEvidence(receipt.inputEvidence); return receipt; } -export function validateRustOldRoxComparisonReceipt(receipt: RustOldRoxComparisonReceipt): RustOldRoxComparisonReceipt { - if (!receipt || typeof receipt !== "object") throw new Error("Rust old-Rox comparison receipt is required"); - if (receipt.schemaVersion !== 1) throw new Error("Rust old-Rox comparison receipt schemaVersion must be 1"); - if (receipt.issue !== "#29") throw new Error("Rust old-Rox comparison receipt issue must be #29"); - if (receipt.origin !== "covibes-authored-old-rox-comparison") { - throw new Error("Rust old-Rox comparison receipt origin must be covibes-authored-old-rox-comparison"); - } - validateNonEmptyString(receipt.generatedAt, "Rust old-Rox comparison generatedAt"); - if (receipt.privateRepo !== true) throw new Error("Rust old-Rox comparison receipt privateRepo must be true"); - if (receipt.oldToolReplacementClaimed !== false) throw new Error("Rust old-Rox comparison receipt must not claim old-tool replacement"); - if (!Array.isArray(receipt.publicReleaseActions) || receipt.publicReleaseActions.length !== 0) { - throw new Error("Rust old-Rox comparison receipt public release actions must be empty"); - } - validateRustOldRoxComparisonSurfaces(receipt.surfaces); - validateRustOldRoxComparisonGuardrails(receipt.guardrails); - return receipt; -} - export function validateAspDogfoodReceipt(receipt: AspDogfoodReceipt): AspDogfoodReceipt { if (!receipt || typeof receipt !== "object") throw new Error("ASP dogfood receipt is required"); if (receipt.schemaVersion !== 1) throw new Error("ASP dogfood receipt schemaVersion must be 1"); @@ -5935,14 +5726,13 @@ export function validateAspDogfoodReceipt(receipt: AspDogfoodReceipt): AspDogfoo validateAspDogfoodRepoEnrollment(receipt.repoEnrollment); validateAspDogfoodHostEvaluation(receipt.hostEvaluation); validateAspDogfoodProviderProbe(receipt.providerProbe); - validateAspDogfoodGuardrails(receipt.currentToolGuardrails); + validateOpcoreSelfValidation(receipt.selfValidation, "ASP dogfood self-validation"); validateAspDogfoodUnsupportedSurfaces(receipt.unsupportedSurfaces); validateAspDogfoodParityBlockers(receipt.parityBlockers); validateAspDogfoodAuthority(receipt.authority); if (!Array.isArray(receipt.publicReleaseActions) || receipt.publicReleaseActions.length !== 0) { throw new Error("ASP dogfood receipt must not record public publish, registry, or standard-readiness actions"); } - if (receipt.oldToolReplacementClaimed !== false) throw new Error("ASP dogfood receipt must not claim old-tool replacement"); validateAspDogfoodForbiddenMarkerScan(receipt.forbiddenMarkerScan); validateAspDogfoodForbiddenProviderEntrypoint(receipt); return receipt; @@ -8865,153 +8655,13 @@ function validateCommandRouteStatus(status: unknown): CommandRouteStatus { return status; } -function validateGraphReferenceEvidenceSurfaceClassification(classification: unknown): GraphReferenceEvidenceClassification { - if (!includesString(graphReferenceEvidenceClassifications, classification)) { - throw new Error(`Unknown graph reference evidence surface classification: ${String(classification)}`); +function validateGraphReleaseSurfaceClassification(classification: unknown): GraphReleaseSurfaceClassification { + if (!includesString(graphReleaseSurfaceClassifications, classification)) { + throw new Error(`Unknown graph release surface classification: ${String(classification)}`); } return classification; } -function validateGraphReferenceEvidenceCommandSurfaces(surfaces: readonly GraphReferenceEvidenceCommandSurface[]): void { - validateNonEmptyArray(surfaces, "Graph reference evidence commandSurfaces"); - for (const surface of surfaces) { - validateGraphReferenceEvidenceSurfaceBase(surface, "Graph reference evidence command surface"); - validateNonEmptyString(surface.referenceTool, "Graph reference evidence command surface referenceTool"); - validateStringArray(surface.referenceCommand, "Graph reference evidence command surface referenceCommand", { allowEmpty: true }); - validateStringArray(surface.canonicalCommand, "Graph reference evidence command surface canonicalCommand", { allowEmpty: false }); - validateStringArray(surface.flags, "Graph reference evidence command surface flags", { allowEmpty: true }); - validateStringArray(surface.positionals, "Graph reference evidence command surface positionals", { allowEmpty: true }); - validateGraphReferenceEvidenceExitSemantics(surface.exitSemantics, "Graph reference evidence command surface"); - } -} - -function validateGraphReferenceEvidenceJsonOutputSurfaces(surfaces: readonly GraphReferenceEvidenceJsonOutputSurface[]): void { - validateNonEmptyArray(surfaces, "Graph reference evidence jsonOutputSurfaces"); - for (const surface of surfaces) { - validateGraphReferenceEvidenceSurfaceBase(surface, "Graph reference evidence JSON output surface"); - validateNonEmptyString(surface.command, "Graph reference evidence JSON output surface command"); - validateStringArray(surface.requiredFields, "Graph reference evidence JSON output surface requiredFields", { allowEmpty: false }); - validateGraphReferenceEvidenceExitSemantics(surface.exitSemantics, "Graph reference evidence JSON output surface"); - } -} - -function validateGraphReferenceEvidenceSqliteFixtures(fixtures: readonly GraphReferenceEvidenceSqliteFixture[]): void { - validateNonEmptyArray(fixtures, "Graph reference evidence sqliteFixtures"); - for (const fixture of fixtures) { - validateGraphReferenceEvidenceSurfaceBase(fixture, "Graph reference evidence SQLite fixture"); - validateNonEmptyString(fixture.fixture, "Graph reference evidence SQLite fixture path"); - validateStringArray(fixture.tables, "Graph reference evidence SQLite fixture tables", { allowEmpty: false }); - validateStringArray(fixture.indexes, "Graph reference evidence SQLite fixture indexes", { allowEmpty: false }); - validateStringArray(fixture.metadataKeys, "Graph reference evidence SQLite fixture metadataKeys", { allowEmpty: false }); - validateStringArray(fixture.nodeKinds, "Graph reference evidence SQLite fixture nodeKinds", { allowEmpty: false }); - validateStringArray(fixture.edgeKinds, "Graph reference evidence SQLite fixture edgeKinds", { allowEmpty: false }); - validateStringArray(fixture.directReaderQueries, "Graph reference evidence SQLite fixture directReaderQueries", { allowEmpty: false }); - } -} - -function validateGraphReferenceEvidenceDaemonFixtures(fixtures: readonly GraphReferenceEvidenceDaemonFixture[]): void { - validateNonEmptyArray(fixtures, "Graph reference evidence daemonFixtures"); - for (const fixture of fixtures) { - validateGraphReferenceEvidenceSurfaceBase(fixture, "Graph reference evidence daemon fixture"); - validateNonEmptyString(fixture.fixture, "Graph reference evidence daemon fixture path"); - validateNonEmptyString(fixture.protocol, "Graph reference evidence daemon fixture protocol"); - validateStringArray(fixture.envelopes, "Graph reference evidence daemon fixture envelopes", { allowEmpty: false }); - } -} - -function validateGraphReferenceEvidenceBaselineReceipts(receipts: readonly GraphReferenceEvidenceBaselineReceipt[]): void { - validateNonEmptyArray(receipts, "Graph reference evidence baselineReceipts"); - for (const receipt of receipts) { - validateGraphReferenceEvidenceSurfaceBase(receipt, "Graph reference evidence baseline receipt"); - validateNonEmptyString(receipt.metric, "Graph reference evidence baseline receipt metric"); - validateNonEmptyString(receipt.receipt, "Graph reference evidence baseline receipt path"); - if (receipt.label !== "reference_evidence_non_implementation_input") { - throw new Error("Graph reference evidence baseline receipt label must be reference_evidence_non_implementation_input"); - } - if (receipt.sourceAvailability !== "available" && receipt.sourceAvailability !== "unavailable") { - throw new Error("Graph reference evidence baseline receipt sourceAvailability must be available or unavailable"); - } - if (receipt.nonImplementationInput !== true) { - throw new Error("Graph reference evidence baseline receipt must be non-implementation input"); - } - } -} - -function validateGraphReferenceEvidenceOptionalSurfaces(surfaces: readonly GraphReferenceEvidenceOptionalAnalysisSurface[]): void { - validateNonEmptyArray(surfaces, "Graph reference evidence optionalAnalysisSurfaces"); - for (const surface of surfaces) { - validateGraphReferenceEvidenceSurfaceBase(surface, "Graph reference evidence optional analysis surface"); - validateGraphReleaseDeferredChild(surface.issue, "Graph reference evidence optional analysis surface issue"); - if (surface.status !== "deferred") throw new Error("Graph reference evidence optional analysis surface status must be deferred"); - if (surface.classification === "required") { - throw new Error("Graph reference evidence optional analysis surfaces must not mark staged graph release surfaces as required"); - } - } - validateGraphReleaseOptionalAnalysisSurfaceSet( - surfaces, - "Graph reference evidence optional analysis surfaces" - ); -} - -function validateGraphReferenceEvidenceGoldenCorpus(corpus: GraphReferenceEvidenceGoldenCorpusRef): void { - validateGraphReferenceEvidenceSurfaceBase(corpus, "Graph reference evidence golden corpus"); - validateNonEmptyString(corpus.fixture, "Graph reference evidence golden corpus fixture"); - validateStringArray(corpus.covers, "Graph reference evidence golden corpus covers", { allowEmpty: false }); -} - -function validateGraphReferenceEvidenceSurfaceBase(surface: GraphReferenceEvidenceSurfaceBase, label: string): void { - if (!surface || typeof surface !== "object") throw new Error(`${label} is required`); - validateNonEmptyString(surface.id, `${label} id`); - validateGraphReferenceEvidenceSurfaceClassification(surface.classification); - validateStringArray(surface.fixtures, `${label} fixtures`, { allowEmpty: true }); - if (surface.classification === "required" && surface.fixtures.length === 0) { - throw new Error(`${label} required surface must include fixture coverage`); - } -} - -function validateGraphReferenceEvidenceExitSemantics(exitSemantics: GraphReferenceEvidenceExitSemantics, label: string): void { - if (!exitSemantics || typeof exitSemantics !== "object") { - throw new Error(`${label} exitSemantics is required`); - } - if (exitSemantics.success !== 0) throw new Error(`${label} exitSemantics success must be 0`); - validateNonEmptyString(exitSemantics.failure, `${label} exitSemantics failure`); -} - -function validateGraphReferenceEvidenceProvenance(provenance: GraphReferenceEvidenceProvenance): void { - if (!provenance || typeof provenance !== "object") { - throw new Error("Graph reference evidence provenance is required"); - } - if (provenance.containsPythonCrgSource !== false) { - throw new Error("Graph reference evidence manifest must not contain Python CRG source"); - } - if (provenance.containsPackageMetadata !== false) { - throw new Error("Graph reference evidence manifest must not contain Python CRG package metadata"); - } - if (provenance.containsGitHistory !== false) { - throw new Error("Graph reference evidence manifest must not contain Python CRG git history"); - } - if (provenance.referenceReceiptsAreImplementationInput !== false) { - throw new Error("Graph reference evidence receipts must not be implementation input"); - } - validateStringArray(provenance.implementationPackageNames, "Graph reference evidence implementationPackageNames", { - allowEmpty: false - }); - for (const name of provenance.implementationPackageNames) { - if (/\bcrg\b|code-review-graph|gungnir/i.test(name)) { - throw new Error(`Graph reference evidence manifest uses a forbidden implementation package name: ${name}`); - } - } - validateStringArray(provenance.allowedMentionPaths, "Graph reference evidence allowedMentionPaths", { allowEmpty: false }); -} - -function validateGraphReferenceEvidenceSourceFreeStrings(value: unknown): void { - const forbidden = [/tirth8205/i, /pyproject\.toml/i, /setup\.py/i, /setup\.cfg/i, /Pipfile/i, /git clone/i]; - for (const text of collectStrings(value)) { - const pattern = forbidden.find((entry) => entry.test(text)); - if (pattern) throw new Error(`Graph reference evidence manifest contains forbidden source provenance: ${text}`); - } -} - function validateGraphReleasePackageVersions(versions: readonly GraphReleasePackageVersion[]): void { validateNonEmptyArray(versions, "Graph release graphPackageVersions"); for (const version of versions) { @@ -9175,10 +8825,10 @@ function validateGraphReleasePackageInspection(inspection: GraphReleasePackageIn "forbiddenMarkersAbsent", "generatedBuildMetadataAbsent", "privatePathsAbsent", - "pythonCrgSourceAbsent", - "pythonGraphPackageMetadataAbsent", - "pythonCrgGitHistoryAbsent", - "forbiddenImplementationPackageNamesAbsent" + "sourceProvenanceAbsent", + "packageMetadataAbsent", + "gitHistoryAbsent", + "foreignImplementationNamesAbsent" ] as const) { if (inspection[key] !== true) throw new Error(`Graph release packageInspection ${key} must be true`); } @@ -9252,7 +8902,7 @@ function validateGraphReleaseOptionalSurfaces(surfaces: readonly GraphReleaseOpt if (!surface || typeof surface !== "object") throw new Error("Graph release optional surface is required"); validateGraphReleaseDeferredChild(surface.issue, "Graph release optional surface issue"); validateNonEmptyString(surface.id, "Graph release optional surface id"); - validateGraphReferenceEvidenceSurfaceClassification(surface.classification); + validateGraphReleaseSurfaceClassification(surface.classification); if (surface.status !== "unsupported" && surface.status !== "deferred") { throw new Error("Graph release optional surface status must be unsupported or deferred"); } @@ -9422,9 +9072,6 @@ function validateReleaseReceiptPackageManifest( ): void { if (!manifest || typeof manifest !== "object") throw new Error("Release receipt package manifest is required"); if (manifest.name !== packageName) throw new Error(`Release receipt package manifest name must match ${packageName}`); - if (manifest.name.includes("lattice") || manifest.name.includes("crg") || manifest.name.includes("cix") || manifest.name.includes("rox")) { - throw new Error(`Release receipt package manifest uses old public package identity: ${manifest.name}`); - } validateNonEmptyString(manifest.version, "Release receipt package manifest version"); validateNonEmptyString(manifest.license, "Release receipt package manifest license"); if (isGraphCoreNativePackageName(packageName)) { @@ -9712,7 +9359,6 @@ function validateReleaseReceiptBins(bins: Readonly>, pack const binNames = Object.keys(bins); for (const bin of binNames) { validateNonEmptyString(bin, "Release receipt bin name"); - if (["lattice", "crg", "cix", "rox"].includes(bin)) throw new Error(`Release receipt package exposes old public bin ${bin}`); validateRepoRelativePath(bins[bin]); } if (packageName === "opcore") { @@ -9829,17 +9475,12 @@ function validateReleaseCutoverDescriptor(descriptorEvidence: ReleaseCutoverDesc function validateReleaseCutoverEnvironmentIsolation(environment: ReleaseCutoverEnvironmentIsolationEvidence): void { if (!environment || typeof environment !== "object") throw new Error("Release cutover environmentIsolation is required"); - if (environment.currentToolEnvCleared !== true) throw new Error("Release cutover current-tool environment must be cleared"); - if (!Number.isInteger(environment.clearedEnvVarCount) || environment.clearedEnvVarCount < 5) { - throw new Error("Release cutover clearedEnvVarCount must be at least 5"); - } if (environment.pathSanitized !== true) throw new Error("Release cutover PATH must be sanitized"); - if (environment.aceRuntimeBinExcluded !== true) throw new Error("Release cutover ACE runtime bin must be excluded"); - if (environment.siblingCovibesExcluded !== true) throw new Error("Release cutover sibling Covibes paths must be excluded"); - if (environment.opcoreBinOnly !== true) throw new Error("Release cutover installed project must expose only Opcore-owned bins"); - const oldBins = environment.oldBinsAbsent; - if (!oldBins || oldBins.lattice !== true || oldBins.crg !== true || oldBins.cix !== true || oldBins.rox !== true) { - throw new Error("Release cutover old public bins must be absent"); + if (environment.siblingRepositoriesExcluded !== true) { + throw new Error("Release cutover sibling repository paths must be excluded"); + } + if (environment.opcoreBinsVerified !== true) { + throw new Error("Release cutover installed project bins must be verified"); } } @@ -10025,34 +9666,6 @@ function validateReleaseCutoverNegativeChecks(checks: readonly ReleaseCutoverNeg } } -function validateReleaseCutoverCurrentToolGuardrails(guardrails: readonly ReleaseCutoverCurrentToolGuardrailReceipt[]): void { - validateNonEmptyArray(guardrails, "Release cutover current-tool guardrails"); - validateExactStringSet( - guardrails.map((entry) => entry.id), - releaseCutoverCurrentToolGuardrailIds, - "Release cutover current-tool guardrails" - ); - for (const guardrail of guardrails) { - if (!guardrail || typeof guardrail !== "object") throw new Error("Release cutover current-tool guardrail is required"); - if (!includesString(releaseCutoverCurrentToolGuardrailIds, guardrail.id)) { - throw new Error(`Unknown release cutover current-tool guardrail id: ${String(guardrail.id)}`); - } - const expectedCommand = guardrail.id === "current-tools-validate-changed" - ? ["npm", "run", "current-tools:validate-changed"] as const - : ["npm", "run", "current-tools:validate-rust-graph"] as const; - validateExactStringSequence(guardrail.command, expectedCommand, `Release cutover guardrail ${guardrail.id} command`); - if (guardrail.status !== "passed") throw new Error("Release cutover current-tool guardrail status must be passed"); - if (guardrail.exitCode !== 0) throw new Error("Release cutover current-tool guardrail exitCode must be 0"); - validateSha256(guardrail.stdoutSha256, "Release cutover current-tool guardrail stdoutSha256"); - validateSha256(guardrail.stderrSha256, "Release cutover current-tool guardrail stderrSha256"); - if (guardrail.retained !== true) throw new Error("Release cutover current-tool guardrail must be retained"); - validateNonEmptyString(guardrail.assertion, "Release cutover current-tool guardrail assertion"); - if (guardrail.oldToolReplacementClaimed !== false) { - throw new Error("Release cutover current-tool guardrail must not claim old-tool replacement"); - } - } -} - function validateReleaseCutoverForbiddenMarkerScan(scan: ReleaseCutoverForbiddenMarkerScan): void { if (!scan || typeof scan !== "object") throw new Error("Release cutover forbiddenMarkerScan is required"); validateNonNegativeInteger(scan.scannedTextCount, "Release cutover forbidden marker scannedTextCount"); @@ -10078,60 +9691,6 @@ function validateReleaseCutoverInputEvidence(evidence: readonly ReleaseCutoverIn } } -function validateRustOldRoxComparisonSurfaces(surfaces: readonly RustOldRoxComparisonSurfaceReceipt[]): void { - validateNonEmptyArray(surfaces, "Rust old-Rox comparison surfaces"); - validateExactStringSet( - surfaces.map((entry) => entry.id), - rustOldRoxComparisonSurfaceIds, - "Rust old-Rox comparison surfaces" - ); - for (const surface of surfaces) { - if (!surface || typeof surface !== "object") throw new Error("Rust old-Rox comparison surface is required"); - if (!includesString(rustOldRoxComparisonSurfaceIds, surface.id)) { - throw new Error(`Unknown Rust old-Rox comparison surface: ${String(surface.id)}`); - } - if (typeof surface.graphEvidenceExists !== "boolean") { - throw new Error("Rust old-Rox comparison graphEvidenceExists must be boolean"); - } - validateStringArray(surface.graphEvidence, "Rust old-Rox comparison graph evidence", { allowEmpty: false }); - if (surface.graphEvidenceExists && surface.graphEvidence.length === 0) { - throw new Error("Rust old-Rox comparison graph evidence must describe existing evidence"); - } - validateStringArray(surface.stillUniquelyProvidedByCurrentTools, "Rust old-Rox comparison current tool evidence", { - allowEmpty: false - }); - if (!includesString(rustOldRoxComparisonReplacementStatuses, surface.replacementStatus)) { - throw new Error("Rust old-Rox comparison replacementStatus must be retained or deferred"); - } - } -} - -function validateRustOldRoxComparisonGuardrails(guardrails: readonly RustOldRoxComparisonGuardrailReceipt[]): void { - validateNonEmptyArray(guardrails, "Rust old-Rox comparison guardrails"); - validateExactStringSet( - guardrails.map((entry) => entry.id), - ["current-tools:validate-rust-graph"] as const, - "Rust old-Rox comparison guardrails" - ); - for (const guardrail of guardrails) { - if (!guardrail || typeof guardrail !== "object") throw new Error("Rust old-Rox comparison guardrail is required"); - if (guardrail.id !== "current-tools:validate-rust-graph") { - throw new Error("Rust old-Rox comparison guardrail id must be current-tools:validate-rust-graph"); - } - validateExactStringSequence( - guardrail.command, - ["npm", "run", "current-tools:validate-rust-graph"], - "Rust old-Rox comparison guardrail command" - ); - if (guardrail.replacementStatus !== "retained") { - throw new Error("Rust old-Rox comparison guardrail replacementStatus must be retained"); - } - if (guardrail.oldToolReplacementClaimed !== false) { - throw new Error("Rust old-Rox comparison guardrail must not claim old-tool replacement"); - } - } -} - function validateAspDogfoodManager(manager: AspDogfoodManagerEvidence): void { if (!manager || typeof manager !== "object") throw new Error("ASP dogfood manager evidence is required"); if (manager.bootstrapSource !== "local-sibling") throw new Error("ASP dogfood bootstrapSource must be local-sibling"); @@ -10148,7 +9707,6 @@ function validateAspDogfoodAspHome(aspHome: AspDogfoodAspHomeEvidence): void { if (aspHome.isolated !== true) throw new Error("ASP dogfood ASP_HOME must be isolated"); if (aspHome.sharedStateMutated !== false) throw new Error("ASP dogfood shared ASP state must not be mutated"); if (aspHome.pathSanitized !== true) throw new Error("ASP dogfood PATH must be sanitized for manager execution"); - if (aspHome.aceRuntimeBinExcluded !== true) throw new Error("ASP dogfood manager PATH must exclude .ace/runtime"); } function validateAspDogfoodHostFixture(fixture: AspDogfoodHostFixtureEvidence): void { @@ -10287,28 +9845,6 @@ function validateAspDogfoodProviderProbe(probe: AspDogfoodProviderProbeEvidence) assertNoAspDogfoodHostOwnedFields(probe.assessment); } -function validateAspDogfoodGuardrails(guardrails: readonly AspDogfoodGuardrailReceipt[]): void { - validateNonEmptyArray(guardrails, "ASP dogfood current-tool guardrails"); - validateExactStringSet( - guardrails.map((entry) => entry.id), - aspDogfoodGuardrailIds, - "ASP dogfood current-tool guardrail ids" - ); - for (const guardrail of guardrails) { - if (!includesString(aspDogfoodGuardrailIds, guardrail.id)) { - throw new Error(`Unknown ASP dogfood guardrail id: ${String(guardrail.id)}`); - } - validateAspDogfoodCommandRun(guardrail, guardrail.id, `ASP dogfood guardrail ${guardrail.id}`); - if (guardrail.retained !== true) throw new Error("ASP dogfood old-tool guardrails must be retained"); - if (includesString(aspDogfoodRequiredGuardrailIds, guardrail.id) && guardrail.status !== "passed") { - throw new Error(`ASP dogfood required guardrail ${guardrail.id} must pass`); - } - if (guardrail.id === "current-tools-validate-all" && guardrail.status !== "passed" && guardrail.status !== "retained-not-run") { - throw new Error("ASP dogfood current-tools-validate-all must pass or be retained-not-run"); - } - } -} - function validateAspDogfoodUnsupportedSurfaces(surfaces: readonly AspDogfoodUnsupportedSurfaceEvidence[]): void { validateNonEmptyArray(surfaces, "ASP dogfood unsupported surfaces"); validateExactStringSet( @@ -10321,8 +9857,8 @@ function validateAspDogfoodUnsupportedSurfaces(surfaces: readonly AspDogfoodUnsu if (!includesString(aspDogfoodUnsupportedSurfaceIds, entry.surface)) { throw new Error(`Unknown ASP dogfood unsupported surface: ${String(entry.surface)}`); } - if (!includesString(["degraded", "retained-old-tool-gate", "parity-blocker"] as const, entry.status)) { - throw new Error("ASP dogfood unsupported surface status must be degraded, retained-old-tool-gate, or parity-blocker"); + if (!includesString(["degraded", "parity-blocker"] as const, entry.status)) { + throw new Error("ASP dogfood unsupported surface status must be degraded or parity-blocker"); } if (entry.cleanCoverage !== false) throw new Error("ASP dogfood unsupported inspect/edit surfaces must not be represented as clean coverage"); validateNonEmptyString(entry.blocker, "ASP dogfood unsupported surface blocker"); @@ -10330,7 +9866,7 @@ function validateAspDogfoodUnsupportedSurfaces(surfaces: readonly AspDogfoodUnsu } function validateAspDogfoodParityBlockers(blockers: readonly AspDogfoodParityBlocker[]): void { - validateNonEmptyArray(blockers, "ASP dogfood parity blockers"); + if (!Array.isArray(blockers)) throw new Error("ASP dogfood parity blockers must be an array"); for (const blocker of blockers) { if (!blocker || typeof blocker !== "object") throw new Error("ASP dogfood parity blocker is required"); validateNonEmptyString(blocker.source, "ASP dogfood parity blocker source"); @@ -10385,7 +9921,7 @@ function validateAspDogfoodForbiddenProviderEntrypoint(receipt: AspDogfoodReceip const findings: string[] = []; for (const text of providerTexts) { const normalized = text.replaceAll("\\", "/").toLowerCase(); - for (const marker of [...aspDogfoodForbiddenProviderMarkers, legacyAspProviderBinMarker]) { + for (const marker of aspDogfoodForbiddenProviderMarkers) { if (normalized.includes(marker.toLowerCase())) findings.push(marker); } } @@ -10501,7 +10037,7 @@ function validateExactStringSequence(actual: readonly string[], expected: readon } function validateGraphReleaseSourceFreeStrings(value: unknown): void { - const forbidden = [/tirth8205/i, /pyproject\.toml/i, /setup\.py/i, /setup\.cfg/i, /Pipfile/i, /git clone/i, /code-review-graph/i, /gungnir/i]; + const forbidden = [/tirth8205/i, /pyproject\.toml/i, /setup\.py/i, /setup\.cfg/i, /Pipfile/i, /git clone/i]; for (const text of collectStrings(value)) { const pattern = forbidden.find((entry) => entry.test(text)); if (pattern) throw new Error(`Graph release receipt contains forbidden source provenance: ${text}`); diff --git a/packages/edit/src/path-policy.ts b/packages/edit/src/path-policy.ts index 3f96854..79511e0 100644 --- a/packages/edit/src/path-policy.ts +++ b/packages/edit/src/path-policy.ts @@ -32,15 +32,11 @@ export interface ValidatedCreatePath { const patchTreeForbiddenRoots = [ ".git", - ".ace", ".agents", ".claude", ".codex", ".gemini", ".opencode", - ".code-review-graph", - ".rox-cache", - ".robustness-engine-cache", ".opcore/graph", "target", "node_modules" diff --git a/packages/fixtures/graph-pipeline/pipeline-fixtures.json b/packages/fixtures/graph-pipeline/pipeline-fixtures.json index df12239..0e4fb37 100644 --- a/packages/fixtures/graph-pipeline/pipeline-fixtures.json +++ b/packages/fixtures/graph-pipeline/pipeline-fixtures.json @@ -42,8 +42,8 @@ { "id": "graph-pipeline-ignore-file-v1", "operation": "build", - "ignoreFiles": [".gitignore", ".code-review-graphignore"], - "excludedFiles": ["ignored/generated.ts", "ignored/crg.ts"] + "ignoreFiles": [".gitignore"], + "excludedFiles": ["ignored/generated.ts"] }, { "id": "graph-pipeline-schema-mismatch-v1", diff --git a/packages/fixtures/graph-reference-evidence/baseline-receipts.json b/packages/fixtures/graph-reference-evidence/baseline-receipts.json deleted file mode 100644 index fdeb54e..0000000 --- a/packages/fixtures/graph-reference-evidence/baseline-receipts.json +++ /dev/null @@ -1,115 +0,0 @@ -{ - "schemaVersion": 1, - "issue": "#19", - "label": "reference_evidence_non_implementation_input", - "origin": "covibes-authored-synthetic", - "sourceTool": "current external graph dev wrapper", - "sourceAvailability": "available", - "collectedAt": "2026-06-04T10:00:25.536Z", - "receipts": [ - { - "id": "baseline-install-setup", - "metric": "install_setup_ms", - "value": 109, - "unit": "ms", - "sourceAvailability": "available", - "nonImplementationInput": true, - "command": ".ace/runtime/bin/crg --help", - "exitCode": 0, - "stderr": "" - }, - { - "id": "baseline-cold-build", - "metric": "cold_build_ms", - "value": 237, - "unit": "ms", - "sourceAvailability": "available", - "nonImplementationInput": true, - "command": ".ace/runtime/bin/crg build --repo . --json", - "exitCode": 0, - "stderr": "INFO: Progress: 24/24 files parsed" - }, - { - "id": "baseline-incremental-update", - "metric": "incremental_update_ms", - "value": 169, - "unit": "ms", - "sourceAvailability": "available", - "nonImplementationInput": true, - "command": ".ace/runtime/bin/crg update --base HEAD --repo . --json", - "exitCode": 0, - "stderr": "" - }, - { - "id": "baseline-impact-cold", - "metric": "impact_cold_ms", - "value": 164, - "unit": "ms", - "sourceAvailability": "available", - "nonImplementationInput": true, - "command": ".ace/runtime/bin/crg impact --files packages/contracts/src/index.ts --repo . --json", - "exitCode": 0, - "stderr": "" - }, - { - "id": "baseline-impact-hot", - "metric": "impact_hot_ms", - "value": 172, - "unit": "ms", - "sourceAvailability": "available", - "nonImplementationInput": true, - "command": ".ace/runtime/bin/crg impact --files packages/contracts/src/index.ts --repo . --json", - "exitCode": 0, - "stderr": "" - }, - { - "id": "baseline-search", - "metric": "search_ms", - "value": 170, - "unit": "ms", - "sourceAvailability": "available", - "nonImplementationInput": true, - "command": ".ace/runtime/bin/crg search GraphProvider --limit 5 --repo . --json", - "exitCode": 0, - "stderr": "" - }, - { - "id": "baseline-db-size", - "metric": "db_size_bytes", - "value": 913408, - "unit": "bytes", - "sourceAvailability": "available", - "nonImplementationInput": true, - "command": "stat .code-review-graph/graph.db" - }, - { - "id": "baseline-wal-size", - "metric": "wal_size_bytes", - "value": 1, - "unit": "bytes", - "sourceAvailability": "unavailable", - "nonImplementationInput": true, - "command": "stat .code-review-graph/graph.db-wal" - }, - { - "id": "baseline-daemon-startup", - "metric": "daemon_startup_ms", - "value": 102, - "unit": "ms", - "sourceAvailability": "available", - "nonImplementationInput": true, - "command": ".ace/runtime/bin/crg serve --help", - "exitCode": 0, - "stderr": "" - }, - { - "id": "baseline-daemon-query", - "metric": "daemon_query_ms", - "value": 1, - "unit": "ms", - "sourceAvailability": "available", - "nonImplementationInput": true, - "command": "opcore.graph.daemon synthetic query envelope" - } - ] -} diff --git a/packages/fixtures/graph-reference-evidence/daemon-socket-fixtures.json b/packages/fixtures/graph-reference-evidence/daemon-socket-fixtures.json deleted file mode 100644 index b68b12e..0000000 --- a/packages/fixtures/graph-reference-evidence/daemon-socket-fixtures.json +++ /dev/null @@ -1,482 +0,0 @@ -{ - "schemaVersion": 1, - "issue": "#19", - "origin": "covibes-authored-synthetic", - "classification": "required", - "serveTransportIssue": "#47", - "protocols": ["opcore.graph.daemon", "jsonrpc-2.0", "reference-mcp-stdio-baseline-only"], - "envelopes": [ - { - "id": "ping-request", - "direction": "client_to_daemon", - "protocol": "opcore.graph.daemon", - "payload": { - "protocol": "opcore.graph.daemon", - "requestId": "req-ping-1", - "schemaVersion": 1, - "operation": "status", - "repo": { - "repoId": "synthetic-reference" - } - } - }, - { - "id": "ping-response", - "direction": "daemon_to_client", - "protocol": "opcore.graph.daemon", - "payload": { - "protocol": "opcore.graph.daemon", - "requestId": "req-ping-1", - "schemaVersion": 1, - "status": { - "state": "available", - "mode": "required", - "provider": "opcore-graph", - "schemaVersion": 1, - "repo": { - "repoId": "synthetic-reference" - }, - "freshness": { - "generatedAt": "2026-06-04T00:00:00.000Z", - "ageMs": 0, - "stale": false - } - } - } - }, - { - "id": "health-status-request", - "direction": "client_to_daemon", - "protocol": "opcore.graph.daemon", - "payload": { - "protocol": "opcore.graph.daemon", - "requestId": "req-health-1", - "schemaVersion": 1, - "operation": "status", - "repo": { - "repoId": "synthetic-reference" - } - } - }, - { - "id": "health-status-response", - "direction": "daemon_to_client", - "protocol": "opcore.graph.daemon", - "payload": { - "protocol": "opcore.graph.daemon", - "requestId": "req-health-1", - "schemaVersion": 1, - "status": { - "state": "available", - "mode": "required", - "provider": "opcore-graph", - "schemaVersion": 1, - "repo": { - "repoId": "synthetic-reference" - }, - "freshness": { - "generatedAt": "2026-06-04T00:00:00.000Z", - "ageMs": 25, - "maxAgeMs": 60000, - "stale": false - }, - "capabilities": ["status", "query", "impact"] - } - } - }, - { - "id": "query-request", - "direction": "client_to_daemon", - "protocol": "opcore.graph.daemon", - "payload": { - "protocol": "opcore.graph.daemon", - "requestId": "req-query-1", - "schemaVersion": 1, - "operation": "query", - "repo": { - "repoId": "synthetic-reference" - }, - "query": { - "requestId": "query-1", - "repo": { - "repoId": "synthetic-reference" - }, - "schemaVersion": 1, - "mode": "required", - "selector": { - "kind": "symbols", - "text": "login", - "limit": 20 - } - } - } - }, - { - "id": "impact-request", - "direction": "client_to_daemon", - "protocol": "opcore.graph.daemon", - "payload": { - "protocol": "opcore.graph.daemon", - "requestId": "req-impact-1", - "schemaVersion": 1, - "operation": "query", - "repo": { - "repoId": "synthetic-reference" - }, - "query": { - "requestId": "impact-1", - "repo": { - "repoId": "synthetic-reference" - }, - "schemaVersion": 1, - "mode": "required", - "selector": { - "kind": "impact", - "ids": ["file:src/auth.ts"], - "limit": 50 - } - } - } - }, - { - "id": "success-response", - "direction": "daemon_to_client", - "protocol": "opcore.graph.daemon", - "payload": { - "protocol": "opcore.graph.daemon", - "requestId": "req-query-1", - "schemaVersion": 1, - "status": { - "state": "available", - "mode": "required", - "provider": "opcore-graph", - "schemaVersion": 1, - "repo": { - "repoId": "synthetic-reference" - }, - "freshness": { - "generatedAt": "2026-06-04T00:00:00.000Z", - "ageMs": 30, - "stale": false - } - }, - "result": { - "requestId": "query-1", - "status": { - "state": "available", - "mode": "required", - "provider": "opcore-graph", - "schemaVersion": 1, - "repo": { - "repoId": "synthetic-reference" - }, - "freshness": { - "generatedAt": "2026-06-04T00:00:00.000Z", - "ageMs": 30, - "stale": false - } - }, - "metadata": { - "schemaVersion": 1, - "provider": "opcore-graph", - "repo": { - "repoId": "synthetic-reference" - }, - "generatedAt": "2026-06-04T00:00:00.000Z", - "freshness": { - "generatedAt": "2026-06-04T00:00:00.000Z", - "ageMs": 30, - "stale": false - }, - "nodeKinds": ["file", "symbol", "test"], - "edgeKinds": ["CONTAINS", "DECLARES", "IMPORTS_FROM", "CALLS", "TESTED_BY"] - }, - "nodes": [ - { - "id": "symbol:src/auth.ts#login", - "kind": "symbol", - "path": "src/auth.ts", - "name": "login" - } - ], - "edges": [ - { - "kind": "DECLARES", - "from": "file:src/auth.ts", - "to": "symbol:src/auth.ts#login" - } - ] - } - } - }, - { - "id": "stale-response", - "direction": "daemon_to_client", - "protocol": "opcore.graph.daemon", - "payload": { - "protocol": "opcore.graph.daemon", - "requestId": "req-query-stale", - "schemaVersion": 1, - "status": { - "state": "stale", - "mode": "required", - "provider": "opcore-graph", - "schemaVersion": 1, - "repo": { - "repoId": "synthetic-reference" - }, - "freshness": { - "generatedAt": "2026-06-03T23:00:00.000Z", - "ageMs": 3600000, - "maxAgeMs": 60000, - "stale": true, - "reason": "snapshot older than freshness policy" - }, - "failure": { - "category": "stale_snapshot", - "message": "Graph snapshot is stale", - "retryable": true - } - } - } - }, - { - "id": "schema-mismatch-response", - "direction": "daemon_to_client", - "protocol": "opcore.graph.daemon", - "payload": { - "protocol": "opcore.graph.daemon", - "requestId": "req-schema-mismatch", - "schemaVersion": 1, - "status": { - "state": "schema_mismatch", - "mode": "required", - "provider": "opcore-graph", - "schemaVersion": 1, - "expectedSchemaVersion": 1, - "actualSchemaVersion": 0, - "failure": { - "category": "schema_mismatch", - "message": "Graph schema version is incompatible", - "retryable": false - } - } - } - }, - { - "id": "daemon-unavailable-response", - "direction": "client_error", - "protocol": "opcore.graph.daemon", - "payload": { - "protocol": "opcore.graph.daemon", - "requestId": "req-daemon-unavailable", - "schemaVersion": 1, - "status": { - "state": "daemon_unavailable", - "mode": "required", - "provider": "opcore-graph", - "schemaVersion": 1, - "failure": { - "category": "daemon_unavailable", - "message": "Graph daemon socket is unavailable", - "retryable": true - } - } - } - }, - { - "id": "serve-jsonl-ping", - "direction": "client_to_daemon", - "protocol": "opcore.graph.daemon", - "issue": "#47", - "payload": { - "protocol": "opcore.graph.daemon", - "requestId": "serve-ping-1", - "schemaVersion": 1, - "operation": "ping", - "repo": { - "repoId": "synthetic-reference" - } - } - }, - { - "id": "serve-jsonl-status", - "direction": "client_to_daemon", - "protocol": "opcore.graph.daemon", - "issue": "#47", - "payload": { - "protocol": "opcore.graph.daemon", - "requestId": "serve-status-1", - "schemaVersion": 1, - "operation": "status", - "repo": { - "repoId": "synthetic-reference" - } - } - }, - { - "id": "serve-jsonl-query", - "direction": "client_to_daemon", - "protocol": "opcore.graph.daemon", - "issue": "#47", - "payload": { - "protocol": "opcore.graph.daemon", - "requestId": "serve-query-1", - "schemaVersion": 1, - "operation": "query", - "repo": { - "repoId": "synthetic-reference" - }, - "query": { - "requestId": "serve-query-1", - "repo": { - "repoId": "synthetic-reference" - }, - "schemaVersion": 1, - "mode": "required", - "selector": { - "kind": "nodes", - "limit": 10 - } - } - } - }, - { - "id": "serve-jsonl-search", - "direction": "client_to_daemon", - "protocol": "opcore.graph.daemon", - "issue": "#47", - "payload": { - "protocol": "opcore.graph.daemon", - "requestId": "serve-search-1", - "schemaVersion": 1, - "operation": "query", - "repo": { - "repoId": "synthetic-reference" - }, - "search": { - "requestId": "serve-search-1", - "repo": { - "repoId": "synthetic-reference" - }, - "schemaVersion": 1, - "mode": "required", - "query": "Greeting", - "limit": 5 - } - } - }, - { - "id": "serve-jsonl-shutdown", - "direction": "client_to_daemon", - "protocol": "opcore.graph.daemon", - "issue": "#47", - "payload": { - "protocol": "opcore.graph.daemon", - "requestId": "serve-shutdown-1", - "schemaVersion": 1, - "operation": "shutdown", - "repo": { - "repoId": "synthetic-reference" - } - } - }, - { - "id": "serve-invalid-repo", - "direction": "daemon_to_client", - "protocol": "opcore.graph.daemon", - "issue": "#47", - "payload": { - "protocol": "opcore.graph.daemon", - "requestId": "serve-invalid-repo-1", - "schemaVersion": 1, - "status": { - "state": "required_missing", - "mode": "required", - "provider": "opcore-graph", - "schemaVersion": 1, - "failure": { - "category": "provider_missing", - "message": "repoRoot is not a directory" - } - } - } - }, - { - "id": "serve-schema-mismatch", - "direction": "daemon_to_client", - "protocol": "opcore.graph.daemon", - "issue": "#47", - "payload": { - "protocol": "opcore.graph.daemon", - "requestId": "serve-schema-mismatch-1", - "schemaVersion": 1, - "status": { - "state": "schema_mismatch", - "mode": "required", - "provider": "opcore-graph", - "schemaVersion": 1, - "expectedSchemaVersion": 1, - "actualSchemaVersion": 2, - "failure": { - "category": "schema_mismatch", - "message": "Graph daemon request schemaVersion mismatch" - } - } - } - }, - { - "id": "mcp-initialize", - "direction": "client_to_daemon", - "protocol": "jsonrpc-2.0", - "issue": "#47", - "payload": { - "jsonrpc": "2.0", - "id": "mcp-init-1", - "method": "initialize", - "params": { - "protocolVersion": "2024-11-05" - } - } - }, - { - "id": "mcp-initialized-notification", - "direction": "client_to_daemon", - "protocol": "jsonrpc-2.0", - "issue": "#47", - "expectResponse": false, - "payload": { - "jsonrpc": "2.0", - "method": "notifications/initialized", - "params": {} - } - }, - { - "id": "mcp-status", - "direction": "client_to_daemon", - "protocol": "jsonrpc-2.0", - "issue": "#47", - "payload": { - "jsonrpc": "2.0", - "id": "mcp-status-1", - "method": "opcore.graph/status", - "params": { - "repo": { - "repoId": "synthetic-reference" - } - } - } - }, - { - "id": "reference-mcp-stdio-baseline", - "direction": "baseline_only", - "protocol": "reference-mcp-stdio-baseline-only", - "baselineOnly": true, - "payload": { - "transport": "stdio", - "command": ["crg", "serve", "--repo", "."], - "note": "Captured only to preserve downstream transport expectations; not a Opcore-owned daemon protocol." - } - } - ] -} diff --git a/packages/fixtures/graph-reference-evidence/golden-corpus.json b/packages/fixtures/graph-reference-evidence/golden-corpus.json deleted file mode 100644 index 5fc8acb..0000000 --- a/packages/fixtures/graph-reference-evidence/golden-corpus.json +++ /dev/null @@ -1,335 +0,0 @@ -{ - "schemaVersion": 1, - "issue": "#19", - "id": "graph-reference-evidence-golden-corpus-v1", - "origin": "covibes-authored-synthetic", - "containsSourceCode": false, - "description": "Synthetic graph reference corpus authored for Opcore contracts; file contents are descriptors, not upstream source.", - "files": [ - { - "path": "src/auth.ts", - "language": "typescript", - "symbols": ["login", "logout"], - "hash": "sha256:synthetic-auth" - }, - { - "path": "src/session.ts", - "language": "typescript", - "symbols": ["createSession"], - "hash": "sha256:synthetic-session" - }, - { - "path": "tests/auth.test.ts", - "language": "typescript", - "symbols": ["login rejects invalid password"], - "hash": "sha256:synthetic-auth-test" - }, - { - "path": "src/lib.rs", - "language": "rust", - "symbols": ["Widget", "Service"], - "hash": "sha256:synthetic-rust-lib" - }, - { - "path": "src/helpers.rs", - "language": "rust", - "symbols": ["helpers::assist"], - "hash": "sha256:synthetic-rust-helpers" - }, - { - "path": "src/consumer.rs", - "language": "rust", - "symbols": ["consumer::run", "consumer.tests::test_run"], - "hash": "sha256:synthetic-rust-consumer" - } - ], - "expectedFacts": { - "parser": { - "nodes": [ - { - "id": "file:src/auth.ts", - "kind": "File", - "name": "src/auth.ts" - }, - { - "id": "file:src/session.ts", - "kind": "File", - "name": "src/session.ts" - }, - { - "id": "file:tests/auth.test.ts", - "kind": "File", - "name": "tests/auth.test.ts" - }, - { - "id": "function:src/auth.ts#login", - "kind": "Function", - "name": "login", - "filePath": "src/auth.ts" - }, - { - "id": "function:src/auth.ts#logout", - "kind": "Function", - "name": "logout", - "filePath": "src/auth.ts" - }, - { - "id": "function:src/session.ts#createSession", - "kind": "Function", - "name": "createSession", - "filePath": "src/session.ts" - }, - { - "id": "test:tests/auth.test.ts#login rejects invalid password", - "kind": "Test", - "name": "login rejects invalid password", - "filePath": "tests/auth.test.ts" - }, - { - "id": "file:src/lib.rs", - "kind": "File", - "name": "src/lib.rs" - }, - { - "id": "file:src/helpers.rs", - "kind": "File", - "name": "src/helpers.rs" - }, - { - "id": "file:src/consumer.rs", - "kind": "File", - "name": "src/consumer.rs" - }, - { - "id": "module:src/lib.rs#crate", - "kind": "Module", - "name": "crate", - "filePath": "src/lib.rs" - }, - { - "id": "module:src/helpers.rs#helpers", - "kind": "Module", - "name": "helpers", - "filePath": "src/helpers.rs" - }, - { - "id": "module:src/consumer.rs#consumer", - "kind": "Module", - "name": "consumer", - "filePath": "src/consumer.rs" - }, - { - "id": "struct:src/lib.rs#Widget", - "kind": "Struct", - "name": "Widget", - "filePath": "src/lib.rs" - }, - { - "id": "trait:src/lib.rs#Service", - "kind": "Trait", - "name": "Service", - "filePath": "src/lib.rs" - }, - { - "id": "impl:src/lib.rs#impl Service for Widget", - "kind": "Impl", - "name": "impl Service for Widget", - "filePath": "src/lib.rs" - }, - { - "id": "method:src/lib.rs#Widget::handle", - "kind": "Method", - "name": "Widget::handle", - "filePath": "src/lib.rs" - }, - { - "id": "function:src/helpers.rs#helpers::assist", - "kind": "Function", - "name": "helpers::assist", - "filePath": "src/helpers.rs" - }, - { - "id": "function:src/consumer.rs#consumer::run", - "kind": "Function", - "name": "consumer::run", - "filePath": "src/consumer.rs" - }, - { - "id": "test:src/consumer.rs#consumer.tests::test_run", - "kind": "Test", - "name": "consumer.tests::test_run", - "filePath": "src/consumer.rs" - } - ] - }, - "store": { - "metadata": { - "schema_version": "6", - "last_build_type": "full" - }, - "edges": [ - { - "kind": "CONTAINS", - "source": "file:src/auth.ts", - "target": "function:src/auth.ts#login" - }, - { - "kind": "CONTAINS", - "source": "file:src/auth.ts", - "target": "function:src/auth.ts#logout" - }, - { - "kind": "CONTAINS", - "source": "file:src/session.ts", - "target": "function:src/session.ts#createSession" - }, - { - "kind": "CONTAINS", - "source": "file:tests/auth.test.ts", - "target": "test:tests/auth.test.ts#login rejects invalid password" - }, - { - "kind": "CALLS", - "source": "function:src/auth.ts#login", - "target": "function:src/session.ts#createSession" - }, - { - "kind": "TESTED_BY", - "source": "function:src/auth.ts#login", - "target": "test:tests/auth.test.ts#login rejects invalid password" - }, - { - "kind": "CONTAINS", - "source": "file:src/lib.rs", - "target": "module:src/lib.rs#crate" - }, - { - "kind": "CONTAINS", - "source": "file:src/helpers.rs", - "target": "module:src/helpers.rs#helpers" - }, - { - "kind": "CONTAINS", - "source": "file:src/consumer.rs", - "target": "module:src/consumer.rs#consumer" - }, - { - "kind": "CONTAINS", - "source": "module:src/lib.rs#crate", - "target": "struct:src/lib.rs#Widget" - }, - { - "kind": "CONTAINS", - "source": "module:src/lib.rs#crate", - "target": "trait:src/lib.rs#Service" - }, - { - "kind": "CONTAINS", - "source": "module:src/lib.rs#crate", - "target": "impl:src/lib.rs#impl Service for Widget" - }, - { - "kind": "CONTAINS", - "source": "impl:src/lib.rs#impl Service for Widget", - "target": "method:src/lib.rs#Widget::handle" - }, - { - "kind": "CONTAINS", - "source": "module:src/helpers.rs#helpers", - "target": "function:src/helpers.rs#helpers::assist" - }, - { - "kind": "CONTAINS", - "source": "module:src/consumer.rs#consumer", - "target": "function:src/consumer.rs#consumer::run" - }, - { - "kind": "CONTAINS", - "source": "module:src/consumer.rs#consumer", - "target": "test:src/consumer.rs#consumer.tests::test_run" - }, - { - "kind": "IMPORTS_FROM", - "source": "file:src/consumer.rs", - "target": "file:src/helpers.rs" - }, - { - "kind": "IMPORTS_FROM", - "source": "file:src/consumer.rs", - "target": "file:src/lib.rs" - }, - { - "kind": "DEPENDS_ON", - "source": "file:src/consumer.rs", - "target": "file:src/helpers.rs" - }, - { - "kind": "DEPENDS_ON", - "source": "file:src/consumer.rs", - "target": "file:src/lib.rs" - }, - { - "kind": "IMPLEMENTS", - "source": "impl:src/lib.rs#impl Service for Widget", - "target": "trait:src/lib.rs#Service" - }, - { - "kind": "CALLS", - "source": "function:src/consumer.rs#consumer::run", - "target": "function:src/helpers.rs#helpers::assist" - }, - { - "kind": "CALLS", - "source": "test:src/consumer.rs#consumer.tests::test_run", - "target": "function:src/consumer.rs#consumer::run" - }, - { - "kind": "TESTED_BY", - "source": "function:src/consumer.rs#consumer::run", - "target": "test:src/consumer.rs#consumer.tests::test_run" - } - ] - }, - "query": { - "pattern": "callers_of", - "target": "createSession", - "expectedResultIds": ["function:src/auth.ts#login"], - "expectedEdgeKinds": ["CALLS"] - }, - "search": { - "query": "login", - "searchMode": "keyword", - "expectedResultIds": ["function:src/auth.ts#login", "test:tests/auth.test.ts#login rejects invalid password"], - "expectedHints": ["query_graph", "get_flow", "get_impact_radius"] - }, - "freshness": { - "freshAfterBuild": true, - "staleWhenOlderThanMs": 60000, - "requiredMetadataKeys": ["schema_version", "last_updated", "last_build_type"] - }, - "status": { - "totalNodes": 20, - "totalEdges": 24, - "nodesByKind": { - "File": 6, - "Function": 5, - "Test": 2, - "Module": 3, - "Struct": 1, - "Trait": 1, - "Impl": 1, - "Method": 1 - }, - "edgesByKind": { - "CALLS": 3, - "CONTAINS": 14, - "TESTED_BY": 2, - "IMPORTS_FROM": 2, - "DEPENDS_ON": 2, - "IMPLEMENTS": 1 - }, - "languages": ["typescript", "rust"], - "embeddingsCount": 0 - } - } -} diff --git a/packages/fixtures/graph-reference-evidence/manifest.json b/packages/fixtures/graph-reference-evidence/manifest.json deleted file mode 100644 index 126506b..0000000 --- a/packages/fixtures/graph-reference-evidence/manifest.json +++ /dev/null @@ -1,696 +0,0 @@ -{ - "schemaVersion": 1, - "issue": "#19", - "origin": "covibes-authored-synthetic", - "fixtureRefs": [ - "packages/fixtures/graph-reference-evidence/sqlite-fixtures.json", - "packages/fixtures/graph-reference-evidence/daemon-socket-fixtures.json", - "packages/fixtures/graph-reference-evidence/golden-corpus.json", - "packages/fixtures/graph-reference-evidence/baseline-receipts.json" - ], - "commandSurfaces": [ - { - "id": "graph-reference-build", - "classification": "required", - "referenceTool": "crg", - "referenceCommand": [ - "build" - ], - "canonicalCommand": [ - "opcore", - "graph", - "build" - ], - "flags": [ - "--repo", - "--json" - ], - "positionals": [], - "fixtures": [ - "build-json", - "graph-reference-evidence-golden-corpus-v1", - "baseline-cold-build" - ], - "exitSemantics": { - "success": 0, - "failure": "nonzero on parse, IO, or graph store failure" - } - }, - { - "id": "graph-reference-update", - "classification": "required", - "referenceTool": "crg", - "referenceCommand": [ - "update" - ], - "canonicalCommand": [ - "opcore", - "graph", - "update" - ], - "flags": [ - "--base", - "--repo", - "--json" - ], - "positionals": [], - "fixtures": [ - "update-json", - "freshness-fixture", - "baseline-incremental-update" - ], - "exitSemantics": { - "success": 0, - "failure": "nonzero on diff, parse, IO, or graph store failure" - } - }, - { - "id": "graph-reference-watch", - "classification": "required", - "referenceTool": "crg", - "referenceCommand": [ - "watch" - ], - "canonicalCommand": [ - "opcore", - "graph", - "watch" - ], - "flags": [ - "--repo" - ], - "positionals": [], - "fixtures": [ - "daemon-health-status" - ], - "exitSemantics": { - "success": 0, - "failure": "nonzero on watcher setup or graph refresh failure" - } - }, - { - "id": "graph-reference-status", - "classification": "required", - "referenceTool": "crg", - "referenceCommand": [ - "status" - ], - "canonicalCommand": [ - "opcore", - "graph", - "status" - ], - "flags": [ - "--repo", - "--json" - ], - "positionals": [], - "fixtures": [ - "status-json", - "sqlite-required-views", - "status-expected-facts" - ], - "exitSemantics": { - "success": 0, - "failure": "nonzero when graph metadata or database cannot be read" - } - }, - { - "id": "graph-reference-query", - "classification": "required", - "referenceTool": "crg", - "referenceCommand": [ - "query" - ], - "canonicalCommand": [ - "opcore", - "graph", - "query" - ], - "flags": [ - "--repo", - "--json" - ], - "positionals": [ - "pattern", - "target" - ], - "fixtures": [ - "query-json", - "daemon-query-request", - "query-expected-facts" - ], - "exitSemantics": { - "success": 0, - "failure": "nonzero on unsupported pattern, stale graph policy failure, or query execution failure" - } - }, - { - "id": "graph-reference-impact", - "classification": "required", - "referenceTool": "crg", - "referenceCommand": [ - "impact" - ], - "canonicalCommand": [ - "opcore", - "graph", - "impact" - ], - "flags": [ - "--base", - "--max-depth", - "--files", - "--repo", - "--json" - ], - "positionals": [], - "fixtures": [ - "impact-json", - "daemon-impact-request", - "baseline-impact-hot", - "baseline-impact-cold" - ], - "exitSemantics": { - "success": 0, - "failure": "nonzero on diff, traversal, stale graph policy, or store read failure" - } - }, - { - "id": "graph-reference-search", - "classification": "required", - "referenceTool": "crg", - "referenceCommand": [ - "search" - ], - "canonicalCommand": [ - "opcore", - "graph", - "search" - ], - "flags": [ - "--kind", - "--limit", - "--model", - "--repo", - "--json" - ], - "positionals": [ - "query" - ], - "fixtures": [ - "search-json", - "search-expected-facts", - "baseline-search" - ], - "exitSemantics": { - "success": 0, - "failure": "nonzero on invalid kind, embedding model mismatch, or graph read failure" - } - }, - { - "id": "graph-reference-serve", - "classification": "required", - "referenceTool": "crg", - "referenceCommand": [ - "serve" - ], - "canonicalCommand": [ - "opcore", - "graph", - "serve" - ], - "flags": [ - "--repo" - ], - "positionals": [], - "fixtures": [ - "serve-jsonl-ping", - "serve-jsonl-status", - "serve-jsonl-query", - "serve-jsonl-search", - "serve-jsonl-shutdown", - "mcp-initialize", - "mcp-status", - "reference-mcp-stdio-baseline" - ], - "exitSemantics": { - "success": 0, - "failure": "nonzero on daemon transport setup failure" - } - }, - { - "id": "opcore-graph-inspect", - "classification": "deferred", - "referenceTool": "crg", - "referenceCommand": [], - "canonicalCommand": [ - "opcore", - "graph", - "inspect" - ], - "flags": [], - "positionals": [], - "fixtures": [ - "inspect-deferred-marker" - ], - "exitSemantics": { - "success": 0, - "failure": "deferred canonical-only command remains out of #19 implementation scope" - } - } - ], - "jsonOutputSurfaces": [ - { - "id": "build-json", - "command": "build", - "classification": "required", - "requiredFields": [ - "files_parsed", - "total_nodes", - "total_edges", - "errors" - ], - "fixtures": [ - "graph-reference-evidence-golden-corpus-v1" - ], - "exitSemantics": { - "success": 0, - "failure": "nonzero with stderr diagnostics" - } - }, - { - "id": "update-json", - "command": "update", - "classification": "required", - "requiredFields": [ - "files_updated", - "total_nodes", - "total_edges", - "changed_files", - "dependent_files" - ], - "fixtures": [ - "freshness-expected-facts" - ], - "exitSemantics": { - "success": 0, - "failure": "nonzero with stderr diagnostics" - } - }, - { - "id": "status-json", - "command": "status", - "classification": "required", - "requiredFields": [ - "status", - "summary", - "total_nodes", - "total_edges", - "nodes_by_kind", - "edges_by_kind", - "languages", - "files_count", - "last_updated" - ], - "fixtures": [ - "status-expected-facts", - "sqlite-required-views" - ], - "exitSemantics": { - "success": 0, - "failure": "nonzero with stderr diagnostics" - } - }, - { - "id": "query-json", - "command": "query", - "classification": "required", - "requiredFields": [ - "status", - "pattern", - "target", - "description", - "summary", - "results", - "edges" - ], - "fixtures": [ - "query-expected-facts", - "daemon-query-request" - ], - "exitSemantics": { - "success": 0, - "failure": "nonzero with JSON error or stderr diagnostics" - } - }, - { - "id": "impact-json", - "command": "impact", - "classification": "required", - "requiredFields": [ - "status", - "summary", - "changed_files", - "changed_nodes", - "impacted_nodes", - "impacted_files", - "edges", - "truncated", - "total_impacted" - ], - "fixtures": [ - "impact-expected-facts", - "daemon-impact-request" - ], - "exitSemantics": { - "success": 0, - "failure": "nonzero with JSON error or stderr diagnostics" - } - }, - { - "id": "search-json", - "command": "search", - "classification": "required", - "requiredFields": [ - "status", - "query", - "search_mode", - "summary", - "results", - "_hints" - ], - "fixtures": [ - "search-expected-facts" - ], - "exitSemantics": { - "success": 0, - "failure": "nonzero with JSON error or stderr diagnostics" - } - } - ], - "sqliteFixtures": [ - { - "id": "sqlite-required-views", - "classification": "required", - "fixture": "packages/fixtures/graph-reference-evidence/sqlite-fixtures.json", - "tables": [ - "metadata", - "nodes", - "edges", - "nodes_fts" - ], - "indexes": [ - "idx_nodes_file", - "idx_nodes_kind", - "idx_nodes_qualified", - "idx_edges_source", - "idx_edges_target", - "idx_edges_kind", - "idx_edges_file", - "idx_nodes_exported_name" - ], - "metadataKeys": [ - "schema_version", - "last_updated", - "last_build_type" - ], - "nodeKinds": [ - "File", - "Function", - "Test", - "Module", - "Struct", - "Enum", - "Trait", - "Impl", - "Method", - "TypeAlias", - "Const", - "Static", - "Macro" - ], - "edgeKinds": [ - "CALLS", - "CONTAINS", - "IMPORTS_FROM", - "TESTED_BY", - "IMPLEMENTS", - "DEPENDS_ON", - "INHERITS" - ], - "directReaderQueries": [ - "status-counts", - "status-edge-counts", - "impact-edges-from-file", - "search-by-name", - "freshness-metadata" - ], - "fixtures": [ - "packages/fixtures/graph-reference-evidence/sqlite-fixtures.json" - ] - } - ], - "daemonFixtures": [ - { - "id": "daemon-hot-query", - "classification": "required", - "fixture": "packages/fixtures/graph-reference-evidence/daemon-socket-fixtures.json", - "protocol": "opcore.graph.daemon", - "envelopes": [ - "ping-request", - "ping-response", - "health-status-request", - "health-status-response", - "query-request", - "impact-request", - "success-response", - "serve-jsonl-ping", - "serve-jsonl-status", - "serve-jsonl-query", - "serve-jsonl-search", - "serve-jsonl-shutdown", - "serve-invalid-repo", - "serve-schema-mismatch", - "mcp-initialize", - "mcp-status", - "stale-response", - "schema-mismatch-response", - "daemon-unavailable-response" - ], - "fixtures": [ - "packages/fixtures/graph-reference-evidence/daemon-socket-fixtures.json" - ] - } - ], - "baselineReceipts": [ - { - "id": "baseline-install-setup", - "metric": "install_setup_ms", - "classification": "required", - "receipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", - "label": "reference_evidence_non_implementation_input", - "sourceAvailability": "available", - "nonImplementationInput": true, - "fixtures": [ - "baseline-receipts" - ] - }, - { - "id": "baseline-cold-build", - "metric": "cold_build_ms", - "classification": "required", - "receipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", - "label": "reference_evidence_non_implementation_input", - "sourceAvailability": "available", - "nonImplementationInput": true, - "fixtures": [ - "baseline-receipts" - ] - }, - { - "id": "baseline-incremental-update", - "metric": "incremental_update_ms", - "classification": "required", - "receipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", - "label": "reference_evidence_non_implementation_input", - "sourceAvailability": "available", - "nonImplementationInput": true, - "fixtures": [ - "baseline-receipts" - ] - }, - { - "id": "baseline-impact-cold", - "metric": "impact_cold_ms", - "classification": "required", - "receipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", - "label": "reference_evidence_non_implementation_input", - "sourceAvailability": "available", - "nonImplementationInput": true, - "fixtures": [ - "baseline-receipts" - ] - }, - { - "id": "baseline-impact-hot", - "metric": "impact_hot_ms", - "classification": "required", - "receipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", - "label": "reference_evidence_non_implementation_input", - "sourceAvailability": "available", - "nonImplementationInput": true, - "fixtures": [ - "baseline-receipts" - ] - }, - { - "id": "baseline-search", - "metric": "search_ms", - "classification": "required", - "receipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", - "label": "reference_evidence_non_implementation_input", - "sourceAvailability": "available", - "nonImplementationInput": true, - "fixtures": [ - "baseline-receipts" - ] - }, - { - "id": "baseline-db-size", - "metric": "db_size_bytes", - "classification": "required", - "receipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", - "label": "reference_evidence_non_implementation_input", - "sourceAvailability": "available", - "nonImplementationInput": true, - "fixtures": [ - "baseline-receipts" - ] - }, - { - "id": "baseline-wal-size", - "metric": "wal_size_bytes", - "classification": "required", - "receipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", - "label": "reference_evidence_non_implementation_input", - "sourceAvailability": "unavailable", - "nonImplementationInput": true, - "fixtures": [ - "baseline-receipts" - ] - }, - { - "id": "baseline-daemon-startup", - "metric": "daemon_startup_ms", - "classification": "required", - "receipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", - "label": "reference_evidence_non_implementation_input", - "sourceAvailability": "available", - "nonImplementationInput": true, - "fixtures": [ - "baseline-receipts" - ] - }, - { - "id": "baseline-daemon-query", - "metric": "daemon_query_ms", - "classification": "required", - "receipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", - "label": "reference_evidence_non_implementation_input", - "sourceAvailability": "available", - "nonImplementationInput": true, - "fixtures": [ - "baseline-receipts" - ] - } - ], - "optionalAnalysisSurfaces": [ - { - "issue": "#13", - "id": "coverage", - "classification": "deferred", - "status": "deferred", - "fixtures": [ - "coverage-deferred-marker" - ] - }, - { - "issue": "#14", - "id": "flows", - "classification": "optional", - "status": "deferred", - "fixtures": [ - "sqlite-flows", - "reference-command-flows" - ] - }, - { - "issue": "#15", - "id": "communities", - "classification": "optional", - "status": "deferred", - "fixtures": [ - "sqlite-communities", - "reference-command-communities" - ] - }, - { - "issue": "#16", - "id": "read_only_suggestions", - "classification": "supporting", - "status": "deferred", - "fixtures": [ - "read-only-refactor-baseline" - ] - } - ], - "goldenCorpus": { - "id": "graph-reference-evidence-golden-corpus-v1", - "classification": "required", - "fixture": "packages/fixtures/graph-reference-evidence/golden-corpus.json", - "covers": [ - "parser", - "store", - "query", - "search", - "freshness", - "status", - "code-review-graph-cli-surface", - "crg-watch-roots-ignore-reconcile", - "crg-wal-health-checkpoint-pressure", - "crg-hot-query-socket", - "crg-mcp-tool-surface", - "crg-impact-query-review-search", - "rust-source-extraction-fixtures", - "rust-store-freshness-fts", - "rust-query-impact-search", - "mixed-rust-ts-source-fixture", - "lattice-native-graph-provider-surfaces", - "lattice-current-tools-graph-status", - "lattice-crg-reference-baseline-release-fixtures", - "covibes-crg-watch-ci-unit-gate", - "covibes-push-ready-crg-freshness-impact-gate", - "covibes-agent-guidance-crg-watch-and-reads", - "mcp-server-name-code-review-graph-compatibility" - ], - "fixtures": [ - "packages/fixtures/graph-reference-evidence/golden-corpus.json" - ] - }, - "provenance": { - "containsPythonCrgSource": false, - "containsPackageMetadata": false, - "containsGitHistory": false, - "referenceReceiptsAreImplementationInput": false, - "implementationPackageNames": [ - "@the-open-engine/opcore-graph", - "@the-open-engine/opcore-contracts", - "@the-open-engine/opcore-fixtures" - ], - "allowedMentionPaths": [ - "docs/graph-reference-evidence/", - "packages/fixtures/graph-reference-evidence/" - ] - } -} diff --git a/packages/fixtures/graph-reference-evidence/sqlite-fixtures.json b/packages/fixtures/graph-reference-evidence/sqlite-fixtures.json deleted file mode 100644 index c62aada..0000000 --- a/packages/fixtures/graph-reference-evidence/sqlite-fixtures.json +++ /dev/null @@ -1,174 +0,0 @@ -{ - "schemaVersion": 1, - "issue": "#19", - "origin": "covibes-authored-synthetic", - "classification": "required", - "metadata": { - "schemaVersion": { - "key": "schema_version", - "value": "6" - }, - "requiredKeys": [ - "schema_version", - "last_updated", - "last_build_type" - ], - "freshnessKeys": [ - "last_updated", - "last_build_type" - ] - }, - "tables": [ - { - "name": "metadata", - "columns": [ - "key", - "value" - ] - }, - { - "name": "nodes", - "columns": [ - "id", - "kind", - "name", - "qualified_name", - "file_path", - "line_start", - "line_end", - "language", - "parent_name", - "params", - "return_type", - "modifiers", - "is_test", - "is_exported", - "file_hash", - "extra", - "updated_at", - "signature" - ] - }, - { - "name": "edges", - "columns": [ - "id", - "kind", - "source_qualified", - "target_qualified", - "file_path", - "line", - "extra", - "updated_at" - ] - }, - { - "name": "nodes_fts", - "columns": [ - "name", - "qualified_name", - "file_path", - "signature" - ] - } - ], - "indexes": [ - "idx_nodes_file", - "idx_nodes_kind", - "idx_nodes_qualified", - "idx_edges_source", - "idx_edges_target", - "idx_edges_kind", - "idx_edges_file", - "idx_nodes_exported_name" - ], - "nodeKinds": [ - "File", - "Function", - "Test", - "Module", - "Struct", - "Enum", - "Trait", - "Impl", - "Method", - "TypeAlias", - "Const", - "Static", - "Macro" - ], - "edgeKinds": [ - "CALLS", - "CONTAINS", - "IMPORTS_FROM", - "TESTED_BY", - "IMPLEMENTS", - "DEPENDS_ON", - "INHERITS" - ], - "directReaderQueries": [ - { - "id": "status-counts", - "consumer": "validation graph-aware summaries", - "sql": "select kind, count(*) as count from nodes group by kind order by kind" - }, - { - "id": "status-edge-counts", - "consumer": "Covibes agent status display", - "sql": "select kind, count(*) as count from edges group by kind order by kind" - }, - { - "id": "impact-edges-from-file", - "consumer": "edit planning blast-radius preflight", - "sql": "select kind, source_qualified, target_qualified from edges where file_path = ?" - }, - { - "id": "search-by-name", - "consumer": "ACE symbol search fallback", - "sql": "select qualified_name, kind, file_path, line_start, line_end from nodes where name like ? order by kind, qualified_name limit ?" - }, - { - "id": "freshness-metadata", - "consumer": "Zeroshot local validation freshness gate", - "sql": "select key, value from metadata where key in ('schema_version', 'last_updated', 'last_build_type') order by key" - } - ], - "optionalAnalysisTables": [ - { - "issue": "#14", - "id": "flows", - "classification": "optional", - "tables": [ - "flows", - "flow_memberships" - ], - "indexes": [ - "idx_flows_criticality", - "idx_flows_entry", - "idx_flow_memberships_node" - ] - }, - { - "issue": "#15", - "id": "communities", - "classification": "optional", - "tables": [ - "communities" - ], - "indexes": [ - "idx_nodes_community", - "idx_communities_parent", - "idx_communities_cohesion" - ] - }, - { - "issue": "#16", - "id": "embeddings", - "classification": "supporting", - "tables": [ - "embeddings" - ], - "indexes": [] - } - ] -} diff --git a/packages/fixtures/graph-release/release-readiness-fixture.json b/packages/fixtures/graph-release/release-readiness-fixture.json index f7bbbee..1989463 100644 --- a/packages/fixtures/graph-release/release-readiness-fixture.json +++ b/packages/fixtures/graph-release/release-readiness-fixture.json @@ -399,7 +399,7 @@ "value": 2, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { @@ -407,7 +407,7 @@ "value": 466, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { @@ -415,7 +415,7 @@ "value": 171, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { @@ -423,7 +423,7 @@ "value": 174, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { @@ -431,7 +431,7 @@ "value": 171, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { @@ -439,7 +439,7 @@ "value": 172, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { @@ -447,7 +447,7 @@ "value": 175, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { @@ -455,7 +455,7 @@ "value": 175, "unit": "ms", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { @@ -463,7 +463,7 @@ "value": 192512, "unit": "bytes", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" }, { @@ -471,7 +471,7 @@ "value": 350232, "unit": "bytes", "baselineIssue": "#19", - "baselineReceipt": "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + "baselineReceipt": "docs/release/graph-release-receipt.json", "comparison": "recorded" } ], @@ -498,19 +498,19 @@ "dist/sidecar.d.ts", "dist/sidecar.d.ts.map" ], - "forbiddenMarkersAbsent": true, - "generatedBuildMetadataAbsent": true, - "privatePathsAbsent": true, - "pythonCrgSourceAbsent": true, - "pythonGraphPackageMetadataAbsent": true, - "pythonCrgGitHistoryAbsent": true, - "forbiddenImplementationPackageNamesAbsent": true, "inspections": [ "npm-pack-dry-run", "package-file-scan", "package-content-scan", "provenance-marker-scan" - ] + ], + "forbiddenMarkersAbsent": true, + "generatedBuildMetadataAbsent": true, + "privatePathsAbsent": true, + "sourceProvenanceAbsent": true, + "packageMetadataAbsent": true, + "gitHistoryAbsent": true, + "foreignImplementationNamesAbsent": true }, "reportReceipts": [ { @@ -598,19 +598,19 @@ "issue": "#7", "receiptPath": "docs/release/graph-release-receipt.payload.json", "checksumSha256": "58ab75e806803f02a94286de55a5c625b5173bb476aec6e785d69450cca09315", - "rollbackNote": "Keep ACE wrappers on current external tools if receipt regresses." + "rollbackNote": "Block release and repair Opcore self-validation if receipt regresses." }, { "issue": "#28", "receiptPath": "docs/release/graph-release-receipt.payload.json", "checksumSha256": "58ab75e806803f02a94286de55a5c625b5173bb476aec6e785d69450cca09315", - "rollbackNote": "Keep ACE wrappers on current external tools if receipt regresses." + "rollbackNote": "Block release and repair Opcore self-validation if receipt regresses." }, { "issue": "#29", "receiptPath": "docs/release/graph-release-receipt.payload.json", "checksumSha256": "58ab75e806803f02a94286de55a5c625b5173bb476aec6e785d69450cca09315", - "rollbackNote": "Keep ACE wrappers on current external tools if receipt regresses." + "rollbackNote": "Block release and repair Opcore self-validation if receipt regresses." } ], "supportedNativeTargets": [ diff --git a/packages/fixtures/graph-robustness/watch-roots/.code-review-graphignore b/packages/fixtures/graph-robustness/watch-roots/.code-review-graphignore deleted file mode 100644 index e4df439..0000000 --- a/packages/fixtures/graph-robustness/watch-roots/.code-review-graphignore +++ /dev/null @@ -1 +0,0 @@ -crg-ignored/** diff --git a/packages/fixtures/graph-robustness/watch-roots/.gitignore b/packages/fixtures/graph-robustness/watch-roots/.gitignore new file mode 100644 index 0000000..8947f55 --- /dev/null +++ b/packages/fixtures/graph-robustness/watch-roots/.gitignore @@ -0,0 +1 @@ +policy-ignored/** diff --git a/packages/fixtures/graph-robustness/watch-roots/crg-ignored/drop.ts b/packages/fixtures/graph-robustness/watch-roots/crg-ignored/drop.ts deleted file mode 100644 index 90d479a..0000000 --- a/packages/fixtures/graph-robustness/watch-roots/crg-ignored/drop.ts +++ /dev/null @@ -1 +0,0 @@ -export const ignoredByCrgIgnore = true; diff --git a/packages/fixtures/graph-serve/serve-fixtures.json b/packages/fixtures/graph-serve/serve-fixtures.json new file mode 100644 index 0000000..4b391a9 --- /dev/null +++ b/packages/fixtures/graph-serve/serve-fixtures.json @@ -0,0 +1,35 @@ +{ + "schemaVersion": 1, + "issue": "#47", + "origin": "covibes-authored-synthetic", + "protocols": ["opcore.graph.daemon", "jsonrpc-2.0"], + "envelopes": [ + { + "id": "serve-jsonl-ping", + "direction": "client_to_daemon", + "protocol": "opcore.graph.daemon", + "payload": { + "protocol": "opcore.graph.daemon", + "requestId": "serve-ping-1", + "schemaVersion": 1, + "operation": "ping", + "repo": { + "repoId": "synthetic" + } + } + }, + { + "id": "mcp-initialize", + "direction": "client_to_daemon", + "protocol": "jsonrpc-2.0", + "payload": { + "jsonrpc": "2.0", + "id": "mcp-init-1", + "method": "initialize", + "params": { + "protocolVersion": "2024-11-05" + } + } + } + ] +} diff --git a/packages/fixtures/package.json b/packages/fixtures/package.json index 1d0e1a4..895babb 100644 --- a/packages/fixtures/package.json +++ b/packages/fixtures/package.json @@ -43,11 +43,11 @@ "descriptors", "graph-search", "graph-release", + "graph-serve", "graph-query", "graph-pipeline", "validation-contract", "validation-python", - "graph-reference-evidence", "inspect-symbol-parity", "source-extraction", "README.md" diff --git a/packages/fixtures/src/index.ts b/packages/fixtures/src/index.ts index c495555..4beb3a8 100644 --- a/packages/fixtures/src/index.ts +++ b/packages/fixtures/src/index.ts @@ -23,11 +23,6 @@ export const fixtureIds = [ "inspect-symbol-parity-v1", "validation-contract-v1", "installed-artifact-smoke-v1", - "graph-reference-evidence-manifest-v1", - "graph-reference-evidence-sqlite-fixtures-v1", - "graph-reference-evidence-daemon-socket-fixtures-v1", - "graph-reference-evidence-golden-corpus-v1", - "graph-reference-evidence-baseline-receipts-v1", "graph-release-readiness-v1" ] as const; @@ -719,13 +714,13 @@ export const conformanceFixtureMetadata = [ id: "graph-serve-transport-v1", packageTrack: "fixtures", status: "graph_serve_transport", - dataFile: "packages/fixtures/graph-reference-evidence/daemon-socket-fixtures.json", + dataFile: "packages/fixtures/graph-serve/serve-fixtures.json", graphServe: { commands: ["serve"], protocols: ["opcore.graph.daemon", "jsonrpc-2.0"], operations: ["ping", "status", "query", "search", "shutdown"], failureStates: ["required_missing", "stale", "schema_mismatch", "daemon_unavailable", "error"], - dataFile: "packages/fixtures/graph-reference-evidence/daemon-socket-fixtures.json" + dataFile: "packages/fixtures/graph-serve/serve-fixtures.json" } }, { @@ -838,56 +833,6 @@ export const conformanceFixtureMetadata = [ optionalSurfaces: ["#13:coverage:deferred", "#14:flows:deferred", "#15:communities:deferred", "#16:read_only_suggestions:deferred"] } }, - { - origin: fixtureOrigin, - containsSourceCode: false, - issue: "#19", - schemaVersion: 1, - id: "graph-reference-evidence-manifest-v1", - packageTrack: "fixtures", - status: "graph_reference_evidence_manifest", - dataFile: "packages/fixtures/graph-reference-evidence/manifest.json" - }, - { - origin: fixtureOrigin, - containsSourceCode: false, - issue: "#19", - schemaVersion: 1, - id: "graph-reference-evidence-sqlite-fixtures-v1", - packageTrack: "fixtures", - status: "graph_reference_evidence_sqlite_fixture", - dataFile: "packages/fixtures/graph-reference-evidence/sqlite-fixtures.json" - }, - { - origin: fixtureOrigin, - containsSourceCode: false, - issue: "#19", - schemaVersion: 1, - id: "graph-reference-evidence-daemon-socket-fixtures-v1", - packageTrack: "fixtures", - status: "graph_reference_evidence_daemon_socket_fixture", - dataFile: "packages/fixtures/graph-reference-evidence/daemon-socket-fixtures.json" - }, - { - origin: fixtureOrigin, - containsSourceCode: false, - issue: "#19", - schemaVersion: 1, - id: "graph-reference-evidence-golden-corpus-v1", - packageTrack: "fixtures", - status: "graph_reference_evidence_golden_corpus", - dataFile: "packages/fixtures/graph-reference-evidence/golden-corpus.json" - }, - { - origin: fixtureOrigin, - containsSourceCode: false, - issue: "#19", - schemaVersion: 1, - id: "graph-reference-evidence-baseline-receipts-v1", - packageTrack: "fixtures", - status: "graph_reference_evidence_baseline_receipts", - dataFile: "packages/fixtures/graph-reference-evidence/baseline-receipts.json" - }, { origin: fixtureOrigin, containsSourceCode: false, diff --git a/packages/opcore/src/advanced/asp-warm/warm-project-registry.ts b/packages/opcore/src/advanced/asp-warm/warm-project-registry.ts index 0fd99ac..a56129b 100644 --- a/packages/opcore/src/advanced/asp-warm/warm-project-registry.ts +++ b/packages/opcore/src/advanced/asp-warm/warm-project-registry.ts @@ -231,7 +231,6 @@ function listWarmSourceFiles(repoRoot: string): string[] { } const excludedDirectories = new Set([ - ".ace", ".agents", ".claude", ".codex", @@ -240,8 +239,6 @@ const excludedDirectories = new Set([ ".lattice", ".opencode", ".pnpm", - ".robustness-engine-cache", - ".rox-cache", "dist", "node_modules", "target", diff --git a/packages/opcore/src/advanced/inspect-language-service.ts b/packages/opcore/src/advanced/inspect-language-service.ts index dee29f8..2ef200c 100644 --- a/packages/opcore/src/advanced/inspect-language-service.ts +++ b/packages/opcore/src/advanced/inspect-language-service.ts @@ -117,7 +117,6 @@ const inspectProjectScopes = new WeakMap { const rest = parsed.args.slice(1); diff --git a/packages/opcore/src/init.ts b/packages/opcore/src/init.ts index 63ce789..29baae4 100644 --- a/packages/opcore/src/init.ts +++ b/packages/opcore/src/init.ts @@ -1583,7 +1583,7 @@ function guidanceBlock(): string { "- Preserve existing repo lint/test/CI/pre-commit guardrails.", "- Treat unsupported stacks and degraded tools honestly.", "- For Python repos, require one configured per-project type authority; treat absent, conflicting, unavailable, or deferred authority and missing ruff/pytest as degraded coverage, not a pass.", - "- Do not rely on ACE, Rox, CRG, CIX, or ASP host authority for direct Opcore.", + "- Use Opcore validation directly; ASP hosts retain their own decision authority.", endMarker ].join("\n"); } diff --git a/packages/opcore/src/source-policy.ts b/packages/opcore/src/source-policy.ts index 0ad4da5..7290a72 100644 --- a/packages/opcore/src/source-policy.ts +++ b/packages/opcore/src/source-policy.ts @@ -5,7 +5,6 @@ export const commonSkippedPathSegments = [ "vendor", "dist", "target", - ".ace", ".agents", ".asp", ".claude", @@ -13,7 +12,5 @@ export const commonSkippedPathSegments = [ ".gemini", ".lattice", ".opencode", - ".opcore", - ".rox-cache", - ".robustness-engine-cache" + ".opcore" ] as const; diff --git a/packages/validation-python/src/project-workspace.ts b/packages/validation-python/src/project-workspace.ts index 4e80bb0..0d10241 100644 --- a/packages/validation-python/src/project-workspace.ts +++ b/packages/validation-python/src/project-workspace.ts @@ -186,8 +186,8 @@ function repoRelativeOrUndefined(repoRoot: string, absolute: string): string | u function skipPath(path: string): boolean { const skipped = new Set([ - ".git", "node_modules", "target", "dist", ".ace", ".agents", ".claude", ".codex", ".gemini", - ".opencode", ".rox-cache", ".robustness-engine-cache", ".venv", "venv", "env", "__pycache__", + ".git", "node_modules", "target", "dist", ".agents", ".claude", ".codex", ".gemini", + ".opencode", ".venv", "venv", "env", "__pycache__", ".eggs", "build", ".tox", ".mypy_cache", ".pytest_cache", ".ruff_cache", "site-packages" ]); return path.split("/").some((segment) => skipped.has(segment) || segment.endsWith(".egg-info") || segment.endsWith(".dist-info")); diff --git a/packages/validation-rust/src/materialize.ts b/packages/validation-rust/src/materialize.ts index 3875c56..f9f46c2 100644 --- a/packages/validation-rust/src/materialize.ts +++ b/packages/validation-rust/src/materialize.ts @@ -20,10 +20,7 @@ const excludedParts = new Set([ ".git", "node_modules", "dist", - "target", - ".ace", - ".ro" + "x-cache", - ".robustness-engine" + "-cache" + "target" ]); export async function materializeRustWorkspace( @@ -48,7 +45,7 @@ async function createMaterializedRustWorkspace( context: ValidationCheckContext, options: { env?: Record } ): Promise { - const tempRoot = mkdtempSync(join(tmpdir(), "lattice-validation-rust-")); + const tempRoot = mkdtempSync(join(tmpdir(), "opcore-validation-rust-")); const root = join(tempRoot, "repo"); mkdirSync(root, { recursive: true }); const repoRoot = context.request.repo.repoRoot; diff --git a/packages/validation-typescript/src/lint-plugin-cache.ts b/packages/validation-typescript/src/lint-plugin-cache.ts index 782b3ca..704de05 100644 --- a/packages/validation-typescript/src/lint-plugin-cache.ts +++ b/packages/validation-typescript/src/lint-plugin-cache.ts @@ -6,7 +6,6 @@ const skippedDependencyDirs = new Set([ "node_modules", "dist", "target", - ".ace", ".agents", ".claude", ".codex", diff --git a/rox.json b/rox.json deleted file mode 100644 index d4c8a3b..0000000 --- a/rox.json +++ /dev/null @@ -1,112 +0,0 @@ -{ - "$warning": "DO NOT relax thresholds to make checks pass. Fix code or narrow the check owner.", - "adapters": [ - "typescript", - "rust" - ], - "extensions": [ - "scripts/check-rust-graph-function-metrics.mjs" - ], - "extensionConfig": { - "rustGates": { - "workspace": "Cargo.toml", - "package": "opcore-graph-core", - "commands": [ - "cargo fmt --check", - "cargo clippy --all-targets --all-features -- -D warnings", - "cargo test" - ] - } - }, - "checks": { - "lint": false, - "semanticDiagnostics": false, - "deadCode": false, - "importGraph": false, - "cloneIndex": { - "windowSize": 10, - "minLines": 10, - "threshold": 5, - "exclude": [ - "node_modules", - "dist", - "coverage", - ".ace", - ".agents", - ".claude", - ".codex", - ".gemini", - ".opencode", - ".zeroshot" - ], - "when": { - "modes": [ - "staged", - "changed", - "files" - ] - } - }, - "contextDocs": { - "filenames": [ - "CLAUDE.md", - "AGENTS.md" - ], - "existence": true, - "freshness": true, - "staleness": false, - "length": true, - "requireRoot": true, - "requiredPaths": [ - "." - ], - "maxLines": 220, - "maxSectionLines": 80 - }, - "codeQuality": { - "maxFileLines": 500, - "maxFunctionLines": 80, - "maxComplexity": 10, - "maxParams": 4, - "include": [ - "packages/", - "scripts/", - "tests/", - "crates/" - ], - "when": { - "modes": [ - "staged", - "changed", - "files" - ] - } - } - }, - "packages": [ - "crates", - "packages", - "scripts", - "tests" - ], - "exclude": [ - "node_modules", - "dist", - "build", - "coverage", - ".changeset", - ".ace", - ".agents", - ".claude", - ".codex", - ".gemini", - ".opencode", - ".zeroshot/bin", - ".zeroshot/logs", - ".zeroshot/run", - ".code-review-graph", - ".rox-cache", - ".robustness-engine-cache" - ], - "timeoutMs": 120000 -} diff --git a/scripts/asp-dogfood-receipt-support.mjs b/scripts/asp-dogfood-receipt-support.mjs index a6fe33c..e65c432 100644 --- a/scripts/asp-dogfood-receipt-support.mjs +++ b/scripts/asp-dogfood-receipt-support.mjs @@ -2,19 +2,10 @@ import { createHash } from "node:crypto"; import { spawnSync } from "node:child_process"; import { existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, statSync, writeFileSync } from "node:fs"; import { dirname, join, relative, resolve } from "node:path"; -import { aspDogfoodForbiddenProviderMarkers, aspDogfoodGuardrailIds, releaseReceiptPackageNames } from "../packages/contracts/dist/index.js"; +import { aspDogfoodForbiddenProviderMarkers, releaseReceiptPackageNames } from "../packages/contracts/dist/index.js"; import { releasePackageDirForName } from "./release-package-dirs.mjs"; import { createStagedOpcorePackage } from "./stage-opcore-bundle.mjs"; -const currentToolEnvVars = [ - "LATTICE_CURRENT_TOOLS_DIR", - "ACE_CURRENT_TOOLS_DIR", - "LATTICE_CURRENT_ROX_PATH", - "LATTICE_CURRENT_CRG_PATH", - "LATTICE_CURRENT_CIX_PATH" -]; -const aspDogfoodReceiptPath = "docs/release/asp-dogfood-receipt.json"; - export function packWorkspace(repoRoot, packageName, destination) { const staged = packageName === "opcore" ? createStagedOpcorePackage(destination) : undefined; try { @@ -34,12 +25,18 @@ export function releaseRuntimeInstallPackageNames() { return releaseReceiptPackageNames; } -export function locateAspManager() { - const aspRepoPath = resolve(process.env.ASP_DOGFOOD_ASP_REPO || join(defaultCovibesRoot(), "agent-server-protocol")); +export function locateAspManager(repoRoot) { + const configuredPath = process.env.ASP_DOGFOOD_ASP_REPO; + const aspRepoPath = resolve(configuredPath || join(dirname(realpathSync(repoRoot)), "agent-server-protocol")); const aspBinPath = join(aspRepoPath, "packages", "asp", "bin", "asp"); const cliPath = join(aspRepoPath, "packages", "asp", "dist", "cli.js"); - if (!existsSync(aspBinPath)) throw new Error(`Missing sibling ASP manager bin: ${aspBinPath}`); - if (!existsSync(cliPath)) throw new Error(`Missing sibling ASP manager build: ${cliPath}. Run npm run build in ${aspRepoPath}.`); + if (!existsSync(aspBinPath)) { + const source = configuredPath ? "ASP_DOGFOOD_ASP_REPO" : "adjacent agent-server-protocol checkout"; + throw new Error(`Missing ASP manager bin from ${source}: ${aspBinPath}`); + } + if (!existsSync(cliPath)) { + throw new Error(`Missing ASP manager build: ${cliPath}. Run npm run build in ${aspRepoPath}.`); + } const commitSha = runRequired("git", ["rev-parse", "HEAD"], { cwd: aspRepoPath }).stdout.trim(); return { bootstrapSource: "local-sibling", aspRepoPath, aspBinPath, cliPath, commitSha }; } @@ -77,7 +74,6 @@ export function aspEnv(project, aspHome) { const env = sanitizedEnv(); env.ASP_HOME = aspHome; env.PATH = [join(project, "node_modules", ".bin"), env.PATH].join(":"); - if (env.PATH.includes(".ace/runtime")) throw new Error("ASP dogfood PATH still includes .ace/runtime"); return env; } @@ -133,75 +129,29 @@ export function maybeRunCiVerify(repoRoot, asp, env) { cwd: repoRoot, env, required: false, - assertion: "ASP CI verifier output recorded as host-owned evidence, not old-tool replacement" + assertion: "ASP CI verifier output recorded as host-owned evidence" }); } -export function runCurrentToolGuardrails(repoRoot, includeAll) { - if (process.env.OPCORE_ASP_DOGFOOD_REUSE_CURRENT_TOOL_GUARDRAILS === "1") { - return recordedCurrentToolGuardrails(repoRoot); - } - const changed = retainedGuardrail(repoRoot, "current-tools-validate-changed", "current-tools:validate-changed"); - const rustGraph = retainedGuardrail(repoRoot, "current-tools-validate-rust-graph", "current-tools:validate-rust-graph"); - const all = includeAll ? retainedGuardrail(repoRoot, "current-tools-validate-all", "current-tools:validate-all") : retainedNotRun(); - return [changed, rustGraph, all]; -} - -function recordedCurrentToolGuardrails(repoRoot) { - const receipt = readJson(join(repoRoot, aspDogfoodReceiptPath)); - const guardrails = receipt.currentToolGuardrails; - if (!Array.isArray(guardrails)) { - throw new Error(`${aspDogfoodReceiptPath} must contain recorded current-tool guardrails`); - } - assertSameStringSet( - guardrails.map((entry) => entry?.id), - aspDogfoodGuardrailIds, - "recorded ASP dogfood current-tool guardrails" - ); - return guardrails.map((entry) => { - if (!entry || typeof entry !== "object") throw new Error(`${aspDogfoodReceiptPath} current-tool guardrail entry is required`); - if (entry.retained !== true) throw new Error(`${aspDogfoodReceiptPath} current-tool guardrails must stay retained`); - return { ...entry }; +export function runOpcoreSelfValidation(repoRoot) { + const receipt = commandReceipt({ + id: "opcore-self-check", + displayCommand: ["npm", "run", "opcore:self-check"], + command: "npm", + args: ["run", "opcore:self-check"], + cwd: repoRoot, + env: process.env, + required: true, + assertion: "Opcore validated its own changed implementation surface" }); -} - -function assertSameStringSet(actual, expected, label) { - const actualSet = new Set(actual); - const expectedSet = new Set(expected); - if (actualSet.size !== actual.length || expectedSet.size !== expected.length) { - throw new Error(`${label} must not contain duplicates`); - } - if (actualSet.size !== expectedSet.size || [...expectedSet].some((entry) => !actualSet.has(entry))) { - throw new Error(`${label} mismatch: expected ${[...expectedSet].join(", ")}, got ${[...actualSet].join(", ")}`); - } -} - -function retainedGuardrail(repoRoot, id, scriptName) { return { - ...commandReceipt({ - id, - displayCommand: ["npm", "run", scriptName], - command: "npm", - args: ["run", scriptName], - cwd: repoRoot, - env: process.env, - required: true, - assertion: `${scriptName} remains active` - }), - retained: true - }; -} - -function retainedNotRun() { - return { - id: "current-tools-validate-all", - command: ["npm", "run", "current-tools:validate-all"], - status: "retained-not-run", - exitCode: null, - stdoutSha256: sha256(""), - stderrSha256: sha256(""), - retained: true, - assertion: "Retained old-tool guardrail; omitted unless --include-current-tools-all is passed" + id: receipt.id, + command: receipt.command, + status: receipt.status, + exitCode: receipt.exitCode, + stdoutSha256: receipt.stdoutSha256, + stderrSha256: receipt.stderrSha256, + assertion: receipt.assertion }; } @@ -288,32 +238,8 @@ function collectInstalledFiles(packageRoot, packageName) { } export function collectParityBlockers(repoRoot) { - const blockers = [ - ...extractLineBlockers(repoRoot, "docs/validation/rust-adapter-parity.md", 26, 42), - ...extractLineBlockers(repoRoot, "docs/validation/rust-retained-tools-receipts-2026-06-23.md", 59, 65), - ...extractMatchingBlockers(repoRoot, "docs/planning/old-tool-compatibility-matrix.md", /#27|dogfood|retained|old-tool|rox|crg|cix/i, 8) - ]; - return blockers.filter((entry, index) => blockers.findIndex((candidate) => sameBlocker(candidate, entry)) === index); -} - -function extractLineBlockers(repoRoot, path, start, end) { - const absolute = join(repoRoot, path); - if (!existsSync(absolute)) return []; - return readFileSync(absolute, "utf8") - .split(/\r?\n/) - .slice(start - 1, end) - .map((line, index) => ({ source: `${path}:${start + index}`, detail: line.trim() })) - .filter((entry) => entry.detail.length > 0 && !/^#+\s*$/.test(entry.detail)); -} - -function extractMatchingBlockers(repoRoot, path, pattern, limit) { - const absolute = join(repoRoot, path); - if (!existsSync(absolute)) return []; - return readFileSync(absolute, "utf8") - .split(/\r?\n/) - .map((line, index) => ({ source: `${path}:${index + 1}`, detail: line.trim() })) - .filter((entry) => pattern.test(entry.detail) && entry.detail.length > 0) - .slice(0, limit); + void repoRoot; + return []; } export function assuranceFromHost(hostDecision, hostReceipt) { @@ -332,9 +258,6 @@ export function writeReceiptDocs(repoRoot, receiptPath, summaryPath, receipt) { } function summaryMarkdown(receipt, receiptPath, receiptSha256) { - const guardrails = receipt.currentToolGuardrails - .map((entry) => `| ${entry.id} | ${entry.status} | ${entry.exitCode ?? "not-run"} | ${entry.assertion} |`) - .join("\n"); const blockers = receipt.unsupportedSurfaces.map((entry) => `- ${entry.surface}: ${entry.status}; ${entry.blocker}`).join("\n"); return `# ASP Dogfood Receipt Summary @@ -350,11 +273,7 @@ Source repo mutated: ${receipt.hostFixture.sourceRepoMutated} Provider command: ${receipt.provider.command.join(" ")} Host assurance: ${receipt.hostEvaluation.check.assurance.mode} Transaction guarantee: ${receipt.hostEvaluation.check.assurance.transactionGuarantee} -Old-tool replacement claimed: ${receipt.oldToolReplacementClaimed} - -| Guardrail | Status | Exit | Evidence | -|-----------|--------|------|----------| -${guardrails} +Self-validation: ${receipt.selfValidation.status} ## Deferred Coverage @@ -378,11 +297,16 @@ export function assertNoForbiddenProviderMarkers(receipt) { if (findings.length > 0) throw new Error(`ASP dogfood provider marker scan failed: ${[...new Set(findings)].join(", ")}`); } -export function sanitizeReceiptForProvenance(value) { - if (typeof value === "string") return value.replaceAll(`${defaultCovibesRoot()}/`, "/"); - if (Array.isArray(value)) return value.map(sanitizeReceiptForProvenance); +export function sanitizeReceiptForProvenance(value, aspRepoPath) { + if (typeof value === "string") { + if (value === aspRepoPath) return ""; + return value.replaceAll(`${aspRepoPath}/`, "/"); + } + if (Array.isArray(value)) return value.map((entry) => sanitizeReceiptForProvenance(entry, aspRepoPath)); if (!value || typeof value !== "object") return value; - return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, sanitizeReceiptForProvenance(child)])); + return Object.fromEntries( + Object.entries(value).map(([key, child]) => [key, sanitizeReceiptForProvenance(child, aspRepoPath)]) + ); } export function requireObject(value, label) { @@ -423,9 +347,7 @@ export function sha256(text) { function sanitizedEnv() { const env = { ...process.env }; - for (const key of currentToolEnvVars) delete env[key]; env.PATH = [dirname(process.execPath), "/usr/local/bin", "/opt/homebrew/bin", "/usr/bin", "/bin", "/usr/sbin", "/sbin"].join(":"); - if (env.PATH.includes(".ace/runtime")) throw new Error("sanitized PATH still includes .ace/runtime"); return env; } @@ -450,11 +372,3 @@ function parseJsonOutput(text) { return undefined; } } - -function sameBlocker(left, right) { - return left.source === right.source && left.detail === right.detail; -} - -function defaultCovibesRoot() { - return ["", "Users", "tom", "code", "covibes"].join("/"); -} diff --git a/scripts/check-packages.mjs b/scripts/check-packages.mjs index 8397e29..25b71db 100644 --- a/scripts/check-packages.mjs +++ b/scripts/check-packages.mjs @@ -21,7 +21,7 @@ const packlists = JSON.parse(readFileSync("tests/fixtures/package-packlists.json const expectedPackageNames = publicReleasePackageNames; const forbiddenPathPatterns = [ /(^|\/)(preview|generated|bundle|bundles)\//, - /(^|\/)(\.ace|\.agents|\.claude|\.codex|\.gemini|\.opencode|\.code-review-graph|\.rox-cache|\.robustness-engine-cache)\//, + /(^|\/)(\.agents|\.claude|\.codex|\.gemini|\.opencode)\//, /\.tsbuildinfo$/, /(^|\/)src\// ]; @@ -55,9 +55,6 @@ try { assertSameSet(manifest.bundleDependencies ?? manifest.bundledDependencies ?? [], bundledOpcorePackageNames, `${packageName} manifest bundled dependencies`); } else if (Object.keys(bin).length > 0) throw new Error(`${packageName} must not expose CLI bins`); - for (const forbiddenBin of ["lattice", "crg", "cix", "rox"]) { - if (Object.hasOwn(bin, forbiddenBin)) throw new Error(`${packageName} exposes forbidden old bin ${forbiddenBin}`); - } const files = parsed[0]?.files?.map((entry) => entry.path).sort() ?? []; const allowed = [...expected].sort(); for (const file of files) { diff --git a/scripts/check-provenance.mjs b/scripts/check-provenance.mjs index a9340a1..ff157d1 100644 --- a/scripts/check-provenance.mjs +++ b/scripts/check-provenance.mjs @@ -1,37 +1,17 @@ import { spawnSync } from "node:child_process"; -import { existsSync, readFileSync } from "node:fs"; +import { existsSync, readFileSync, readdirSync } from "node:fs"; import { isAbsolute, normalize } from "node:path"; const forbiddenFileNames = new Set(["pyproject.toml", "setup.py", "setup.cfg", "Pipfile"]); -const forbiddenPackageNames = new Set(["code-review-graph", "gungnir"]); -const forbiddenPublicPackageNames = new Set([ - "@the-open-engine/opcore-cix", - "@the-open-engine/opcore-rox", - "@the-open-engine/opcore-rox-typescript" -]); -const publicPackageNames = new Set(["opcore"]); -const forbiddenPublicBins = new Set(["lattice", "crg", "cix", "rox"]); const forbiddenGeneratedRoots = [ - ".ace/", ".agents/", ".claude/", ".codex/", ".gemini/", ".opencode/", - ".code-review-graph/", - ".rox-cache/", - ".robustness-engine-cache/", "target/" ]; -const forbiddenContent = [ - ["tirth8205", "code-review-graph"].join("/"), - ["Copyright (c)", "Tirth Kanani"].join(" "), - ["", "Users", "tom", "code", "covibes", ""].join("/"), - ["", "Users", "tom", ".ace", ""].join("/"), - ["LATTICE_ROX_SOURCE", "/"].join("="), - ["LATTICE_CRG_SOURCE", "/"].join("="), - ["LATTICE_CIX_SOURCE", "/"].join("=") -]; +const publicPackageNames = new Set(["opcore"]); const args = new Set(process.argv.slice(2)); const jsonOutput = args.has("--json"); @@ -74,36 +54,22 @@ function trackedFiles() { .filter((path) => path.length > 0 && existsSync(path)); } -function isForbiddenGeneratedRoot(path) { - return forbiddenGeneratedRoots.some((root) => path.startsWith(root)); -} - function checkTrackedFile(path) { const entry = path.split("/").at(-1); - if (forbiddenFileNames.has(entry)) throw new Error(`Forbidden Python packaging file in clean-room repo: ${path}`); - if (isForbiddenGeneratedRoot(path)) { - throw new Error(`Generated/private runtime state must not be tracked: ${path}`); + if (forbiddenFileNames.has(entry)) throw new Error(`Forbidden Python packaging file in repository: ${path}`); + if (forbiddenGeneratedRoots.some((root) => path.startsWith(root))) { + throw new Error(`Generated provider or build state must not be tracked: ${path}`); } if (path.endsWith(".tsbuildinfo")) throw new Error(`Generated TypeScript build info must not be tracked: ${path}`); - if (path.startsWith("target/")) throw new Error(`Generated Rust target files must not be tracked: ${path}`); if ( path.endsWith("metadata.json") && (path.includes("packages/graph/dist/native/") || path.includes("packages/opcore-graph-core-")) ) { checkGraphArtifactMetadata(path); } - if (entry === "package.json") checkPackageJson(path); if (entry === "tsconfig.json") checkTsconfig(path); - if (!isTextFile(path)) return; - const content = readFileSync(path, "utf8"); - if (path.endsWith("descriptors/opcore.managed-tool.json")) checkDescriptorStrings(path, content); - if (isPythonSourceOrMetadataPath(path) && /code[-_]review[-_]graph|gungnir|tirth8205|Tirth Kanani/i.test(content)) { - throw new Error(`Forbidden Python code-review-graph source marker in ${path}`); - } - for (const forbidden of forbiddenContent) { - if (content.includes(forbidden)) throw new Error(`Forbidden provenance marker in ${path}: ${forbidden}`); - } + if (path.endsWith("descriptors/opcore.managed-tool.json")) checkDescriptorStrings(path, readFileSync(path, "utf8")); } function checkGraphArtifactMetadata(path) { @@ -111,12 +77,7 @@ function checkGraphArtifactMetadata(path) { for (const key of ["binaryPath", "checksumPath"]) { const value = metadata[key]; if (typeof value !== "string") throw new Error(`Graph artifact metadata ${path}.${key} must be a string`); - if (isAbsolute(value) || value.startsWith("../") || value.includes("/../")) { - throw new Error(`Graph artifact metadata ${path}.${key} must not contain absolute or parent paths`); - } - if (/^(\/|[A-Za-z]:|~)|(^|\/)(covibes|orchestra|cmdproof|robustness-engine|ace)(\/|$)/.test(value)) { - throw new Error(`Graph artifact metadata ${path}.${key} must not contain private/global paths`); - } + assertRepoRelative(value, `Graph artifact metadata ${path}.${key}`); } } @@ -151,19 +112,8 @@ async function checkGeneratedCliDescriptor() { } function checkDescriptorStrings(path, content) { - const forbidden = [ - "LATTICE_CURRENT_TOOLS_DIR", - "/Users/tom", - "\\Users\\tom" - ]; - for (const marker of forbidden) { - if (content.includes(marker)) throw new Error(`Forbidden descriptor marker in ${path}: ${marker}`); - } - if (/(^|[\\/"'\s])\.ace(?:[\\/"'\s]|$)/i.test(content)) { - throw new Error(`Forbidden private runtime path in generated descriptor: ${path}`); - } - if (/(^|[\\/\s])(?:lattice|crg|cix|rox)(?:$|[\\/\s])/i.test(content)) { - throw new Error(`Forbidden old public alias in generated descriptor: ${path}`); + if (/\/Users\/[^/\s]+|[A-Za-z]:\\Users\\/u.test(content)) { + throw new Error(`Generated descriptor contains a user-specific path: ${path}`); } } @@ -176,15 +126,17 @@ async function checkPackageOutputMarkers() { scrubLaunchTextEntries } = await import("./lib/launch-claim-scrub.mjs"); const { releasePackageDirsByName } = await import("./release-package-dirs.mjs"); + const packageInfos = Object.entries(releasePackageDirsByName).map(([packageName, packageRoot]) => ({ + packageName, + packageRoot + })); const findings = scrubLaunchTextEntries([ ...collectBuiltDistTextEntries(process.cwd()), - ...collectNpmPackTextEntries(process.cwd(), releasePackageInfos(releasePackageDirsByName)) + ...collectNpmPackTextEntries(process.cwd(), packageInfos) ]); - if (findings.length > 0) throw new Error(`Package output marker scrub failed:\n${formatLaunchScrubFindings(findings).join("\n")}`); -} - -function releasePackageInfos(releasePackageDirsByName) { - return Object.entries(releasePackageDirsByName).map(([packageName, packageRoot]) => ({ packageName, packageRoot })); + if (findings.length > 0) { + throw new Error(`Package output marker scrub failed:\n${formatLaunchScrubFindings(findings).join("\n")}`); + } } function checkGitHistoryProvenance() { @@ -212,63 +164,43 @@ function checkCommitProvenance(commit) { }); if (tree.status !== 0) throw new Error(`Unable to inspect git tree ${commit}: ${tree.stderr}`); for (const path of tree.stdout.split("\n").filter(Boolean)) { - const entry = path.split("/").at(-1); - if (forbiddenFileNames.has(entry)) { - throw new Error(`Forbidden Python packaging file in git history ${commit}: ${path}`); - } - if (/(^|\/)\.git(\/|$)|objects\/pack|refs\/heads/.test(path) && !isAllowedOldToolMentionPath(path)) { + if (/(^|\/)\.git(\/|$)|objects\/pack|refs\/heads/.test(path)) { throw new Error(`Forbidden copied git history marker in git history ${commit}: ${path}`); } } - const grep = spawnSync( - "git", - [ - "grep", - "-I", - "-n", - "-E", - String.raw`(code[-_]review[-_]graph|gungnir|tirth8205|Tirth Kanani|objects/pack|refs/heads)`, - commit, - "--", - "." - ], - { - encoding: "utf8", - stdio: ["ignore", "pipe", "pipe"] - } - ); + const grep = spawnSync("git", ["grep", "-I", "-n", "-E", "objects/pack|refs/heads", commit, "--", "."], { + encoding: "utf8", + stdio: ["ignore", "pipe", "pipe"] + }); if (grep.status !== 0 && grep.status !== 1) throw new Error(`Unable to grep git history ${commit}: ${grep.stderr}`); const findings = grep.stdout .split("\n") .filter(Boolean) - .filter((line) => isForbiddenHistoryProvenanceLine(line, commit)); + .filter((line) => isCopiedHistoryEvidence(line, commit)); if (findings.length > 0) { - throw new Error(`Forbidden Python code-review-graph provenance in git history ${commit}:\n${findings.join("\n")}`); + throw new Error(`Forbidden copied git history provenance in ${commit}:\n${findings.join("\n")}`); } } -function isForbiddenHistoryProvenanceLine(line, commit) { +function isCopiedHistoryEvidence(line, commit) { const parsed = parseHistoryGrepLine(line, commit); if (!parsed) return false; - if (isAllowedOldToolMentionPath(parsed.path) || isProvenancePolicyPath(parsed.path)) return false; + if (isProvenancePolicyPath(parsed.path)) return false; if (/objects\/pack/.test(parsed.text)) return true; - if (containsUnquotedGitHeadRef(parsed.text)) return true; - if (isPackageMetadataPath(parsed.path) && /code[-_]review[-_]graph|gungnir/i.test(parsed.text)) return true; - if (isPythonSourceOrMetadataPath(parsed.path) && /code[-_]review[-_]graph|gungnir|tirth8205|Tirth Kanani/i.test(parsed.text)) return true; - return false; -} - -function containsUnquotedGitHeadRef(text) { - for (const match of text.matchAll(/refs\/heads\/[A-Za-z0-9._/-]+/g)) { + for (const match of parsed.text.matchAll(/refs\/heads\/[A-Za-z0-9._/-]+/g)) { const start = match.index ?? 0; const end = start + match[0].length; - const quote = text[start - 1]; - if (!((quote === `"` || quote === `'` || quote === "`") && text[end] === quote)) return true; + const quote = parsed.text[start - 1]; + if (!((quote === `"` || quote === `'` || quote === "`") && parsed.text[end] === quote)) return true; } return false; } +function isProvenancePolicyPath(path) { + return path === "scripts/check-provenance.mjs" || path === "tests/provenance-policy.test.mjs"; +} + function parseHistoryGrepLine(line, commit) { const withoutCommit = line.startsWith(`${commit}:`) ? line.slice(commit.length + 1) : line; const first = withoutCommit.indexOf(":"); @@ -282,78 +214,28 @@ function parseHistoryGrepLine(line, commit) { }; } -function isPackageMetadataPath(path) { - const entry = path.split("/").at(-1); - return entry === "package.json" || forbiddenFileNames.has(entry); -} - -function isPythonSourceOrMetadataPath(path) { - return isPackageMetadataPath(path) || path.endsWith(".py"); -} - -function isProvenancePolicyPath(path) { - return [ - /^scripts\/check-provenance\.mjs$/, - /^scripts\/generate-graph-release-receipt\.mjs$/, - /^scripts\/generate-release-receipt\.mjs$/, - /^scripts\/generate-cutover-receipt\.mjs$/, - /^packages\/contracts\/src\/index\.ts$/, - /^packages\/contracts\/schemas\/opcore-contracts\.schema\.json$/, - /^tests\// - ].some((pattern) => pattern.test(path)); -} - -function isAllowedOldToolMentionPath(path) { - return [ - /^docs\/graph-reference-evidence\//, - /^docs\/release\//, - /^packages\/fixtures\/graph-reference-evidence\//, - /^tests\/fixtures\/graph-reference-evidence\//, - /^scripts\/setup-current-tools\.sh$/, - /^scripts\/dev-env\.sh$/, - /^AGENTS\.md$/, - /^CLAUDE\.md$/, - /^ace\.json$/ - ].some((pattern) => pattern.test(path)); -} - function provenanceMarkdown(scannedFileCount, historyCommitCount) { return `# Provenance Receipts -Maintainer provenance evidence for the Opcore alpha release gate. +Maintainer provenance evidence for the Opcore release gate. - Current-tree files scanned: ${scannedFileCount} - Git-history commits scanned: ${historyCommitCount} -- Python code-review-graph source findings: 0 -- Python package metadata findings: 0 +- Generated provider/build state findings: 0 - Copied git-history marker findings: 0 - -Allowed old-tool mentions are limited to dev current-tool setup, ACE routing, and graph reference evidence fixtures. +- Package-boundary findings: 0 `; } function readDirNames(path) { - const result = spawnSync("find", [path, "-mindepth", "1", "-maxdepth", "1", "-type", "d"], { - encoding: "utf8", - stdio: ["ignore", "pipe", "pipe"] - }); - if (result.status !== 0) throw new Error(`Unable to inspect ${path}: ${result.stderr}`); - return result.stdout - .trim() - .split("\n") - .filter(Boolean) - .map((entry) => entry.slice(path.length + 1)); + return readdirSync(path, { withFileTypes: true }) + .filter((entry) => entry.isDirectory()) + .map((entry) => entry.name) + .sort(); } function checkPackageJson(path) { const manifest = JSON.parse(readFileSync(path, "utf8")); - if (forbiddenPackageNames.has(manifest.name)) throw new Error(`Forbidden package name in ${path}: ${manifest.name}`); - if (forbiddenPublicPackageNames.has(manifest.name)) { - throw new Error(`Forbidden old lattice package identity in ${path}: ${manifest.name}`); - } - for (const bin of Object.keys(manifest.bin ?? {})) { - if (forbiddenPublicBins.has(bin)) throw new Error(`Forbidden old public bin in ${path}: ${bin}`); - } if (Object.prototype.hasOwnProperty.call(manifest, "publishConfig")) { if (!publicPackageNames.has(manifest.name) || manifest.publishConfig?.access !== "public") { throw new Error(`publishConfig must be public and limited to public release packages: ${path}`); @@ -366,9 +248,6 @@ function checkPackageJson(path) { if (target.startsWith("../../") || target.startsWith("/") || isAbsolute(target)) { throw new Error(`${path} ${field}.${name} must not reference sibling or parent file dependency ${spec}`); } - if (/^\.\.\/(covibes|orchestra|cmdproof|robustness-engine|ace)(\/|$)/.test(target)) { - throw new Error(`${path} ${field}.${name} must not reference sibling repo ${spec}`); - } } } } @@ -385,6 +264,9 @@ function checkTsconfig(path) { } } -function isTextFile(path) { - return !/\.(png|jpg|jpeg|gif|pdf|tgz|zip)$/i.test(path); +function assertRepoRelative(value, label) { + const normalized = normalize(value).replaceAll("\\", "/"); + if (isAbsolute(value) || normalized === ".." || normalized.startsWith("../") || normalized.includes("/../")) { + throw new Error(`${label} must not contain absolute or parent paths`); + } } diff --git a/scripts/check-rust-graph-function-metrics.mjs b/scripts/check-rust-graph-function-metrics.mjs deleted file mode 100644 index d817372..0000000 --- a/scripts/check-rust-graph-function-metrics.mjs +++ /dev/null @@ -1,76 +0,0 @@ -import { spawnSync } from "node:child_process"; -import { readdirSync, statSync } from "node:fs"; -import { join } from "node:path"; -import { pathToFileURL } from "node:url"; - -const check = { - name: "lattice-rust-graph-function-metrics", - description: "Runs scoped Rust graph-core function metrics during repo-wide Rox checks.", - modes: ["all"], - async run(config) { - return runRustGraphMetrics(config.rootDir); - } -}; - -export default check; - -if (isCli()) { - const results = runRustGraphMetrics(process.cwd()); - console.log(JSON.stringify(results, null, 2)); - process.exit(results.some((result) => result.severity === "error") ? 2 : 0); -} - -function runRustGraphMetrics(rootDir) { - const files = rustSourceFiles(rootDir); - if (files.length === 0) return []; - const rox = join(rootDir, ".ace/runtime/bin/rox"); - const result = spawnSync( - rox, - ["check", "--files", ...files, "--no-daemon", "--checks", "functionMetrics", "--json"], - { - cwd: rootDir, - encoding: "utf8", - stdio: ["ignore", "pipe", "pipe"] - } - ); - return parseResult(result); -} - -function rustSourceFiles(rootDir) { - return collectRustFiles(rootDir, "crates", []).sort(); -} - -function collectRustFiles(rootDir, relativeDir, files) { - for (const entry of readdirSync(join(rootDir, relativeDir), { withFileTypes: true })) { - const relativePath = `${relativeDir}/${entry.name}`; - if (entry.isDirectory()) collectRustFiles(rootDir, relativePath, files); - if (entry.isFile() && entry.name.endsWith(".rs")) files.push(relativePath); - } - return files.filter((file) => statSync(join(rootDir, file)).isFile()); -} - -function parseResult(result) { - const stdout = result.stdout.trim(); - if (stdout.length > 0) { - try { - const parsed = JSON.parse(stdout); - if (Array.isArray(parsed)) return parsed; - } catch { - return [executionError(result, "Rox Rust metric output was not valid JSON")]; - } - } - if (result.status === 0) return []; - return [executionError(result, "Rox Rust metric command failed")]; -} - -function executionError(result, message) { - return { - severity: "error", - message: `${message}: exit ${result.status ?? "signal"}${result.stderr ? `; stderr: ${result.stderr.trim()}` : ""}`, - rule: "lattice-rust-graph-function-metrics/error" - }; -} - -function isCli() { - return process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href; -} diff --git a/scripts/check-workspace.mjs b/scripts/check-workspace.mjs index 85bb915..b45ecee 100644 --- a/scripts/check-workspace.mjs +++ b/scripts/check-workspace.mjs @@ -31,20 +31,18 @@ const requiredGitignoreTokens = [ "node_modules/", "dist/", "*.tsbuildinfo", - ".ace/", ".agents/", ".claude/", ".codex/", ".gemini/", ".opencode/", - ".code-review-graph/", - ".rox-cache/", - ".robustness-engine-cache/", "target/", + ".opcore/*", + "!.opcore/config", ".zeroshot/*", "!.zeroshot/settings.json" ]; -const siblingRepoTokens = ["covibes", "orchestra", "cmdproof", "robustness-engine", "ace"]; +const siblingRepoTokens = ["covibes", "orchestra", "cmdproof", "robustness-engine"]; const readJson = (path) => JSON.parse(readFileSync(path, "utf8")); const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key); @@ -67,8 +65,8 @@ for (const scriptName of [ "ci:local", "setup", "setup:check-clean", - "setup:tools", "verify", + "opcore:self-check", "test:ci", "conformance:check", "pack:check", @@ -87,28 +85,16 @@ for (const scriptName of [ "rust:fmt", "rust:clippy", "rust:test", - "rust:check", - "ace:check", - "ace:install", - "ace:status", - "ace:sync", - "ace:validate", - "current-tools:validate-all", - "current-tools:validate-rust-graph", - "current-tools:validate-changed", - "current-tools:graph-status" + "rust:check" ]) { if (!root.scripts?.[scriptName]) fail(`Root package must expose ${scriptName} script`); } -for (const scriptName of ["ace:check", "ace:install", "ace:status", "ace:sync", "ace:validate"]) { - requireIncludes(`package.json scripts.${scriptName}`, root.scripts[scriptName], "scripts/run-ace.sh"); -} -if (!root.scripts["current-tools:validate-rust-graph"].includes("scripts/check-rust-graph-function-metrics.mjs")) { - fail("current-tools:validate-rust-graph must run scoped Rust graph function metrics script"); -} -if (!root.scripts["current-tools:validate-changed"].includes("scripts/ci/run-rox-clean-changed-gate.mjs")) { - fail("current-tools:validate-changed must run the clean changed-file Rox gate script"); -} +if (root.scripts.setup !== "npm ci") fail("Root setup script must install dependencies only"); +requireIncludes( + "package.json scripts.opcore:self-check", + root.scripts["opcore:self-check"], + "scripts/run-opcore-self-check.mjs" +); for (const ciToken of [ "lint", "rust:check", @@ -117,8 +103,7 @@ for (const ciToken of [ "release-receipt:check", "graph-release:check", "cutover:check", - "OPCORE_CUTOVER_REUSE_RELEASE_PACKAGES=1", - "OPCORE_CUTOVER_REUSE_CURRENT_TOOL_GUARDRAILS=1" + "OPCORE_CUTOVER_REUSE_RELEASE_PACKAGES=1" ]) { requireIncludes("package.json scripts.ci", root.scripts.ci, ciToken); } @@ -148,13 +133,6 @@ for (const token of ["scripts/check-release-hygiene.mjs", "scripts/check-provena } const cutoverReceiptScript = readFileSync("scripts/generate-cutover-receipt.mjs", "utf8"); requireIncludes("scripts/generate-cutover-receipt.mjs", cutoverReceiptScript, "OPCORE_CUTOVER_REUSE_RELEASE_PACKAGES"); -requireIncludes("scripts/generate-cutover-receipt.mjs", cutoverReceiptScript, "OPCORE_CUTOVER_REUSE_CURRENT_TOOL_GUARDRAILS"); -const aspDogfoodReceiptSupportScript = readFileSync("scripts/asp-dogfood-receipt-support.mjs", "utf8"); -requireIncludes( - "scripts/asp-dogfood-receipt-support.mjs", - aspDogfoodReceiptSupportScript, - "OPCORE_ASP_DOGFOOD_REUSE_CURRENT_TOOL_GUARDRAILS" -); validateDependencySpecs("package.json", root); assertDeepEqual(root.optionalDependencies ?? {}, Object.fromEntries(rootNativeOptionalDependencies), "Root native optionalDependencies"); @@ -258,77 +236,32 @@ for (const token of [ } for (const path of [ - "ace.json", - "rox.json", + ".opcore/config", ".zeroshot/settings.json", - "scripts/setup-current-tools.sh", - "scripts/dev-env.sh", - "scripts/check-rust-graph-function-metrics.mjs", + "scripts/run-opcore-self-check.mjs", "scripts/build-graph-core-artifact.mjs", "scripts/ci/run-local-ci-equivalent.sh" ]) { if (!existsSync(path)) fail(`Missing agent tooling file: ${path}`); } -const ace = readJson("ace.json"); -const mcpArgs = ace.mcpServers?.["code-review-graph"]?.args ?? []; -if (!mcpArgs.some((arg) => arg.includes(".ace/runtime/bin/crg") && arg.includes("serve --repo"))) { - fail("ace.json must route code-review-graph MCP through the generated current crg wrapper"); -} - -const rox = readJson("rox.json"); -if (!rox.adapters?.includes("typescript")) fail("rox.json must validate the current TypeScript scaffold"); -if (!rox.adapters?.includes("rust")) fail("rox.json must declare the current Rust scaffold adapter"); -if (!rox.extensions?.includes("scripts/check-rust-graph-function-metrics.mjs")) { - fail("rox.json must run scoped Rust graph function metrics during repo-wide Rox checks"); -} -if (!rox.packages?.includes("crates")) { - fail('rox.json packages must include "crates" for all-mode Rust graph-core function metrics'); -} -for (const includePath of ["packages/", "scripts/", "tests/", "crates/"]) { - if (!rox.checks?.codeQuality?.include?.includes(includePath)) { - fail(`rox.json checks.codeQuality.include must include "${includePath}"`); - } -} -for (const mode of ["staged", "changed", "files"]) { - if (!rox.checks?.codeQuality?.when?.modes?.includes(mode)) { - fail(`rox.json checks.codeQuality.when.modes must include "${mode}"`); - } -} -const rustGates = rox.extensionConfig?.rustGates; -if (rustGates?.workspace !== "Cargo.toml" || rustGates?.package !== "opcore-graph-core") { - fail("rox.json must include schema-compatible Rust graph-core gate metadata under extensionConfig.rustGates"); -} -for (const rustCheck of ["cargo fmt --check", "cargo clippy --all-targets --all-features -- -D warnings", "cargo test"]) { - if (!rustGates.commands?.includes(rustCheck)) fail(`rox.json rustGates must include ${rustCheck}`); -} - const zeroshot = readJson(".zeroshot/settings.json"); if (zeroshot.github?.prBase !== "dev" || zeroshot.worktree?.baseRef !== "origin/dev") { - fail("Zeroshot feature runs must target the lattice dev branch"); -} -if (!zeroshot.worktree?.setup?.includes("npm run setup")) fail("Zeroshot setup must generate current-tool wrappers"); - -const setupTools = readFileSync("scripts/setup-current-tools.sh", "utf8"); -for (const token of [ - "LATTICE_CURRENT_TOOLS_DIR", - "external ACE-managed tools", - "implementation_package_dir", - "packages/graph", - "aceTools", - "binRoot", - "latticeCurrentTools", - "rust-code-analysis-cli" -]) { - requireIncludes("scripts/setup-current-tools.sh", setupTools, token); + fail("Zeroshot feature runs must target the Opcore dev branch"); } +assertDeepEqual(zeroshot.worktree?.setup, ["npm ci"], "Zeroshot setup"); +requireIncludes( + ".zeroshot/settings.json", + JSON.stringify(zeroshot.ship?.commandProofs ?? []), + "scripts/ci/run-local-ci-equivalent.sh" +); if (!existsSync(".changeset")) fail("Missing .changeset directory"); const gitignore = readFileSync(".gitignore", "utf8"); for (const token of requiredGitignoreTokens) requireIncludes(".gitignore", gitignore, token); if (gitignore.includes("!.claude/skills/")) { - fail(".gitignore must not allowlist .claude/skills; ACE-generated provider skills are ignored runtime state"); + fail(".gitignore must not allowlist .claude/skills; generated provider skills are ignored runtime state"); } for (const workflow of [".github/workflows/ci.yml", ".github/workflows/provenance.yml"]) { @@ -342,8 +275,6 @@ for (const workflow of [".github/workflows/ci.yml", ".github/workflows/provenanc } } -validateReservedGraphNaming(); -validateRustGraphCoreNaming(); validateGraphConsumerBoundaries(); validateLocalCiEquivalent(); @@ -386,11 +317,11 @@ function validatePackageManifest(packagePath, manifest, track) { "descriptors", "graph-search", "graph-release", + "graph-serve", "graph-query", "graph-pipeline", "validation-contract", "validation-python", - "graph-reference-evidence", "inspect-symbol-parity", "source-extraction", "README.md" @@ -414,9 +345,6 @@ function validatePackageManifest(packagePath, manifest, track) { if (manifest.private !== true) fail(`${manifest.name} must stay private/internal for ${releaseVersion}`); if (hasOwn(manifest, "publishConfig")) fail(`${manifest.name} must not declare publishConfig`); } - if (manifest.name.includes("code-review-graph") || manifest.name.includes("gungnir")) { - fail(`${manifest.name} uses a forbidden public package name`); - } if (track.dir === "opcore") { assertDeepEqual(manifest.bin, { opcore: "dist/index.js", "opcore-asp-provider": "dist/asp-provider-bin.js" }, `${manifest.name} bin`); } else if (track.dir === "asp-provider") { @@ -424,11 +352,6 @@ function validatePackageManifest(packagePath, manifest, track) { } else if (hasOwn(manifest, "bin")) { fail(`${manifest.name} must not declare CLI bins`); } - for (const forbiddenBin of ["lattice", "crg", "cix", "rox"]) { - if (manifest.bin && hasOwn(manifest.bin, forbiddenBin)) { - fail(`${manifest.name} exposes forbidden old bin ${forbiddenBin}`); - } - } } function validateRustLintPolicy() { @@ -454,11 +377,9 @@ function validateRustLintPolicy() { function validateLocalCiEquivalent() { const localCi = readFileSync("scripts/ci/run-local-ci-equivalent.sh", "utf8"); for (const token of [ - "npm run setup:tools", "npm run setup:check-clean", "npm run ci", - "npm run current-tools:validate-all", - "npm run current-tools:validate-rust-graph" + "npm run opcore:self-check" ]) { requireIncludes("scripts/ci/run-local-ci-equivalent.sh", localCi, token); } @@ -538,59 +459,6 @@ function requireCommandBefore(path, content, earlier, later, reason) { } } -function validateReservedGraphNaming() { - const legacyPackagePath = ["packages", "crg"].join("/"); - const legacyPackageName = `@the-open-engine/opcore-${"crg"}`; - const legacyProviderName = ["cr", "g"].join(""); - const quotedLegacyProviderName = `["']${escapeRegExp(legacyProviderName)}["']`; - const providerLiteralPattern = new RegExp( - `(?:^|[^A-Za-z0-9_$])["']?provider["']?\\s*:\\s*${quotedLegacyProviderName}` - ); - const providerNameMetadataPattern = new RegExp( - `(?:^|[^A-Za-z0-9_$])["']?providerName["']?\\s*:\\s*${quotedLegacyProviderName}` - ); - const providerNameConstantPattern = new RegExp( - `(?:^|[^A-Za-z0-9_$])(?:[A-Za-z_$][\\w$]*ProviderName|providerName)\\s*(?::\\s*[^=]+)?=\\s*${quotedLegacyProviderName}` - ); - const checks = [ - { label: "legacy graph package path", token: legacyPackagePath }, - { label: "legacy graph package name", token: legacyPackageName }, - { label: "legacy graph provider literal", pattern: providerLiteralPattern }, - { label: "legacy graph provider name metadata", pattern: providerNameMetadataPattern }, - { label: "legacy graph provider name constant", pattern: providerNameConstantPattern }, - { label: "legacy graph product description", token: `code-intelligence monorepo for \`${legacyProviderName}\`` }, - { label: "legacy graph package-track description", token: `graph production belongs in \`${legacyProviderName}\`` } - ]; - const violations = []; - for (const path of scanTextFiles(".")) { - if (isReservedGraphNamingAllowlisted(path)) continue; - const content = readFileSync(path, "utf8"); - const lines = content.split(/\r?\n/); - for (const [index, line] of lines.entries()) { - for (const check of checks) { - const matched = check.token ? line.includes(check.token) : check.pattern.test(line); - if (matched) violations.push(`${path}:${index + 1}: ${check.label}`); - } - } - } - if (violations.length > 0) { - fail(`reserved graph naming references must use graph implementation names:\n${violations.join("\n")}`); - } -} - -function validateRustGraphCoreNaming() { - for (const path of ["Cargo.toml", "crates/graph-core/Cargo.toml"]) { - const content = readFileSync(path, "utf8"); - if (/name\s*=\s*["'][^"']*crg[^"']*["']/i.test(content)) { - fail(`${path} must not use crg in Rust package, crate, or native artifact names`); - } - } - const graphPackage = readJson("packages/graph/package.json"); - if (JSON.stringify(graphPackage).match(/lattice-crg-core|graph-crg-core/i)) { - fail("packages/graph/package.json must not use crg in native artifact metadata"); - } -} - function validateGraphConsumerBoundaries() { const forbidden = [ /@the-open-engine\/opcore-graph/, @@ -618,15 +486,11 @@ function scanTextFiles(dir) { ".git", "node_modules", "dist", - ".ace", ".agents", ".claude", ".codex", ".gemini", ".opencode", - ".code-review-graph", - ".rox-cache", - ".robustness-engine-cache", "target" ]); const files = []; @@ -669,20 +533,6 @@ function isTextFile(path) { return dot !== -1 && textExtensions.has(path.slice(dot)); } -function isReservedGraphNamingAllowlisted(path) { - return [ - /^docs\/graph-reference-evidence\//, - /^packages\/fixtures\/graph-pipeline\//, - /^packages\/fixtures\/graph-query\//, - /^packages\/fixtures\/graph-reference-evidence\//, - /^tests\/fixtures\/graph-reference-evidence\// - ].some((pattern) => pattern.test(path)); -} - -function escapeRegExp(value) { - return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); -} - function extractPushBranches(content) { const lines = content.split(/\r?\n/); const branches = []; diff --git a/scripts/ci/run-local-ci-equivalent.sh b/scripts/ci/run-local-ci-equivalent.sh index 3da54bb..9762154 100755 --- a/scripts/ci/run-local-ci-equivalent.sh +++ b/scripts/ci/run-local-ci-equivalent.sh @@ -81,12 +81,12 @@ docs_or_agent_only_changes() { } run_docs_or_agent_gate() { - run_step npm run setup:tools run_step bash -n scripts/ci/run-local-ci-equivalent.sh run_step node scripts/check-release-hygiene.mjs run_step node scripts/check-workspace.mjs run_step node scripts/check-provenance.mjs - run_step npm run current-tools:validate-changed + run_step npm run build + run_step npm run opcore:self-check } changed_file_list="$(mktemp "${TMPDIR:-/tmp}/opcore-local-ci-changed.XXXXXX")" @@ -98,10 +98,8 @@ if docs_or_agent_only_changes "${changed_file_list}"; then exit 0 fi -run_step npm run setup:tools run_step npm run setup:check-clean snapshot_generated_artifacts run_step npm run ci restore_generated_artifacts -run_step npm run current-tools:validate-all -run_step npm run current-tools:validate-rust-graph +run_step npm run opcore:self-check diff --git a/scripts/ci/run-rox-clean-changed-gate.mjs b/scripts/ci/run-rox-clean-changed-gate.mjs deleted file mode 100644 index ab6cc06..0000000 --- a/scripts/ci/run-rox-clean-changed-gate.mjs +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/env node -import { cpSync, existsSync, mkdtempSync, rmSync } from "node:fs"; -import { tmpdir } from "node:os"; -import { dirname, join, resolve } from "node:path"; -import { spawnSync } from "node:child_process"; -import { fileURLToPath } from "node:url"; - -const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../.."); -const rox = join(repoRoot, ".ace/runtime/bin/rox"); -const maxBuffer = 512 * 1024 * 1024; -const retainedAdvancedRenameFingerprints = new Set([ - "code-quality/file-length:packages/opcore/src/advanced/inspect-adapter.ts", - "code-quality/file-length:packages/opcore/src/advanced/inspect-language-service.ts", - "typescript-adapter/function-length:packages/opcore/src/advanced/descriptor.ts:createOpcoreManagedToolDescriptor", - "typescript-adapter/cyclomatic-complexity:packages/opcore/src/advanced/inspect-adapter.ts:inspectNodeReferencesResult", - "typescript-adapter/cyclomatic-complexity:packages/opcore/src/advanced/inspect-adapter.ts:inspectNodeSignatureResult", - "typescript-adapter/cyclomatic-complexity:packages/opcore/src/advanced/inspect-adapter.ts:inspectFileSymbolSignatureResult", - "typescript-adapter/function-length:packages/opcore/src/advanced/inspect-adapter.ts:inspectImplementationsResult", - "typescript-adapter/cyclomatic-complexity:packages/opcore/src/advanced/inspect-adapter.ts:inspectImplementationsResult", - "typescript-adapter/max-params:packages/opcore/src/advanced/inspect-adapter.ts:inspectFailureResult", - "typescript-adapter/max-params:packages/opcore/src/advanced/inspect-adapter.ts:inspectUnsupportedRouteResult", - "typescript-adapter/cyclomatic-complexity:packages/opcore/src/advanced/inspect-adapter.ts:parseInspectOptions", - "typescript-adapter/cyclomatic-complexity:packages/opcore/src/advanced/inspect-language-service.ts:resolveInspectSignatures", - "typescript-adapter/cyclomatic-complexity:packages/opcore/src/advanced/inspect-language-service.ts:resolveInspectImplementations", - "typescript-adapter/cyclomatic-complexity:packages/opcore/src/advanced/inspect-language-service.ts:preflightImplementationTarget", - "typescript-adapter/cyclomatic-complexity:packages/opcore/src/advanced/inspect-language-service.ts:resolveImplementationTargetInProject", - "typescript-adapter/cyclomatic-complexity:packages/opcore/src/advanced/inspect-language-service.ts:collectImplementationRelationships", - "typescript-adapter/max-params:packages/opcore/src/advanced/inspect-language-service.ts:implementationEntry", - "typescript-adapter/cyclomatic-complexity:packages/opcore/src/advanced/inspect-language-service.ts:implementationNameNode", - "typescript-adapter/cyclomatic-complexity:packages/opcore/src/advanced/inspect-language-service.ts:signatureDeclarationsForTarget", - "typescript-adapter/cyclomatic-complexity:packages/opcore/src/advanced/inspect-language-service.ts:signatureReturnType", - "typescript-adapter/cyclomatic-complexity:packages/opcore/src/advanced/inspect-language-service.ts:graphDeclarationShape" -]); - -try { - run(); -} catch (error) { - console.error(error instanceof Error ? error.message : String(error)); - process.exit(1); -} - -function run() { - const base = resolveBaseRef(); - const files = changedFiles(base); - const renameMap = renamedFiles(base); - cleanRoxState(repoRoot); - command(rox, ["stop"], { cwd: repoRoot, allowFailure: true }); - if (files.length === 0) { - console.log("Rox changed gate passed with no changed files."); - return; - } - const current = roxJson(repoRoot, ["check", "--files", ...files, "--no-daemon", "--json"]); - if (current.status === 0) { - process.stdout.write(current.stdout); - return; - } - const currentDiagnostics = parseDiagnostics(current, "current changed Rox"); - const baseline = baselineDiagnosticFingerprints(base, files, renameMap); - const remaining = currentDiagnostics.filter( - (diagnostic) => - !baseline.has(diagnosticFingerprint(diagnostic, renameMap)) && - !isRetainedAdvancedRenameDiagnostic(diagnostic) && - !isRetainedCloneDiagnostic(diagnostic) - ); - if (remaining.length > 0) failWithDiagnostics(remaining, current.status); - console.log(`Rox changed gate passed with ${currentDiagnostics.length} baseline-equivalent legacy code-quality findings retained.`); -} - -function baselineDiagnosticFingerprints(base, files, renameMap) { - const temp = mkdtempSync(join(tmpdir(), "opcore-rox-baseline-")); - try { - extractBaseTree(base, temp); - cpSync(join(repoRoot, "rox.json"), join(temp, "rox.json")); - cleanRoxState(temp); - const existingFiles = files.map((file) => renameMap.get(file) ?? file).filter((file) => existsSync(join(temp, file))); - if (existingFiles.length === 0) return new Set(); - const result = roxJson(temp, ["check", "--files", ...existingFiles, "--no-daemon", "--json"]); - return new Set( - parseDiagnostics(result, "baseline Rox") - .filter(isLegacyCodeQualityDiagnostic) - .map((diagnostic) => diagnosticFingerprint(diagnostic)) - ); - } finally { - rmSync(temp, { recursive: true, force: true }); - } -} - -function changedFiles(base) { - return uniqueSorted([ - ...lines(git(["diff", "--name-only", "--diff-filter=ACMRT", base, "--"]).stdout), - ...lines(git(["ls-files", "--others", "--exclude-standard"]).stdout) - ]); -} - -function renamedFiles(base) { - const entries = lines(git(["diff", "--name-status", "--find-renames", "--diff-filter=R", base, "--"]).stdout); - const renames = new Map(); - for (const entry of entries) { - const [, oldPath, newPath] = entry.split("\t"); - if (oldPath && newPath) renames.set(newPath, oldPath); - } - return renames; -} - -function extractBaseTree(base, target) { - const archive = git(["archive", base], { encoding: "buffer" }); - const untar = command("tar", ["-x", "-C", target], { cwd: repoRoot, input: archive.stdout }); - if (untar.status !== 0) throw new Error(commandFailure("tar", ["-x", "-C", target], untar)); -} - -function roxJson(cwd, args) { - return command(rox, args, { cwd, allowFailure: true }); -} - -function resolveBaseRef() { - for (const ref of ["origin/main", "main", "HEAD"]) { - if (git(["rev-parse", "--verify", `${ref}^{commit}`], { allowFailure: true }).status === 0) return ref; - } - throw new Error("No git base ref available for Rox changed gate"); -} - -function parseDiagnostics(result, label) { - const output = result.stdout.trim(); - if (output.length === 0) return []; - try { - const parsed = JSON.parse(output); - if (Array.isArray(parsed)) return parsed; - } catch (error) { - throw new Error(`${label} returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`); - } - throw new Error(`${label} returned non-array JSON diagnostics`); -} - -function failWithDiagnostics(diagnostics, status) { - console.error(`Rox changed gate found ${diagnostics.length} non-baseline findings`); - for (const diagnostic of diagnostics) { - const location = [diagnostic.file, diagnostic.line].filter((value) => value !== undefined).join(":"); - console.error(` ${location} - ${diagnostic.message} [${diagnostic.rule}]`); - } - process.exit(status === 0 ? 2 : status); -} - -function diagnosticFingerprint(diagnostic, renameMap = new Map()) { - if (typeof diagnostic.hypotheticalFingerprint === "string") return normalizeRenamedPathText(diagnostic.hypotheticalFingerprint, renameMap); - return [ - diagnostic.rule, - normalizeRenamedPathText(diagnostic.file, renameMap), - normalizeRenamedPathText(diagnostic.message, renameMap) - ].filter(Boolean).join(":"); -} - -function isLegacyCodeQualityDiagnostic(diagnostic) { - const rule = typeof diagnostic.rule === "string" ? diagnostic.rule : ""; - return rule.startsWith("code-quality/") || rule.startsWith("typescript-adapter/") || rule.startsWith("clone-indexer/"); -} - -function isRetainedAdvancedRenameDiagnostic(diagnostic) { - // #2 only renames the old internal router directory; current Rox cannot emit - // comparable baseline rows for the old path, so retain only exact known rows. - return retainedAdvancedRenameFingerprints.has(diagnosticFingerprint(diagnostic)); -} - -function isRetainedCloneDiagnostic(diagnostic) { - const rule = typeof diagnostic.rule === "string" ? diagnostic.rule : ""; - // Clone-indexer fingerprints are unstable across identity-only path and package renames; - // retain them as legacy code-quality noise in this changed-file guardrail wrapper. - return rule.startsWith("clone-indexer/"); -} - -function git(args, options = {}) { - return command("git", args, { cwd: repoRoot, ...options }); -} - -function command(commandName, args, options = {}) { - const result = spawnSync(commandName, args, { - cwd: options.cwd, - encoding: options.encoding ?? "utf8", - input: options.input, - maxBuffer, - stdio: ["pipe", "pipe", "pipe"] - }); - if (result.status !== 0 && options.allowFailure !== true) throw new Error(commandFailure(commandName, args, result)); - return result; -} - -function commandFailure(commandName, args, result) { - return [ - `${commandName} ${args.join(" ")} failed with status ${result.status ?? "unknown"}`, - String(result.stderr ?? "").trim(), - String(result.stdout ?? "").trim() - ].filter((line) => line.length > 0).join("\n"); -} - -function lines(output) { - return String(output).split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0); -} - -function uniqueSorted(values) { - return [...new Set(values)].sort(); -} - -function normalizeRenamedPathText(value, renameMap) { - if (typeof value !== "string" || renameMap.size === 0) return value; - let normalized = value; - for (const [newPath, oldPath] of renameMap) { - normalized = normalized.split(newPath).join(oldPath); - } - return normalized; -} - -function cleanRoxState(root) { - for (const cache of [".rox-cache", ".robustness-engine-cache"]) { - rmSync(join(root, cache), { recursive: true, force: true }); - } -} diff --git a/scripts/dev-env.sh b/scripts/dev-env.sh deleted file mode 100755 index 78b326a..0000000 --- a/scripts/dev-env.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash - -if [[ "${BASH_SOURCE[0]:-}" == "${0}" ]]; then - printf 'source this file instead: source scripts/dev-env.sh\n' >&2 - exit 1 -fi - -lattice_repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)" -lattice_bin_dir="${lattice_repo_root}/.ace/runtime/bin" -lattice_runtime_dir="${lattice_repo_root}/.ace/rox" - -if [[ ! -x "${lattice_bin_dir}/rox" || ! -x "${lattice_bin_dir}/crg" || ! -x "${lattice_bin_dir}/cix" ]]; then - printf 'lattice current-tool wrappers are missing; run npm run setup:tools\n' >&2 - return 1 -fi - -case ":${PATH:-}:" in - *:"${lattice_bin_dir}":*) ;; - *) export PATH="${lattice_bin_dir}${PATH:+:${PATH}}" ;; -esac - -export LATTICE_CURRENT_TOOL_RUNTIME_DIR="${LATTICE_CURRENT_TOOL_RUNTIME_DIR:-${lattice_runtime_dir}}" -export CIX_DAEMON_ROOT_DIR="${CIX_DAEMON_ROOT_DIR:-$(cd "${lattice_repo_root}" && pwd -P)}" diff --git a/scripts/generate-asp-dogfood-receipt.mjs b/scripts/generate-asp-dogfood-receipt.mjs index 11fb01a..d3e0313 100644 --- a/scripts/generate-asp-dogfood-receipt.mjs +++ b/scripts/generate-asp-dogfood-receipt.mjs @@ -1,5 +1,5 @@ #!/usr/bin/env node -import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs"; +import { existsSync, mkdirSync, mkdtempSync, rmSync } from "node:fs"; import { join } from "node:path"; import { tmpdir } from "node:os"; import { fileURLToPath } from "node:url"; @@ -21,7 +21,7 @@ import { requireObject, releaseRuntimeInstallPackageNames, runAspCommand, - runCurrentToolGuardrails, + runOpcoreSelfValidation, runRequired, sanitizeReceiptForProvenance, sha256File, @@ -35,7 +35,6 @@ const summaryPath = "docs/release/asp-dogfood-receipt.summary.md"; const args = process.argv.slice(2); const writeDocs = args.includes("--write"); const jsonOutput = args.includes("--json") || !writeDocs; -const includeCurrentToolsAll = args.includes("--include-current-tools-all"); await main(); @@ -64,13 +63,16 @@ async function generateReceipt() { try { const install = installPackedProject(tempRoot); const provider = providerEvidence(tempRoot, install.project); - const manager = locateAspManager(); + const manager = locateAspManager(repoRoot); const aspHome = mkdtempSync(join(tempRoot, "asp-home-")); const env = aspEnv(install.project, aspHome); const fixture = createAspHostFixtureRepo(tempRoot); const flow = runAspFlow(manager, env, provider.manifest.manifestPath, fixture); const probe = await runProviderProbe(binPath(install.project, "opcore-asp-provider")); - return sanitizeReceiptForProvenance(buildReceipt({ install, provider, manager, aspHome, fixture, flow, probe })); + return sanitizeReceiptForProvenance( + buildReceipt({ install, provider, manager, aspHome, fixture, flow, probe }), + manager.aspRepoPath + ); } finally { rmSync(tempRoot, { recursive: true, force: true }); } @@ -184,12 +186,11 @@ function buildReceipt({ install, provider, manager, aspHome, fixture, flow, prob ? flow.hostEvaluation : { check: flow.hostEvaluation.check }, providerProbe: probe, - currentToolGuardrails: runCurrentToolGuardrails(repoRoot, includeCurrentToolsAll), + selfValidation: runOpcoreSelfValidation(repoRoot), unsupportedSurfaces: unsupportedSurfaces(), parityBlockers: collectParityBlockers(repoRoot), authority: authorityEvidence(), publicReleaseActions: [], - oldToolReplacementClaimed: false, forbiddenMarkerScan: { scannedTextCount: providerScanTexts(provider.manifest.manifest).length, findingCount: 0, @@ -205,7 +206,6 @@ function aspHomeEvidence(aspHome) { isolated: true, sharedStateMutated: false, pathSanitized: true, - aceRuntimeBinExcluded: true }; } @@ -219,9 +219,11 @@ function unsupportedSurfaces() { }, { surface: "edit", - status: "retained-old-tool-gate", + status: "parity-blocker", cleanCoverage: false, - blocker: "ASP dogfood does not authorize edits or apply behavior; edit parity remains covered by current old-tool and cutover gates." + blocker: + "ASP dogfood does not authorize edits or apply behavior; " + + "edit parity remains covered by installed cutover evidence." } ]; } diff --git a/scripts/generate-cutover-receipt.mjs b/scripts/generate-cutover-receipt.mjs index 93f7b4d..2df7f24 100644 --- a/scripts/generate-cutover-receipt.mjs +++ b/scripts/generate-cutover-receipt.mjs @@ -16,7 +16,6 @@ import { basename, dirname, join, relative, resolve } from "node:path"; import { tmpdir } from "node:os"; import { fileURLToPath } from "node:url"; import { - releaseCutoverCurrentToolGuardrailIds, releaseCutoverPythonCommandIds, releaseCutoverRustCommandIds, releaseCutoverRequiredCommandIds, @@ -44,19 +43,10 @@ const artifactAttestationPath = "docs/release/artifact-attestation.md"; const releasePackDestination = ".opcore/release/packages"; const fixtureRoot = "packages/fixtures/source-extraction/wave1"; const rustFixtureRoot = "packages/fixtures/source-extraction/rust-only"; -const currentToolEnvVars = [ - "LATTICE_CURRENT_TOOLS_DIR", - "ACE_CURRENT_TOOLS_DIR", - "LATTICE_CURRENT_ROX_PATH", - "LATTICE_CURRENT_CRG_PATH", - "LATTICE_CURRENT_CIX_PATH" -]; - const args = process.argv.slice(2); const writeDocs = args.includes("--write"); const jsonOutput = args.includes("--json") || !writeDocs; const reuseReleasePackages = process.env.OPCORE_CUTOVER_REUSE_RELEASE_PACKAGES === "1"; -const reuseCurrentToolGuardrails = process.env.OPCORE_CUTOVER_REUSE_CURRENT_TOOL_GUARDRAILS === "1"; try { const validateReceiptFile = valueAfter("--validate-receipt-file"); @@ -372,7 +362,7 @@ function generateReceipt() { ]; const descriptor = collectInstalledDescriptor(project, tarballs); const installedPackages = collectInstalledPackages(project, tarballs); - const currentToolGuardrails = currentToolGuardrailsForCutover(); + const selfValidation = runSelfValidation(); const markerScanEntries = receiptScanTextsWithoutReceipt(project, descriptor, commandTexts, tarballs); assertNoForbiddenMarkers(markerScanEntries); const receipt = { @@ -386,24 +376,19 @@ function generateReceipt() { installedPackages, descriptor, environmentIsolation: { - currentToolEnvCleared: true, - clearedEnvVarCount: currentToolEnvVars.length, pathSanitized: true, - aceRuntimeBinExcluded: true, - siblingCovibesExcluded: true, - opcoreBinOnly: true, - oldBinsAbsent: { lattice: true, crg: true, cix: true, rox: true } + siblingRepositoriesExcluded: true, + opcoreBinsVerified: true }, commandReceipts: sortCommandReceipts(commandReceipts), rustCommandReceipts: sortRustCommandReceipts(rustCommandReceipts), pythonCommandReceipts: sortPythonCommandReceipts(pythonCommandReceipts), negativeChecks, - currentToolGuardrails, - oldToolReplacementClaimed: false, + selfValidation, forbiddenMarkerScan: { scannedTextCount: markerScanEntries.length + 1, findingCount: 0, - markersBlocked: ["private-runtime", "current-tool-env", "private-home", "old-tool-bins", "old-product-name", "doubled-token"] + markersBlocked: ["private-home", "launch-claim"] }, inputEvidence: collectInputEvidence() }; @@ -563,58 +548,20 @@ function runPythonToolDegradationNegativeChecks(tempRoot, opcoreBin, env, comman ]; } -function currentToolGuardrailsForCutover() { - if (reuseCurrentToolGuardrails) return recordedCurrentToolGuardrailsForCutover(); - return runCurrentToolGuardrailsForCutover(); -} - -function recordedCurrentToolGuardrailsForCutover() { - const receipt = readJson(join(repoRoot, cutoverReceiptPath)); - const guardrails = receipt.currentToolGuardrails; - if (!Array.isArray(guardrails)) { - throw new Error(`${cutoverReceiptPath} must contain recorded current-tool guardrails`); - } - assertSameSet( - guardrails.map((entry) => entry?.id), - releaseCutoverCurrentToolGuardrailIds, - "recorded current-tool guardrails" - ); - return guardrails.map((entry) => { - if (!entry || typeof entry !== "object") throw new Error(`${cutoverReceiptPath} current-tool guardrail entry is required`); - if (entry.retained !== true || entry.oldToolReplacementClaimed !== false) { - throw new Error(`${cutoverReceiptPath} current-tool guardrails must stay retained without old-tool replacement claims`); - } - return { ...entry }; +function runSelfValidation() { + const result = run("npm", ["run", "opcore:self-check"], { + cwd: repoRoot, + env: process.env, + expectedStatus: 0 }); -} - -function runCurrentToolGuardrailsForCutover() { - return [ - runCurrentToolGuardrail( - "current-tools-validate-changed", - ["run", "current-tools:validate-changed"], - "retained external changed-file guardrail passed during installed-artifact cutover proof" - ), - runCurrentToolGuardrail( - "current-tools-validate-rust-graph", - ["run", "current-tools:validate-rust-graph"], - "retained external Rust graph guardrail passed during installed-artifact cutover proof" - ) - ]; -} - -function runCurrentToolGuardrail(id, npmArgs, assertion) { - const result = run("npm", npmArgs, { cwd: repoRoot, env: process.env, expectedStatus: 0 }); return { - id, - command: ["npm", ...npmArgs], + id: "opcore-self-check", + command: ["npm", "run", "opcore:self-check"], status: "passed", exitCode: 0, stdoutSha256: sha256(result.stdout), stderrSha256: sha256(result.stderr), - retained: true, - assertion, - oldToolReplacementClaimed: false + assertion: "Opcore validated its own changed implementation surface" }; } @@ -765,9 +712,6 @@ function preparePythonSmokeRepo(tempRoot, name = "python-smoke") { function inspectInstalledBins(project) { if (!existsSync(binPath(project, "opcore"))) throw new Error("installed project is missing opcore bin"); if (!existsSync(binPath(project, "opcore-asp-provider"))) throw new Error("installed project is missing opcore-asp-provider bin"); - for (const oldBin of ["lattice", "crg", "cix", "rox"]) { - if (existsSync(binPath(project, oldBin))) throw new Error(`installed project exposes old public bin ${oldBin}`); - } } function collectInstalledPackages(project, tarballs) { @@ -1035,11 +979,10 @@ function writeValidationRequest(project, filename, overrides = {}) { function sanitizedEnv() { const env = { ...process.env }; - for (const key of currentToolEnvVars) delete env[key]; env.PATH = [dirname(process.execPath), "/usr/local/bin", "/opt/homebrew/bin", "/usr/bin", "/bin", "/usr/sbin", "/sbin"].join(":"); env.npm_lifecycle_event = undefined; - if (env.PATH.includes(".ace/runtime/bin") || env.PATH.includes("/covibes/")) { - throw new Error("sanitized PATH still includes current-tool or sibling Covibes paths"); + if (env.PATH.includes("/covibes/")) { + throw new Error("sanitized PATH still includes sibling repository paths"); } return env; } @@ -1065,10 +1008,7 @@ function receiptScanTexts(receipt) { function assertNoForbiddenMarkers(entries) { const forbidden = [ - { label: "private runtime", pattern: /(^|[\\/"'\s])\.ace(?:[\\/"'\s]|$)/i }, - { label: "current-tool env", pattern: /LATTICE_CURRENT_TOOLS_DIR|ACE_CURRENT_TOOLS_DIR|LATTICE_CURRENT_(?:ROX|CRG|CIX)_PATH/i }, - { label: "private home", pattern: /\/Users\/tom\b/ }, - { label: "old tool bins", pattern: /(^|[\\/"'\s])(?:crg|cix|rox)(?:$|[\\/"'\s])/i } + { label: "private home", pattern: /\/Users\/tom\b/ } ]; const findings = []; for (const entry of entries) { @@ -1078,7 +1018,6 @@ function assertNoForbiddenMarkers(entries) { for (let index = 0; index < lines.length; index += 1) { const line = lines[index]; if (!marker.pattern.test(line)) continue; - if (isAllowlistedCutoverMarkerLine(line, marker.label)) continue; findings.push(`${entry.label}:${index + 1}: ${marker.label}: ${line.trim()}`); } } @@ -1092,11 +1031,6 @@ function isPackageTextScanEntry(label) { return label.startsWith("installed-package:") || label.startsWith("npm-pack:"); } -function isAllowlistedCutoverMarkerLine(line, label) { - if (label !== "old tool bins") return false; - return /oldBins(?:Absent)?|old public bin|old tool bins|forbiddenPublicBins|forbiddenBin|oldBin|Release receipt package exposes old public bin|\["lattice",\s*"crg",\s*"cix",\s*"rox"\]|\["crg",\s*"cix",\s*"rox"\]/i.test(line); -} - function collectStringValues(value) { if (typeof value === "string") return [value]; if (Array.isArray(value)) return value.flatMap((entry) => collectStringValues(entry)); @@ -1142,8 +1076,7 @@ Installed packages: ${receipt.installedPackages.length} Command receipts: ${receipt.commandReceipts.length} Rust command receipts: ${receipt.rustCommandReceipts.length} Python command receipts: ${receipt.pythonCommandReceipts.length} -Current-tool guardrails retained: ${receipt.currentToolGuardrails.length} -Old-tool replacement claimed: ${receipt.oldToolReplacementClaimed} +Self-validation: ${receipt.selfValidation.status} Forbidden marker findings: ${receipt.forbiddenMarkerScan.findingCount} Input evidence: ${receipt.inputEvidence.map((entry) => entry.issue).join(", ")} @@ -1157,9 +1090,18 @@ function appendCutoverAttestation(receipt, receiptSha256) { const existing = existsSync(join(repoRoot, artifactAttestationPath)) ? readFileSync(join(repoRoot, artifactAttestationPath), "utf8") : "# Artifact Attestation\n"; - const block = `\n## Cutover Gate\n\nIssue #30 receipt: ${cutoverReceiptPath}\nCutover receipt SHA-256: ${receiptSha256}\nInstalled command receipts: ${receipt.commandReceipts.length}\nRust command receipts: ${receipt.rustCommandReceipts.length}\nPython command receipts: ${receipt.pythonCommandReceipts.length}\nCurrent-tool guardrails retained: ${receipt.currentToolGuardrails.length}\nOld-tool replacement claimed: ${receipt.oldToolReplacementClaimed}\n`; - const withoutOld = existing.replace(/\n## Cutover Gate\n[\s\S]*$/, ""); - writeFileSync(join(repoRoot, artifactAttestationPath), `${withoutOld.trimEnd()}\n${block}`); + const block = ` +## Cutover Gate + +Issue #30 receipt: ${cutoverReceiptPath} +Cutover receipt SHA-256: ${receiptSha256} +Installed command receipts: ${receipt.commandReceipts.length} +Rust command receipts: ${receipt.rustCommandReceipts.length} +Python command receipts: ${receipt.pythonCommandReceipts.length} +Self-validation: ${receipt.selfValidation.status} +`; + const withoutPrevious = existing.replace(/\n## Cutover Gate\n[\s\S]*$/, ""); + writeFileSync(join(repoRoot, artifactAttestationPath), `${withoutPrevious.trimEnd()}\n${block}`); } function sortCommandReceipts(receipts) { diff --git a/scripts/generate-graph-release-receipt.mjs b/scripts/generate-graph-release-receipt.mjs index 773ddd5..ebc696d 100644 --- a/scripts/generate-graph-release-receipt.mjs +++ b/scripts/generate-graph-release-receipt.mjs @@ -34,8 +34,27 @@ const repoRoot = fileURLToPath(new URL("..", import.meta.url)); const opcoreBin = join(repoRoot, "packages/opcore/dist/index.js"); const sourceFixtureRoot = join(repoRoot, "packages/fixtures/source-extraction/wave1"); const rustSourceFixtureRoot = join(repoRoot, "packages/fixtures/source-extraction/rust-only"); -const baselineReceipt = "packages/fixtures/graph-reference-evidence/baseline-receipts.json"; -const sqliteReferenceFixture = "packages/fixtures/graph-reference-evidence/sqlite-fixtures.json"; +const baselineReceipt = "docs/release/graph-release-receipt.json"; +const directSqliteQueries = [ + { id: "status-counts", sql: "select kind, count(*) as count from nodes group by kind order by kind" }, + { id: "status-edge-counts", sql: "select kind, count(*) as count from edges group by kind order by kind" }, + { + id: "impact-edges-from-file", + sql: "select kind, source_qualified, target_qualified from edges where file_path = ?" + }, + { + id: "search-by-name", + sql: + "select qualified_name, kind, file_path, line_start, line_end from nodes " + + "where name like ? order by kind, qualified_name limit ?" + }, + { + id: "freshness-metadata", + sql: + "select key, value from metadata " + + "where key in ('schema_version', 'last_updated', 'last_build_type') order by key" + } +]; const graphPackageRoot = join(repoRoot, "packages/graph"); const receiptPath = "docs/release/graph-release-receipt.json"; const handoffReceiptPath = "docs/release/graph-release-receipt.payload.json"; @@ -394,13 +413,11 @@ function runDirectSqliteQueries(fixtureRoot) { const dbPath = join(realpathSync(fixtureRoot), ".opcore/graph/graph.db"); const db = new DatabaseSync(dbPath, { readOnly: true }); try { - const manifest = JSON.parse(readFileSync(join(repoRoot, sqliteReferenceFixture), "utf8")); - const queries = manifest.directReaderQueries ?? []; - const ids = queries.map((entry) => entry.id); + const ids = directSqliteQueries.map((entry) => entry.id); if (ids.join("\0") !== graphReleaseDirectSqliteQueryIds.join("\0")) { throw new Error(`#19 direct-reader query ids changed: ${ids.join(", ")}`); } - return queries.map((entry) => { + return directSqliteQueries.map((entry) => { const rows = db.prepare(entry.sql).all(...directSqliteParams(entry.id, fixtureRoot)); if (rows.length === 0) throw new Error(`#19 direct-reader query returned no rows: ${entry.id}`); return { @@ -574,10 +591,10 @@ function inspectGraphPackage() { forbiddenMarkersAbsent: true, generatedBuildMetadataAbsent: true, privatePathsAbsent: true, - pythonCrgSourceAbsent: true, - pythonGraphPackageMetadataAbsent: true, - pythonCrgGitHistoryAbsent: true, - forbiddenImplementationPackageNamesAbsent: true, + sourceProvenanceAbsent: true, + packageMetadataAbsent: true, + gitHistoryAbsent: true, + foreignImplementationNamesAbsent: true, inspections: ["npm-pack-dry-run", "package-file-scan", "package-content-scan", "provenance-marker-scan"] }; } @@ -590,7 +607,6 @@ function scanGraphPackagePaths(files) { findings.push(`${file}: python package metadata path`); } if (/(^|\/)\.git(\/|$)/i.test(file)) findings.push(`${file}: git history path`); - if (/code-review-graph|gungnir/i.test(file)) findings.push(`${file}: forbidden implementation package path`); if (/\.tsbuildinfo$/i.test(file)) findings.push(`${file}: generated build metadata path`); } return findings; @@ -619,15 +635,10 @@ function resolvePackagedGraphFile(file) { function graphPackageForbiddenContentMarkers() { return [ - { label: "python CRG source author", pattern: /tirth8205|Tirth Kanani/i }, + { label: "foreign source author", pattern: /tirth8205|Tirth Kanani/i }, { label: "python package metadata", pattern: /(^|[\\/"'\s])(pyproject\.toml|setup\.py|setup\.cfg|Pipfile)($|[\\/"'\s])/i }, - { label: "python CRG git history", pattern: /git clone|refs\/heads|objects\/pack/i }, - { - label: "forbidden implementation package name", - pattern: /(["']name["']\s*:\s*["'](?:code-review-graph|gungnir)["']|name\s*=\s*["'](?:code-review-graph|gungnir)["'])/i - }, + { label: "embedded git history", pattern: /git clone|refs\/heads|objects\/pack/i }, { label: "private local path", pattern: /\/Users\/tom\/|\/private\/var\/folders\/|[A-Za-z]:\\Users\\/i }, - { label: "current tool source override", pattern: /LATTICE_(ROX|CRG|CIX)_SOURCE=\/|LATTICE_CURRENT_TOOLS_DIR=\//i }, { label: "generated build metadata", pattern: /\.tsbuildinfo|tsconfig\.tsbuildinfo/i } ]; } @@ -645,7 +656,7 @@ function inspectPackagedGraphArtifactMetadata(file, text) { if (/^(\/|[A-Za-z]:|~)|(^|\/)\.\.(\/|$)/.test(value)) { findings.push(`${file}: graph artifact metadata ${key} contains absolute or parent path`); } - if (/(^|\/)(covibes|orchestra|cmdproof|robustness-engine|ace)(\/|$)/.test(value)) { + if (/(^|\/)(covibes|orchestra|cmdproof|robustness-engine)(\/|$)/.test(value)) { findings.push(`${file}: graph artifact metadata ${key} contains private/global path`); } } @@ -763,7 +774,7 @@ function withHandoff(receipt, checksumSha256) { issue, receiptPath: handoffReceiptPath, checksumSha256, - rollbackNote: "Keep ACE wrappers on current external tools if receipt regresses." + rollbackNote: "Block release and repair Opcore self-validation if this receipt regresses." })) }; } @@ -821,7 +832,7 @@ ${parentScopeRows} License report: docs/release/license-report.md Provenance receipt: docs/release/provenance-receipts.md -Rollback: keep ACE wrappers on current external tools if receipt regresses. +Rollback: block release and repair Opcore self-validation if this receipt regresses. Maintainer note: these graph release checks must pass before publishing alpha artifacts. `; } diff --git a/scripts/generate-release-receipt.mjs b/scripts/generate-release-receipt.mjs index 3b5cb2a..393861a 100644 --- a/scripts/generate-release-receipt.mjs +++ b/scripts/generate-release-receipt.mjs @@ -9,7 +9,7 @@ import { statSync, writeFileSync } from "node:fs"; -import { join, relative, resolve } from "node:path"; +import { join, relative } from "node:path"; import { fileURLToPath } from "node:url"; import { graphCoreNativePackageNameForTarget, @@ -587,7 +587,9 @@ function parseGitGrepLine(line, commit) { } function isSecretScanPathSkipped(path) { - return /(^|\/)(node_modules|dist|target|\.git|\.ace|\.lattice|\.zeroshot)(\/|$)/.test(path) || /\.(png|jpe?g|gif|pdf|tgz|zip|sqlite|db)$/i.test(path); + const skippedRoot = /(^|\/)(node_modules|dist|target|\.git|\.lattice|\.zeroshot)(\/|$)/.test(path); + const skippedExtension = /\.(png|jpe?g|gif|pdf|tgz|zip|sqlite|db)$/i.test(path); + return skippedRoot || skippedExtension; } function isTextFile(path) { @@ -719,12 +721,6 @@ function readDescriptor() { } function validateNoOldPublicIdentity(packageName, manifest, bins) { - if (/(?:^|[-/])(lattice|crg|cix|rox)(?:$|-)/i.test(String(manifest.name))) { - throw new Error(`${packageName} exposes forbidden old package identity ${manifest.name}`); - } - for (const bin of Object.keys(bins)) { - if (["lattice", "crg", "cix", "rox"].includes(bin)) throw new Error(`${packageName} exposes forbidden old public bin ${bin}`); - } if (packageName === "opcore") assertSameSet(Object.keys(bins), ["opcore", "opcore-asp-provider"], `${packageName} bins`); else if (Object.keys(bins).length > 0) throw new Error(`${packageName} must not expose public bins`); } diff --git a/scripts/lib/launch-claim-scrub.mjs b/scripts/lib/launch-claim-scrub.mjs index 58d5d1d..bf108d9 100644 --- a/scripts/lib/launch-claim-scrub.mjs +++ b/scripts/lib/launch-claim-scrub.mjs @@ -9,7 +9,6 @@ import { join, relative, resolve } from "node:path"; // this module so the gate and its test never drift apart. export const forbiddenLaunchClaims = [ { label: "public ASP standard claim", pattern: /\bASP\b.{0,80}\b(public standard|standard now|standardized|the standard)\b/i }, - { label: "old-tool replacement claim", pattern: /\breplaces?\s+(Rox|CRG|CIX)\b|\b(Rox|CRG|CIX)\b.{0,80}\breplaces?\b/i }, { label: "generic Opcore replacement claim", pattern: /\bopcore\b[^.\n]{0,40}\breplaces?\b/i }, { label: "universal stack claim", pattern: /\b(every|all)\s+(stack|language|platform)\b|\buniversal\s+(stack|language|platform)\s+coverage\b/i }, { label: "universal agent claim", pattern: /\b(every|all)\s+agents?\b|\bworks with every agent\b/i }, @@ -21,7 +20,6 @@ export const forbiddenLaunchClaims = [ { label: "blended score claim", pattern: /\b(blended|overall|composite|unified|single|aggregate)[\s-]+((quality|health|robustness)[\s-]+)?score\b|\b(quality|health|robustness)[\s-]+score\b/i }, { label: "asp router command claim", pattern: /\b(opcore|lattice)\s+asp\b/i }, { label: "provider authority claim", pattern: /\bgate\s+(authority|permission)\b|\bprovider\b[^.\n]{0,40}\b(grants?|confers?|owns?|holds?)\b[^.\n]{0,25}\b(authority|permission|gate decision)\b/i }, - { label: "ACE-managed distribution claim", pattern: /\bACE[- ]managed\b|\bACE[- ]provision/i }, { label: "old product name", pattern: /\b[Ll]attice\b/ }, { label: "doubled Opcore token", pattern: /\bOpcore\/Opcore\b/ } ]; @@ -248,12 +246,6 @@ const launchScrubAllowlist = [ reason: "explicit provider authority disclaimer", matches: (_entry, line) => /does not grant authority/i.test(line) }, - { - reason: "old-bin policy definitions", - matches: (entry, line) => - /oldBins(?:Absent)?|old public bin|old tool bins|forbiddenPublicBins|forbiddenBin|oldBin|Release receipt package exposes old public bin|oldAliasPattern|manifest\.name\.includes\("lattice"\)|\["lattice",\s*"crg",\s*"cix",\s*"rox"\]/i.test(line) || - (/(?:opcore-contracts|packages\/contracts)/.test(entry.label) && /^\s*(?:lattice:\s*true;|"lattice"[:,]?\s*(?:\{|$))/.test(line)) - }, { reason: "ASP dogfood forbidden-marker schema", matches: (entry, line) => diff --git a/scripts/measure-graph-reference-baselines.mjs b/scripts/measure-graph-reference-baselines.mjs deleted file mode 100644 index 337a24f..0000000 --- a/scripts/measure-graph-reference-baselines.mjs +++ /dev/null @@ -1,115 +0,0 @@ -import { spawnSync } from "node:child_process"; -import { existsSync, statSync, writeFileSync } from "node:fs"; -import { performance } from "node:perf_hooks"; - -const write = process.argv.includes("--write"); -const referenceGraphTool = ".ace/runtime/bin/crg"; -const sourceAvailability = existsSync(referenceGraphTool) ? "available" : "unavailable"; -const outputPath = "packages/fixtures/graph-reference-evidence/baseline-receipts.json"; - -const receipts = [ - measureCommand("baseline-install-setup", "install_setup_ms", [referenceGraphTool, "--help"]), - measureCommand("baseline-cold-build", "cold_build_ms", [referenceGraphTool, "build", "--repo", ".", "--json"]), - measureCommand("baseline-incremental-update", "incremental_update_ms", [referenceGraphTool, "update", "--base", "HEAD", "--repo", ".", "--json"]), - measureCommand("baseline-impact-cold", "impact_cold_ms", [ - referenceGraphTool, - "impact", - "--files", - "packages/contracts/src/index.ts", - "--repo", - ".", - "--json" - ]), - measureCommand("baseline-impact-hot", "impact_hot_ms", [ - referenceGraphTool, - "impact", - "--files", - "packages/contracts/src/index.ts", - "--repo", - ".", - "--json" - ]), - measureCommand("baseline-search", "search_ms", [referenceGraphTool, "search", "GraphProvider", "--limit", "5", "--repo", ".", "--json"]), - measureFile("baseline-db-size", "db_size_bytes", ".code-review-graph/graph.db"), - measureFile("baseline-wal-size", "wal_size_bytes", ".code-review-graph/graph.db-wal"), - measureCommand("baseline-daemon-startup", "daemon_startup_ms", [referenceGraphTool, "serve", "--help"]), - syntheticReceipt("baseline-daemon-query", "daemon_query_ms", "opcore.graph.daemon synthetic query envelope") -]; - -const payload = { - schemaVersion: 1, - issue: "#19", - label: "reference_evidence_non_implementation_input", - origin: "covibes-authored-synthetic", - sourceTool: "current external dev wrapper", - sourceAvailability, - collectedAt: new Date().toISOString(), - receipts -}; - -if (write) { - writeFileSync(outputPath, `${JSON.stringify(payload, null, 2)}\n`); -} else { - process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`); -} - -function measureCommand(id, metric, command) { - if (sourceAvailability === "unavailable") { - return unavailableReceipt(id, metric, command.join(" ")); - } - const start = performance.now(); - const result = spawnSync(command[0], command.slice(1), { - encoding: "utf8", - stdio: ["ignore", "pipe", "pipe"], - timeout: 30000 - }); - const elapsed = Math.max(1, Math.round(performance.now() - start)); - return { - id, - metric, - value: elapsed, - unit: "ms", - sourceAvailability, - nonImplementationInput: true, - command: command.join(" "), - exitCode: result.status ?? 124, - stderr: result.stderr.trim() - }; -} - -function measureFile(id, metric, path) { - if (!existsSync(path)) return unavailableReceipt(id, metric, `stat ${path}`, "bytes"); - return { - id, - metric, - value: Math.max(1, statSync(path).size), - unit: "bytes", - sourceAvailability, - nonImplementationInput: true, - command: `stat ${path}` - }; -} - -function syntheticReceipt(id, metric, command) { - return { - id, - metric, - value: 1, - unit: "ms", - sourceAvailability, - nonImplementationInput: true, - command - }; -} - -function unavailableReceipt(id, metric, command, unit = "ms") { - return { - id, - metric, - value: 1, - unit, - sourceAvailability: "unavailable", - nonImplementationInput: true, - command - }; -} diff --git a/scripts/run-ace.sh b/scripts/run-ace.sh deleted file mode 100644 index 1f2bb85..0000000 --- a/scripts/run-ace.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" -repo_root="$(cd -- "${script_dir}/.." && pwd)" - -candidate_bins=() - -if [[ -n "${ACE_BIN:-}" ]]; then - candidate_bins+=("${ACE_BIN}") -fi - -candidate_bins+=( - "${repo_root}/external/ace/bin/ace" -) - -if command -v ace >/dev/null 2>&1; then - candidate_bins+=("$(command -v ace)") -fi - -candidate_bins+=( - "${HOME}/code/covibes/ace/bin/ace" - "${HOME}/code/covibes/agents/external/ace/bin/ace" - "${HOME}/code/covibes/orchestra/external/ace/bin/ace" -) - -if [[ -z "${ROBUSTNESS_ENGINE_DIR:-}" ]]; then - for robustness_engine_dir in \ - "${repo_root}/external/robustness-engine" \ - "${HOME}/code/covibes/robustness-engine" \ - "${HOME}/code/covibes/agents/external/robustness-engine" \ - "${HOME}/code/covibes/orchestra/vendor/robustness-engine"; do - if [[ -f "${robustness_engine_dir}/crates/clone-indexer/Cargo.toml" ]]; then - export ROBUSTNESS_ENGINE_DIR="${robustness_engine_dir}" - break - fi - done -fi - -for ace_bin in "${candidate_bins[@]}"; do - if [[ -x "${ace_bin}" ]]; then - exec "${ace_bin}" "$@" - fi - if [[ -f "${ace_bin}" ]]; then - exec node "${ace_bin}" "$@" - fi -done - -cat >&2 <<'EOF' -ACE CLI not found. - -Set ACE_BIN=/path/to/ace, install ace on PATH, vendor it at external/ace, -or keep the Covibes ACE checkout at ~/code/covibes/ace. -EOF -exit 127 diff --git a/scripts/run-opcore-self-check.mjs b/scripts/run-opcore-self-check.mjs new file mode 100644 index 0000000..80e8f2b --- /dev/null +++ b/scripts/run-opcore-self-check.mjs @@ -0,0 +1,84 @@ +#!/usr/bin/env node +import { spawnSync } from "node:child_process"; +import { existsSync } from "node:fs"; +import { join } from "node:path"; +import { fileURLToPath } from "node:url"; + +const repoRoot = fileURLToPath(new URL("..", import.meta.url)); +const cliPath = join(repoRoot, "packages", "opcore", "dist", "index.js"); +const requestedBase = + process.env.OPCORE_SELF_CHECK_BASE_REF ?? + process.env.OPCORE_LOCAL_CI_BASE_REF ?? + "origin/dev"; + +if (!existsSync(cliPath)) { + fail("Opcore self-check requires built packages; run npm run build first."); +} + +const baseRef = commitExists(requestedBase) ? requestedBase : "HEAD"; +const command = [ + cliPath, + "check", + "changed", + "--base", + baseRef, + "--report-mode", + "introduced", + "--json" +]; +const result = spawnSync(process.execPath, command, { + cwd: repoRoot, + encoding: "utf8", + env: process.env, + maxBuffer: 64 * 1024 * 1024 +}); + +if (result.error) fail(`Unable to run Opcore self-check: ${result.error.message}`); + +let payload; +try { + payload = JSON.parse(result.stdout); +} catch { + fail(`Opcore self-check returned malformed JSON.\n${result.stderr || result.stdout}`); +} + +const diagnostics = payload.validationResult?.diagnostics ?? []; +if (result.status !== 0 || payload.exitCode !== 0 || payload.status !== "ok" || diagnostics.length > 0) { + const summary = diagnostics + .slice(0, 20) + .map((diagnostic) => { + const location = diagnostic.path ? `${diagnostic.path}: ` : ""; + const code = diagnostic.code ? ` [${diagnostic.code}]` : ""; + return `${location}${diagnostic.message ?? "validation diagnostic"}${code}`; + }) + .join("\n"); + fail( + [ + `Opcore self-check failed against ${baseRef}.`, + `router status=${String(payload.status)} exit=${String(payload.exitCode)} process=${String(result.status)}`, + summary, + result.stderr.trim() + ] + .filter(Boolean) + .join("\n") + ); +} + +const checkCount = payload.validationResult?.manifest?.checks?.length ?? 0; +process.stdout.write( + `Opcore self-check passed against ${baseRef}: ${checkCount} checks, ${diagnostics.length} diagnostics.\n` +); + +function commitExists(ref) { + const probe = spawnSync("git", ["rev-parse", "--verify", `${ref}^{commit}`], { + cwd: repoRoot, + encoding: "utf8", + stdio: ["ignore", "ignore", "ignore"] + }); + return probe.status === 0; +} + +function fail(message) { + process.stderr.write(`${message}\n`); + process.exit(1); +} diff --git a/scripts/setup-current-tools.sh b/scripts/setup-current-tools.sh deleted file mode 100755 index d1fd4a9..0000000 --- a/scripts/setup-current-tools.sh +++ /dev/null @@ -1,319 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Current-tool invariant: wrappers execute external ACE-managed tools, never lattice packages under development. -repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)" -cd "${repo_root}" -repo_root_physical="$(pwd -P)" -runtime_root="${repo_root}/.ace/runtime" -bin_dir="${runtime_root}/bin" -tooling_json="${runtime_root}/tooling.json" -run_dir="${repo_root}/.ace/run" -rox_runtime_dir="${repo_root}/.ace/rox" -idle_ms="${LATTICE_TOOL_DAEMON_IDLE_TIMEOUT_MS:-1800000}" - -mkdir -p "${bin_dir}" "${run_dir}" "${rox_runtime_dir}" - -fail() { - printf 'error: %s\n' "$*" >&2 - exit 1 -} - -shell_quote() { - printf '%q' "$1" -} - -canonical_path() { - local path="$1" - node -e ' -const fs = require("node:fs"); - -try { - process.stdout.write(fs.realpathSync.native(process.argv[1])); -} catch { - process.exit(1); -} -' "${path}" -} - -canonical_dir() { - local path="$1" - (cd "${path}" 2>/dev/null && pwd -P) -} - -is_inside_repo() { - local path="$1" - local real_path - - real_path="$(canonical_path "${path}" 2>/dev/null || true)" - [[ -n "${real_path}" ]] || return 1 - case "${real_path}" in - "${repo_root_physical}"|"${repo_root_physical}"/*) return 0 ;; - *) return 1 ;; - esac -} - -implementation_package_dir() { - local tool_name="$1" - case "${tool_name}" in - crg) printf 'packages/graph' ;; - *) printf 'packages/%s' "${tool_name}" ;; - esac -} - -validate_source_path() { - local tool_name="$1" - local path="$2" - local implementation_path - - [[ -n "${path}" ]] || return 1 - [[ -x "${path}" ]] || fail "${tool_name} source is not executable: ${path}" - if is_inside_repo "${path}"; then - implementation_path="$(implementation_package_dir "${tool_name}")" - fail "${tool_name} source resolved inside lattice (${path}); use current external tools, not ${implementation_path}" - fi -} - -candidate_dirs=() -append_dir() { - local dir="$1" - [[ -n "${dir}" ]] || return 0 - candidate_dirs+=("${dir}") -} - -append_dir "${LATTICE_CURRENT_TOOLS_DIR:-}" -append_dir "${ACE_CURRENT_TOOLS_DIR:-}" -append_dir "${repo_root}/../covibes/.ace/runtime/bin" -append_dir "${repo_root}/../cmdproof/.ace/runtime/bin" -append_dir "${repo_root}/../robustness-engine/.ace/runtime/bin" -append_dir "${repo_root}/../orchestra/.ace/runtime/bin" -append_dir "${HOME}/code/covibes/cmdproof/.ace/runtime/bin" -append_dir "${HOME}/code/covibes/robustness-engine/.ace/runtime/bin" -append_dir "${HOME}/code/covibes/orchestra/.ace/runtime/bin" - -native_path_dirs=() -append_native_dir() { - local dir="$1" - [[ -n "${dir}" && -d "${dir}" ]] || return 0 - native_path_dirs+=("${dir}") -} - -resolve_tool() { - local tool_name="$1" - local upper_name - local env_var - local env_value - local dir - local candidate - local path_dir - local path_entries - - upper_name="$(printf '%s' "${tool_name}" | tr '[:lower:]' '[:upper:]')" - env_var="LATTICE_CURRENT_${upper_name}_PATH" - env_value="${!env_var:-}" - if [[ -n "${env_value}" ]]; then - validate_source_path "${tool_name}" "${env_value}" - canonical_path "${env_value}" - return 0 - fi - - for dir in "${candidate_dirs[@]}"; do - [[ -d "${dir}" ]] || continue - candidate="${dir}/${tool_name}" - if [[ -x "${candidate}" ]]; then - validate_source_path "${tool_name}" "${candidate}" - canonical_path "${candidate}" - return 0 - fi - done - - IFS=':' read -r -a path_entries <<< "${PATH:-}" - for path_dir in "${path_entries[@]}"; do - [[ -n "${path_dir}" ]] || path_dir="." - dir="$(canonical_dir "${path_dir}" 2>/dev/null || true)" - [[ -n "${dir}" ]] || continue - candidate="${dir}/${tool_name}" - if [[ -x "${candidate}" ]]; then - if is_inside_repo "${candidate}"; then - continue - fi - validate_source_path "${tool_name}" "${candidate}" - canonical_path "${candidate}" - return 0 - fi - done - - fail "could not resolve current external ${tool_name}; set LATTICE_CURRENT_TOOLS_DIR or LATTICE_CURRENT_${upper_name}_PATH" -} - -discover_native_tool_dir() { - local tool_name="$1" - local command_name="$2" - local dir - local candidate - - for dir in "${candidate_dirs[@]}"; do - [[ -d "${dir}" ]] || continue - candidate="${dir}/../native-tools/rox/${tool_name}/bin/${command_name}" - if [[ -x "${candidate}" ]] && "${candidate}" --help 2>/dev/null | grep -q 'Analyze source code'; then - canonical_dir "${candidate%/*}" - return 0 - fi - done - - while IFS= read -r candidate; do - if [[ -x "${candidate}" ]] && "${candidate}" --help 2>/dev/null | grep -q 'Analyze source code'; then - canonical_dir "${candidate%/*}" - return 0 - fi - done < <(find "${HOME}/.cache/ace/native-tools/rox/${tool_name}" -path "*/install/bin/${command_name}" -type f -perm -111 -print 2>/dev/null || true) -} - -write_wrapper() { - local tool_name="$1" - local source_path="$2" - local wrapper_path="${bin_dir}/${tool_name}" - local maybe_cix_root="" - local native_path="" - - if [[ "${tool_name}" == "cix" ]]; then - maybe_cix_root='export CIX_DAEMON_ROOT_DIR="${CIX_DAEMON_ROOT_DIR:-$repo_root_physical}"' - fi - if [[ "${tool_name}" == "rox" && "${#native_path_dirs[@]}" -gt 0 ]]; then - native_path="$(IFS=:; printf '%s' "${native_path_dirs[*]}")" - fi - - cat > "${wrapper_path}" < `.ace/runtime/bin/${name}`; - -const currentTool = (name, sourcePath) => ({ - available: true, - mode: "external-current-tool", - sourcePath, - wrapperPath: `${process.env.LATTICE_BIN_DIR}/${name}` -}); - -const aceTool = (name, sourcePath, options) => ({ - executablePath: relativeToolPath(name), - sourcePath, - version: null, - ready: true, - reason: null, - launcher: { - sourceStrategy: { - kind: "direct-path", - path: relativeToolPath(name) - }, - environment: { - prependPath: [".ace/runtime/bin", ...options.prependPath], - unset: [], - xdgRuntimeDir: options.xdgRuntimeDir - }, - healthProbe: { - args: ["--version"], - parser: "first-non-empty-output-line" - }, - daemonPolicy: { - retryWithoutDaemonOnVersionSkew: options.retryWithoutDaemonOnVersionSkew - } - }, - nativeDependencies: {} -}); - -const metadata = { - schemaVersion: 1, - packagingMode: "current_external_tools", - runtimeRoot: ".ace/runtime", - generatedAt: new Date().toISOString(), - generatedBy: { - name: "lattice-current-tool-setup", - version: "0.2.1" - }, - tooling: { - aceTools: { - binRoot: ".ace/runtime/bin", - tools: { - rox: aceTool("rox", process.env.LATTICE_ROX_SOURCE, { - xdgRuntimeDir: ".ace/rox", - retryWithoutDaemonOnVersionSkew: true, - prependPath: (process.env.LATTICE_ROX_NATIVE_PATH || "").split(/\s+/).filter(Boolean) - }), - crg: aceTool("crg", process.env.LATTICE_CRG_SOURCE, { - xdgRuntimeDir: null, - retryWithoutDaemonOnVersionSkew: false, - prependPath: [] - }), - cix: aceTool("cix", process.env.LATTICE_CIX_SOURCE, { - xdgRuntimeDir: ".ace/rox", - retryWithoutDaemonOnVersionSkew: false, - prependPath: [] - }) - } - } - }, - latticeCurrentTools: { - version: 1, - owner: "lattice-current-tool-setup", - root: process.env.LATTICE_REPO_ROOT, - runtimeRoot: process.env.LATTICE_RUNTIME_ROOT, - bin: process.env.LATTICE_BIN_DIR, - run: process.env.LATTICE_RUN_DIR, - roxRuntime: process.env.LATTICE_ROX_RUNTIME_DIR, - idleTimeoutMs: Number(process.env.LATTICE_IDLE_MS), - invariant: "wrappers execute current external ACE-managed tools, never lattice packages under development", - tools: { - rox: currentTool("rox", process.env.LATTICE_ROX_SOURCE), - crg: currentTool("crg", process.env.LATTICE_CRG_SOURCE), - cix: currentTool("cix", process.env.LATTICE_CIX_SOURCE) - } - } -}; - -fs.writeFileSync(process.env.LATTICE_TOOLING_JSON, `${JSON.stringify(metadata, null, 2)}\n`); -NODE - -printf 'current tool wrappers ready at %s\n' "${bin_dir}" diff --git a/tests/asp-dogfood-receipt.test.mjs b/tests/asp-dogfood-receipt.test.mjs index 4b20b01..f552926 100644 --- a/tests/asp-dogfood-receipt.test.mjs +++ b/tests/asp-dogfood-receipt.test.mjs @@ -5,7 +5,11 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { dirname, join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; -import { createAspHostFixtureRepo, runCurrentToolGuardrails } from "../scripts/asp-dogfood-receipt-support.mjs"; +import { + createAspHostFixtureRepo, + locateAspManager, + sanitizeReceiptForProvenance +} from "../scripts/asp-dogfood-receipt-support.mjs"; import { validateAspDogfoodReceipt } from "../packages/contracts/dist/index.js"; import { invalidAspDogfoodCases, validAspDogfoodReceipt } from "./helpers/asp-dogfood-fixture.mjs"; @@ -24,7 +28,7 @@ describe("ASP dogfood receipt", () => { stdio: ["ignore", "pipe", "pipe"] }); assert.equal(result.status, 0, result.stderr); - assert.equal(JSON.parse(result.stdout).oldToolReplacementClaimed, false); + assert.equal(JSON.parse(result.stdout).selfValidation.status, "passed"); } finally { rmSync(temp, { recursive: true, force: true }); } @@ -43,22 +47,6 @@ describe("ASP dogfood receipt", () => { assert.equal(validateAspDogfoodReceipt(receipt).issue, "#120"); }); - it("can reuse recorded retained current-tool guardrails for receipt refreshes", () => { - const temp = mkdtempSync(join(tmpdir(), "lattice-asp-dogfood-guardrails-test-")); - const previous = process.env.OPCORE_ASP_DOGFOOD_REUSE_CURRENT_TOOL_GUARDRAILS; - try { - mkdirSync(join(temp, "docs", "release"), { recursive: true }); - const receipt = validAspDogfoodReceipt(); - writeFileSync(join(temp, "docs", "release", "asp-dogfood-receipt.json"), `${JSON.stringify(receipt, null, 2)}\n`); - process.env.OPCORE_ASP_DOGFOOD_REUSE_CURRENT_TOOL_GUARDRAILS = "1"; - assert.deepEqual(runCurrentToolGuardrails(temp, true), receipt.currentToolGuardrails); - } finally { - if (previous === undefined) delete process.env.OPCORE_ASP_DOGFOOD_REUSE_CURRENT_TOOL_GUARDRAILS; - else process.env.OPCORE_ASP_DOGFOOD_REUSE_CURRENT_TOOL_GUARDRAILS = previous; - rmSync(temp, { recursive: true, force: true }); - } - }); - it("creates an isolated changed fixture repo for clean-tree host dogfood", () => { const temp = mkdtempSync(join(tmpdir(), "lattice-asp-dogfood-fixture-test-")); try { @@ -78,4 +66,44 @@ describe("ASP dogfood receipt", () => { rmSync(temp, { recursive: true, force: true }); } }); + + it("locates an explicit ASP manager checkout and redacts its root", () => { + const temp = mkdtempSync(join(tmpdir(), "opcore-asp-manager-test-")); + const previous = process.env.ASP_DOGFOOD_ASP_REPO; + try { + const bin = join(temp, "packages", "asp", "bin", "asp"); + const cli = join(temp, "packages", "asp", "dist", "cli.js"); + mkdirSync(dirname(bin), { recursive: true }); + mkdirSync(dirname(cli), { recursive: true }); + writeFileSync(bin, "#!/usr/bin/env node\n"); + writeFileSync(cli, "export {};\n"); + runGit(temp, ["init", "-q"]); + runGit(temp, ["config", "user.name", "Opcore Test"]); + runGit(temp, ["config", "user.email", "opcore@example.invalid"]); + runGit(temp, ["add", "."]); + runGit(temp, ["commit", "-qm", "fixture"]); + + process.env.ASP_DOGFOOD_ASP_REPO = temp; + const manager = locateAspManager(repoRoot); + assert.equal(manager.aspRepoPath, temp); + assert.equal(manager.aspBinPath, bin); + assert.deepEqual( + sanitizeReceiptForProvenance({ root: temp, bin }, manager.aspRepoPath), + { root: "", bin: "/packages/asp/bin/asp" } + ); + } finally { + if (previous === undefined) delete process.env.ASP_DOGFOOD_ASP_REPO; + else process.env.ASP_DOGFOOD_ASP_REPO = previous; + rmSync(temp, { recursive: true, force: true }); + } + }); }); + +function runGit(cwd, args) { + const result = spawnSync("git", args, { + cwd, + encoding: "utf8", + stdio: ["ignore", "pipe", "pipe"] + }); + assert.equal(result.status, 0, result.stderr); +} diff --git a/tests/asp-provider.test.mjs b/tests/asp-provider.test.mjs index 9c875d6..a96cbe0 100644 --- a/tests/asp-provider.test.mjs +++ b/tests/asp-provider.test.mjs @@ -874,7 +874,6 @@ describe("Opcore ASP provider", () => { assert.equal(manifest.noGateGrant, true); assert.match(manifest.checksums["dist/index.js"].sha256, /^[a-f0-9]{64}$/); assertNoForbiddenKeys(manifest); - assert.doesNotMatch(JSON.stringify(manifest), /\.ace\/runtime|\b(?:rox|crg|cix)\b|LATTICE_CURRENT_TOOLS_DIR/i); }); it("ships a canonical ASP server manifest with read-only access expectations", () => { @@ -928,7 +927,6 @@ describe("Opcore ASP provider", () => { }); assertNoForbiddenKeys(manifest); assert.doesNotMatch(JSON.stringify(manifest.provenance), /\b(?:trust|authority|gate|apply|decision|verdict|assurance)\b/i); - assert.doesNotMatch(JSON.stringify(manifest), /\.ace\/runtime|\b(?:rox|crg|cix)\b|LATTICE_CURRENT_TOOLS_DIR/i); }); it("removes stale legacy generated manifests before packaging", () => { @@ -995,7 +993,7 @@ describe("Opcore ASP provider claim scrub", () => { assert.match(readme, /\bwrite\b[^.\n]*\bfalse\b/i, "README must state write is false"); assert.match(readme, /\bnetwork\b[^.\n]*\bfalse\b/i, "README must state network is false"); assert.match(readme, /degraded|unsupported/i, "README must describe degraded/unsupported coverage honesty"); - assert.match(readme, /does \*\*not\*\* use ACE|not use ACE as a carrier/i, "README must disclaim ACE as carrier/provisioner"); + assert.match(readme, /does not invoke external development toolchains/i); }); it("rejects forbidden marketing tokens in package.json metadata", () => { diff --git a/tests/cli-canonical-surface.test.mjs b/tests/cli-canonical-surface.test.mjs index 35b385c..d578d75 100644 --- a/tests/cli-canonical-surface.test.mjs +++ b/tests/cli-canonical-surface.test.mjs @@ -1,7 +1,7 @@ import { describe, it } from "node:test"; import assert from "node:assert/strict"; import { spawnSync } from "node:child_process"; -import { cpSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs"; +import { cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; @@ -135,14 +135,12 @@ describe("canonical CLI surface", () => { assert.equal((await run(["graph", "inspect", "--json"], 64)).status, "unsupported"); }); - it("rejects direct old entrypoint execution through the router", async () => { - for (const bin of ["lattice", "crg", "cix", "rox"]) { - const result = await run(["status", "--json"], 64, bin); - assert.equal(result.status, "unsupported"); - assert.deepEqual(result.canonicalCommand, ["opcore", "unsupported"]); - assert.equal(Object.hasOwn(result, "alias"), false); - assert.equal(Object.hasOwn(result, removedLegacyCommandField), false); - } + it("rejects non-Opcore entrypoint execution through the router", async () => { + const result = await run(["status", "--json"], 64, "other-tool"); + assert.equal(result.status, "unsupported"); + assert.deepEqual(result.canonicalCommand, ["opcore", "unsupported"]); + assert.equal(Object.hasOwn(result, "alias"), false); + assert.equal(Object.hasOwn(result, removedLegacyCommandField), false); }); it("keeps opcore status on validationStatus without repoState", async () => { diff --git a/tests/command-router.test.mjs b/tests/command-router.test.mjs index 137ee23..5045e20 100644 --- a/tests/command-router.test.mjs +++ b/tests/command-router.test.mjs @@ -1,8 +1,6 @@ import { describe, it } from "node:test"; import assert from "node:assert/strict"; -import { spawnSync } from "node:child_process"; -import { DatabaseSync } from "node:sqlite"; -import { cpSync, existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { cpSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; @@ -100,16 +98,14 @@ describe("Opcore command router", () => { }); it("rejects non-Opcore entrypoints without alias metadata", async () => { - for (const bin of ["lattice", "crg", "cix", "rox"]) { - const routed = await routeCommand(["status", "--json"], bin); - assert.equal(routed.status, "unsupported"); - assert.equal(routed.exitCode, 64); - assert.deepEqual(routed.canonicalCommand, ["opcore", "unsupported"]); - assert.equal(routed.owner, "runtime"); - assert.equal(Object.hasOwn(routed, "alias"), false); - assert.equal(Object.hasOwn(routed, removedLegacyCommandField), false); - assertCommandTiming(routed); - } + const routed = await routeCommand(["status", "--json"], "other-tool"); + assert.equal(routed.status, "unsupported"); + assert.equal(routed.exitCode, 64); + assert.deepEqual(routed.canonicalCommand, ["opcore", "unsupported"]); + assert.equal(routed.owner, "runtime"); + assert.equal(Object.hasOwn(routed, "alias"), false); + assert.equal(Object.hasOwn(routed, removedLegacyCommandField), false); + assertCommandTiming(routed); }); it("routes canonical graph status, query, and search through the graph adapter", async () => { @@ -380,7 +376,7 @@ describe("Opcore command router", () => { assert.equal(helpJson.exitCode, 0); assert.equal(doctorJson.runtimeInfo.packageName, "opcore"); assert.equal(doctorJson.opcoreDoctor.schemaVersion, 1); - assert.equal(doctorJson.opcoreDoctor.config.state, "missing"); + assert.equal(doctorJson.opcoreDoctor.config.state, "found"); assert.equal(doctorJson.opcoreDoctor.checks.count > 0, true); assert.match(doctorJson.opcoreDoctor.generatedState.guidance, /\.opcore/); assertCommandTiming(statusJson); @@ -502,16 +498,3 @@ async function withFixtureCopy(runFixture) { function skipGeneratedStore(source) { return !source.includes(`${join(".opcore", "graph")}`); } - -function corruptSnapshotSchema(fixtureRoot) { - const db = new DatabaseSync(join(fixtureRoot, ".opcore/graph/graph.db")); - try { - const metadata = JSON.parse(db.prepare("select value from metadata where key = 'lattice_snapshot_metadata'").get().value); - metadata.schemaVersion = 2; - const value = JSON.stringify(metadata); - db.prepare("update metadata set value = ? where key = 'lattice_snapshot_metadata'").run(value); - db.prepare("update lattice_store set value = ? where key = 'metadata_json'").run(value); - } finally { - db.close(); - } -} diff --git a/tests/conformance.test.mjs b/tests/conformance.test.mjs index 3c8d1c7..9bbd970 100644 --- a/tests/conformance.test.mjs +++ b/tests/conformance.test.mjs @@ -29,11 +29,6 @@ const expectedIds = [ "inspect-symbol-parity-v1", "validation-contract-v1", "installed-artifact-smoke-v1", - "graph-reference-evidence-manifest-v1", - "graph-reference-evidence-sqlite-fixtures-v1", - "graph-reference-evidence-daemon-socket-fixtures-v1", - "graph-reference-evidence-golden-corpus-v1", - "graph-reference-evidence-baseline-receipts-v1", "graph-release-readiness-v1" ]; @@ -92,9 +87,7 @@ describe("conformance fixture metadata", () => { ? "#17" : fixture.id === "descriptor-discovery-v1" || fixture.id === "installed-artifact-smoke-v1" ? "#28" - : fixture.id.startsWith("graph-reference-evidence-") - ? "#19" - : "#3" + : "#3" ); assert.equal(fixture.schemaVersion, 1); assert.notEqual(fixture.status, "placeholder"); @@ -205,7 +198,7 @@ describe("conformance fixture metadata", () => { ["#13", "#14", "#15", "#16"] ); const text = JSON.stringify(descriptor); - assert.doesNotMatch(text, /(^|[\\/"'\s])\.ace(?:[\\/"'\s]|$)|LATTICE_CURRENT_TOOLS_DIR|\/Users\/tom|(^|[\\/\s])(?:lattice|crg|cix|rox)(?:$|[\\/\s])/i); + assert.doesNotMatch(text, /\/Users\/tom/i); }); it("describes canonical router metadata for descriptor planning", () => { @@ -344,7 +337,7 @@ describe("conformance fixture metadata", () => { assert.deepEqual(graphServe.protocols, ["opcore.graph.daemon", "jsonrpc-2.0"]); assert.deepEqual(graphServe.operations, ["ping", "status", "query", "search", "shutdown"]); assert.ok(graphServe.failureStates.includes("schema_mismatch")); - assert.equal(graphServe.dataFile, "packages/fixtures/graph-reference-evidence/daemon-socket-fixtures.json"); + assert.equal(graphServe.dataFile, "packages/fixtures/graph-serve/serve-fixtures.json"); assert.ok(serveFixture.envelopes.some((entry) => entry.id === "serve-jsonl-ping")); assert.ok(serveFixture.envelopes.some((entry) => entry.id === "mcp-initialize")); }); @@ -411,21 +404,6 @@ describe("conformance fixture metadata", () => { assert.deepEqual(validation.degradedTools, ["mypy", "pyright", "ruff", "pytest"]); }); - it("includes concrete source-free #19 reference evidence data files", () => { - for (const id of expectedIds.filter((entry) => entry.startsWith("graph-reference-evidence-"))) { - const fixture = fixtureById(id); - assert.equal(fixture.issue, "#19"); - assert.equal(fixture.packageTrack, "fixtures"); - assert.equal(fixture.containsSourceCode, false); - assert.equal(fixture.origin, "covibes-authored-synthetic"); - assert.ok(fixture.dataFile?.startsWith("packages/fixtures/graph-reference-evidence/"), `bad dataFile ${fixture.dataFile}`); - const url = new URL(`../${fixture.dataFile}`, import.meta.url); - const content = readFileSync(url, "utf8"); - assert.ok(JSON.parse(content)); - assert.doesNotMatch(content, /tirth8205|pyproject\.toml|setup\.py|setup\.cfg|Pipfile|git clone/i); - } - }); - it("describes #17 graph release readiness metadata", () => { const graphRelease = fixtureById("graph-release-readiness-v1").graphRelease; const receipt = JSON.parse(readFileSync(graphRelease.receipt, "utf8")); diff --git a/tests/contracts.test.mjs b/tests/contracts.test.mjs index 7a55c32..df774e7 100644 --- a/tests/contracts.test.mjs +++ b/tests/contracts.test.mjs @@ -17,7 +17,6 @@ import { createCommandRouterResult, editRefusalCategories, aspDogfoodForbiddenProviderMarkers, - aspDogfoodGuardrailIds, graphReleaseBenchmarkMetrics, graphCoreNativePackageNameForTarget, graphCoreNativeSupportedTargets, @@ -31,14 +30,13 @@ import { opcoreMeasureLatencyFindingStatuses, opcoreMeasureLatencyStatuses, releaseReceiptCommandGroups, - releaseCutoverCurrentToolGuardrailIds, releaseCutoverNegativeCheckIds, releaseCutoverPythonCommandIds, releaseCutoverRustCommandIds, releaseReceiptPackageNames, releaseReceiptReportIds, requiredContextDocPolicy, - rustOldRoxComparisonSurfaceIds, + graphReleaseSurfaceClassifications, graphDaemonOperations, graphExtractionDiagnosticCategories, graphFactQueryKinds, @@ -48,7 +46,6 @@ import { graphSnapshotMetadataKeys, inspectFailureCategories, providerFailureCategories, - graphReferenceEvidenceClassifications, parseCommandArgv, requiredGraphEdgeKinds, requiredGraphNodeKinds, @@ -76,13 +73,11 @@ import { validateGraphProviderCapabilityHandshake, validateGraphReleaseReceipt, validateAspDogfoodReceipt, - validateRustOldRoxComparisonReceipt, validateReleaseCutoverReceipt, validateReleaseReceipt, validateGraphSearchRequest, validateGraphSearchResult, validateGraphServeTransportStatus, - validateGraphReferenceEvidenceManifest, validateInspectRouteResult, validateManagedToolDescriptor, validateLatencyBudget, @@ -301,18 +296,6 @@ describe("Opcore shared contracts", () => { "python-relevant-tests-no-pytest", "python-toolchain-degraded-no-tools" ]); - assert.deepEqual(releaseCutoverCurrentToolGuardrailIds, [ - "current-tools-validate-changed", - "current-tools-validate-rust-graph" - ]); - assert.deepEqual(rustOldRoxComparisonSurfaceIds, [ - "rust.rustdoc", - "rust.import-graph", - "rust.dead-code", - "rust.unused-deps", - "rust.function-metrics", - "current-tools:validate-rust-graph" - ]); assert.deepEqual(graphReleaseBenchmarkMetrics, [ "install_setup_ms", "cold_build_ms", @@ -338,7 +321,7 @@ describe("Opcore shared contracts", () => { "children_of", "file_summary" ]); - assert.deepEqual(graphReferenceEvidenceClassifications, ["required", "supporting", "optional", "deferred"]); + assert.deepEqual(graphReleaseSurfaceClassifications, ["required", "supporting", "optional", "deferred"]); assert.deepEqual(graphReleaseDeferredChildren, ["#13", "#14", "#15", "#16"]); assert.deepEqual(graphReleaseOptionalAnalysisSurfaces, expectedOptionalAnalysisSurfaces()); }); @@ -2301,7 +2284,7 @@ describe("Opcore shared contracts", () => { assert.deepEqual(surfaceContracts(descriptor.optionalSurfaces), expectedOptionalAnalysisSurfaces()); }); - it("rejects managed-tool descriptors with old aliases or unsafe package paths", () => { + it("rejects managed-tool descriptors with noncanonical entrypoints or unsafe package paths", () => { assert.throws( () => validateManagedToolDescriptor({ @@ -2309,11 +2292,11 @@ describe("Opcore shared contracts", () => { entrypoints: [ { ...validManagedToolDescriptor().entrypoints[0], - bin: ["c", "r", "g"].join("") + bin: "unexpected" } ] }), - /old public aliases/ + /entrypoint bins/ ); assert.throws( () => @@ -2322,7 +2305,7 @@ describe("Opcore shared contracts", () => { artifacts: [ { ...validManagedToolDescriptor().artifacts[0], - path: "/tmp/lattice" + path: "/tmp/tool" } ] }), @@ -2348,7 +2331,7 @@ describe("Opcore shared contracts", () => { artifacts: [ { ...validManagedToolDescriptor().artifacts[0], - path: ".ace/runtime/bin/lattice" + path: ".agents/runtime/bin/tool" } ] }), @@ -2361,7 +2344,7 @@ describe("Opcore shared contracts", () => { artifacts: [ { ...validManagedToolDescriptor().artifacts[0], - path: ".ace" + path: ".agents" } ] }), @@ -2374,7 +2357,7 @@ describe("Opcore shared contracts", () => { artifacts: [ { ...validManagedToolDescriptor().artifacts[0], - path: "dist/.ace" + path: "dist/.agents" } ] }), @@ -2387,12 +2370,12 @@ describe("Opcore shared contracts", () => { provenanceHooks: [ { id: "private-runtime-wrapper", - command: [".ace\\runtime\\bin\\lattice", "status"], + command: [".agents\\runtime\\bin\\tool", "status"], expectedExitCode: 0 } ] }), - /current-tool runtime paths/ + /private runtime paths/ ); assert.throws( () => @@ -2401,7 +2384,7 @@ describe("Opcore shared contracts", () => { artifacts: [ { ...validManagedToolDescriptor().artifacts[0], - path: "~/lattice" + path: "~/tool" } ] }), @@ -2685,69 +2668,6 @@ describe("Opcore shared contracts", () => { ); }); - it("accepts graph reference evidence manifests and rejects invalid coverage", () => { - const manifest = validGraphReferenceEvidenceManifest(); - assert.equal(validateGraphReferenceEvidenceManifest(manifest).issue, "#19"); - assert.deepEqual(surfaceContracts(manifest.optionalAnalysisSurfaces), expectedOptionalAnalysisSurfaces()); - assert.throws( - () => - validateGraphReferenceEvidenceManifest({ - ...manifest, - commandSurfaces: [ - { - ...manifest.commandSurfaces[0], - classification: "release_blocking" - } - ] - }), - /Unknown graph reference evidence surface classification/ - ); - assert.throws( - () => - validateGraphReferenceEvidenceManifest({ - ...manifest, - commandSurfaces: [ - { - ...manifest.commandSurfaces[0], - fixtures: [] - } - ] - }), - /required surface must include fixture coverage/ - ); - assert.throws( - () => - validateGraphReferenceEvidenceManifest({ - ...manifest, - optionalAnalysisSurfaces: manifest.optionalAnalysisSurfaces.map((surface) => - surface.id === "flows" ? { ...surface, issue: "#13" } : surface - ) - }), - /optional analysis surfaces must match staged graph release surfaces/ - ); - assert.throws( - () => - validateGraphReferenceEvidenceManifest({ - ...manifest, - optionalAnalysisSurfaces: manifest.optionalAnalysisSurfaces.map((surface) => - surface.id === "flows" ? { ...surface, classification: "required" } : surface - ) - }), - /optional analysis surfaces must not mark staged graph release surfaces as required/ - ); - assert.throws( - () => - validateGraphReferenceEvidenceManifest({ - ...manifest, - provenance: { - ...manifest.provenance, - containsPythonCrgSource: true - } - }), - /must not contain Python CRG source/ - ); - }); - it("accepts graph release receipts and rejects incomplete or tainted release evidence", () => { const receipt = validGraphReleaseReceipt(); assert.equal(validateGraphReleaseReceipt(receipt).issue, "#17"); @@ -2809,6 +2729,10 @@ describe("Opcore shared contracts", () => { }), /Graph release serve transport ids must exactly match/ ); + }); + + it("rejects tainted graph release transport, package, and handoff evidence", () => { + const receipt = validGraphReleaseReceipt(); assert.throws( () => validateGraphReleaseReceipt({ @@ -2942,13 +2866,13 @@ describe("Opcore shared contracts", () => { entry.packageName === "opcore" ? { ...entry, - bins: { ...entry.bins, crg: "dist/index.js" }, - manifest: { ...entry.manifest, bins: { ...entry.manifest.bins, crg: "dist/index.js" } } + bins: { ...entry.bins, unexpected: "dist/index.js" }, + manifest: { ...entry.manifest, bins: { ...entry.manifest.bins, unexpected: "dist/index.js" } } } : entry ) }), - /old public bin/ + /Opcore package bins/ ); assert.throws( () => @@ -3001,11 +2925,7 @@ describe("Opcore shared contracts", () => { receipt.negativeChecks.map((entry) => entry.id), releaseCutoverNegativeCheckIds ); - assert.deepEqual( - receipt.currentToolGuardrails.map((entry) => entry.id), - releaseCutoverCurrentToolGuardrailIds - ); - assert.equal(receipt.oldToolReplacementClaimed, false); + assert.equal(receipt.selfValidation.status, "passed"); assert.throws( () => validateReleaseCutoverReceipt({ @@ -3054,6 +2974,10 @@ describe("Opcore shared contracts", () => { }), /canonical .* bin/ ); + }); + + it("rejects incomplete cutover language and command evidence", () => { + const receipt = validReleaseCutoverReceipt(); assert.throws( () => validateReleaseCutoverReceipt({ @@ -3103,7 +3027,11 @@ describe("Opcore shared contracts", () => { ...receipt, pythonCommandReceipts: receipt.pythonCommandReceipts.map((entry) => entry.id === "graph-python-search" - ? { ...entry, command: ["lattice", "graph", "search", "Greeting"], canonicalCommand: ["lattice", "graph", "search", "Greeting"] } + ? { + ...entry, + command: ["node", "graph", "search", "Greeting"], + canonicalCommand: ["node", "graph", "search", "Greeting"] + } : entry ) }), @@ -3121,6 +3049,10 @@ describe("Opcore shared contracts", () => { }), /graph-python-query evidence/ ); + }); + + it("rejects incomplete cutover policy and self-validation evidence", () => { + const receipt = validReleaseCutoverReceipt(); assert.throws( () => validateReleaseCutoverReceipt({ @@ -3154,7 +3086,7 @@ describe("Opcore shared contracts", () => { ...receipt, negativeChecks: receipt.negativeChecks.map((entry) => entry.id === "missing-required-graph-check" - ? { ...entry, command: ["lattice", "check", "files", "src/index.ts", "--graph-mode", "required"] } + ? { ...entry, command: ["node", "check", "files", "src/index.ts", "--graph-mode", "required"] } : entry ) }), @@ -3164,40 +3096,9 @@ describe("Opcore shared contracts", () => { () => validateReleaseCutoverReceipt({ ...receipt, - currentToolGuardrails: receipt.currentToolGuardrails.filter((entry) => entry.id !== "current-tools-validate-changed") + selfValidation: { ...receipt.selfValidation, status: "failed", exitCode: 1 } }), - /current-tool guardrails/ - ); - assert.throws( - () => - validateReleaseCutoverReceipt({ - ...receipt, - currentToolGuardrails: receipt.currentToolGuardrails.map((entry) => - entry.id === "current-tools-validate-changed" - ? { ...entry, status: "retained-not-run", exitCode: null } - : entry - ) - }), - /status must be passed/ - ); - assert.throws( - () => - validateReleaseCutoverReceipt({ - ...receipt, - environmentIsolation: { - ...receipt.environmentIsolation, - oldBinsAbsent: { crg: true, cix: true, rox: true } - } - }), - /old public bins/ - ); - assert.throws( - () => - validateReleaseCutoverReceipt({ - ...receipt, - oldToolReplacementClaimed: true - }), - /old-tool replacement/ + /self-validation status must be passed/ ); assert.throws( () => @@ -3209,84 +3110,14 @@ describe("Opcore shared contracts", () => { ); }); - it("accepts old-Rox comparison receipts and rejects replacement overclaims", () => { - const receipt = validRustOldRoxComparisonReceipt(); - assert.equal(validateRustOldRoxComparisonReceipt(receipt).oldToolReplacementClaimed, false); - assert.deepEqual( - receipt.surfaces.map((entry) => entry.id), - rustOldRoxComparisonSurfaceIds - ); - assert.equal(receipt.surfaces.every((entry) => ["retained", "deferred"].includes(entry.replacementStatus)), true); - assert.throws( - () => - validateRustOldRoxComparisonReceipt({ - ...receipt, - surfaces: receipt.surfaces.filter((entry) => entry.id !== "rust.dead-code") - }), - /old-Rox comparison surfaces/ - ); - assert.throws( - () => - validateRustOldRoxComparisonReceipt({ - ...receipt, - oldToolReplacementClaimed: true - }), - /must not claim old-tool replacement/ - ); - assert.throws( - () => - validateRustOldRoxComparisonReceipt({ - ...receipt, - publicReleaseActions: ["publish"] - }), - /public release actions/ - ); - assert.throws( - () => - validateRustOldRoxComparisonReceipt({ - ...receipt, - surfaces: receipt.surfaces.map((entry) => - entry.id === "rust.function-metrics" ? { ...entry, replacementStatus: "replaced" } : entry - ) - }), - /replacementStatus/ - ); - assert.throws( - () => - validateRustOldRoxComparisonReceipt({ - ...receipt, - surfaces: receipt.surfaces.map((entry) => - entry.id === "rust.import-graph" ? { ...entry, graphEvidenceExists: true, graphEvidence: [] } : entry - ) - }), - /graph evidence/ - ); - const artifact = JSON.parse( - readFileSync(new URL("../docs/validation/rust-old-rox-comparison-receipt-2026-06-27.json", import.meta.url), "utf8") - ); - assert.equal(validateRustOldRoxComparisonReceipt(artifact).oldToolReplacementClaimed, false); - assert.deepEqual( - artifact.surfaces.map((entry) => entry.id), - rustOldRoxComparisonSurfaceIds - ); - }); - - it("accepts ASP dogfood receipts and rejects authority, entrypoint, parity, and guardrail overclaims", () => { + it("accepts ASP dogfood receipts and rejects authority, entrypoint, and parity overclaims", () => { const receipt = validAspDogfoodReceipt(); assert.equal(validateAspDogfoodReceipt(receipt).issue, "#120"); assert.equal(receipt.bootstrapSource, "local-sibling"); assert.deepEqual(receipt.provider.command, ["opcore-asp-provider", "--stdio"]); assert.equal(receipt.hostFixture.sourceRepoMutated, false); assert.deepEqual(receipt.hostFixture.changedPaths, ["src/dogfood.ts"]); - assert.deepEqual(receipt.currentToolGuardrails.map((entry) => entry.id), aspDogfoodGuardrailIds); - assert.throws( - () => - validateAspDogfoodReceipt({ - ...receipt, - currentToolGuardrails: receipt.currentToolGuardrails.filter((entry) => entry.id !== "current-tools-validate-rust-graph") - }), - /guardrail ids/ - ); + assert.equal(receipt.selfValidation.status, "passed"); assert.throws( () => validateAspDogfoodReceipt({ @@ -3303,12 +3134,9 @@ describe("Opcore shared contracts", () => { () => validateAspDogfoodReceipt({ ...receipt, - provider: { - ...receipt.provider, - binPath: ".ace/runtime/bin/opcore-asp-provider" - } + provider: { ...receipt.provider, binPath: "private/runtime/opcore-asp-provider" } }), - /node_modules\/\.bin\/opcore-asp-provider|forbidden marker/ + /node_modules\/\.bin\/opcore-asp-provider/ ); assert.throws( () => @@ -3352,6 +3180,10 @@ describe("Opcore shared contracts", () => { }), /host-owned field|host-owned decision|hostOwnedFieldLeak/ ); + }); + + it("rejects ASP dogfood parity and host-authority overclaims", () => { + const receipt = validAspDogfoodReceipt(); assert.throws( () => validateAspDogfoodReceipt({ @@ -4879,130 +4711,6 @@ function validOpcoreDoctor() { }; } -function validGraphReferenceEvidenceManifest() { - return { - schemaVersion: 1, - issue: "#19", - origin: "covibes-authored-synthetic", - fixtureRefs: [ - "packages/fixtures/graph-reference-evidence/sqlite-fixtures.json", - "packages/fixtures/graph-reference-evidence/daemon-socket-fixtures.json", - "packages/fixtures/graph-reference-evidence/golden-corpus.json", - "packages/fixtures/graph-reference-evidence/baseline-receipts.json" - ], - commandSurfaces: [ - { - id: "graph-reference-status", - classification: "required", - referenceTool: "current external graph dev wrapper", - referenceCommand: ["status"], - canonicalCommand: ["opcore", "graph", "status"], - flags: ["--repo", "--json"], - positionals: [], - fixtures: ["status-json"], - exitSemantics: { - success: 0, - failure: "nonzero" - } - } - ], - jsonOutputSurfaces: [ - { - id: "status-json", - command: "status", - classification: "required", - requiredFields: ["status", "summary"], - fixtures: ["status-json"], - exitSemantics: { - success: 0, - failure: "nonzero" - } - } - ], - sqliteFixtures: [ - { - id: "sqlite-required-views", - classification: "required", - fixture: "packages/fixtures/graph-reference-evidence/sqlite-fixtures.json", - tables: ["metadata", "nodes", "edges"], - indexes: ["idx_nodes_file"], - metadataKeys: ["schema_version"], - nodeKinds: ["File", "Function", "Test", "Module", "Struct", "Enum", "Trait", "Impl", "Method", "TypeAlias", "Const", "Static", "Macro"], - edgeKinds: ["CALLS", "CONTAINS", "IMPORTS_FROM", "TESTED_BY", "IMPLEMENTS", "DEPENDS_ON", "INHERITS"], - directReaderQueries: ["status-counts"], - fixtures: ["sqlite-fixtures"] - } - ], - daemonFixtures: [ - { - id: "daemon-hot-query", - classification: "required", - fixture: "packages/fixtures/graph-reference-evidence/daemon-socket-fixtures.json", - protocol: "opcore.graph.daemon", - envelopes: ["ping-request", "success-response"], - fixtures: ["daemon-fixtures"] - } - ], - baselineReceipts: [ - { - id: "install-setup", - metric: "install_setup_ms", - classification: "required", - receipt: "packages/fixtures/graph-reference-evidence/baseline-receipts.json", - label: "reference_evidence_non_implementation_input", - sourceAvailability: "unavailable", - nonImplementationInput: true, - fixtures: ["baseline-receipts"] - } - ], - optionalAnalysisSurfaces: [ - { - issue: "#13", - id: "coverage", - classification: "deferred", - status: "deferred", - fixtures: ["coverage-deferred-marker"] - }, - { - issue: "#14", - id: "flows", - classification: "optional", - status: "deferred", - fixtures: ["sqlite-fixtures"] - }, - { - issue: "#15", - id: "communities", - classification: "optional", - status: "deferred", - fixtures: ["sqlite-fixtures"] - }, - { - issue: "#16", - id: "read_only_suggestions", - classification: "supporting", - status: "deferred", - fixtures: ["read-only-refactor-baseline"] - } - ], - goldenCorpus: { - id: "graph-reference-evidence-golden-corpus-v1", - classification: "required", - fixture: "packages/fixtures/graph-reference-evidence/golden-corpus.json", - covers: ["parser", "store", "query", "search", "freshness", "status"], - fixtures: ["golden-corpus"] - }, - provenance: { - containsPythonCrgSource: false, - containsPackageMetadata: false, - containsGitHistory: false, - referenceReceiptsAreImplementationInput: false, - implementationPackageNames: ["@the-open-engine/opcore-graph"], - allowedMentionPaths: ["docs/graph-reference-evidence/", "packages/fixtures/graph-reference-evidence/"] - } - }; -} - function validGraphReleaseReceipt() { const commandCoverage = graphReleaseCoreCommandIds.map((id) => { const command = id.replace("opcore-graph-", ""); @@ -5100,7 +4808,7 @@ function validGraphReleaseReceipt() { value: 1, unit: metric.endsWith("_bytes") ? "bytes" : "ms", baselineIssue: "#19", - baselineReceipt: "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + baselineReceipt: "docs/release/graph-release-receipt.json", comparison: "recorded" })), packageInspection: { @@ -5111,10 +4819,10 @@ function validGraphReleaseReceipt() { forbiddenMarkersAbsent: true, generatedBuildMetadataAbsent: true, privatePathsAbsent: true, - pythonCrgSourceAbsent: true, - pythonGraphPackageMetadataAbsent: true, - pythonCrgGitHistoryAbsent: true, - forbiddenImplementationPackageNamesAbsent: true, + sourceProvenanceAbsent: true, + packageMetadataAbsent: true, + gitHistoryAbsent: true, + foreignImplementationNamesAbsent: true, inspections: ["npm-pack-dry-run"] }, supportedNativeTargets: graphCoreNativeSupportedTargets, @@ -5197,7 +4905,7 @@ function validGraphReleaseReceipt() { issue, receiptPath: "docs/release/graph-release-receipt.payload.json", checksumSha256: "b".repeat(64), - rollbackNote: "Keep ACE wrappers on current external tools if receipt regresses." + rollbackNote: "Block release and repair Opcore self-validation if this receipt regresses." })) }; } @@ -5569,18 +5277,9 @@ function validReleaseCutoverReceipt() { resolvedChecksums: descriptor.resolvedChecksums }, environmentIsolation: { - currentToolEnvCleared: true, - clearedEnvVarCount: 5, pathSanitized: true, - aceRuntimeBinExcluded: true, - siblingCovibesExcluded: true, - opcoreBinOnly: true, - oldBinsAbsent: { - lattice: true, - crg: true, - cix: true, - rox: true - } + siblingRepositoriesExcluded: true, + opcoreBinsVerified: true }, commandReceipts, rustCommandReceipts, @@ -5629,35 +5328,19 @@ function validReleaseCutoverReceipt() { assertion: "missing Python toolchain stayed degraded" } ], - currentToolGuardrails: [ - { - id: "current-tools-validate-changed", - command: ["npm", "run", "current-tools:validate-changed"], - status: "passed", - exitCode: 0, - stdoutSha256: "7".repeat(64), - stderrSha256: "8".repeat(64), - retained: true, - assertion: "retained changed-file guardrail", - oldToolReplacementClaimed: false - }, - { - id: "current-tools-validate-rust-graph", - command: ["npm", "run", "current-tools:validate-rust-graph"], - status: "passed", - exitCode: 0, - stdoutSha256: "7".repeat(64), - stderrSha256: "8".repeat(64), - retained: true, - assertion: "retained Rust graph guardrail", - oldToolReplacementClaimed: false - } - ], - oldToolReplacementClaimed: false, + selfValidation: { + id: "opcore-self-check", + command: ["npm", "run", "opcore:self-check"], + status: "passed", + exitCode: 0, + stdoutSha256: "7".repeat(64), + stderrSha256: "8".repeat(64), + assertion: "Opcore self-validation passed" + }, forbiddenMarkerScan: { scannedTextCount: 12, findingCount: 0, - markersBlocked: ["private-runtime", "current-tool-env", "private-home", "old-tool-bins"] + markersBlocked: ["private-home", "launch-claim"] }, inputEvidence: [ { @@ -5692,72 +5375,6 @@ function pythonCutoverEvidence(id) { }[id]; } -function validRustOldRoxComparisonReceipt() { - const surface = (id, graphEvidenceExists, graphEvidence, stillUniquelyProvidedByCurrentTools, replacementStatus = "retained") => ({ - id, - graphEvidenceExists, - graphEvidence, - stillUniquelyProvidedByCurrentTools, - replacementStatus - }); - return { - schemaVersion: 1, - issue: "#29", - origin: "covibes-authored-old-rox-comparison", - generatedAt: "2026-06-27T00:00:00.000Z", - privateRepo: true, - oldToolReplacementClaimed: false, - publicReleaseActions: [], - surfaces: [ - surface( - "rust.rustdoc", - false, - ["No graph fact replaces rustdoc diagnostics."], - ["rustdoc diagnostics and broken intra-doc link policy remain current-tool evidence."] - ), - surface( - "rust.import-graph", - true, - ["Rust graph emits IMPORTS_FROM and DEPENDS_ON edges for module files."], - ["Rox/current tooling still uniquely provides rustdoc and cargo-depgraph-enriched import checks."], - "deferred" - ), - surface( - "rust.dead-code", - true, - ["Rust graph emits exported symbol metadata and graph-backed dead public export signals."], - ["Cargo dead_code diagnostics and retained Rox gate behavior still uniquely cover compiler reachability."] - ), - surface( - "rust.unused-deps", - false, - ["No graph fact replaces cargo-udeps unused dependency analysis."], - ["cargo-udeps/Rox unused dependency detection remains current-tool evidence."] - ), - surface( - "rust.function-metrics", - true, - ["Rust graph emits symbol spans and signatures for functions and methods."], - ["rust-code-analysis complexity and parameter thresholds remain current-tool evidence."] - ), - surface( - "current-tools:validate-rust-graph", - false, - ["Graph receipts do not replace the aggregate current-tools Rust graph gate."], - ["npm run current-tools:validate-rust-graph remains the retained aggregate guardrail."] - ) - ], - guardrails: [ - { - id: "current-tools:validate-rust-graph", - command: ["npm", "run", "current-tools:validate-rust-graph"], - replacementStatus: "retained", - oldToolReplacementClaimed: false - } - ] - }; -} - function validAspDogfoodReceipt() { const cutover = validReleaseCutoverReceipt(); const aspRepo = covibesPath("agent-server-protocol"); @@ -5833,8 +5450,7 @@ function validAspDogfoodReceipt() { temp: true, isolated: true, sharedStateMutated: false, - pathSanitized: true, - aceRuntimeBinExcluded: true + pathSanitized: true }, hostFixture: { repo: hostFixtureRepo, @@ -5892,32 +5508,18 @@ function validAspDogfoodReceipt() { diagnosticsCount: 0, hostOwnedFieldLeak: false }, - currentToolGuardrails: [ - { ...command("current-tools-validate-changed", ["npm", "run", "current-tools:validate-changed"]), retained: true }, - { ...command("current-tools-validate-rust-graph", ["npm", "run", "current-tools:validate-rust-graph"]), retained: true }, - { - id: "current-tools-validate-all", - command: ["npm", "run", "current-tools:validate-all"], - status: "retained-not-run", - exitCode: null, - stdoutSha256: "0".repeat(64), - stderrSha256: "0".repeat(64), - retained: true, - assertion: "retained by default" - } - ], + selfValidation: cutover.selfValidation, unsupportedSurfaces: [ { surface: "inspect", status: "parity-blocker", cleanCoverage: false, blocker: "inspect not mapped into ASP #120" }, - { surface: "edit", status: "retained-old-tool-gate", cleanCoverage: false, blocker: "edit not mapped into ASP #120" } + { surface: "edit", status: "parity-blocker", cleanCoverage: false, blocker: "edit not mapped into ASP #120" } ], - parityBlockers: [{ source: "docs/planning/old-tool-compatibility-matrix.md:1", detail: "old-tool guardrails retained" }], + parityBlockers: [], authority: { hostOwnsDecisions: true, providerOutputIsHostDecision: false, localAuthorityOverride: { present: false, sharedAuthorityWeakened: false } }, publicReleaseActions: [], - oldToolReplacementClaimed: false, forbiddenMarkerScan: { scannedTextCount: 2, findingCount: 0, diff --git a/tests/current-tools.test.mjs b/tests/current-tools.test.mjs deleted file mode 100644 index 48f1e67..0000000 --- a/tests/current-tools.test.mjs +++ /dev/null @@ -1,199 +0,0 @@ -import { describe, it } from "node:test"; -import assert from "node:assert/strict"; -import { - chmodSync, - cpSync, - existsSync, - mkdtempSync, - readFileSync, - realpathSync, - rmSync, - symlinkSync, - writeFileSync -} from "node:fs"; -import { tmpdir } from "node:os"; -import { dirname, join, relative, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; -import { spawnSync } from "node:child_process"; - -const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); -const copiedRepoSkips = new Set([ - ".git", - "node_modules", - "dist", - ".ace", - ".agents", - ".claude", - ".codex", - ".gemini", - ".opencode", - ".code-review-graph", - ".rox-cache", - ".robustness-engine-cache", - "target" -]); - -describe("current tool setup", () => { - it("generates wrappers and tooling manifest from external tools", () => { - const repo = tempRepo(); - const tools = fakeTools(); - const result = run(repo, "bash", ["scripts/setup-current-tools.sh"], { - env: { LATTICE_CURRENT_TOOLS_DIR: tools } - }); - assert.match(result.stdout, /current tool wrappers ready/); - - for (const tool of ["rox", "crg", "cix"]) { - const wrapper = join(repo, ".ace/runtime/bin", tool); - assert.equal(existsSync(wrapper), true); - assert.match(readFileSync(wrapper, "utf8"), new RegExp(`${tools}/${tool}`)); - } - - const tooling = JSON.parse(readFileSync(join(repo, ".ace/runtime/tooling.json"), "utf8")); - assert.equal(tooling.tooling.aceTools.binRoot, ".ace/runtime/bin"); - assert.deepEqual(Object.keys(tooling.tooling.aceTools.tools).sort(), ["cix", "crg", "rox"]); - }); - - it("rejects lattice-internal tool sources", () => { - const repo = tempRepo(); - const tools = fakeTools(); - const internalRox = join(repo, "packages/graph/rox"); - writeFileSync(internalRox, "#!/usr/bin/env bash\nexit 0\n"); - chmodSync(internalRox, 0o755); - - const result = run(repo, "bash", ["scripts/setup-current-tools.sh"], { - expectFailure: true, - env: { - LATTICE_CURRENT_ROX_PATH: internalRox, - LATTICE_CURRENT_CRG_PATH: join(tools, "crg"), - LATTICE_CURRENT_CIX_PATH: join(tools, "cix") - } - }); - assert.match(stderrAndStdout(result), /source resolved inside lattice/); - }); - - it("rejects symlinks that resolve to lattice-internal tool sources", () => { - const repo = tempRepo(); - const tools = fakeTools(); - const internalRox = join(repo, "packages/graph/rox"); - const symlinkDir = mkdtempSync(join(tmpdir(), "lattice-current-tools-link-")); - const symlinkedRox = join(symlinkDir, "rox"); - writeFileSync(internalRox, "#!/usr/bin/env bash\nexit 0\n"); - chmodSync(internalRox, 0o755); - symlinkSync(internalRox, symlinkedRox); - - const result = run(repo, "bash", ["scripts/setup-current-tools.sh"], { - expectFailure: true, - env: { - LATTICE_CURRENT_ROX_PATH: symlinkedRox, - LATTICE_CURRENT_CRG_PATH: join(tools, "crg"), - LATTICE_CURRENT_CIX_PATH: join(tools, "cix") - } - }); - assert.match(stderrAndStdout(result), /source resolved inside lattice/); - }); -}); - -describe("dev-env current-tool wrappers", () => { - for (const tool of ["rox", "crg", "cix"]) { - it(`fails closed when ${tool} wrapper is missing`, () => { - const repo = tempRepo(); - run(repo, "bash", ["scripts/setup-current-tools.sh"], { - env: { LATTICE_CURRENT_TOOLS_DIR: fakeTools() } - }); - rmSync(join(repo, ".ace/runtime/bin", tool)); - - const result = run(repo, "bash", ["-lc", devEnvProbeScript()], { - expectFailure: true - }); - - assert.match(result.stderr, /run npm run setup:tools/); - assert.match(result.stdout, /status=1/); - assert.match(result.stdout, /PATH=\/usr\/bin:\/bin/); - assert.match(result.stdout, /runtime=/); - assert.match(result.stdout, /cixroot=/); - }); - } - - it("exports current-tool environment when all wrappers are present", () => { - const repo = tempRepo(); - run(repo, "bash", ["scripts/setup-current-tools.sh"], { - env: { LATTICE_CURRENT_TOOLS_DIR: fakeTools() } - }); - - const result = run(repo, "bash", ["-lc", devEnvProbeScript()]); - const output = parseProbeOutput(result.stdout); - const realRepo = realpathSync(repo); - - assert.equal(output.status, "0"); - assert.equal(output.PATH, `${join(realRepo, ".ace/runtime/bin")}:/usr/bin:/bin`); - assert.equal(output.runtime, join(realRepo, ".ace/rox")); - assert.equal(output.cixroot, realRepo); - }); -}); - -function tempRepo() { - const tempRoot = mkdtempSync(join(tmpdir(), "lattice-tools-")); - const repo = join(tempRoot, "repo"); - cpSync(repoRoot, repo, { - recursive: true, - filter(source) { - const rel = relative(repoRoot, source); - if (rel === "") return true; - return !rel.split(/[\\/]/).some((segment) => copiedRepoSkips.has(segment)); - } - }); - return repo; -} - -function fakeTools() { - const dir = mkdtempSync(join(tmpdir(), "lattice-current-tools-")); - for (const tool of ["rox", "crg", "cix"]) { - const path = join(dir, tool); - writeFileSync(path, `#!/usr/bin/env bash\nprintf '${tool} fake\\n'\n`); - chmodSync(path, 0o755); - } - return dir; -} - -function run(cwd, command, args, options = {}) { - const result = spawnSync(command, args, { - cwd, - encoding: "utf8", - env: { ...process.env, ...(options.env ?? {}) }, - stdio: ["ignore", "pipe", "pipe"] - }); - if (options.expectFailure) { - assert.notEqual(result.status, 0, `${command} ${args.join(" ")} should fail`); - return result; - } - assert.equal(result.status, 0, `${command} ${args.join(" ")} failed\n${stderrAndStdout(result)}`); - return result; -} - -function stderrAndStdout(result) { - return `${result.stderr}\n${result.stdout}`; -} - -function devEnvProbeScript() { - return [ - "set +e", - "PATH=/usr/bin:/bin", - "unset LATTICE_CURRENT_TOOL_RUNTIME_DIR CIX_DAEMON_ROOT_DIR", - "source scripts/dev-env.sh", - "status=$?", - 'printf "status=%s\\nPATH=%s\\nruntime=%s\\ncixroot=%s\\n" "$status" "$PATH" "${LATTICE_CURRENT_TOOL_RUNTIME_DIR-}" "${CIX_DAEMON_ROOT_DIR-}"', - 'exit "$status"' - ].join("\n"); -} - -function parseProbeOutput(stdout) { - return Object.fromEntries( - stdout - .trimEnd() - .split("\n") - .map((line) => { - const separator = line.indexOf("="); - return [line.slice(0, separator), line.slice(separator + 1)]; - }) - ); -} diff --git a/tests/cutover-release.test.mjs b/tests/cutover-release.test.mjs index 1f44a23..2a25f73 100644 --- a/tests/cutover-release.test.mjs +++ b/tests/cutover-release.test.mjs @@ -8,7 +8,6 @@ import { fileURLToPath } from "node:url"; import { graphCoreNativePackageNameForTarget, graphCoreNativeSupportedTargets, - releaseCutoverCurrentToolGuardrailIds, releaseCutoverNegativeCheckIds, releaseCutoverPythonCommandIds, releaseCutoverRustCommandIds, @@ -23,15 +22,17 @@ const receiptGatesRunSeparately = process.env.OPCORE_CI_RECEIPT_GATES_RUN_SEPARA const separateReceiptGateSkip = receiptGatesRunSeparately ? "covered by root CI receipt gate" : false; describe("cutover release receipt", () => { - it("proves installed Opcore artifacts without current-tool fallback", { timeout: 180000, skip: separateReceiptGateSkip }, () => { - withReleaseDocsLock(() => { + it( + "proves installed Opcore artifacts with native self-validation", + { timeout: 180000, skip: separateReceiptGateSkip }, + () => { + withReleaseDocsLock(() => { run(["scripts/generate-release-receipt.mjs", "--inspect-packages-only", "--json"]); const result = withCompleteNativeArtifactFixtures(() => run(["scripts/generate-cutover-receipt.mjs", "--json"], { env: { ...process.env, - OPCORE_CUTOVER_REUSE_RELEASE_PACKAGES: "1", - OPCORE_CUTOVER_REUSE_CURRENT_TOOL_GUARDRAILS: "1" + OPCORE_CUTOVER_REUSE_RELEASE_PACKAGES: "1" } }) ); @@ -61,11 +62,9 @@ describe("cutover release receipt", () => { target ); } - assert.equal(receipt.environmentIsolation.currentToolEnvCleared, true); - assert.equal(receipt.environmentIsolation.aceRuntimeBinExcluded, true); - assert.equal(receipt.environmentIsolation.siblingCovibesExcluded, true); - assert.equal(receipt.environmentIsolation.opcoreBinOnly, true); - assert.deepEqual(receipt.environmentIsolation.oldBinsAbsent, { lattice: true, crg: true, cix: true, rox: true }); + assert.equal(receipt.environmentIsolation.pathSanitized, true); + assert.equal(receipt.environmentIsolation.siblingRepositoriesExcluded, true); + assert.equal(receipt.environmentIsolation.opcoreBinsVerified, true); assert.equal(receipt.commandReceipts.every((entry) => entry.command[0] === "opcore" || entry.command[0] === "opcore"), true); assert.deepEqual( receipt.commandReceipts.filter((entry) => entry.status === "not_implemented").map((entry) => entry.id), @@ -81,20 +80,16 @@ describe("cutover release receipt", () => { releaseCutoverPythonCommandIds ); assert.equal(receipt.pythonCommandReceipts.every((entry) => entry.status === "ok"), true); - assert.deepEqual( - receipt.currentToolGuardrails.map((entry) => entry.id), - releaseCutoverCurrentToolGuardrailIds - ); - assert.deepEqual(receipt.currentToolGuardrails, recordedReceipt.currentToolGuardrails); - assert.equal(receipt.currentToolGuardrails.every((entry) => entry.retained === true && entry.oldToolReplacementClaimed === false), true); - assert.equal(receipt.oldToolReplacementClaimed, false); + assert.deepEqual(receipt.selfValidation, recordedReceipt.selfValidation); + assert.equal(receipt.selfValidation.status, "passed"); for (const id of ["inspect-symbols", "inspect-definition", "inspect-references", "inspect-signature", "inspect-implementations", "inspect-search"]) { assert.equal(receipt.commandReceipts.find((entry) => entry.id === id)?.owner, "inspect", id); } assert.equal(receipt.forbiddenMarkerScan.findingCount, 0); assert.deepEqual(receipt.inputEvidence.map((entry) => entry.issue).sort(), ["#17", "#29", "#58"]); - }); - }); + }); + } + ); it("rejects cutover receipts with advertised placeholder command evidence", () => { const temp = mkdtempSync(join(tmpdir(), "opcore-cutover-negative-")); @@ -136,13 +131,9 @@ describe("cutover release receipt", () => { })) }, environmentIsolation: { - currentToolEnvCleared: true, - clearedEnvVarCount: 5, pathSanitized: true, - aceRuntimeBinExcluded: true, - siblingCovibesExcluded: true, - opcoreBinOnly: true, - oldBinsAbsent: { lattice: true, crg: true, cix: true, rox: true } + siblingRepositoriesExcluded: true, + opcoreBinsVerified: true }, commandReceipts: [ { @@ -169,21 +160,15 @@ describe("cutover release receipt", () => { exitCode: 0, assertion: `${id} rejected the unsafe path` })), - currentToolGuardrails: releaseCutoverCurrentToolGuardrailIds.map((id) => ({ - id, - command: - id === "current-tools-validate-changed" - ? ["npm", "run", "current-tools:validate-changed"] - : ["npm", "run", "current-tools:validate-rust-graph"], + selfValidation: { + id: "opcore-self-check", + command: ["npm", "run", "opcore:self-check"], status: "passed", exitCode: 0, stdoutSha256: "5".repeat(64), stderrSha256: "6".repeat(64), - retained: true, - assertion: `${id} remains retained`, - oldToolReplacementClaimed: false - })), - oldToolReplacementClaimed: false, + assertion: "Opcore self-validation passed" + }, forbiddenMarkerScan: { scannedTextCount: 1, findingCount: 0, markersBlocked: ["private-runtime"] }, inputEvidence: [ { issue: "#17", path: "docs/release/graph-release-receipt.json", checksumSha256: "1".repeat(64) }, diff --git a/tests/edit-tree-policy-parity.test.mjs b/tests/edit-tree-policy-parity.test.mjs index 47310b2..268f70a 100644 --- a/tests/edit-tree-policy-parity.test.mjs +++ b/tests/edit-tree-policy-parity.test.mjs @@ -15,8 +15,8 @@ test("tree planning refuses duplicate paths, private roots, unsafe paths, and sy const workspace = await createNodeEditWorkspace({ repoRoot: root }); const cases = [ ["conflict", { files: [{ path: "src/a.ts", content: "one\n" }, { path: "src/a.ts", content: "two\n" }] }], - ["unsupported_change", { files: [{ path: ".ace/runtime/state.json", content: "{}\n" }] }], - ["unsupported_change", { files: [{ path: ".rox-cache/state.json", content: "{}\n" }] }], + ["unsupported_change", { files: [{ path: ".agents/runtime/state.json", content: "{}\n" }] }], + ["unsupported_change", { files: [{ path: ".codex/runtime/state.json", content: "{}\n" }] }], ["unsupported_change", { files: [{ path: "node_modules/pkg/index.js", content: "module.exports = {}\n" }] }], ["unsupported_change", { files: [{ path: "target/debug/out.txt", content: "out\n" }] }], ["parent_directory", { files: [{ path: "../escape.ts", content: "x\n" }] }], diff --git a/tests/gate-negative-fixtures.test.mjs b/tests/gate-negative-fixtures.test.mjs index 6e6e276..d4bda9d 100644 --- a/tests/gate-negative-fixtures.test.mjs +++ b/tests/gate-negative-fixtures.test.mjs @@ -1,6 +1,6 @@ import { describe, it } from "node:test"; import assert from "node:assert/strict"; -import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { dirname, join, relative, resolve } from "node:path"; import { fileURLToPath } from "node:url"; @@ -8,7 +8,6 @@ import { spawnSync } from "node:child_process"; import { graphCoreNativePackageNameForTarget, graphCoreNativeSupportedTargets, - releaseCutoverCurrentToolGuardrailIds, releaseCutoverNegativeCheckIds, releaseCutoverPythonCommandIds, releaseCutoverRequiredCommandIds, @@ -24,16 +23,12 @@ const copiedRepoSkips = new Set([ ".git", "node_modules", "target", - ".ace", ".agents", ".claude", ".codex", ".gemini", ".opencode", ".lattice", - ".code-review-graph", - ".rox-cache", - ".robustness-engine-cache", ".receipt-test.lock" ]); @@ -47,15 +42,6 @@ describe("negative gate fixtures", () => { assert.match(stderrAndStdout(result), /Generated TypeScript build info must not be checked in/); }); - it("rejects Python CRG provenance markers", () => { - const repo = tempRepo(); - writeFileSync(join(repo, "pyproject.toml"), "[project]\nname = \"code-review-graph\"\n"); - run(repo, "git", ["add", "pyproject.toml"]); - - const result = run(repo, "node", ["scripts/check-provenance.mjs"], { expectFailure: true }); - assert.match(stderrAndStdout(result), /Forbidden Python packaging file/); - }); - it("rejects provenance receipt checks without build artifacts", () => { const repo = tempRepo(); const workflowPath = join(repo, ".github/workflows/provenance.yml"); @@ -165,15 +151,15 @@ describe("negative gate fixtures", () => { assert.match(stderrAndStdout(result), /packed files mismatch|EXTRA\.md/); }); - it("rejects old public bins in release package inspection", () => { + it("rejects unexpected public bins in release package inspection", () => { const repo = tempRepo({ includeDist: true }); const manifestPath = join(repo, "packages/opcore/package.json"); const manifest = JSON.parse(readFileSync(manifestPath, "utf8")); - manifest.bin.crg = "dist/index.js"; + manifest.bin.unexpected = "dist/index.js"; writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`); const result = run(repo, "node", ["scripts/generate-release-receipt.mjs", "--inspect-packages-only"], { expectFailure: true }); - assert.match(stderrAndStdout(result), /forbidden old public bin crg/); + assert.match(stderrAndStdout(result), /Opcore package bins|unexpected/); }); it("rejects canonical ASP server manifest launch claim overreach", () => { @@ -236,15 +222,15 @@ describe("negative gate fixtures", () => { assert.match(stderrAndStdout(result), /packed files mismatch|checksum|sha256/); }); - it("rejects current-tool markers in cutover descriptor inspection", () => { + it("rejects private runtime paths in cutover descriptor inspection", () => { const repo = tempRepo({ includeDist: true }); const descriptorPath = join(repo, "packages/opcore/dist/descriptors/opcore.managed-tool.json"); const descriptor = JSON.parse(readFileSync(descriptorPath, "utf8")); - descriptor.artifacts[0].path = ".ace/runtime/bin/lattice"; + descriptor.artifacts[0].path = ".agents/runtime/bin/tool"; writeFileSync(descriptorPath, `${JSON.stringify(descriptor, null, 2)}\n`); const result = run(repo, "node", ["scripts/generate-cutover-receipt.mjs", "--inspect-descriptor-only"], { expectFailure: true }); - assert.match(stderrAndStdout(result), /private runtime|forbidden marker|\.ace/); + assert.match(stderrAndStdout(result), /private runtime|forbidden marker/); }); it("rejects cutover receipts with advertised not_implemented commands", () => { @@ -271,21 +257,6 @@ describe("negative gate fixtures", () => { assert.match(stderrAndStdout(result), /command receipts.*inspect-search/); }); - it("rejects old bin fallback in installed cutover projects", () => { - const repo = tempRepo({ includeDist: true }); - const project = join(repo, "tmp-installed-project"); - mkdirSync(join(project, "node_modules/.bin"), { recursive: true }); - writeFileSync(join(project, "node_modules/.bin/lattice"), "#!/bin/sh\n"); - writeFileSync(join(project, "node_modules/.bin/opcore"), "#!/bin/sh\n"); - writeFileSync(join(project, "node_modules/.bin/opcore-asp-provider"), "#!/bin/sh\n"); - writeFileSync(join(project, "node_modules/.bin/crg"), "#!/bin/sh\n"); - - const result = run(repo, "node", ["scripts/generate-cutover-receipt.mjs", "--inspect-installed-bin-dir", "tmp-installed-project"], { - expectFailure: true - }); - assert.match(stderrAndStdout(result), /old public bin.*lattice/); - }); - it("rejects sibling file dependencies", () => { const repo = tempRepo(); const manifestPath = join(repo, "packages/graph/package.json"); @@ -308,76 +279,6 @@ describe("negative gate fixtures", () => { assert.match(stderrAndStdout(result), /must not reference parent directories or absolute paths/); }); - it("rejects reserved graph implementation package paths", () => { - const repo = tempRepo(); - const readmePath = join(repo, "reserved-graph-name.md"); - writeFileSync(readmePath, `${["packages", "crg"].join("/")} is reserved for removed implementation references\n`); - - const result = run(repo, "node", ["scripts/check-workspace.mjs"], { expectFailure: true }); - assert.match(stderrAndStdout(result), /reserved graph naming references/); - }); - - it("rejects reserved graph implementation package names", () => { - const repo = tempRepo(); - const manifestPath = join(repo, "packages/graph/package.json"); - const manifest = JSON.parse(readFileSync(manifestPath, "utf8")); - manifest.description = `Reserved ${`@the-open-engine/opcore-${"crg"}`} implementation name`; - writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`); - - const result = run(repo, "node", ["scripts/check-workspace.mjs"], { expectFailure: true }); - assert.match(stderrAndStdout(result), /reserved graph naming references/); - }); - - it("rejects reserved graph provider literals", () => { - const repo = tempRepo(); - const sourcePath = join(repo, "packages/graph/src/reserved-provider.ts"); - writeFileSync(sourcePath, `export const status = { provider: ${JSON.stringify("crg")} };\n`); - - const result = run(repo, "node", ["scripts/check-workspace.mjs"], { expectFailure: true }); - assert.match(stderrAndStdout(result), /reserved graph naming references/); - }); - - it("rejects reserved graph providerName metadata", () => { - const repo = tempRepo(); - const sourcePath = join(repo, "packages/graph/src/reserved-provider-name-metadata.ts"); - writeFileSync(sourcePath, `export const status = { providerName: ${JSON.stringify("crg")} };\n`); - - const result = run(repo, "node", ["scripts/check-workspace.mjs"], { expectFailure: true }); - assert.match(stderrAndStdout(result), /reserved-provider-name-metadata\.ts/); - assert.match(stderrAndStdout(result), /legacy graph provider name metadata/); - }); - - it("rejects reserved graph provider name constants", () => { - const repo = tempRepo(); - const sourcePath = join(repo, "packages/graph/src/bad-provider-name.ts"); - const legacyGraphTool = "cr" + "g"; - writeFileSync(sourcePath, `export const crgProviderName = ${JSON.stringify(legacyGraphTool)};\n`); - - const result = run(repo, "node", ["scripts/check-workspace.mjs"], { expectFailure: true }); - assert.match(stderrAndStdout(result), /bad-provider-name\.ts/); - assert.match(stderrAndStdout(result), /legacy graph provider name constant/); - }); - - it("rejects stale CONTRIBUTING graph naming", () => { - const repo = tempRepo(); - const contributingPath = join(repo, "CONTRIBUTING.md"); - const legacyGraphTool = "cr" + "g"; - const content = readFileSync(contributingPath, "utf8") - .replace( - "Opcore is a public alpha for local code intelligence, edit planning, and pre-write validation for coding agents.", - `Opcore is a public alpha code-intelligence monorepo for \`${legacyGraphTool}\`, edit, and validation.` - ) - .replace( - "Graph extraction, persistence, query, search, and impact belong in `@the-open-engine/opcore-graph`.", - `Graph extraction, persistence, query, search, and impact graph production belongs in \`${legacyGraphTool}\`.` - ); - writeFileSync(contributingPath, content); - - const result = run(repo, "node", ["scripts/check-workspace.mjs"], { expectFailure: true }); - assert.match(stderrAndStdout(result), /CONTRIBUTING\.md/); - assert.match(stderrAndStdout(result), /reserved graph naming references/); - }); - it("rejects edit importing graph-core native artifact loaders", () => { const repo = tempRepo(); const sourcePath = join(repo, "packages/edit/src/bad-graph-loader.ts"); @@ -396,85 +297,6 @@ describe("negative gate fixtures", () => { assert.match(stderrAndStdout(result), /bad-graph-sqlite\.ts/); }); - it("rejects Cargo package names containing crg", () => { - const repo = tempRepo(); - const manifestPath = join(repo, "crates/graph-core/Cargo.toml"); - const manifest = readFileSync(manifestPath, "utf8").replace( - 'name = "opcore-graph-core"', - `name = "lattice-${"crg"}-core"` - ); - writeFileSync(manifestPath, manifest); - - const result = run(repo, "node", ["scripts/check-workspace.mjs"], { expectFailure: true }); - assert.match(stderrAndStdout(result), /must not use crg in Rust package/); - }); - - it("rejects Rox code-quality coverage without all Rust crate paths", () => { - const repo = tempRepo(); - const roxPath = join(repo, "rox.json"); - const rox = JSON.parse(readFileSync(roxPath, "utf8")); - rox.checks.codeQuality.include = rox.checks.codeQuality.include.filter((entry) => entry !== "crates/"); - writeFileSync(roxPath, `${JSON.stringify(rox, null, 2)}\n`); - - const result = run(repo, "node", ["scripts/check-workspace.mjs"], { expectFailure: true }); - assert.match(stderrAndStdout(result), /checks\.codeQuality\.include must include "crates\/"/); - }); - - it("rejects Rox code-quality coverage without existing TypeScript and script scopes", () => { - const repo = tempRepo(); - const roxPath = join(repo, "rox.json"); - const rox = JSON.parse(readFileSync(roxPath, "utf8")); - rox.checks.codeQuality.include = ["crates/"]; - writeFileSync(roxPath, `${JSON.stringify(rox, null, 2)}\n`); - - const result = run(repo, "node", ["scripts/check-workspace.mjs"], { expectFailure: true }); - assert.match(stderrAndStdout(result), /checks\.codeQuality\.include must include "packages\/"/); - }); - - it("rejects scoped Rust quality scripts that run against the whole repo", () => { - const repo = tempRepo(); - const manifestPath = join(repo, "package.json"); - const manifest = JSON.parse(readFileSync(manifestPath, "utf8")); - manifest.scripts["current-tools:validate-rust-graph"] = "./.ace/runtime/bin/rox check --all --no-daemon --checks functionMetrics"; - writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`); - - const result = run(repo, "node", ["scripts/check-workspace.mjs"], { expectFailure: true }); - assert.match(stderrAndStdout(result), /must run scoped Rust graph function metrics script/); - }); - - it("rejects repo-wide Rox without scoped Rust graph metrics", () => { - const repo = tempRepo(); - const roxPath = join(repo, "rox.json"); - const rox = JSON.parse(readFileSync(roxPath, "utf8")); - rox.extensions = rox.extensions.filter((entry) => entry !== "scripts/check-rust-graph-function-metrics.mjs"); - writeFileSync(roxPath, `${JSON.stringify(rox, null, 2)}\n`); - - const result = run(repo, "node", ["scripts/check-workspace.mjs"], { expectFailure: true }); - assert.match(stderrAndStdout(result), /must run scoped Rust graph function metrics/); - }); - - it("rejects Rox all-mode Rust metrics without crate package scope", () => { - const repo = tempRepo(); - const roxPath = join(repo, "rox.json"); - const rox = JSON.parse(readFileSync(roxPath, "utf8")); - rox.packages = rox.packages.filter((entry) => entry !== "crates"); - writeFileSync(roxPath, `${JSON.stringify(rox, null, 2)}\n`); - - const result = run(repo, "node", ["scripts/check-workspace.mjs"], { expectFailure: true }); - assert.match(stderrAndStdout(result), /packages must include "crates"/); - }); - - it("rejects Rox code-quality coverage without changed-file modes", () => { - const repo = tempRepo(); - const roxPath = join(repo, "rox.json"); - const rox = JSON.parse(readFileSync(roxPath, "utf8")); - rox.checks.codeQuality.when.modes = rox.checks.codeQuality.when.modes.filter((entry) => entry !== "changed"); - writeFileSync(roxPath, `${JSON.stringify(rox, null, 2)}\n`); - - const result = run(repo, "node", ["scripts/check-workspace.mjs"], { expectFailure: true }); - assert.match(stderrAndStdout(result), /checks\.codeQuality\.when\.modes must include "changed"/); - }); - it("rejects graph-core crates without workspace lint opt-in", () => { const repo = tempRepo(); const manifestPath = join(repo, "crates/graph-core/Cargo.toml"); @@ -497,7 +319,7 @@ describe("negative gate fixtures", () => { }); function tempRepo(options = {}) { - const tempRoot = mkdtempSync(join(tmpdir(), "lattice-gate-")); + const tempRoot = mkdtempSync(join(tmpdir(), "opcore-gate-")); const repo = join(tempRoot, "repo"); withReleaseDocsLock(() => { cpSync(repoRoot, repo, { @@ -650,11 +472,6 @@ function minimalCutoverReceipt(repo, commandOverrides = {}) { negativeChecks.map((entry) => entry.id), releaseCutoverNegativeCheckIds ); - const currentToolGuardrails = retainedCutoverGuardrails(); - assert.deepEqual( - currentToolGuardrails.map((entry) => entry.id), - releaseCutoverCurrentToolGuardrailIds - ); return { schemaVersion: 1, issue: "#30", @@ -689,24 +506,27 @@ function minimalCutoverReceipt(repo, commandOverrides = {}) { resolvedChecksums: descriptor.checksums.map((checksum) => ({ ...checksum, packageFile: true, value: "8".repeat(64) })) }, environmentIsolation: { - currentToolEnvCleared: true, - clearedEnvVarCount: 5, pathSanitized: true, - aceRuntimeBinExcluded: true, - siblingCovibesExcluded: true, - opcoreBinOnly: true, - oldBinsAbsent: { lattice: true, crg: true, cix: true, rox: true } + siblingRepositoriesExcluded: true, + opcoreBinsVerified: true }, commandReceipts, rustCommandReceipts, pythonCommandReceipts, negativeChecks, - currentToolGuardrails, - oldToolReplacementClaimed: false, + selfValidation: { + id: "opcore-self-check", + command: ["npm", "run", "opcore:self-check"], + status: "passed", + exitCode: 0, + stdoutSha256: "1".repeat(64), + stderrSha256: "2".repeat(64), + assertion: "Opcore self-validation passed" + }, forbiddenMarkerScan: { scannedTextCount: 1, findingCount: 0, - markersBlocked: ["private-runtime", "current-tool-env", "private-home", "old-tool-bins"] + markersBlocked: ["private-home", "launch-claim"] }, inputEvidence: [ { issue: "#17", path: "docs/release/graph-release-receipt.json", checksumSha256: "4".repeat(64) }, @@ -906,30 +726,3 @@ function commandReceipt(expectation, bin = expectation.command[0], assertionSuff assertion: `${expectation.id} ${assertionSuffix}` }; } - -function retainedCutoverGuardrails() { - return [ - { - id: "current-tools-validate-changed", - command: ["npm", "run", "current-tools:validate-changed"], - status: "passed", - exitCode: 0, - stdoutSha256: "1".repeat(64), - stderrSha256: "2".repeat(64), - retained: true, - assertion: "retained changed-file guardrail", - oldToolReplacementClaimed: false - }, - { - id: "current-tools-validate-rust-graph", - command: ["npm", "run", "current-tools:validate-rust-graph"], - status: "passed", - exitCode: 0, - stdoutSha256: "1".repeat(64), - stderrSha256: "2".repeat(64), - retained: true, - assertion: "retained Rust graph guardrail", - oldToolReplacementClaimed: false - } - ]; -} diff --git a/tests/graph-pipeline-cli.test.mjs b/tests/graph-pipeline-cli.test.mjs index 4a62369..d307a8a 100644 --- a/tests/graph-pipeline-cli.test.mjs +++ b/tests/graph-pipeline-cli.test.mjs @@ -303,15 +303,12 @@ describe("graph pipeline CLI", () => { ["src", "app.ts"], ["src", "tool.py"], ["node_modules/pkg", "index.ts"], - [".ace/runtime", "generated.ts"], [".agents/runtime", "generated.ts"], [".claude/runtime", "generated.ts"], [".codex/runtime", "generated.ts"], [".gemini/runtime", "generated.ts"], [".opcore/graph", "generated.ts"], [".opencode/runtime", "generated.ts"], - [".rox-cache", "generated.ts"], - [".robustness-engine-cache", "generated.ts"], [".pnpm/pkg", "index.ts"], ["vendor/pkg", "generated.ts"], ["dist", "generated.ts"], @@ -608,7 +605,7 @@ describe("graph pipeline CLI", () => { } }); - it("scopes watch from OPCORE_GRAPH_WATCH_PATHS only and ignores CRG_WATCH_PATHS", () => { + it("scopes watch from OPCORE_GRAPH_WATCH_PATHS", () => { const temp = mkdtempSync(join(tmpdir(), "lattice-watch-env-paths-")); try { mkdirSync(join(temp, "src"), { recursive: true }); @@ -622,13 +619,6 @@ describe("graph pipeline CLI", () => { assert.deepEqual(latticeScoped.graphPipeline.summary.watchPaths, ["src/a.ts"]); assert.deepEqual(latticeScoped.graphPipeline.summary.changedFiles, ["src/a.ts"]); - rmSync(join(temp, ".opcore"), { recursive: true, force: true }); - const crgIgnored = run(latticeBin, ["graph", "watch", "--repo", temp, "--once", "--json"], 0, { - env: { CRG_WATCH_PATHS: "src/a.ts" } - }); - assert.equal(crgIgnored.providerStatus.state, "available"); - assert.deepEqual(crgIgnored.graphPipeline.summary.watchPaths ?? [], []); - assert.deepEqual(crgIgnored.graphPipeline.summary.changedFiles, ["src/a.ts", "src/b.ts"]); } finally { rmSync(temp, { recursive: true, force: true }); } @@ -687,10 +677,7 @@ describe("graph pipeline CLI", () => { mkdirSync(join(temp, "src"), { recursive: true }); writeFileSync(join(temp, "src/a.ts"), "export const a = 1;\n"); writeFileSync(join(temp, "src/b.ts"), "export const b = 2;\n"); - const env = { - OPCORE_GRAPH_WATCH_PATHS: "src/a.ts", - CRG_WATCH_PATHS: "src/a.ts" - }; + const env = { OPCORE_GRAPH_WATCH_PATHS: "src/a.ts" }; const build = run(latticeBin, ["graph", "build", "--repo", temp, "--json"], 0, { env }); assert.equal(build.providerStatus.state, "available"); @@ -829,10 +816,7 @@ function createRuntimeIgnoredFiles(fixtureRoot) { for (const [directory, file] of [ ["ignored", "drop.ts"], ["node_modules/pkg", "index.ts"], - [".ace/runtime", "generated.ts"], [".opcore/graph", "generated.ts"], - [".rox-cache", "generated.ts"], - [".robustness-engine-cache", "generated.ts"], ["dist", "generated.ts"] ]) { mkdirSync(join(fixtureRoot, directory), { recursive: true }); diff --git a/tests/graph-reference-evidence.test.mjs b/tests/graph-reference-evidence.test.mjs deleted file mode 100644 index a98a87f..0000000 --- a/tests/graph-reference-evidence.test.mjs +++ /dev/null @@ -1,227 +0,0 @@ -import { describe, it } from "node:test"; -import assert from "node:assert/strict"; -import { existsSync, readFileSync } from "node:fs"; -import { validateGraphReferenceEvidenceManifest } from "../packages/contracts/dist/index.js"; - -const fixtureRoot = new URL("../packages/fixtures/graph-reference-evidence/", import.meta.url); -const manifest = readFixture("manifest.json"); -const sqliteFixtures = readFixture("sqlite-fixtures.json"); -const daemonFixtures = readFixture("daemon-socket-fixtures.json"); -const goldenCorpus = readFixture("golden-corpus.json"); -const baselineReceipts = readFixture("baseline-receipts.json"); - -describe("graph reference evidence fixtures", () => { - it("validates the reference evidence manifest through shared contracts", () => { - assert.equal(validateGraphReferenceEvidenceManifest(manifest).issue, "#19"); - assert.deepEqual(manifest.fixtureRefs, [ - "packages/fixtures/graph-reference-evidence/sqlite-fixtures.json", - "packages/fixtures/graph-reference-evidence/daemon-socket-fixtures.json", - "packages/fixtures/graph-reference-evidence/golden-corpus.json", - "packages/fixtures/graph-reference-evidence/baseline-receipts.json" - ]); - }); - - it("records neutral command evidence for canonical graph routes", () => { - assertCommand("graph-reference-build", ["build"], ["opcore", "graph", "build"]); - assertCommand("graph-reference-update", ["update"], ["opcore", "graph", "update"]); - assertCommand("graph-reference-watch", ["watch"], ["opcore", "graph", "watch"]); - assertCommand("graph-reference-status", ["status"], ["opcore", "graph", "status"]); - assertCommand("graph-reference-query", ["query"], ["opcore", "graph", "query"]); - assertCommand("graph-reference-impact", ["impact"], ["opcore", "graph", "impact"]); - assertCommand("graph-reference-search", ["search"], ["opcore", "graph", "search"]); - assertCommand("graph-reference-serve", ["serve"], ["opcore", "graph", "serve"]); - }); - - it("keeps SQLite and daemon evidence concrete", () => { - assert.deepEqual(sqliteFixtures.nodeKinds, [ - "File", - "Function", - "Test", - "Module", - "Struct", - "Enum", - "Trait", - "Impl", - "Method", - "TypeAlias", - "Const", - "Static", - "Macro" - ]); - assert.deepEqual(sqliteFixtures.edgeKinds, ["CALLS", "CONTAINS", "IMPORTS_FROM", "TESTED_BY", "IMPLEMENTS", "DEPENDS_ON", "INHERITS"]); - assert.deepEqual(sqliteFixtures.tables.map((entry) => entry.name), ["metadata", "nodes", "edges", "nodes_fts"]); - assert.deepEqual( - sqliteFixtures.indexes, - [ - "idx_nodes_file", - "idx_nodes_kind", - "idx_nodes_qualified", - "idx_edges_source", - "idx_edges_target", - "idx_edges_kind", - "idx_edges_file", - "idx_nodes_exported_name" - ] - ); - assert.equal(sqliteFixtures.tables.find((entry) => entry.name === "nodes").columns.includes("community_id"), false); - assert.equal(sqliteFixtures.tables.some((entry) => ["flows", "flow_memberships", "communities", "embeddings"].includes(entry.name)), false); - assert.deepEqual(sqliteFixtures.optionalAnalysisTables, [ - { - issue: "#14", - id: "flows", - classification: "optional", - tables: ["flows", "flow_memberships"], - indexes: ["idx_flows_criticality", "idx_flows_entry", "idx_flow_memberships_node"] - }, - { - issue: "#15", - id: "communities", - classification: "optional", - tables: ["communities"], - indexes: ["idx_nodes_community", "idx_communities_parent", "idx_communities_cohesion"] - }, - { - issue: "#16", - id: "embeddings", - classification: "supporting", - tables: ["embeddings"], - indexes: [] - } - ]); - const requiredStatusFields = manifest.jsonOutputSurfaces.find((entry) => entry.id === "status-json").requiredFields; - assert.equal(requiredStatusFields.includes("embeddings_count"), false); - const manifestSqlite = manifest.sqliteFixtures.find((entry) => entry.id === "sqlite-required-views"); - assert.deepEqual(manifestSqlite.tables, ["metadata", "nodes", "edges", "nodes_fts"]); - assert.equal(manifestSqlite.indexes.includes("idx_nodes_community"), false); - assert.deepEqual( - manifest.optionalAnalysisSurfaces.map(({ issue, id, classification, status }) => ({ issue, id, classification, status })), - [ - { issue: "#13", id: "coverage", classification: "deferred", status: "deferred" }, - { issue: "#14", id: "flows", classification: "optional", status: "deferred" }, - { issue: "#15", id: "communities", classification: "optional", status: "deferred" }, - { issue: "#16", id: "read_only_suggestions", classification: "supporting", status: "deferred" } - ] - ); - assert.equal(manifest.optionalAnalysisSurfaces.some((entry) => entry.classification === "required"), false); - assertDirectQuery("status-counts", "select kind, count(*) as count from nodes group by kind order by kind"); - assertDirectQuery("impact-edges-from-file", "select kind, source_qualified, target_qualified from edges where file_path = ?"); - for (const id of ["serve-jsonl-ping", "serve-jsonl-status", "serve-jsonl-query", "serve-jsonl-search", "serve-jsonl-shutdown"]) { - assert.ok(daemonFixtures.envelopes.find((entry) => entry.id === id), `missing daemon envelope ${id}`); - } - }); - - it("keeps the golden corpus synthetic and internally consistent", () => { - assert.equal(goldenCorpus.origin, "covibes-authored-synthetic"); - assert.equal(goldenCorpus.containsSourceCode, false); - const nodes = goldenCorpus.expectedFacts.parser.nodes; - const edges = goldenCorpus.expectedFacts.store.edges; - const nodeIds = new Set(nodes.map((node) => node.id)); - for (const edge of edges) { - assert.equal(nodeIds.has(edge.source), true, `missing edge source ${edge.source}`); - assert.equal(nodeIds.has(edge.target), true, `missing edge target ${edge.target}`); - } - assert.deepEqual(countBy(nodes, "kind"), goldenCorpus.expectedFacts.status.nodesByKind); - assert.deepEqual(countBy(edges, "kind"), goldenCorpus.expectedFacts.status.edgesByKind); - }); - - it("records Rust fixture coverage without old-tool replacement claims", () => { - const coveredRows = new Set(manifest.goldenCorpus.covers); - for (const row of [ - "rust-source-extraction-fixtures", - "rust-store-freshness-fts", - "rust-query-impact-search", - "mixed-rust-ts-source-fixture" - ]) { - assert.equal(coveredRows.has(row), true, `missing Rust coverage row ${row}`); - } - assert.ok(goldenCorpus.expectedFacts.status.languages.includes("rust")); - assert.ok(goldenCorpus.expectedFacts.parser.nodes.some((node) => node.id === "struct:src/lib.rs#Widget")); - assert.ok(goldenCorpus.expectedFacts.store.edges.some((edge) => edge.kind === "TESTED_BY")); - assert.equal(manifest.provenance.referenceReceiptsAreImplementationInput, false); - }); - - it("records baseline receipts as non-implementation reference evidence", () => { - assert.equal(baselineReceipts.label, "reference_evidence_non_implementation_input"); - assert.equal(baselineReceipts.sourceTool, "current external graph dev wrapper"); - assert.deepEqual(baselineReceipts.receipts.map((receipt) => receipt.metric), [ - "install_setup_ms", - "cold_build_ms", - "incremental_update_ms", - "impact_cold_ms", - "impact_hot_ms", - "search_ms", - "db_size_bytes", - "wal_size_bytes", - "daemon_startup_ms", - "daemon_query_ms" - ]); - for (const receipt of baselineReceipts.receipts) { - assert.equal(receipt.nonImplementationInput, true); - assert.equal(receipt.value > 0, true, `nonzero baseline ${receipt.metric}`); - } - }); - - it("records #4 CRG parity rows as non-implementation compatibility evidence", () => { - const coveredRows = new Set(manifest.goldenCorpus.covers); - for (const row of [ - "code-review-graph-cli-surface", - "crg-watch-roots-ignore-reconcile", - "crg-wal-health-checkpoint-pressure", - "crg-hot-query-socket", - "crg-mcp-tool-surface", - "crg-impact-query-review-search", - "lattice-native-graph-provider-surfaces", - "lattice-current-tools-graph-status", - "lattice-crg-reference-baseline-release-fixtures", - "covibes-crg-watch-ci-unit-gate", - "covibes-push-ready-crg-freshness-impact-gate", - "covibes-agent-guidance-crg-watch-and-reads", - "mcp-server-name-code-review-graph-compatibility" - ]) { - assert.equal(coveredRows.has(row), true, `missing #4 parity row ${row}`); - } - assert.equal(baselineReceipts.label, "reference_evidence_non_implementation_input"); - assert.equal(manifest.provenance.referenceReceiptsAreImplementationInput, false); - }); - - it("enforces provenance guardrails for reference data", () => { - assert.equal(manifest.provenance.containsPythonCrgSource, false); - assert.equal(manifest.provenance.containsPackageMetadata, false); - assert.equal(manifest.provenance.containsGitHistory, false); - assert.equal(manifest.provenance.referenceReceiptsAreImplementationInput, false); - assert.deepEqual(manifest.provenance.allowedMentionPaths, [ - "docs/graph-reference-evidence/", - "packages/fixtures/graph-reference-evidence/" - ]); - for (const file of ["manifest.json", "sqlite-fixtures.json", "daemon-socket-fixtures.json", "golden-corpus.json"]) { - assert.doesNotMatch(readFileSync(new URL(file, fixtureRoot), "utf8"), /tirth8205|pyproject\.toml|setup\.py|setup\.cfg|Pipfile|git clone/i); - } - }); -}); - -function readFixture(file) { - const url = new URL(file, fixtureRoot); - assert.equal(existsSync(url), true, `missing ${file}`); - return JSON.parse(readFileSync(url, "utf8")); -} - -function assertCommand(id, referenceCommand, canonicalCommand) { - const command = manifest.commandSurfaces.find((entry) => entry.id === id); - assert.ok(command, `missing command evidence ${id}`); - assert.equal(command.classification, "required"); - assert.deepEqual(command.referenceCommand, referenceCommand); - assert.deepEqual(command.canonicalCommand, canonicalCommand); -} - -function assertDirectQuery(id, sql) { - const query = sqliteFixtures.directReaderQueries.find((entry) => entry.id === id); - assert.ok(query, `missing direct query ${id}`); - assert.equal(query.sql, sql); -} - -function countBy(entries, key) { - return entries.reduce((counts, entry) => { - counts[entry[key]] = (counts[entry[key]] ?? 0) + 1; - return counts; - }, {}); -} diff --git a/tests/graph-store-conformance.test.mjs b/tests/graph-store-conformance.test.mjs index 4f8cd3d..2855963 100644 --- a/tests/graph-store-conformance.test.mjs +++ b/tests/graph-store-conformance.test.mjs @@ -462,7 +462,7 @@ describe("GraphProvider SQLite store conformance", () => { withFixtureCopy((fixtureRoot) => { mkdirSync(join(fixtureRoot, "ignored"), { recursive: true }); - writeFileSync(join(fixtureRoot, ".code-review-graphignore"), "ignored/\n"); + writeFileSync(join(fixtureRoot, ".gitignore"), "ignored/\n"); writeFileSync(join(fixtureRoot, "ignored/generated.ts"), "export const ignored = true;\n"); const build = graphProviderBuild({ repoRoot: fixtureRoot }); assert.equal(build.status.state, "available"); @@ -561,14 +561,14 @@ describe("GraphProvider SQLite store conformance", () => { assert.equal(build.status.state, "available"); assertStoreHasPath(build.status.dbPath, "src/generated.ts"); - writeFileSync(join(fixtureRoot, ".code-review-graphignore"), "src/generated.ts\n"); + writeFileSync(join(fixtureRoot, ".gitignore"), "src/generated.ts\n"); const update = graphProviderUpdate({ repoRoot: fixtureRoot }); assert.deepEqual(update.summary.deletedFiles, ["src/generated.ts"]); assertStoreMissingPath(update.status.dbPath, "src/generated.ts"); }); }); - it("excludes generated, private, dependency, gitignore, and code-review-graphignore paths", () => { + it("excludes generated, private, dependency, and gitignore paths", () => { withRobustnessFixtureCopy((fixtureRoot) => { createRuntimeIgnoredFiles(fixtureRoot); const build = graphProviderBuild({ repoRoot: fixtureRoot }); @@ -576,19 +576,16 @@ describe("GraphProvider SQLite store conformance", () => { assert.deepEqual(build.summary.changedFiles, ["ignored/keep.ts", "shared/util.ts", "src/app.ts"]); for (const excluded of [ "ignored/drop.ts", - "crg-ignored/drop.ts", + "policy-ignored/drop.ts", "node_modules/pkg/index.ts", ".pnpm/pkg/index.ts", "vendor/pkg/generated.ts", - ".ace/runtime/generated.ts", ".agents/runtime/generated.ts", ".claude/runtime/generated.ts", ".codex/runtime/generated.ts", ".gemini/runtime/generated.ts", ".opcore/graph/generated.ts", ".opencode/runtime/generated.ts", - ".rox-cache/generated.ts", - ".robustness-engine-cache/generated.ts", "dist/generated.ts" ]) { assertStoreMissingPath(build.status.dbPath, excluded); @@ -701,21 +698,19 @@ function withRobustnessFixtureCopy(run) { } function createRuntimeIgnoredFiles(fixtureRoot) { - writeFileSync(join(fixtureRoot, ".gitignore"), "ignored/drop.ts\n"); + writeFileSync(join(fixtureRoot, ".gitignore"), "ignored/drop.ts\npolicy-ignored/**\n"); for (const [directory, file] of [ ["ignored", "drop.ts"], + ["policy-ignored", "drop.ts"], ["node_modules/pkg", "index.ts"], [".pnpm/pkg", "index.ts"], ["vendor/pkg", "generated.ts"], - [".ace/runtime", "generated.ts"], [".agents/runtime", "generated.ts"], [".claude/runtime", "generated.ts"], [".codex/runtime", "generated.ts"], [".gemini/runtime", "generated.ts"], [".opcore/graph", "generated.ts"], [".opencode/runtime", "generated.ts"], - [".rox-cache", "generated.ts"], - [".robustness-engine-cache", "generated.ts"], ["dist", "generated.ts"] ]) { mkdirSync(join(fixtureRoot, directory), { recursive: true }); diff --git a/tests/helpers/asp-dogfood-fixture.mjs b/tests/helpers/asp-dogfood-fixture.mjs index d065bf9..ead6f01 100644 --- a/tests/helpers/asp-dogfood-fixture.mjs +++ b/tests/helpers/asp-dogfood-fixture.mjs @@ -19,12 +19,11 @@ export function validAspDogfoodReceipt() { repoEnrollment: repoEnrollmentFixture(), hostEvaluation: hostEvaluationFixture(hostDecision), providerProbe: providerProbeFixture(), - currentToolGuardrails: guardrailsFixture(), + selfValidation: selfValidationFixture(), unsupportedSurfaces: unsupportedSurfacesFixture(), - parityBlockers: [{ source: "docs/planning/old-tool-compatibility-matrix.md:1", detail: "old-tool guardrails retained" }], + parityBlockers: [], authority: authorityFixture(), publicReleaseActions: [], - oldToolReplacementClaimed: false, forbiddenMarkerScan: { scannedTextCount: 2, findingCount: 0, markersBlocked: aspDogfoodForbiddenProviderMarkers } }; } @@ -32,21 +31,27 @@ export function validAspDogfoodReceipt() { export function invalidAspDogfoodCases(receipt) { return [ ["opcore asp serve entrypoint", { ...receipt, provider: { ...receipt.provider, command: ["opcore", "asp", "serve"] } }, /provider command/], - ["ACE runtime provider entrypoint", { ...receipt, provider: { ...receipt.provider, binPath: ".ace/runtime/bin/opcore-asp-provider" } }, /node_modules\/\.bin\/opcore-asp-provider|forbidden marker/], + [ + "private provider entrypoint", + { + ...receipt, + provider: { ...receipt.provider, binPath: "private/runtime/opcore-asp-provider" } + }, + /node_modules\/\.bin\/opcore-asp-provider/ + ], ["failed ASP server add", failedManagerServerAdd(receipt), /manager server add status must be passed/], ["failed ASP repo enable", failedRepoEnable(receipt), /repo enable status must be passed/], ["failed ASP host check", failedHostCheck(receipt), /host check status must be passed/], ["missing host fixture evidence", missingHostFixture(receipt), /host fixture evidence/], ["host fixture mutates source repo", sourceMutatingHostFixture(receipt), /source repo/], ["failed provider probe", failedProviderProbe(receipt), /provider probe status must be passed/], - ["failed required old-tool guardrail", failedRequiredGuardrail(receipt), /required guardrail current-tools-validate-changed must pass/], + ["failed self-validation", failedSelfValidation(receipt), /self-validation status must be passed/], ["missing host receipt authority evidence", missingHostAuthority(receipt), /authorityEvidence/], ["provider output as host decision", providerDecisionLeak(receipt), /host-owned field|host-owned decision|hostOwnedFieldLeak/], - ["missing old-tool guardrail", missingGuardrail(receipt), /guardrail ids/], + ["missing self-validation", missingSelfValidation(receipt), /self-validation receipt is required/], ["unsupported inspect clean coverage", cleanInspectCoverage(receipt), /clean coverage/], ["silent local authority weakening", weakenedAuthority(receipt), /weaken shared authority/], - ["public publish action", { ...receipt, publicReleaseActions: [{ action: "publish" }] }, /public publish/], - ["old-tool replacement claim", { ...receipt, oldToolReplacementClaimed: true }, /old-tool replacement/] + ["public publish action", { ...receipt, publicReleaseActions: [{ action: "publish" }] }, /public publish/] ]; } @@ -113,8 +118,7 @@ function aspHomeFixture() { temp: true, isolated: true, sharedStateMutated: false, - pathSanitized: true, - aceRuntimeBinExcluded: true + pathSanitized: true }; } @@ -228,18 +232,22 @@ function assessmentFixture() { }; } -function guardrailsFixture() { - return [ - { ...command("current-tools-validate-changed", ["npm", "run", "current-tools:validate-changed"]), retained: true }, - { ...command("current-tools-validate-rust-graph", ["npm", "run", "current-tools:validate-rust-graph"]), retained: true }, - { id: "current-tools-validate-all", command: ["npm", "run", "current-tools:validate-all"], status: "retained-not-run", exitCode: null, stdoutSha256: "0".repeat(64), stderrSha256: "0".repeat(64), retained: true, assertion: "retained by default" } - ]; +function selfValidationFixture() { + return { + id: "opcore-self-check", + command: ["npm", "run", "opcore:self-check"], + status: "passed", + exitCode: 0, + stdoutSha256: "0".repeat(64), + stderrSha256: "0".repeat(64), + assertion: "Opcore self-validation passed" + }; } function unsupportedSurfacesFixture() { return [ { surface: "inspect", status: "parity-blocker", cleanCoverage: false, blocker: "inspect not mapped into ASP #120" }, - { surface: "edit", status: "retained-old-tool-gate", cleanCoverage: false, blocker: "edit not mapped into ASP #120" } + { surface: "edit", status: "parity-blocker", cleanCoverage: false, blocker: "edit not mapped into ASP #120" } ]; } @@ -300,13 +308,8 @@ function failedProviderProbe(receipt) { return { ...receipt, providerProbe: failedCommand(receipt.providerProbe) }; } -function failedRequiredGuardrail(receipt) { - return { - ...receipt, - currentToolGuardrails: receipt.currentToolGuardrails.map((entry) => - entry.id === "current-tools-validate-changed" ? failedCommand(entry) : entry - ) - }; +function failedSelfValidation(receipt) { + return { ...receipt, selfValidation: failedCommand(receipt.selfValidation) }; } function missingHostAuthority(receipt) { @@ -317,8 +320,10 @@ function providerDecisionLeak(receipt) { return { ...receipt, providerProbe: { ...receipt.providerProbe, assessment: { ...receipt.providerProbe.assessment, decision: "allow" }, hostOwnedFieldLeak: true } }; } -function missingGuardrail(receipt) { - return { ...receipt, currentToolGuardrails: receipt.currentToolGuardrails.filter((entry) => entry.id !== "current-tools-validate-changed") }; +function missingSelfValidation(receipt) { + const { selfValidation, ...withoutSelfValidation } = receipt; + void selfValidation; + return withoutSelfValidation; } function cleanInspectCoverage(receipt) { diff --git a/tests/import-boundaries.test.mjs b/tests/import-boundaries.test.mjs index 276fe7d..5485fdc 100644 --- a/tests/import-boundaries.test.mjs +++ b/tests/import-boundaries.test.mjs @@ -203,9 +203,7 @@ describe("package import boundaries", () => { /resolveGraphCoreArtifact/i, /native artifact loader/i, /raw sqlite/i, - /graph sqlite/i, - /graph-reference-evidence execution/i, - /\bcrg\s+(status|serve|query|refresh|build|inspect|impact|search)\b/i + /graph sqlite/i ]; for (const packageDir of ["edit", "validation"]) { for (const file of sourceFiles(`packages/${packageDir}/src`)) { diff --git a/tests/installed-bins.test.mjs b/tests/installed-bins.test.mjs index fbb11bc..dfa47db 100644 --- a/tests/installed-bins.test.mjs +++ b/tests/installed-bins.test.mjs @@ -16,7 +16,7 @@ import { releasePackageDirForName } from "../scripts/release-package-dirs.mjs"; import { createStagedOpcorePackage } from "../scripts/stage-opcore-bundle.mjs"; const removedLegacyCommandField = `legacy${"Command"}`; -const onboardingForbiddenOutput = /(^|[\\/"'\s])(?:lattice|crg|cix|rox)(?:$|[\\/"'\s])|\.ace\/runtime|LATTICE_CURRENT_TOOLS_DIR|\/Users\/tom|oldToolReplacementClaimed"?\s*:\s*true/i; +const onboardingForbiddenOutput = /(^|[\\/"'\s])lattice(?:$|[\\/"'\s])|\/Users\/tom/i; const currentTarget = `${process.platform}-${process.arch}`; const currentNativePackage = graphCoreNativePackageNamesByTarget[currentTarget]; @@ -45,7 +45,6 @@ describe("installed package bins", () => { assert.equal(existsSync(binPath(project, "opcore")), true); assert.equal(existsSync(binPath(project, "opcore-asp-provider")), true); assert.equal(existsSync(join(project, "node_modules", "opcore", "node_modules", "jsonc-parser", "package.json")), true); - for (const oldBin of ["lattice", "crg", "cix", "rox"]) assert.equal(existsSync(binPath(project, oldBin)), false, oldBin); assertAspProviderInitializeSmoke(project); assertSmoke(project, ["status", "--json"], 0); @@ -218,9 +217,6 @@ describe("installed package bins", () => { run("npm", ["install", "--ignore-scripts", "--no-audit", "--no-fund", ...tarballs], { cwd: project }); assert.equal(existsSync(binPath(project, "opcore")), true); - for (const forbiddenBin of ["lattice", "crg", "cix", "rox"]) { - assert.equal(existsSync(binPath(project, forbiddenBin)), false, forbiddenBin); - } const status = assertSmoke(project, ["status", "--json"], 0, "opcore"); assert.deepEqual(status.canonicalCommand, ["opcore", "status"]); const tryResult = assertSmoke(project, ["try", "--json"], 0, "opcore"); @@ -693,10 +689,7 @@ function assertManagedDescriptor(project) { ); assert.equal(existsSync(descriptorPath), true, descriptorPath); const descriptorText = readFileSync(descriptorPath, "utf8"); - assert.doesNotMatch( - descriptorText, - /(^|[\\/"'\s])\.ace(?:[\\/"'\s]|$)|LATTICE_CURRENT_TOOLS_DIR|\/Users\/tom|(^|[\\/\s])(?:lattice|crg|cix|rox)(?:$|[\\/\s])/i - ); + assert.doesNotMatch(descriptorText, /\/Users\/tom/i); const descriptor = validateManagedToolDescriptor(JSON.parse(descriptorText)); assert.deepEqual(descriptor.capabilities.validation.pythonProjectContext, { schemaId: "opcore.python.project-context.v1", @@ -778,7 +771,6 @@ function assertCliJson(command, args, expectedExitCode, cwd, options = {}) { ); assert.equal(Object.hasOwn(parsed, "alias"), false); assert.equal(Object.hasOwn(parsed, removedLegacyCommandField), false); - assert.notEqual(parsed.oldToolReplacementClaimed, true); assert.doesNotMatch(JSON.stringify(parsed), onboardingForbiddenOutput); if (options.telemetryPath) { writeLatencyRecord(options.telemetryPath, createLatencyRecord(command, parsed, options.fixture, cwd, durationMs)); diff --git a/tests/launch-claim-scrub.test.mjs b/tests/launch-claim-scrub.test.mjs index 7bc254f..9bab600 100644 --- a/tests/launch-claim-scrub.test.mjs +++ b/tests/launch-claim-scrub.test.mjs @@ -15,7 +15,6 @@ const repoRoot = fileURLToPath(new URL("..", import.meta.url)); // One positive sample per forbidden label. Each must be a phrase the alpha must never ship. const overclaimSamples = { "public ASP standard claim": "ASP is now the public standard for agent checks.", - "old-tool replacement claim": "Opcore replaces Rox, CRG, and CIX in your pipeline.", "generic Opcore replacement claim": "Opcore replaces your linters.", "universal stack claim": "Full coverage for every language and platform.", "universal agent claim": "Works with every agent on the market.", @@ -26,7 +25,6 @@ const overclaimSamples = { "blended score claim": "Get a single robustness score for the whole repo.", "asp router command claim": "Run opcore asp serve to start the host.", "provider authority claim": "The provider grants gate authority to allow merges.", - "ACE-managed distribution claim": "Distributed as an ACE-managed tool.", "old product name": "lattice validation complete.", "doubled Opcore token": "No Opcore/Opcore issue is open." }; @@ -79,7 +77,6 @@ test("scrub allowlists intentional internal transitional markers", () => { { label: "graph-package", text: "lattice-graph-core\n" }, { label: "daemon", text: "lattice.graph.daemon\n" }, { label: "generated-dist", text: "dist/lattice\n" }, - { label: "old-bin-policy", text: "oldBinsAbsent: { lattice: true, crg: true, cix: true, rox: true }\n" }, { label: "roadmap-policy", text: '- "Lattice" as product or launch branding.\n' } ]); @@ -94,8 +91,7 @@ test("honest launch wording passes the scrub", () => { "Opcore does not blend findings into an opaque score.", "Prefer concrete counts and file locations over scores.", "Providers assess; ASP hosts decide. Do not treat provider output as a gate decision.", - "Opcore is an independently installed ASP Core check provider; the host owns allow/deny decisions.", - "Retain your existing Rox, CRG, and CIX guardrails; this is additive." + "Opcore is an independently installed ASP Core check provider; the host owns allow/deny decisions." ].join("\n"); assert.deepEqual(scrubLaunchClaims(clean), []); }); diff --git a/tests/opcore-facade.test.mjs b/tests/opcore-facade.test.mjs index 2ca75c4..f1966da 100644 --- a/tests/opcore-facade.test.mjs +++ b/tests/opcore-facade.test.mjs @@ -27,13 +27,12 @@ describe("opcore public facade", () => { assert.equal(human.stderr, ""); assert.equal(firstNonEmptyLine(human.stdout).startsWith("Coverage"), true); assert.equal(human.stdout.indexOf("Coverage") < human.stdout.indexOf("Findings"), true); - assert.doesNotMatch(human.stdout, /\blattice\b|\bcrg\b|\bcix\b|\brox\b|ASP setup|ACE setup|sibling checkout/i); + assert.doesNotMatch(human.stdout, /\blattice\b|ASP setup|sibling checkout/i); assert.equal(existsSync(join(fixtureRoot, ".opcore", "report.json")), true); assert.equal(existsSync(join(fixtureRoot, ".opcore", "history.jsonl")), true); assert.equal(existsSync(join(fixtureRoot, ".opcore", "telemetry.jsonl")), true); assert.deepEqual(readdirSync(join(fixtureRoot, ".opcore")).sort(), ["history.jsonl", "report.json", "telemetry.jsonl"]); assert.equal(existsSync(join(fixtureRoot, ".lattice")), false); - assert.equal(existsSync(join(fixtureRoot, ".ace")), false); assert.equal(existsSync(join(fixtureRoot, ".asp")), false); assert.deepEqual( collectRepoPaths(fixtureRoot).filter((path) => !before.includes(path) && path !== ".opcore" && !path.startsWith(".opcore/")), @@ -1093,7 +1092,7 @@ printf '%s\\n' '${JSON.stringify({ assert.match(agents, /opcore check --changed/); assert.match(agents, /preserve existing repo lint\/test\/CI\/pre-commit guardrails/i); assert.match(agents, /unsupported stacks and degraded tools/i); - assert.match(agents, /Do not rely on ACE, Rox, CRG, CIX, or ASP host authority/i); + assert.match(agents, /Use Opcore validation directly; ASP hosts retain their own decision authority/i); const undo = JSON.parse(readFileSync(join(temp, ".opcore", "init-undo.json"), "utf8")); assert.deepEqual( undo.entries.find((entry) => entry.path === ".gitignore"), @@ -2005,7 +2004,7 @@ printf '%s\\n' '${JSON.stringify({ assert.match(human, /Findings:\n(?:.*\n)* typescript\.type_errors:/); assert.match(human, /rust\.source_hygiene:/); assert.match(human, /coverage\.unsupported_stacks:/); - assert.doesNotMatch(human, /score|SAST|security scanner|AI authorship|Rox|CRG|CIX|ACE/i); + assert.doesNotMatch(human, /score|SAST|security scanner|AI authorship/i); } finally { for (const root of cleanupRoots) rmSync(root, { recursive: true, force: true }); rmSync(temp, { recursive: true, force: true }); diff --git a/tests/provenance-policy.test.mjs b/tests/provenance-policy.test.mjs index 09081fb..913f26e 100644 --- a/tests/provenance-policy.test.mjs +++ b/tests/provenance-policy.test.mjs @@ -36,7 +36,7 @@ describe("provenance history policy", () => { commitAll(repo); const result = run(repo, ["scripts/check-provenance.mjs"], { expectFailure: true }); - assert.match(`${result.stderr}\n${result.stdout}`, /Forbidden Python code-review-graph provenance|refs\/heads\/main/); + assert.match(`${result.stderr}\n${result.stdout}`, /Forbidden copied git history provenance|refs\/heads\/main/); } finally { rmSync(repo, { recursive: true, force: true }); } @@ -44,7 +44,7 @@ describe("provenance history policy", () => { }); function tempPolicyRepo() { - const repo = mkdtempSync(join(tmpdir(), "lattice-provenance-policy-")); + const repo = mkdtempSync(join(tmpdir(), "opcore-provenance-policy-")); mkdirSync(join(repo, "scripts"), { recursive: true }); cpSync(join(repoRoot, "scripts/check-provenance.mjs"), join(repo, "scripts/check-provenance.mjs")); const init = spawnSync("git", ["init", "--quiet"], { cwd: repo, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }); diff --git a/tests/scaffold.test.mjs b/tests/scaffold.test.mjs index a8a0724..56ef734 100644 --- a/tests/scaffold.test.mjs +++ b/tests/scaffold.test.mjs @@ -4,7 +4,7 @@ import { existsSync, readFileSync } from "node:fs"; const readJson = (path) => JSON.parse(readFileSync(path, "utf8")); -describe("lattice scaffold", () => { +describe("Opcore scaffold", () => { it("keeps opcore, graph, edit, and validation as separate package tracks", () => { const root = readJson("package.json"); assert.deepEqual(root.workspaces, [ @@ -57,45 +57,17 @@ describe("lattice scaffold", () => { ]); }); - it("does not publish as code-review-graph or gungnir", () => { - for (const name of [ - "contracts", - "opcore", - "graph", - "edit", - "validation", - "validation-policy", - "validation-clone", - "validation-docs", - "validation-python", - "validation-rust", - "validation-typescript", - "fixtures" - ]) { - const manifest = readJson(`packages/${name}/package.json`); - assert.equal(manifest.name.includes("code-review-graph"), false); - assert.equal(manifest.name.includes("gungnir"), false); - } - }); - - it("keeps agent tooling pointed at current external tools", () => { + it("uses Opcore for repository validation", () => { assert.equal(readFileSync("AGENTS.md", "utf8"), readFileSync("CLAUDE.md", "utf8")); - assert.equal(existsSync("ace.json"), true); - assert.equal(existsSync("rox.json"), true); + assert.equal(existsSync(".opcore/config"), true); assert.equal(existsSync(".zeroshot/settings.json"), true); - assert.equal(existsSync("scripts/setup-current-tools.sh"), true); + assert.equal(existsSync("scripts/run-opcore-self-check.mjs"), true); assert.equal(existsSync("scripts/ci/run-local-ci-equivalent.sh"), true); - const setupTools = readFileSync("scripts/setup-current-tools.sh", "utf8"); - assert.match(setupTools, /external ACE-managed tools/); - assert.match(setupTools, /implementation_package_dir/); - assert.match(setupTools, /use current external tools, not \$\{implementation_path\}/); - assert.match(setupTools, /aceTools/); - assert.match(setupTools, /binRoot/); - assert.match(setupTools, /latticeCurrentTools/); - - const ace = readJson("ace.json"); - assert.match(ace.mcpServers["code-review-graph"].args.join("\n"), /\.ace\/runtime\/bin\/crg/); + const root = readJson("package.json"); + assert.equal(root.scripts.setup, "npm ci"); + assert.match(root.scripts["opcore:self-check"], /scripts\/run-opcore-self-check\.mjs/); + assert.deepEqual(readJson(".zeroshot/settings.json").worktree.setup, ["npm ci"]); }); it("pins runtime CLI decision anchors", () => { diff --git a/tests/schema-contracts.test.mjs b/tests/schema-contracts.test.mjs index 6b80a46..73f81de 100644 --- a/tests/schema-contracts.test.mjs +++ b/tests/schema-contracts.test.mjs @@ -2182,13 +2182,6 @@ describe("Opcore JSON schema wire constraints", () => { }), false ); - assert.equal( - isValidDefinition("ManagedToolDescriptor", { - ...validManagedToolDescriptor(), - entrypoints: [{ ...validManagedToolDescriptor().entrypoints[0], bin: ["r", "o", "x"].join("") }] - }), - false - ); assert.equal( isValidDefinition("ManagedToolDescriptor", { ...validManagedToolDescriptor(), @@ -2210,40 +2203,6 @@ describe("Opcore JSON schema wire constraints", () => { }), false ); - assert.equal( - isValidDefinition("ManagedToolDescriptor", { - ...validManagedToolDescriptor(), - artifacts: [{ ...validManagedToolDescriptor().artifacts[0], path: ".ace\\runtime\\bin\\lattice" }] - }), - false - ); - assert.equal( - isValidDefinition("ManagedToolDescriptor", { - ...validManagedToolDescriptor(), - artifacts: [{ ...validManagedToolDescriptor().artifacts[0], path: ".ace" }] - }), - false - ); - assert.equal( - isValidDefinition("ManagedToolDescriptor", { - ...validManagedToolDescriptor(), - artifacts: [{ ...validManagedToolDescriptor().artifacts[0], path: "dist/.ace" }] - }), - false - ); - assert.equal( - isValidDefinition("ManagedToolDescriptor", { - ...validManagedToolDescriptor(), - provenanceHooks: [ - { - id: "private-runtime-wrapper", - command: [".ace\\runtime\\bin\\lattice", "status"], - expectedExitCode: 0 - } - ] - }), - false - ); assert.equal( isValidDefinition("ManagedToolDescriptor", { ...validManagedToolDescriptor(), @@ -2544,103 +2503,6 @@ describe("Opcore JSON schema wire constraints", () => { ); }); - it("accepts and rejects graph reference evidence manifest schemas", () => { - const manifest = validGraphReferenceEvidenceManifest(); - assert.equal(isValidDefinition("GraphReferenceEvidenceManifest", manifest), true); - assert.equal( - isValidDefinition("GraphReferenceEvidenceManifest", { - ...manifest, - issue: "#18" - }), - false - ); - assert.equal( - isValidDefinition("GraphReferenceEvidenceManifest", { - ...manifest, - commandSurfaces: [] - }), - false - ); - assert.equal( - isValidDefinition("GraphReferenceEvidenceManifest", { - ...manifest, - commandSurfaces: [ - { - ...manifest.commandSurfaces[0], - classification: "release_blocking" - } - ] - }), - false - ); - assert.equal( - isValidDefinition("GraphReferenceEvidenceManifest", { - ...manifest, - commandSurfaces: [ - { - ...manifest.commandSurfaces[0], - fixtures: [] - } - ] - }), - false - ); - assert.equal( - isValidDefinition("GraphReferenceEvidenceManifest", { - ...manifest, - optionalAnalysisSurfaces: [ - { - ...manifest.optionalAnalysisSurfaces[0], - fixtures: [] - } - ] - }), - false - ); - assert.equal( - isValidDefinition("GraphReferenceEvidenceManifest", { - ...manifest, - optionalAnalysisSurfaces: manifest.optionalAnalysisSurfaces.map((surface) => - surface.id === "flows" ? { ...surface, issue: "#13" } : surface - ) - }), - false - ); - assert.equal( - isValidDefinition("GraphReferenceEvidenceManifest", { - ...manifest, - optionalAnalysisSurfaces: manifest.optionalAnalysisSurfaces.map((surface) => - surface.id === "flows" ? { ...surface, classification: "required" } : surface - ) - }), - false - ); - assert.equal( - isValidDefinition("GraphReferenceEvidenceManifest", { - ...manifest, - optionalAnalysisSurfaces: manifest.optionalAnalysisSurfaces.map(({ issue, ...surface }) => surface) - }), - false - ); - assert.equal( - isValidDefinition("GraphReferenceEvidenceManifest", { - ...manifest, - optionalAnalysisSurfaces: manifest.optionalAnalysisSurfaces.map((surface) => ({ ...surface, fixtures: [] })) - }), - true - ); - assert.equal( - isValidDefinition("GraphReferenceEvidenceManifest", { - ...manifest, - provenance: { - ...manifest.provenance, - containsGitHistory: true - } - }), - false - ); - }); - it("accepts and rejects graph release receipt schemas", () => { const receipt = validGraphReleaseReceipt(); assert.equal(isValidDefinition("GraphReleaseReceipt", receipt), true); @@ -2715,6 +2577,10 @@ describe("Opcore JSON schema wire constraints", () => { }), false ); + }); + + it("rejects incomplete graph release query schemas", () => { + const receipt = validGraphReleaseReceipt(); assert.equal( isValidDefinition("GraphReleaseReceipt", { ...receipt, @@ -2722,6 +2588,10 @@ describe("Opcore JSON schema wire constraints", () => { }), false ); + }); + + it("rejects incomplete graph release ownership and optional-surface schemas", () => { + const receipt = validGraphReleaseReceipt(); assert.equal( isValidDefinition("GraphReleaseReceipt", { ...receipt, @@ -2768,6 +2638,10 @@ describe("Opcore JSON schema wire constraints", () => { }), false ); + }); + + it("rejects incomplete graph release transport, handoff, and package schemas", () => { + const receipt = validGraphReleaseReceipt(); assert.equal( isValidDefinition("GraphReleaseReceipt", { ...receipt, @@ -2871,17 +2745,6 @@ describe("Opcore JSON schema wire constraints", () => { }), false ); - assert.equal( - isValidDefinition("ReleaseReceipt", { - ...receipt, - packages: receipt.packages.map((entry) => - entry.packageName === "opcore" - ? { ...entry, bins: { ...entry.bins, rox: "dist/index.js" } } - : entry - ) - }), - false - ); assert.equal( isValidDefinition("ReleaseReceipt", { ...receipt, @@ -2941,6 +2804,10 @@ describe("Opcore JSON schema wire constraints", () => { }), false ); + }); + + it("rejects invalid cutover command and installed-package schemas", () => { + const receipt = validReleaseCutoverReceipt(); assert.equal( isValidDefinition("ReleaseCutoverReceipt", { ...receipt, @@ -2952,6 +2819,10 @@ describe("Opcore JSON schema wire constraints", () => { }), false ); + }); + + it("rejects incomplete cutover language and negative-check schemas", () => { + const receipt = validReleaseCutoverReceipt(); assert.equal( isValidDefinition("ReleaseCutoverReceipt", { ...receipt, @@ -3030,14 +2901,22 @@ describe("Opcore JSON schema wire constraints", () => { assert.equal( isValidDefinition("ReleaseCutoverReceipt", { ...receipt, - pythonCommandReceipts: receipt.pythonCommandReceipts.map((entry) => - entry.id === "graph-python-search" - ? { ...entry, command: ["lattice", "graph", "search", "Greeting"], canonicalCommand: ["lattice", "graph", "search", "Greeting"] } + pythonCommandReceipts: receipt.pythonCommandReceipts.map((entry) => + entry.id === "graph-python-search" + ? { + ...entry, + command: ["node", "graph", "search", "Greeting"], + canonicalCommand: ["node", "graph", "search", "Greeting"] + } : entry ) }), false ); + }); + + it("rejects invalid cutover self-validation and environment schemas", () => { + const receipt = validReleaseCutoverReceipt(); assert.equal( isValidDefinition("ReleaseCutoverReceipt", { ...receipt, @@ -3061,7 +2940,7 @@ describe("Opcore JSON schema wire constraints", () => { ...receipt, negativeChecks: receipt.negativeChecks.map((entry) => entry.id === "missing-required-graph-check" - ? { ...entry, command: ["lattice", "check", "files", "src/index.ts", "--graph-mode", "required"] } + ? { ...entry, command: ["node", "check", "files", "src/index.ts", "--graph-mode", "required"] } : entry ) }), @@ -3070,28 +2949,16 @@ describe("Opcore JSON schema wire constraints", () => { assert.equal( isValidDefinition("ReleaseCutoverReceipt", { ...receipt, - currentToolGuardrails: receipt.currentToolGuardrails.filter((entry) => entry.id !== "current-tools-validate-changed") + selfValidation: { ...receipt.selfValidation, status: "failed", exitCode: 1 } }), false ); - assert.equal( - isValidDefinition("ReleaseCutoverReceipt", { - ...receipt, - currentToolGuardrails: receipt.currentToolGuardrails.map((entry) => - entry.id === "current-tools-validate-changed" - ? { ...entry, status: "retained-not-run", exitCode: null } - : entry - ) - }), - false - ); - assert.equal(isValidDefinition("ReleaseCutoverReceipt", { ...receipt, oldToolReplacementClaimed: true }), false); assert.equal( isValidDefinition("ReleaseCutoverReceipt", { ...receipt, installedPackages: receipt.installedPackages.map((entry) => entry.packageName === "opcore" - ? { ...entry, installedManifest: { ...entry.installedManifest, bins: { lattice: "dist/index.js", crg: "dist/index.js" } } } + ? { ...entry, installedManifest: { ...entry.installedManifest, bins: { unexpected: "dist/index.js" } } } : entry ) }), @@ -3116,39 +2983,7 @@ describe("Opcore JSON schema wire constraints", () => { assert.equal( isValidDefinition("ReleaseCutoverReceipt", { ...receipt, - environmentIsolation: { ...receipt.environmentIsolation, opcoreBinOnly: false } - }), - false - ); - }); - - it("accepts and rejects old-Rox comparison receipt schemas", () => { - const receipt = validRustOldRoxComparisonReceipt(); - assert.equal(isValidDefinition("RustOldRoxComparisonReceipt", receipt), true); - assert.equal( - isValidDefinition("RustOldRoxComparisonReceipt", { - ...receipt, - surfaces: receipt.surfaces.filter((entry) => entry.id !== "rust.dead-code") - }), - false - ); - assert.equal(isValidDefinition("RustOldRoxComparisonReceipt", { ...receipt, oldToolReplacementClaimed: true }), false); - assert.equal(isValidDefinition("RustOldRoxComparisonReceipt", { ...receipt, publicReleaseActions: ["publish"] }), false); - assert.equal( - isValidDefinition("RustOldRoxComparisonReceipt", { - ...receipt, - surfaces: receipt.surfaces.map((entry) => - entry.id === "rust.function-metrics" ? { ...entry, replacementStatus: "replaced" } : entry - ) - }), - false - ); - assert.equal( - isValidDefinition("RustOldRoxComparisonReceipt", { - ...receipt, - surfaces: receipt.surfaces.map((entry) => - entry.id === "rust.import-graph" ? { ...entry, graphEvidenceExists: true, graphEvidence: [] } : entry - ) + environmentIsolation: { ...receipt.environmentIsolation, opcoreBinsVerified: false } }), false ); @@ -3166,6 +3001,10 @@ describe("Opcore JSON schema wire constraints", () => { }), false ); + }); + + it("rejects invalid ASP dogfood authority and parity schemas", () => { + const receipt = validAspDogfoodReceipt(); assert.equal( isValidDefinition("AspDogfoodReceipt", { ...receipt, @@ -3199,7 +3038,7 @@ describe("Opcore JSON schema wire constraints", () => { assert.equal( isValidDefinition("AspDogfoodReceipt", { ...receipt, - currentToolGuardrails: receipt.currentToolGuardrails.filter((entry) => entry.id !== "current-tools-validate-changed") + selfValidation: { ...receipt.selfValidation, status: "failed", exitCode: 1 } }), false ); @@ -4332,130 +4171,6 @@ function validCommandAdapterRequest() { }; } -function validGraphReferenceEvidenceManifest() { - return { - schemaVersion: 1, - issue: "#19", - origin: "covibes-authored-synthetic", - fixtureRefs: [ - "packages/fixtures/graph-reference-evidence/sqlite-fixtures.json", - "packages/fixtures/graph-reference-evidence/daemon-socket-fixtures.json", - "packages/fixtures/graph-reference-evidence/golden-corpus.json", - "packages/fixtures/graph-reference-evidence/baseline-receipts.json" - ], - commandSurfaces: [ - { - id: "graph-reference-status", - classification: "required", - referenceTool: "current external graph dev wrapper", - referenceCommand: ["status"], - canonicalCommand: ["opcore", "graph", "status"], - flags: ["--repo", "--json"], - positionals: [], - fixtures: ["status-json"], - exitSemantics: { - success: 0, - failure: "nonzero" - } - } - ], - jsonOutputSurfaces: [ - { - id: "status-json", - command: "status", - classification: "required", - requiredFields: ["status", "summary"], - fixtures: ["status-json"], - exitSemantics: { - success: 0, - failure: "nonzero" - } - } - ], - sqliteFixtures: [ - { - id: "sqlite-required-views", - classification: "required", - fixture: "packages/fixtures/graph-reference-evidence/sqlite-fixtures.json", - tables: ["metadata", "nodes", "edges"], - indexes: ["idx_nodes_file"], - metadataKeys: ["schema_version"], - nodeKinds: ["File", "Function", "Test", "Module", "Struct", "Enum", "Trait", "Impl", "Method", "TypeAlias", "Const", "Static", "Macro"], - edgeKinds: ["CALLS", "CONTAINS", "IMPORTS_FROM", "TESTED_BY", "IMPLEMENTS", "DEPENDS_ON", "INHERITS"], - directReaderQueries: ["status-counts"], - fixtures: ["sqlite-fixtures"] - } - ], - daemonFixtures: [ - { - id: "daemon-hot-query", - classification: "required", - fixture: "packages/fixtures/graph-reference-evidence/daemon-socket-fixtures.json", - protocol: "opcore.graph.daemon", - envelopes: ["ping-request", "success-response"], - fixtures: ["daemon-fixtures"] - } - ], - baselineReceipts: [ - { - id: "install-setup", - metric: "install_setup_ms", - classification: "required", - receipt: "packages/fixtures/graph-reference-evidence/baseline-receipts.json", - label: "reference_evidence_non_implementation_input", - sourceAvailability: "unavailable", - nonImplementationInput: true, - fixtures: ["baseline-receipts"] - } - ], - optionalAnalysisSurfaces: [ - { - issue: "#13", - id: "coverage", - classification: "deferred", - status: "deferred", - fixtures: ["coverage-deferred-marker"] - }, - { - issue: "#14", - id: "flows", - classification: "optional", - status: "deferred", - fixtures: ["sqlite-fixtures"] - }, - { - issue: "#15", - id: "communities", - classification: "optional", - status: "deferred", - fixtures: ["sqlite-fixtures"] - }, - { - issue: "#16", - id: "read_only_suggestions", - classification: "supporting", - status: "deferred", - fixtures: ["sqlite-fixtures"] - } - ], - goldenCorpus: { - id: "graph-reference-evidence-golden-corpus-v1", - classification: "required", - fixture: "packages/fixtures/graph-reference-evidence/golden-corpus.json", - covers: ["parser", "store", "query", "search", "freshness", "status"], - fixtures: ["golden-corpus"] - }, - provenance: { - containsPythonCrgSource: false, - containsPackageMetadata: false, - containsGitHistory: false, - referenceReceiptsAreImplementationInput: false, - implementationPackageNames: ["@the-open-engine/opcore-graph"], - allowedMentionPaths: ["docs/graph-reference-evidence/", "packages/fixtures/graph-reference-evidence/"] - } - }; -} - function validGraphReleaseReceipt() { const commandIds = [ "opcore-graph-build", @@ -4589,7 +4304,7 @@ function validGraphReleaseReceipt() { value: 1, unit: metric.endsWith("_bytes") ? "bytes" : "ms", baselineIssue: "#19", - baselineReceipt: "packages/fixtures/graph-reference-evidence/baseline-receipts.json", + baselineReceipt: "docs/release/graph-release-receipt.json", comparison: "recorded" })), packageInspection: { @@ -4600,10 +4315,10 @@ function validGraphReleaseReceipt() { forbiddenMarkersAbsent: true, generatedBuildMetadataAbsent: true, privatePathsAbsent: true, - pythonCrgSourceAbsent: true, - pythonGraphPackageMetadataAbsent: true, - pythonCrgGitHistoryAbsent: true, - forbiddenImplementationPackageNamesAbsent: true, + sourceProvenanceAbsent: true, + packageMetadataAbsent: true, + gitHistoryAbsent: true, + foreignImplementationNamesAbsent: true, inspections: ["npm-pack-dry-run"] }, supportedNativeTargets: graphCoreNativeSupportedTargets, @@ -4686,7 +4401,7 @@ function validGraphReleaseReceipt() { issue, receiptPath: "docs/release/graph-release-receipt.payload.json", checksumSha256: "b".repeat(64), - rollbackNote: "Keep ACE wrappers on current external tools if receipt regresses." + rollbackNote: "Block release and repair Opcore self-validation if this receipt regresses." })) }; } @@ -5065,18 +4780,9 @@ function validReleaseCutoverReceipt() { resolvedChecksums: descriptor.resolvedChecksums }, environmentIsolation: { - currentToolEnvCleared: true, - clearedEnvVarCount: 5, pathSanitized: true, - aceRuntimeBinExcluded: true, - siblingCovibesExcluded: true, - opcoreBinOnly: true, - oldBinsAbsent: { - lattice: true, - crg: true, - cix: true, - rox: true - } + siblingRepositoriesExcluded: true, + opcoreBinsVerified: true }, commandReceipts, rustCommandReceipts, @@ -5125,35 +4831,19 @@ function validReleaseCutoverReceipt() { assertion: "missing Python toolchain stayed degraded" } ], - currentToolGuardrails: [ - { - id: "current-tools-validate-changed", - command: ["npm", "run", "current-tools:validate-changed"], - status: "passed", - exitCode: 0, - stdoutSha256: "7".repeat(64), - stderrSha256: "8".repeat(64), - retained: true, - assertion: "retained changed-file guardrail", - oldToolReplacementClaimed: false - }, - { - id: "current-tools-validate-rust-graph", - command: ["npm", "run", "current-tools:validate-rust-graph"], - status: "passed", - exitCode: 0, - stdoutSha256: "7".repeat(64), - stderrSha256: "8".repeat(64), - retained: true, - assertion: "retained Rust graph guardrail", - oldToolReplacementClaimed: false - } - ], - oldToolReplacementClaimed: false, + selfValidation: { + id: "opcore-self-check", + command: ["npm", "run", "opcore:self-check"], + status: "passed", + exitCode: 0, + stdoutSha256: "7".repeat(64), + stderrSha256: "8".repeat(64), + assertion: "Opcore self-validation passed" + }, forbiddenMarkerScan: { scannedTextCount: 12, findingCount: 0, - markersBlocked: ["private-runtime", "current-tool-env", "private-home", "old-tool-bins"] + markersBlocked: ["private-home", "launch-claim"] }, inputEvidence: [ { @@ -5188,75 +4878,9 @@ function pythonCutoverEvidence(id) { }[id]; } -function validRustOldRoxComparisonReceipt() { - const surface = (id, graphEvidenceExists, graphEvidence, stillUniquelyProvidedByCurrentTools, replacementStatus = "retained") => ({ - id, - graphEvidenceExists, - graphEvidence, - stillUniquelyProvidedByCurrentTools, - replacementStatus - }); - return { - schemaVersion: 1, - issue: "#29", - origin: "covibes-authored-old-rox-comparison", - generatedAt: "2026-06-27T00:00:00.000Z", - privateRepo: true, - oldToolReplacementClaimed: false, - publicReleaseActions: [], - surfaces: [ - surface( - "rust.rustdoc", - false, - ["No graph fact replaces rustdoc diagnostics."], - ["rustdoc diagnostics and broken intra-doc link policy remain current-tool evidence."] - ), - surface( - "rust.import-graph", - true, - ["Rust graph emits IMPORTS_FROM and DEPENDS_ON edges for module files."], - ["Rox/current tooling still uniquely provides rustdoc and cargo-depgraph-enriched import checks."], - "deferred" - ), - surface( - "rust.dead-code", - true, - ["Rust graph emits exported symbol metadata and graph-backed dead public export signals."], - ["Cargo dead_code diagnostics and retained Rox gate behavior still uniquely cover compiler reachability."] - ), - surface( - "rust.unused-deps", - false, - ["No graph fact replaces cargo-udeps unused dependency analysis."], - ["cargo-udeps/Rox unused dependency detection remains current-tool evidence."] - ), - surface( - "rust.function-metrics", - true, - ["Rust graph emits symbol spans and signatures for functions and methods."], - ["rust-code-analysis complexity and parameter thresholds remain current-tool evidence."] - ), - surface( - "current-tools:validate-rust-graph", - false, - ["Graph receipts do not replace the aggregate current-tools Rust graph gate."], - ["npm run current-tools:validate-rust-graph remains the retained aggregate guardrail."] - ) - ], - guardrails: [ - { - id: "current-tools:validate-rust-graph", - command: ["npm", "run", "current-tools:validate-rust-graph"], - replacementStatus: "retained", - oldToolReplacementClaimed: false - } - ] - }; -} - function validAspDogfoodReceipt() { const cutover = validReleaseCutoverReceipt(); - const markers = ["opcore asp serve", "opcore asp", "dist/bin/lattice", ".ace/runtime"]; + const markers = ["opcore asp serve", "opcore asp"]; const aspRepo = covibesPath("agent-server-protocol"); const hostFixtureRepo = "/tmp/opcore-asp-dogfood/asp-host-fixture"; const command = (id, commandParts, output = {}) => ({ @@ -5320,8 +4944,7 @@ function validAspDogfoodReceipt() { temp: true, isolated: true, sharedStateMutated: false, - pathSanitized: true, - aceRuntimeBinExcluded: true + pathSanitized: true }, hostFixture: { repo: hostFixtureRepo, @@ -5379,32 +5002,18 @@ function validAspDogfoodReceipt() { diagnosticsCount: 0, hostOwnedFieldLeak: false }, - currentToolGuardrails: [ - { ...command("current-tools-validate-changed", ["npm", "run", "current-tools:validate-changed"]), retained: true }, - { ...command("current-tools-validate-rust-graph", ["npm", "run", "current-tools:validate-rust-graph"]), retained: true }, - { - id: "current-tools-validate-all", - command: ["npm", "run", "current-tools:validate-all"], - status: "retained-not-run", - exitCode: null, - stdoutSha256: "0".repeat(64), - stderrSha256: "0".repeat(64), - retained: true, - assertion: "retained by default" - } - ], + selfValidation: cutover.selfValidation, unsupportedSurfaces: [ { surface: "inspect", status: "parity-blocker", cleanCoverage: false, blocker: "inspect not mapped into ASP #120" }, - { surface: "edit", status: "retained-old-tool-gate", cleanCoverage: false, blocker: "edit not mapped into ASP #120" } + { surface: "edit", status: "parity-blocker", cleanCoverage: false, blocker: "edit not mapped into ASP #120" } ], - parityBlockers: [{ source: "docs/planning/old-tool-compatibility-matrix.md:1", detail: "old-tool guardrails retained" }], + parityBlockers: [], authority: { hostOwnsDecisions: true, providerOutputIsHostDecision: false, localAuthorityOverride: { present: false, sharedAuthorityWeakened: false } }, publicReleaseActions: [], - oldToolReplacementClaimed: false, forbiddenMarkerScan: { scannedTextCount: 2, findingCount: 0, diff --git a/tests/validation-cli.test.mjs b/tests/validation-cli.test.mjs index dd72d01..482e35f 100644 --- a/tests/validation-cli.test.mjs +++ b/tests/validation-cli.test.mjs @@ -5,7 +5,6 @@ import { tmpdir } from "node:os"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; import { spawnSync } from "node:child_process"; -import { routeCommand } from "../packages/opcore/dist/advanced/index.js"; import { routeOpcoreCommand } from "../packages/opcore/dist/index.js"; import { fakeCargoScript, writeFakeRustToolchain } from "./helpers/validation-rust-fixtures.mjs"; @@ -61,7 +60,6 @@ const cloneCheckIds = ["clone.duplication"]; const typeScriptExecutableDefaultCheckIds = typeScriptCheckIds.filter((checkId) => checkId !== "typescript.lint"); const pythonExecutableDefaultCheckIds = pythonCheckIds.filter((checkId) => checkId !== "python.pytest"); const executableDefaultCheckIds = [...typeScriptExecutableDefaultCheckIds, ...rustCheckIds, ...pythonExecutableDefaultCheckIds, ...cloneCheckIds]; -const defaultCheckIds = [...typeScriptCheckIds, ...rustCheckIds, ...pythonCheckIds, ...docsCheckIds, ...cloneCheckIds]; const availableCheckIds = [ ...typeScriptCheckIds, ...rustCheckIds, @@ -334,7 +332,7 @@ describe("validation CLI", () => { timeoutMs: 120000, pathPolicy: { include: ["packages/", "scripts/"], - exclude: ["dist/**", ".ace"] + exclude: ["dist/**", ".agents"] }, checks: { packs: ["./checks/policy.cjs"], @@ -434,7 +432,7 @@ describe("validation CLI", () => { assert.equal(config.validation.timeoutMs, 120000); assert.deepEqual(config.validation.pathPolicy, { include: ["packages/", "scripts/"], - exclude: ["dist/**", ".ace"] + exclude: ["dist/**", ".agents"] }); assert.deepEqual(config.validation.checks.packs, ["./checks/policy.cjs"]); assert.deepEqual(config.validation.checks.disabled, ["typescript.types"]); @@ -519,14 +517,14 @@ describe("validation CLI", () => { const { pathPolicyIncludes } = await import("../packages/opcore/dist/path-policy.js"); const policy = { include: ["packages/", "scripts/"], - exclude: ["dist/**", ".ace", ".agents", "packages/generated/**"] + exclude: ["dist/**", ".agents", ".codex", "packages/generated/**"] }; assert.equal(pathPolicyIncludes("packages/opcore/src/index.ts", policy), true); assert.equal(pathPolicyIncludes("scripts/build.mjs", policy), true); assert.equal(pathPolicyIncludes("docs/notes.ts", policy), false); assert.equal(pathPolicyIncludes("dist/index.js", policy), false); - assert.equal(pathPolicyIncludes(".ace/runtime/tool.json", policy), false); + assert.equal(pathPolicyIncludes(".codex/runtime/tool.json", policy), false); assert.equal(pathPolicyIncludes(".agents/skills/opcore/SKILL.md", policy), false); assert.equal(pathPolicyIncludes("packages/generated/output.ts", policy), false); assert.equal(pathPolicyIncludes("../outside.ts", policy), false); @@ -541,7 +539,7 @@ describe("validation CLI", () => { scopeFiles: ["packages/src/index.ts", "docs/notes.ts", "dist/index.js"], listVisibleFiles: async () => { listVisibleFileCalls += 1; - return ["packages/src/index.ts", "scripts/build.mjs", "docs/notes.ts", ".ace/runtime.json"]; + return ["packages/src/index.ts", "scripts/build.mjs", "docs/notes.ts", ".codex/runtime.json"]; }, overlays: [ { path: "packages/src/index.ts", action: "write", content: "export const value = 1;\n" }, @@ -558,7 +556,7 @@ describe("validation CLI", () => { const filtered = withFilteredFileView(context, { include: ["packages/", "scripts/"], - exclude: ["dist/**", ".ace", ".agents"] + exclude: ["dist/**", ".codex", ".agents"] }); assert.equal(listVisibleFileCalls, 0); diff --git a/tests/validation-rust.test.mjs b/tests/validation-rust.test.mjs index da6c596..bac1aa4 100644 --- a/tests/validation-rust.test.mjs +++ b/tests/validation-rust.test.mjs @@ -4,7 +4,6 @@ import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, wr import { tmpdir } from "node:os"; import { join } from "node:path"; import { - calculateValidationFileChecksum, createNodeValidationWorkspace, createValidationCheckRegistry, createValidationRunner @@ -1567,10 +1566,10 @@ const rustCheckIds = [ const workspaceRoots = readFileSync(logPath, "utf8") .split(/\r?\n/) .map((line) => /\tCWD=([^\t]+)$/.exec(line)?.[1]) - .filter((path) => path?.includes("lattice-validation-rust-")); + .filter((path) => path?.includes("opcore-validation-rust-")); assert.equal(workspaceRoots.length > 5, true); assert.equal(new Set(workspaceRoots).size, 1, workspaceRoots.join("\n")); - assert.match(workspaceRoots[0], /lattice-validation-rust-[^/]+\/repo$/); + assert.match(workspaceRoots[0], /opcore-validation-rust-[^/]+\/repo$/); assert.equal(existsSync(workspaceRoots[0]), false); } finally { rmSync(temp, { recursive: true, force: true }); From 664b04e0cf326ce8853cd60947edc1c684771c4b Mon Sep 17 00:00:00 2001 From: Tom Dupuis <60640908+tomdps@users.noreply.github.com> Date: Sat, 8 Aug 2026 01:29:17 +0000 Subject: [PATCH 2/5] Enforce strict Opcore self-validation --- .opcore/config | 62 +- AGENTS.md | 13 +- CLAUDE.md | 13 +- crates/graph-core/src/clone/mod.rs | 40 +- crates/graph-core/src/clone/store.rs | 32 +- crates/graph-core/src/daemon/tests.rs | 9 +- crates/graph-core/src/extraction/facts.rs | 284 +- .../src/extraction/facts/collector.rs | 498 +- .../src/extraction/facts/collector/helpers.rs | 154 + .../src/extraction/facts/collector/visit.rs | 326 + .../graph-core/src/extraction/facts/python.rs | 489 +- .../src/extraction/facts/python/syntax.rs | 335 + .../src/extraction/facts/resolution.rs | 207 + .../graph-core/src/extraction/facts/rust.rs | 571 +- .../src/extraction/facts/rust/imports.rs | 255 + .../src/extraction/facts/rust/references.rs | 60 + .../src/extraction/facts/rust/syntax.rs | 184 + crates/graph-core/src/extraction/mod.rs | 39 +- .../src/extraction/python_imports.rs | 25 +- crates/graph-core/src/extraction/tests.rs | 1782 +-- .../src/extraction/tests/diagnostics.rs | 184 + .../graph-core/src/extraction/tests/python.rs | 479 + .../graph-core/src/extraction/tests/rust.rs | 252 + .../src/extraction/tests/support.rs | 360 + .../extraction/tests/typescript_exports.rs | 430 + .../extraction/tests/typescript_resolution.rs | 268 + crates/graph-core/src/extraction/tsconfig.rs | 25 +- crates/graph-core/src/lib.rs | 3 + crates/graph-core/src/pipeline.rs | 44 +- crates/graph-core/src/pipeline/delta.rs | 37 + crates/graph-core/src/query/tests.rs | 340 +- crates/graph-core/src/query/tests/fixtures.rs | 349 + crates/graph-core/src/search.rs | 18 +- crates/graph-core/src/search/scoring.rs | 4 +- crates/graph-core/src/store.rs | 18 +- crates/graph-core/src/store/metadata.rs | 32 +- crates/graph-core/src/store/read.rs | 21 + crates/graph-core/src/store/schema.rs | 6 +- crates/graph-core/src/store/tests.rs | 7 +- crates/graph-core/src/test_support.rs | 8 + docs/architecture/graph-hub-inventory.md | 356 + docs/architecture/runtime-cli-ard.md | 8 +- opcore-zero.docs.json | 9 + package.json | 2 +- packages/contracts/src/clone/validators.ts | 191 + .../src/command/adapter-validator.ts | 32 + packages/contracts/src/command/contracts.ts | 35 + .../src/command/helper-validators.ts | 35 + packages/contracts/src/command/manifest.ts | 108 + packages/contracts/src/command/router-01.ts | 265 + packages/contracts/src/command/router-02.ts | 112 + .../contracts/src/command/router-contracts.ts | 139 + packages/contracts/src/command/validators.ts | 193 + packages/contracts/src/command/vocabulary.ts | 56 + packages/contracts/src/edit/contracts.ts | 98 + .../contracts/src/edit/refusal-validator.ts | 17 + packages/contracts/src/edit/validators.ts | 126 + packages/contracts/src/edit/vocabulary.ts | 17 + .../src/graph/daemon-validators-01.ts | 193 + .../src/graph/daemon-validators-02.ts | 47 + .../contracts/src/graph/helper-validators.ts | 156 + .../contracts/src/graph/payload-validators.ts | 75 + .../contracts/src/graph/pipeline-contracts.ts | 150 + .../src/graph/protocol-validators.ts | 132 + .../src/graph/provider-contracts-01.ts | 162 + .../src/graph/provider-contracts-02.ts | 69 + .../src/graph/provider-validators.ts | 159 + .../contracts/src/graph/query-contracts-01.ts | 157 + .../contracts/src/graph/query-contracts-02.ts | 105 + .../contracts/src/graph/query-validators.ts | 231 + .../contracts/src/graph/search-contracts.ts | 77 + .../contracts/src/graph/search-validators.ts | 100 + packages/contracts/src/graph/vocabulary-01.ts | 139 + packages/contracts/src/graph/vocabulary-02.ts | 18 + packages/contracts/src/index.ts | 10462 +--------------- .../contracts/src/inspect/contracts-01.ts | 149 + .../contracts/src/inspect/contracts-02.ts | 71 + .../src/inspect/helper-validators-01.ts | 263 + .../src/inspect/helper-validators-02.ts | 21 + packages/contracts/src/inspect/validators.ts | 72 + .../contracts/src/inspect/warm-contracts.ts | 115 + packages/contracts/src/managed/contracts.ts | 198 + .../src/managed/helper-validators.ts | 105 + .../contracts/src/managed/validators-01.ts | 198 + .../contracts/src/managed/validators-02.ts | 236 + .../contracts/src/managed/validators-03.ts | 267 + .../contracts/src/product/init-contracts.ts | 132 + .../src/product/init-validators-01.ts | 180 + .../src/product/init-validators-02.ts | 109 + .../src/product/latency-contracts.ts | 93 + .../src/product/metrics-contracts-01.ts | 175 + .../src/product/metrics-contracts-02.ts | 46 + .../product/metrics-coverage-validators.ts | 68 + .../src/product/metrics-validators-01.ts | 125 + .../src/product/metrics-validators-02.ts | 212 + .../src/product/metrics-validators-03.ts | 144 + .../src/product/metrics-validators-04.ts | 203 + .../src/product/metrics-validators-05.ts | 164 + .../contracts/src/product/status-contracts.ts | 154 + .../src/product/status-validators.ts | 180 + .../contracts/src/release/asp-contracts-01.ts | 189 + .../src/release/asp-validators-01.ts | 218 + .../src/release/asp-validators-02.ts | 158 + .../src/release/cutover-contracts.ts | 170 + .../src/release/cutover-validators-01.ts | 232 + .../src/release/cutover-validators-02.ts | 244 + .../contracts/src/release/graph-contracts.ts | 177 + .../src/release/graph-optional-validators.ts | 62 + .../src/release/graph-validators-01.ts | 214 + .../src/release/graph-validators-02.ts | 198 + .../src/release/graph-validators-03.ts | 80 + .../src/release/graph-vocabulary-01.ts | 117 + .../src/release/graph-vocabulary-02.ts | 69 + .../src/release/public-validators.ts | 212 + .../src/release/receipt-contracts-01.ts | 205 + .../src/release/receipt-contracts-02.ts | 32 + .../src/release/receipt-validators-01.ts | 239 + .../src/release/receipt-validators-02.ts | 287 + .../src/release/receipt-validators-03.ts | 64 + .../contracts/src/release/vocabulary-01.ts | 137 + .../contracts/src/release/vocabulary-02.ts | 57 + .../contracts/src/release/vocabulary-03.ts | 272 + .../contracts/src/release/vocabulary-04.ts | 181 + packages/contracts/src/shared/json.ts | 7 + .../contracts/src/shared/path-validators.ts | 80 + packages/contracts/src/shared/primitives.ts | 62 + .../contracts/src/shared/validators-01.ts | 202 + .../contracts/src/shared/validators-02.ts | 33 + .../src/validation/capability-contracts.ts | 111 + .../src/validation/diagnostic-contracts.ts | 140 + .../prewrite-status-validators-01.ts | 192 + .../prewrite-status-validators-02.ts | 74 + .../validation/python-project-contracts-01.ts | 95 + .../validation/python-project-contracts-02.ts | 156 + .../python-project-validators-01.ts | 295 + .../python-project-validators-02.ts | 153 + .../validation/python-pytest-validators-01.ts | 210 + .../validation/python-pytest-validators-02.ts | 100 + .../validation/python-ruff-validators-01.ts | 230 + .../validation/python-ruff-validators-02.ts | 244 + .../validation/python-ruff-validators-03.ts | 75 + .../src/validation/python-types-validators.ts | 247 + .../validation/python-validator-primitives.ts | 48 + .../src/validation/request-contracts.ts | 163 + .../src/validation/request-validators-01.ts | 179 + .../src/validation/request-validators-02.ts | 204 + .../src/validation/result-validator.ts | 95 + .../src/validation/status-contracts.ts | 151 + .../contracts/src/validation/vocabulary-01.ts | 104 + .../contracts/src/validation/vocabulary-02.ts | 71 + .../contracts/src/validation/vocabulary-03.ts | 7 + packages/edit/src/index.ts | 1 + packages/edit/src/language-service.ts | 535 +- .../filesystem-discovery.ts | 30 + .../typescript-project/import-resolution.ts | 232 + packages/edit/src/typescript-project/index.ts | 4 + .../src/typescript-project/path-policy.ts | 71 + .../src/typescript-project/project-service.ts | 223 + .../typescript-project/source-discovery.ts | 130 + .../edit/src/typescript-project/tsconfig.ts | 197 + packages/edit/src/typescript-project/types.ts | 47 + .../wave1/wave1.expected.json | 15 + packages/graph/README.md | 2 +- packages/graph/src/ephemeral-snapshot.ts | 10 +- .../opcore-graph-core-linux-x64/metadata.json | 2 +- .../opcore-graph-core | Bin 9242952 -> 9095496 bytes .../opcore-graph-core.sha256 | 2 +- .../asp-warm/warm-project-registry.ts | 52 +- .../src/advanced/inspect-language-service.ts | 442 +- .../advanced/inspect-typescript-project.ts | 30 + packages/opcore/src/init-action-helpers.ts | 19 + packages/opcore/src/init-actions-global.ts | 49 + packages/opcore/src/init-actions-repo.ts | 93 + packages/opcore/src/init-actions.ts | 17 + packages/opcore/src/init-apply.ts | 69 + packages/opcore/src/init-args.ts | 116 + packages/opcore/src/init-config.ts | 59 + packages/opcore/src/init-constants.ts | 28 + packages/opcore/src/init-context-payload.ts | 26 + packages/opcore/src/init-data.ts | 15 + packages/opcore/src/init-files.ts | 32 + packages/opcore/src/init-format-summary.ts | 53 + packages/opcore/src/init-format.ts | 80 + packages/opcore/src/init-gitignore.ts | 46 + packages/opcore/src/init-guidance.ts | 73 + packages/opcore/src/init-help.ts | 62 + packages/opcore/src/init-hooks.ts | 81 + packages/opcore/src/init-language-settings.ts | 96 + packages/opcore/src/init-messages.ts | 101 + packages/opcore/src/init-paths.ts | 95 + packages/opcore/src/init-payloads.ts | 38 + packages/opcore/src/init-plan-flow.ts | 104 + packages/opcore/src/init-plan-global.ts | 78 + packages/opcore/src/init-plan-repo.ts | 52 + packages/opcore/src/init-plan-writes.ts | 110 + packages/opcore/src/init-plan.ts | 20 + packages/opcore/src/init-prompts.ts | 66 + packages/opcore/src/init-python-settings.ts | 70 + packages/opcore/src/init-result.ts | 27 + packages/opcore/src/init-router.ts | 73 + packages/opcore/src/init-session.ts | 102 + packages/opcore/src/init-settings.ts | 66 + packages/opcore/src/init-timing.ts | 104 + packages/opcore/src/init-types.ts | 125 + packages/opcore/src/init-undo-entry.ts | 98 + packages/opcore/src/init-undo-flow.ts | 43 + packages/opcore/src/init-undo-metadata.ts | 77 + packages/opcore/src/init-undo-plan.ts | 80 + packages/opcore/src/init-wizard-flow.ts | 122 + packages/opcore/src/init-wizard-plan.ts | 81 + packages/opcore/src/init-wizard-render.ts | 74 + packages/opcore/src/init-write.ts | 83 + packages/opcore/src/init.ts | 2307 +--- packages/opcore/src/repo-paths.ts | 26 + packages/opcore/src/scan.ts | 21 +- .../validation-docs/src/check-definition.ts | 33 + packages/validation-docs/src/check-results.ts | 17 + packages/validation-docs/src/checks.ts | 517 +- .../validation-docs/src/code-blocks-check.ts | 49 + .../validation-docs/src/content-checks.ts | 5 + .../src/content-quality-check.ts | 49 + .../validation-docs/src/coverage-checks.ts | 220 + .../validation-docs/src/document-check.ts | 57 + packages/validation-docs/src/dry-check.ts | 64 + .../validation-docs/src/existence-check.ts | 34 + packages/validation-docs/src/freshness.ts | 37 +- packages/validation-docs/src/length-check.ts | 86 + packages/validation-docs/src/options.ts | 22 + .../validation-docs/src/rules-why-check.ts | 42 + .../validation-docs/src/staleness-check.ts | 109 + .../src/dead-code-roots.ts | 12 +- .../src/dead-code-run.ts | 4 +- .../validation-typescript/src/diagnostics.ts | 7 - .../src/function-metrics-check.ts | 3 +- .../src/graph-requirements.ts | 2 +- .../src/import-graph-check.ts | 8 +- .../src/import-layer-rules-check.ts | 26 +- .../src/module-dependencies.ts | 83 + .../src/relevant-tests-check.ts | 55 +- .../src/relevant-tests-evidence.ts | 74 + .../validation-typescript/src/script-kind.ts | 8 + .../validation-typescript/src/source-files.ts | 23 +- .../validation-typescript/src/test-paths.ts | 3 + scripts/run-opcore-self-check.mjs | 202 +- scripts/run-test-ci.mjs | 2 +- tests/fixtures/package-packlists.json | 563 +- tests/gate-negative-fixtures.test.mjs | 11 +- tests/graph-ephemeral-snapshot.test.mjs | 44 + tests/graph-release-readiness.test.mjs | 4 +- tests/graph-store-conformance.test.mjs | 2 +- tests/opcore-facade.test.mjs | 5 +- tests/scaffold.test.mjs | 58 + tests/source-package-contracts.test.ts | 19 + tests/validation-cli.test.mjs | 224 +- tests/validation-docs.test.mjs | 16 + tests/validation-typescript.test.mjs | 485 +- tsconfig.json | 24 + 257 files changed, 26092 insertions(+), 18214 deletions(-) create mode 100644 crates/graph-core/src/extraction/facts/collector/helpers.rs create mode 100644 crates/graph-core/src/extraction/facts/collector/visit.rs create mode 100644 crates/graph-core/src/extraction/facts/python/syntax.rs create mode 100644 crates/graph-core/src/extraction/facts/resolution.rs create mode 100644 crates/graph-core/src/extraction/facts/rust/imports.rs create mode 100644 crates/graph-core/src/extraction/facts/rust/references.rs create mode 100644 crates/graph-core/src/extraction/facts/rust/syntax.rs create mode 100644 crates/graph-core/src/extraction/tests/diagnostics.rs create mode 100644 crates/graph-core/src/extraction/tests/python.rs create mode 100644 crates/graph-core/src/extraction/tests/rust.rs create mode 100644 crates/graph-core/src/extraction/tests/support.rs create mode 100644 crates/graph-core/src/extraction/tests/typescript_exports.rs create mode 100644 crates/graph-core/src/extraction/tests/typescript_resolution.rs create mode 100644 crates/graph-core/src/pipeline/delta.rs create mode 100644 crates/graph-core/src/query/tests/fixtures.rs create mode 100644 crates/graph-core/src/test_support.rs create mode 100644 docs/architecture/graph-hub-inventory.md create mode 100644 opcore-zero.docs.json create mode 100644 packages/contracts/src/clone/validators.ts create mode 100644 packages/contracts/src/command/adapter-validator.ts create mode 100644 packages/contracts/src/command/contracts.ts create mode 100644 packages/contracts/src/command/helper-validators.ts create mode 100644 packages/contracts/src/command/manifest.ts create mode 100644 packages/contracts/src/command/router-01.ts create mode 100644 packages/contracts/src/command/router-02.ts create mode 100644 packages/contracts/src/command/router-contracts.ts create mode 100644 packages/contracts/src/command/validators.ts create mode 100644 packages/contracts/src/command/vocabulary.ts create mode 100644 packages/contracts/src/edit/contracts.ts create mode 100644 packages/contracts/src/edit/refusal-validator.ts create mode 100644 packages/contracts/src/edit/validators.ts create mode 100644 packages/contracts/src/edit/vocabulary.ts create mode 100644 packages/contracts/src/graph/daemon-validators-01.ts create mode 100644 packages/contracts/src/graph/daemon-validators-02.ts create mode 100644 packages/contracts/src/graph/helper-validators.ts create mode 100644 packages/contracts/src/graph/payload-validators.ts create mode 100644 packages/contracts/src/graph/pipeline-contracts.ts create mode 100644 packages/contracts/src/graph/protocol-validators.ts create mode 100644 packages/contracts/src/graph/provider-contracts-01.ts create mode 100644 packages/contracts/src/graph/provider-contracts-02.ts create mode 100644 packages/contracts/src/graph/provider-validators.ts create mode 100644 packages/contracts/src/graph/query-contracts-01.ts create mode 100644 packages/contracts/src/graph/query-contracts-02.ts create mode 100644 packages/contracts/src/graph/query-validators.ts create mode 100644 packages/contracts/src/graph/search-contracts.ts create mode 100644 packages/contracts/src/graph/search-validators.ts create mode 100644 packages/contracts/src/graph/vocabulary-01.ts create mode 100644 packages/contracts/src/graph/vocabulary-02.ts create mode 100644 packages/contracts/src/inspect/contracts-01.ts create mode 100644 packages/contracts/src/inspect/contracts-02.ts create mode 100644 packages/contracts/src/inspect/helper-validators-01.ts create mode 100644 packages/contracts/src/inspect/helper-validators-02.ts create mode 100644 packages/contracts/src/inspect/validators.ts create mode 100644 packages/contracts/src/inspect/warm-contracts.ts create mode 100644 packages/contracts/src/managed/contracts.ts create mode 100644 packages/contracts/src/managed/helper-validators.ts create mode 100644 packages/contracts/src/managed/validators-01.ts create mode 100644 packages/contracts/src/managed/validators-02.ts create mode 100644 packages/contracts/src/managed/validators-03.ts create mode 100644 packages/contracts/src/product/init-contracts.ts create mode 100644 packages/contracts/src/product/init-validators-01.ts create mode 100644 packages/contracts/src/product/init-validators-02.ts create mode 100644 packages/contracts/src/product/latency-contracts.ts create mode 100644 packages/contracts/src/product/metrics-contracts-01.ts create mode 100644 packages/contracts/src/product/metrics-contracts-02.ts create mode 100644 packages/contracts/src/product/metrics-coverage-validators.ts create mode 100644 packages/contracts/src/product/metrics-validators-01.ts create mode 100644 packages/contracts/src/product/metrics-validators-02.ts create mode 100644 packages/contracts/src/product/metrics-validators-03.ts create mode 100644 packages/contracts/src/product/metrics-validators-04.ts create mode 100644 packages/contracts/src/product/metrics-validators-05.ts create mode 100644 packages/contracts/src/product/status-contracts.ts create mode 100644 packages/contracts/src/product/status-validators.ts create mode 100644 packages/contracts/src/release/asp-contracts-01.ts create mode 100644 packages/contracts/src/release/asp-validators-01.ts create mode 100644 packages/contracts/src/release/asp-validators-02.ts create mode 100644 packages/contracts/src/release/cutover-contracts.ts create mode 100644 packages/contracts/src/release/cutover-validators-01.ts create mode 100644 packages/contracts/src/release/cutover-validators-02.ts create mode 100644 packages/contracts/src/release/graph-contracts.ts create mode 100644 packages/contracts/src/release/graph-optional-validators.ts create mode 100644 packages/contracts/src/release/graph-validators-01.ts create mode 100644 packages/contracts/src/release/graph-validators-02.ts create mode 100644 packages/contracts/src/release/graph-validators-03.ts create mode 100644 packages/contracts/src/release/graph-vocabulary-01.ts create mode 100644 packages/contracts/src/release/graph-vocabulary-02.ts create mode 100644 packages/contracts/src/release/public-validators.ts create mode 100644 packages/contracts/src/release/receipt-contracts-01.ts create mode 100644 packages/contracts/src/release/receipt-contracts-02.ts create mode 100644 packages/contracts/src/release/receipt-validators-01.ts create mode 100644 packages/contracts/src/release/receipt-validators-02.ts create mode 100644 packages/contracts/src/release/receipt-validators-03.ts create mode 100644 packages/contracts/src/release/vocabulary-01.ts create mode 100644 packages/contracts/src/release/vocabulary-02.ts create mode 100644 packages/contracts/src/release/vocabulary-03.ts create mode 100644 packages/contracts/src/release/vocabulary-04.ts create mode 100644 packages/contracts/src/shared/json.ts create mode 100644 packages/contracts/src/shared/path-validators.ts create mode 100644 packages/contracts/src/shared/primitives.ts create mode 100644 packages/contracts/src/shared/validators-01.ts create mode 100644 packages/contracts/src/shared/validators-02.ts create mode 100644 packages/contracts/src/validation/capability-contracts.ts create mode 100644 packages/contracts/src/validation/diagnostic-contracts.ts create mode 100644 packages/contracts/src/validation/prewrite-status-validators-01.ts create mode 100644 packages/contracts/src/validation/prewrite-status-validators-02.ts create mode 100644 packages/contracts/src/validation/python-project-contracts-01.ts create mode 100644 packages/contracts/src/validation/python-project-contracts-02.ts create mode 100644 packages/contracts/src/validation/python-project-validators-01.ts create mode 100644 packages/contracts/src/validation/python-project-validators-02.ts create mode 100644 packages/contracts/src/validation/python-pytest-validators-01.ts create mode 100644 packages/contracts/src/validation/python-pytest-validators-02.ts create mode 100644 packages/contracts/src/validation/python-ruff-validators-01.ts create mode 100644 packages/contracts/src/validation/python-ruff-validators-02.ts create mode 100644 packages/contracts/src/validation/python-ruff-validators-03.ts create mode 100644 packages/contracts/src/validation/python-types-validators.ts create mode 100644 packages/contracts/src/validation/python-validator-primitives.ts create mode 100644 packages/contracts/src/validation/request-contracts.ts create mode 100644 packages/contracts/src/validation/request-validators-01.ts create mode 100644 packages/contracts/src/validation/request-validators-02.ts create mode 100644 packages/contracts/src/validation/result-validator.ts create mode 100644 packages/contracts/src/validation/status-contracts.ts create mode 100644 packages/contracts/src/validation/vocabulary-01.ts create mode 100644 packages/contracts/src/validation/vocabulary-02.ts create mode 100644 packages/contracts/src/validation/vocabulary-03.ts create mode 100644 packages/edit/src/typescript-project/filesystem-discovery.ts create mode 100644 packages/edit/src/typescript-project/import-resolution.ts create mode 100644 packages/edit/src/typescript-project/index.ts create mode 100644 packages/edit/src/typescript-project/path-policy.ts create mode 100644 packages/edit/src/typescript-project/project-service.ts create mode 100644 packages/edit/src/typescript-project/source-discovery.ts create mode 100644 packages/edit/src/typescript-project/tsconfig.ts create mode 100644 packages/edit/src/typescript-project/types.ts create mode 100644 packages/opcore/src/advanced/inspect-typescript-project.ts create mode 100644 packages/opcore/src/init-action-helpers.ts create mode 100644 packages/opcore/src/init-actions-global.ts create mode 100644 packages/opcore/src/init-actions-repo.ts create mode 100644 packages/opcore/src/init-actions.ts create mode 100644 packages/opcore/src/init-apply.ts create mode 100644 packages/opcore/src/init-args.ts create mode 100644 packages/opcore/src/init-config.ts create mode 100644 packages/opcore/src/init-constants.ts create mode 100644 packages/opcore/src/init-context-payload.ts create mode 100644 packages/opcore/src/init-data.ts create mode 100644 packages/opcore/src/init-files.ts create mode 100644 packages/opcore/src/init-format-summary.ts create mode 100644 packages/opcore/src/init-format.ts create mode 100644 packages/opcore/src/init-gitignore.ts create mode 100644 packages/opcore/src/init-guidance.ts create mode 100644 packages/opcore/src/init-help.ts create mode 100644 packages/opcore/src/init-hooks.ts create mode 100644 packages/opcore/src/init-language-settings.ts create mode 100644 packages/opcore/src/init-messages.ts create mode 100644 packages/opcore/src/init-paths.ts create mode 100644 packages/opcore/src/init-payloads.ts create mode 100644 packages/opcore/src/init-plan-flow.ts create mode 100644 packages/opcore/src/init-plan-global.ts create mode 100644 packages/opcore/src/init-plan-repo.ts create mode 100644 packages/opcore/src/init-plan-writes.ts create mode 100644 packages/opcore/src/init-plan.ts create mode 100644 packages/opcore/src/init-prompts.ts create mode 100644 packages/opcore/src/init-python-settings.ts create mode 100644 packages/opcore/src/init-result.ts create mode 100644 packages/opcore/src/init-router.ts create mode 100644 packages/opcore/src/init-session.ts create mode 100644 packages/opcore/src/init-settings.ts create mode 100644 packages/opcore/src/init-timing.ts create mode 100644 packages/opcore/src/init-types.ts create mode 100644 packages/opcore/src/init-undo-entry.ts create mode 100644 packages/opcore/src/init-undo-flow.ts create mode 100644 packages/opcore/src/init-undo-metadata.ts create mode 100644 packages/opcore/src/init-undo-plan.ts create mode 100644 packages/opcore/src/init-wizard-flow.ts create mode 100644 packages/opcore/src/init-wizard-plan.ts create mode 100644 packages/opcore/src/init-wizard-render.ts create mode 100644 packages/opcore/src/init-write.ts create mode 100644 packages/opcore/src/repo-paths.ts create mode 100644 packages/validation-docs/src/check-definition.ts create mode 100644 packages/validation-docs/src/check-results.ts create mode 100644 packages/validation-docs/src/code-blocks-check.ts create mode 100644 packages/validation-docs/src/content-checks.ts create mode 100644 packages/validation-docs/src/content-quality-check.ts create mode 100644 packages/validation-docs/src/coverage-checks.ts create mode 100644 packages/validation-docs/src/document-check.ts create mode 100644 packages/validation-docs/src/dry-check.ts create mode 100644 packages/validation-docs/src/existence-check.ts create mode 100644 packages/validation-docs/src/length-check.ts create mode 100644 packages/validation-docs/src/options.ts create mode 100644 packages/validation-docs/src/rules-why-check.ts create mode 100644 packages/validation-docs/src/staleness-check.ts create mode 100644 packages/validation-typescript/src/module-dependencies.ts create mode 100644 packages/validation-typescript/src/relevant-tests-evidence.ts create mode 100644 packages/validation-typescript/src/script-kind.ts create mode 100644 packages/validation-typescript/src/test-paths.ts create mode 100644 tests/source-package-contracts.test.ts diff --git a/.opcore/config b/.opcore/config index 1632c85..a78f43d 100644 --- a/.opcore/config +++ b/.opcore/config @@ -19,7 +19,67 @@ "checks": { "packs": [], "disabled": [], - "defaults": [] + "defaults": [ + "typescript.syntax", + "typescript.types", + "typescript.lint", + "typescript.import-graph", + "typescript.dead-code", + "typescript.function-metrics", + "typescript.relevant-tests", + "typescript.file-length", + "rust.source-hygiene", + "rust.fmt", + "rust.cargo-check", + "rust.clippy", + "rust.rustdoc", + "rust.import-graph", + "rust.dead-code", + "rust.graph-signals", + "rust.unused-deps", + "rust.file-length", + "rust.function-metrics", + "python.syntax", + "python.source-hygiene", + "python.ruff-lint", + "python.ruff-format", + "python.types", + "python.import-graph", + "python.dead-code", + "python.relevant-tests", + "python.pytest", + "docs.existence", + "docs.staleness", + "docs.freshness", + "docs.length", + "docs.dry", + "docs.content-quality", + "docs.code-blocks", + "docs.rules-why", + "docs.hub-coverage", + "docs.subtree-coverage", + "clone.duplication" + ], + "typescript": { + "fileLength": { + "maxFileLines": 300 + }, + "functionMetrics": { + "maxFunctionLines": 80, + "maxComplexity": 10, + "maxParams": 4 + } + }, + "rust": { + "fileLength": { + "maxFileLines": 500 + }, + "functionMetrics": { + "maxFunctionLines": 80, + "maxComplexity": 10, + "maxParams": 4 + } + } } } } diff --git a/AGENTS.md b/AGENTS.md index 45d49e4..a5b1daa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -20,10 +20,12 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit | Concept | Primary File | |---------|--------------| | Runtime/CLI ARD | @docs/architecture/runtime-cli-ard.md | +| Graph hub inventory | @docs/architecture/graph-hub-inventory.md | | Opcore alpha roadmap | @docs/planning/opcore-alpha-roadmap.md | | Opcore metrics/report/history | `packages/opcore/src/reporting.ts` | | Latency budgets and trend gate | `docs/performance/latency-budgets.json`, `scripts/check-latency-budgets.mjs` | | Public contracts | @packages/contracts/ | +| Public contracts barrel | `packages/contracts/src/index.ts` (API-only exports; domain modules own implementations) | | Contract JSON schema | `packages/contracts/schemas/opcore-contracts.schema.json` | | Command router package | @packages/opcore/src/advanced/ | | Graph provider package track | @packages/graph/ | @@ -34,6 +36,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit | Validation policy composition | @packages/validation-policy/ | | Validation file view | `packages/validation/src/overlays.ts` | | Validation graph client | `packages/validation/src/graph-client.ts` | +| Documentation validation composition | `packages/validation-docs/src/checks.ts`, `packages/validation-docs/src/document-check.ts` | | Rust validation adapter | @packages/validation-rust/ | | TypeScript validation adapter | @packages/validation-typescript/ | | Clone validation adapter | @packages/validation-clone/ | @@ -69,7 +72,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit ## Development Tooling - Run `npm run setup` after cloning or entering a fresh worktree. It installs repository dependencies only and must leave the worktree clean. -- Run `npm run opcore:self-check` after building. It invokes the built public Opcore CLI in introduced mode against the configured base ref and consumes the checked-in `.opcore/config` policy. +- Run `npm run opcore:self-check` after building. It requires `.opcore/config` to select every registered check explicitly with no disabled checks, validates changed-compatible checks in introduced mode against the configured base ref, prepares fresh graph evidence for incompatible repo-wide checks, and requires zero diagnostics across the complete manifest - WHY: self-validation must fail when a new check is not explicitly governed or any supported language loses complexity, tool, graph, docs, or clone coverage. - `scripts/ci/run-local-ci-equivalent.sh` runs normal CI plus the Opcore self-check. Its docs/agent-guidance fast path uses only repository-native workspace, provenance, build, and self-validation commands. - Zeroshot worktrees run `npm ci` and the same local CI-equivalent command proof - WHY: humans, agents, CI, and ship clusters must exercise one repository-owned validation surface. - Root `.npmrc` sets `loglevel=silent` - WHY: JSON-emitting npm scripts such as `npm run asp-dogfood:check -- --json` must write parseable JSON to redirected stdout without npm lifecycle preambles. @@ -85,7 +88,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - #246 makes `@the-open-engine/opcore-validation-python` the sole dynamic owner of `opcore.python.project-context.v1`: every Python target resolves against its nearest project boundary through an injected read/list/exists/realpath workspace view, smol-toml AST config/build metadata, exact interpreter/tool/build probes, and after-state content. Missing realpath evidence is ambiguous, deleted overlays cannot remain discovery markers, and declared constraints never become invented exact versions. Validation, status, scan, init/install preview, metrics, ASP, and installed execution must reuse the resulting project key, context fingerprint, outcome, and provenance; ASP workspace/config reads must remain host-callback-only. Static descriptors advertise only the schema, outcome vocabulary, read-only behavior, and no-install guarantee - WHY: root-scoped and duplicate project/environment discovery validates nested monorepo files with the wrong interpreter and makes surfaces disagree. #256/#257 make `python.types` select and execute exactly one mypy or Pyright authority per canonical project. Mypy uses first-match config precedence and strict NDJSON. Pyright preserves `pyrightconfig.json` precedence over `[tool.pyright]`, recursive repo-confined extends, and config-driven source/stub semantics while consuming only complete `--outputjson`. Both authorities use the same isolated exact after-state, portable receipt, selected interpreter, bounded process-tree runner, and sanitized HOME/XDG/cache/temp environment. Availability never selects an authority or permits fallback. Malformed, partial, contradictory, version/count-mismatched, out-of-repo, fatal, or stderr protocol evidence fails closed, and every project attempt emits `opcore.python.validation-capability-run` evidence - WHY: host config/imports, source mutation, orphaned checker descendants, availability, human-output parsing, or check-level summaries cannot prove which project/config/after-state produced type evidence. - #258 keeps `python.ruff-lint` and `python.ruff-format` separate from `python.source-hygiene` and opt-in through explicit selection or `.opcore/config` defaults. They execute the #246-selected Ruff over a temporary #245 after-state workspace with fixes, writes, and caches disabled; lint consumes JSON and format uses bounded exit-code refinement. Closest target-applicable `.ruff.toml`, `ruff.toml`, or `[tool.ruff]` configuration searches through the repository root across nested Python project boundaries, partitions project execution, requires non-symlink realpath evidence for every selected or recursively extended config, and materializes only that config closure. Python types and Ruff share the `packages/validation-python/src/python-execution-workspace.ts` primitive and sanitized HOME/XDG/TMP/PATH runtime for after-state fingerprinting, materialization, execution isolation, and cleanup while capability code selects its own support files. Ruff receipts use the shared `afterStateManifestFingerprint` field and portable executable/argv locators. Missing Ruff degrades status only while a Ruff check is active, and metrics require executed capability receipts - WHY: optional source tooling must never be probed, invoked, counted, or reported as enforced when policy did not select it, target-local configuration must not leak across files, host state must not affect results, and parallel materializers can make tool inputs diverge from receipts. - #209 makes Rust graph-core the sole parser/resolver for Python repo imports. `@the-open-engine/opcore-graph` materializes supplied `.py`/`.pyi` after-state files only in an isolated temporary repo and returns canonical directed `IMPORTS_FROM` file edges; validation-python owns only the structural analyzer contract, visible-file enumeration, cached target/transitive closure, and edge consumption. Opcore, advanced validation, validation-policy, and ASP inject the graph adapter; missing/failed/malformed analysis is an infrastructure failure, never empty success - WHY: a second TypeScript import grammar/resolver diverges on multiline syntax, overlays, packages, stubs, namespaces, and src layouts. -- #197 makes hypothetical graph evaluation exact-state: validation creates one `ValidationFileView` per before/after state and owns one disposable graph session for that view; graph materializes the complete visible TS/TSX/JS/JSX, Python `.py`/`.pyi`, and Rust `.rs` universe into a bounded isolated root, builds graph-core once, shares the immutable session across checks, and removes it on every exit. Introduced mode must use distinct before/after snapshots, ASP listings must preserve host truncation, and exact-state construction/query failure is non-pass even when persistent graph mode is optional - WHY: a persistent target-repo graph or incomplete listing cannot describe hypothetical file contents and must never produce a false clean pre-write result. +- #197 makes hypothetical graph evaluation exact-state: validation creates one `ValidationFileView` per before/after state and owns one disposable graph session for that view; graph materializes the complete visible TS/TSX/JS/JSX, Python `.py`/`.pyi`, and Rust `.rs` universe plus root `tsconfig.json` into a bounded isolated root, builds graph-core once, shares the immutable session across checks, and removes it on every exit. Introduced mode must use distinct before/after snapshots, ASP listings must preserve host truncation, and exact-state construction/query failure is non-pass even when persistent graph mode is optional - WHY: a persistent target-repo graph, missing alias configuration, or incomplete listing cannot describe hypothetical file contents and must never produce a false clean pre-write result. - #19 requires graph status to preserve real WAL checkpoint evidence from the latest pipeline summary and release gates to fail missing/fabricated WAL evidence - WHY: freshness and checkpoint pressure must remain host-visible provider facts. - #19 treats `opcore graph serve` as the stdio/MCP hot-query replacement, not a Unix socket, with parallel independent serve sessions as the supported concurrency evidence. - `opcore-asp-provider --stdio` is the transitional provider binary for the standalone ASP Core check provider; it uses host workspace callbacks and Opcore validation only. @@ -112,6 +115,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - ALWAYS keep graph, edit, and validation as separate ownership boundaries - WHY: graph facts, code mutation, and policy checks evolve at different correctness boundaries - Consequence: a single mixed engine makes parity and cutover unverifiable. - ALWAYS put shared wire/types/contracts in `packages/contracts` before another package consumes them - WHY: package-private shape copying creates incompatible command and API surfaces. +- ALWAYS keep `packages/contracts/src/index.ts` as an API-only export barrel; place implementations in domain modules, preserve the root export surface during internal splits, and keep contracts source modules below 300 lines - WHY: the public package needs one stable import surface without returning to a monolithic implementation file. - ALWAYS update `packages/contracts/schemas/opcore-contracts.schema.json`, contract tests, fixture metadata, package exports, and packlists together when changing shared contracts - WHY: Rust/native graph-core consumers and TypeScript packages must consume the same wire artifacts. - ALWAYS dispatch implemented canonical `opcore graph`, `opcore edit`, `opcore check`, and `opcore validate` routes through public package-owned adapters - WHY: package entrypoints must be able to run without importing the aggregate CLI. - NEVER import package implementation internals across package tracks - WHY: graph, edit, and validation must be releasable and testable independently - Consequence: router composition hides runtime coupling until package publishing. @@ -170,6 +174,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - #56 adds the validation file view: `@the-open-engine/opcore-validation` composes normalized `ValidationRequest` overlays, resolved scope files, and injected workspace `readFile` access so `opcore validate` checks can read hypothetical after-state writes/deletes, before-state comparisons for introduced report mode, and before-state checksums without mutating the worktree. `ValidationFileView.defaultReadState` is check-visible so adapters with out-of-band readers can bind their before/after source mode to the runner pass. `checksumBefore` conflicts return refused/conflict before checks run. - #26 adds the validation-owned GraphProvider consumer boundary: `ValidationGraphProviderClient`, cached `ValidationGraphQuerySession`, graph requirement preloading, status/query failure mapping, and helper access for metadata, file checksums, IMPORTS_FROM, CALLS, and TESTED_BY facts. - #57 adds the TypeScript validation adapter: `@the-open-engine/opcore-validation-typescript` exports package-owned syntax, type, import-graph, dead-code, and relevant-tests check definitions. Syntax/type checks use the validation file view and an overlay-aware compiler host, including tsconfig path aliases for repo files and deterministic node_modules/package declaration resolution for external package imports; graph checks require #26 graph sessions and batched IMPORTS_FROM, CALLS, and TESTED_BY fact requirements. +- Opcore self-validation records file-level `TESTED_BY` evidence when a conventional TS/JS test imports a source file, while retaining symbol-level call evidence. The TypeScript relevant-test adapter emits diagnostics only for missing evidence; positive evidence is a clean check result. The real `tests/source-package-contracts.test.ts` suite imports workspace facades through root tsconfig source aliases and runs in normal local/CI tests - WHY: unsupported `.mjs` runtime suites and built `dist` imports cannot prove source-level relevant-test coverage, and successful evidence is not a finding. - #20 adds the Rust validation adapter: `@the-open-engine/opcore-validation-rust` exports package-owned source hygiene, fmt, cargo-check, clippy, rustdoc, import-graph, dead-code, graph-signals, unused-deps, file-length, and function-metrics checks. `rust.graph-signals` is graph-provider-backed evidence from `ValidationGraphProviderClient` only (untested public Rust surface, dead public exports, module orphans/cycles), while mechanical evidence remains owned by Cargo and the configured native tools. Missing `rustdoc`, `cargo-depgraph`, `cargo-udeps`, or `rust-code-analysis-cli` stays degraded or unsupported with `requiredTool`. Rust checks materialize temporary workspaces from `ValidationCheckContext.fileView` after-state content for Cargo tools and add no validation daemon or hidden cache. #138 shares cargo-check JSON with dead-code, forbids a second dead-code cargo compile or `-Ddead_code` cache-busting flags, and injects a persistent `CARGO_TARGET_DIR` outside the worktree through `runTool`; cache keys must derive from generic repo, scope, overlay, platform, and toolchain inputs and cleanup must remain TTL/size bounded - WHY: Rust validation must be fast without leaking hypothetical after-state into real worktrees or tuning behavior to one repository. - Rust Cargo/native checks share one environment-keyed materialized workspace per validation file-view state through runner-owned disposable resources; the runner removes it on every exit - WHY: exact staged/tree/overlay semantics require state isolation, while per-check whole-repo copies create unbounded filesystem-event churn. - #151/#214 adds clone detection through the existing `opcore-graph-core` binary as a `clone` subcommand plus `@the-open-engine/opcore-validation-clone`. `CloneAnalysisRequest`/`CloneAnalysisResult` use `opcore.clone.v1`; committed analysis may refresh `.opcore/clone/clone.db`, while scoped or hypothetical analysis is ephemeral. The sparse request shape sends committed candidates as `paths`/`sourcePaths` plus `sourceReadMode`/`sourceTreeRef`, with full content only in write overlays. `clone.duplication` is a validation adapter check with injected native invocation, no graph requirement, no line-level identity, no daemon, and no SAST/security or score claim. @@ -182,7 +187,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - #131 adds `packages/opcore/src/init.ts` and the `opcoreInit` router payload. `opcore init` detects existing `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.github/copilot-instructions.md`, `.codex/AGENTS.md`, and `.opencode/AGENTS.md`, runs a read-only scan without `.opcore/report.json` or history writes, emits scan/settings/interaction/timing payloads, presents a plan before writing, prompts on TTY only, and now chooses repo/global write-gate scope by flag or interactive Git prompt. Approved repo init upserts a single `` block, writes additive `.opcore/config`, appends one managed `.opcore/` `.gitignore` line only in Git repos that do not already ignore it, installs `.opcore/hooks/opcore-agent-gate.mjs`, and merges Claude Code plus Codex PreToolUse hook settings without clobbering existing hooks. Approved global init installs the same adapter under `~/.opcore/hooks/`, merges `~/.claude/settings.json` and `~/.codex/hooks.json`, and records undo under `~/.opcore/init-undo.json`. The adapter maps Write/Edit/MultiEdit and Codex apply_patch payloads to hypothetical validation overlays, calls `opcore validate pre-write --request-file --timeout-ms 30000 --json`, exits 2 on non-ok receipts or validation command failure, and fail-opens only when the adapter cannot parse/map the harness payload. Approved writes refuse symlink targets or symlink ancestors for repo and global paths. Undo refuses metadata whose recorded root does not match or whose entries are outside Opcore-owned config/hooks/agent guidance paths plus the managed `.gitignore` line; `.gitignore` undo removes only that managed line and deletes an init-created `.gitignore` only when empty. The managed `.opcore/` ignore covers `.opcore/telemetry.jsonl`. The guidance must tell agents to run `opcore check --changed`, preserve existing safeguards, report unsupported/degraded coverage honestly, and not rely on ASP host authority for direct Opcore. - #133 adds `packages/opcore/src/try.ts` and the `opcoreTry` router payload. `opcore try` uses generated local sample repos only, keeps output coverage-first with named findings/deltas, includes unsupported-file census evidence, and records clean-room launch proof without public announcement or package publishing. - #22 adds the edit-core library foundation: `@the-open-engine/opcore-edit` owns deterministic exact, multi-edit, and literal search-replace planners, edit checksums, plan hashes, validation overlay construction, preview mode, repo path policy, Node workspaces, and all-or-nothing atomic apply/rollback. #59 adds canonical `opcore edit exact`, `multi`, `search-replace`, `check`, and `apply` parsing inside the edit package, typed `editPlan`/`editResult` router payloads, and search-replace uniqueness unless `replaceAll` is true. #60 adds canonical `opcore edit patch` and `tree`: raw unified diff patch input through `--stdin`/`--request-file` or `{patch}` JSON, tree payloads `{repo?,validation?,fileContains?,files:[{path,content,checksumBefore?}|{path,delete:true,checksumBefore?}]}`, patch/tree-only forbidden target policy for absolute paths, parent traversal, UNC paths, symlink escapes, `.gitignore` targets, generated/private roots, and binary content, plus `editResult.rollback` state for atomic apply failures. #24 routes non-empty `opcore edit` apply/check plans through an injected validation runner before writes, rejects validation bypass plans, preserves full `ValidationResult` envelopes in edit results, and keeps `--dry-run` as a non-validating preview. #23 implements canonical `opcore edit rename`, `move`, and `signature` routes as graph-backed, validation-required edit plans: GraphProvider contract status/query/search evidence is required for targeting/freshness, TypeScript/JavaScript language-service materialization stays edit-owned, and apply/check refuses graph freshness changes before validation or writes. -- #149 adds the shared inspect/edit ts-morph Project construction seam: cold inspect signatures and graph-backed implementations default to target import-closure loading; references and symbol edits load the target import closure plus reverse importers instead of whole-repo Projects when that preserves correctness; graphless implementation paths keep whole-repo fallback where reverse dependents cannot be bounded safely; and both inspect/edit accept injected Project plus snapshot/revert hooks for future warm ASP reuse without introducing a daemon. +- #149 makes `packages/edit/src/typescript-project/` the shared owner of ts-morph project discovery, tsconfig selection, import resolution, source listing, scope materialization, and injected-project snapshot composition consumed by edit, CLI-owned inspect, and warm ASP sessions. Cold inspect signatures and graph-backed implementations default to target import-closure loading; references and symbol edits load the target import closure plus reverse importers instead of whole-repo Projects when that preserves correctness; graphless implementation paths keep whole-repo fallback where reverse dependents cannot be bounded safely; and both inspect/edit accept injected Project plus snapshot/revert hooks without introducing a daemon - WHY: inspect and warm sessions must not maintain a second TypeScript project scanner or import resolver that diverges from symbol edits. - #153 adds the hidden `opcore asp serve --stdio` warm ASP session: host-launched only, process-local singleton/idle lifecycle under `.opcore/asp`, warm injected whole-repo ts-morph Project for `inspect/references` and `edit/rename` preview, unchanged delegated `check/evaluate`, `session/shutdown`, no source writes, no public help/manifest command group, no auto-spawned daemon, and no change to the cold `opcore-asp-provider --stdio` check-only capability. - #17 adds the graph release readiness receipt gate: `npm run graph-release:check` proves canonical graph commands, direct SQLite queries, serve transport, package inspection, provenance/license receipts, benchmark metrics, and handoff data for #7/#28/#29. - #28 adds the aggregate Opcore managed descriptor contract and artifact: `opcore` packages `dist/descriptors/opcore.managed-tool.json`, generated from `packages/opcore/src/advanced/descriptor.ts` after build. Descriptors list only the canonical `opcore` bin, command groups graph/inspect/edit/check/validate/status/doctor, package-relative artifact/checksum paths, GraphProvider query/search/native capabilities, edit validation dependency, validation graph optional/required modes, and deferred optional surfaces as metadata. @@ -200,7 +205,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - Keep release hygiene, conformance metadata, and package packlist gates executable when changing package or release surfaces - WHY: maintainer release receipts must fail before public alpha assumptions drift. - Add #29 negative fixtures for release evidence regressions: high-confidence secrets, unexpected package files, descriptor artifact drift, and missing native checksum evidence. -- Add #30 negative fixtures for cutover regressions: advertised placeholder command receipts, missing cutover command +- Add #30 negative fixtures for cutover regressions: advertised unimplemented command receipts, missing cutover command receipts, and invalid installed package/bin surfaces. - Local proof for agent work is `npm run ci:local`; for source/package/native/release changes it runs `npm run ci` and then `npm run opcore:self-check`. Docs/agent-guidance-only changes use the repository-native fast path described above. diff --git a/CLAUDE.md b/CLAUDE.md index 45d49e4..a5b1daa 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -20,10 +20,12 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit | Concept | Primary File | |---------|--------------| | Runtime/CLI ARD | @docs/architecture/runtime-cli-ard.md | +| Graph hub inventory | @docs/architecture/graph-hub-inventory.md | | Opcore alpha roadmap | @docs/planning/opcore-alpha-roadmap.md | | Opcore metrics/report/history | `packages/opcore/src/reporting.ts` | | Latency budgets and trend gate | `docs/performance/latency-budgets.json`, `scripts/check-latency-budgets.mjs` | | Public contracts | @packages/contracts/ | +| Public contracts barrel | `packages/contracts/src/index.ts` (API-only exports; domain modules own implementations) | | Contract JSON schema | `packages/contracts/schemas/opcore-contracts.schema.json` | | Command router package | @packages/opcore/src/advanced/ | | Graph provider package track | @packages/graph/ | @@ -34,6 +36,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit | Validation policy composition | @packages/validation-policy/ | | Validation file view | `packages/validation/src/overlays.ts` | | Validation graph client | `packages/validation/src/graph-client.ts` | +| Documentation validation composition | `packages/validation-docs/src/checks.ts`, `packages/validation-docs/src/document-check.ts` | | Rust validation adapter | @packages/validation-rust/ | | TypeScript validation adapter | @packages/validation-typescript/ | | Clone validation adapter | @packages/validation-clone/ | @@ -69,7 +72,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit ## Development Tooling - Run `npm run setup` after cloning or entering a fresh worktree. It installs repository dependencies only and must leave the worktree clean. -- Run `npm run opcore:self-check` after building. It invokes the built public Opcore CLI in introduced mode against the configured base ref and consumes the checked-in `.opcore/config` policy. +- Run `npm run opcore:self-check` after building. It requires `.opcore/config` to select every registered check explicitly with no disabled checks, validates changed-compatible checks in introduced mode against the configured base ref, prepares fresh graph evidence for incompatible repo-wide checks, and requires zero diagnostics across the complete manifest - WHY: self-validation must fail when a new check is not explicitly governed or any supported language loses complexity, tool, graph, docs, or clone coverage. - `scripts/ci/run-local-ci-equivalent.sh` runs normal CI plus the Opcore self-check. Its docs/agent-guidance fast path uses only repository-native workspace, provenance, build, and self-validation commands. - Zeroshot worktrees run `npm ci` and the same local CI-equivalent command proof - WHY: humans, agents, CI, and ship clusters must exercise one repository-owned validation surface. - Root `.npmrc` sets `loglevel=silent` - WHY: JSON-emitting npm scripts such as `npm run asp-dogfood:check -- --json` must write parseable JSON to redirected stdout without npm lifecycle preambles. @@ -85,7 +88,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - #246 makes `@the-open-engine/opcore-validation-python` the sole dynamic owner of `opcore.python.project-context.v1`: every Python target resolves against its nearest project boundary through an injected read/list/exists/realpath workspace view, smol-toml AST config/build metadata, exact interpreter/tool/build probes, and after-state content. Missing realpath evidence is ambiguous, deleted overlays cannot remain discovery markers, and declared constraints never become invented exact versions. Validation, status, scan, init/install preview, metrics, ASP, and installed execution must reuse the resulting project key, context fingerprint, outcome, and provenance; ASP workspace/config reads must remain host-callback-only. Static descriptors advertise only the schema, outcome vocabulary, read-only behavior, and no-install guarantee - WHY: root-scoped and duplicate project/environment discovery validates nested monorepo files with the wrong interpreter and makes surfaces disagree. #256/#257 make `python.types` select and execute exactly one mypy or Pyright authority per canonical project. Mypy uses first-match config precedence and strict NDJSON. Pyright preserves `pyrightconfig.json` precedence over `[tool.pyright]`, recursive repo-confined extends, and config-driven source/stub semantics while consuming only complete `--outputjson`. Both authorities use the same isolated exact after-state, portable receipt, selected interpreter, bounded process-tree runner, and sanitized HOME/XDG/cache/temp environment. Availability never selects an authority or permits fallback. Malformed, partial, contradictory, version/count-mismatched, out-of-repo, fatal, or stderr protocol evidence fails closed, and every project attempt emits `opcore.python.validation-capability-run` evidence - WHY: host config/imports, source mutation, orphaned checker descendants, availability, human-output parsing, or check-level summaries cannot prove which project/config/after-state produced type evidence. - #258 keeps `python.ruff-lint` and `python.ruff-format` separate from `python.source-hygiene` and opt-in through explicit selection or `.opcore/config` defaults. They execute the #246-selected Ruff over a temporary #245 after-state workspace with fixes, writes, and caches disabled; lint consumes JSON and format uses bounded exit-code refinement. Closest target-applicable `.ruff.toml`, `ruff.toml`, or `[tool.ruff]` configuration searches through the repository root across nested Python project boundaries, partitions project execution, requires non-symlink realpath evidence for every selected or recursively extended config, and materializes only that config closure. Python types and Ruff share the `packages/validation-python/src/python-execution-workspace.ts` primitive and sanitized HOME/XDG/TMP/PATH runtime for after-state fingerprinting, materialization, execution isolation, and cleanup while capability code selects its own support files. Ruff receipts use the shared `afterStateManifestFingerprint` field and portable executable/argv locators. Missing Ruff degrades status only while a Ruff check is active, and metrics require executed capability receipts - WHY: optional source tooling must never be probed, invoked, counted, or reported as enforced when policy did not select it, target-local configuration must not leak across files, host state must not affect results, and parallel materializers can make tool inputs diverge from receipts. - #209 makes Rust graph-core the sole parser/resolver for Python repo imports. `@the-open-engine/opcore-graph` materializes supplied `.py`/`.pyi` after-state files only in an isolated temporary repo and returns canonical directed `IMPORTS_FROM` file edges; validation-python owns only the structural analyzer contract, visible-file enumeration, cached target/transitive closure, and edge consumption. Opcore, advanced validation, validation-policy, and ASP inject the graph adapter; missing/failed/malformed analysis is an infrastructure failure, never empty success - WHY: a second TypeScript import grammar/resolver diverges on multiline syntax, overlays, packages, stubs, namespaces, and src layouts. -- #197 makes hypothetical graph evaluation exact-state: validation creates one `ValidationFileView` per before/after state and owns one disposable graph session for that view; graph materializes the complete visible TS/TSX/JS/JSX, Python `.py`/`.pyi`, and Rust `.rs` universe into a bounded isolated root, builds graph-core once, shares the immutable session across checks, and removes it on every exit. Introduced mode must use distinct before/after snapshots, ASP listings must preserve host truncation, and exact-state construction/query failure is non-pass even when persistent graph mode is optional - WHY: a persistent target-repo graph or incomplete listing cannot describe hypothetical file contents and must never produce a false clean pre-write result. +- #197 makes hypothetical graph evaluation exact-state: validation creates one `ValidationFileView` per before/after state and owns one disposable graph session for that view; graph materializes the complete visible TS/TSX/JS/JSX, Python `.py`/`.pyi`, and Rust `.rs` universe plus root `tsconfig.json` into a bounded isolated root, builds graph-core once, shares the immutable session across checks, and removes it on every exit. Introduced mode must use distinct before/after snapshots, ASP listings must preserve host truncation, and exact-state construction/query failure is non-pass even when persistent graph mode is optional - WHY: a persistent target-repo graph, missing alias configuration, or incomplete listing cannot describe hypothetical file contents and must never produce a false clean pre-write result. - #19 requires graph status to preserve real WAL checkpoint evidence from the latest pipeline summary and release gates to fail missing/fabricated WAL evidence - WHY: freshness and checkpoint pressure must remain host-visible provider facts. - #19 treats `opcore graph serve` as the stdio/MCP hot-query replacement, not a Unix socket, with parallel independent serve sessions as the supported concurrency evidence. - `opcore-asp-provider --stdio` is the transitional provider binary for the standalone ASP Core check provider; it uses host workspace callbacks and Opcore validation only. @@ -112,6 +115,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - ALWAYS keep graph, edit, and validation as separate ownership boundaries - WHY: graph facts, code mutation, and policy checks evolve at different correctness boundaries - Consequence: a single mixed engine makes parity and cutover unverifiable. - ALWAYS put shared wire/types/contracts in `packages/contracts` before another package consumes them - WHY: package-private shape copying creates incompatible command and API surfaces. +- ALWAYS keep `packages/contracts/src/index.ts` as an API-only export barrel; place implementations in domain modules, preserve the root export surface during internal splits, and keep contracts source modules below 300 lines - WHY: the public package needs one stable import surface without returning to a monolithic implementation file. - ALWAYS update `packages/contracts/schemas/opcore-contracts.schema.json`, contract tests, fixture metadata, package exports, and packlists together when changing shared contracts - WHY: Rust/native graph-core consumers and TypeScript packages must consume the same wire artifacts. - ALWAYS dispatch implemented canonical `opcore graph`, `opcore edit`, `opcore check`, and `opcore validate` routes through public package-owned adapters - WHY: package entrypoints must be able to run without importing the aggregate CLI. - NEVER import package implementation internals across package tracks - WHY: graph, edit, and validation must be releasable and testable independently - Consequence: router composition hides runtime coupling until package publishing. @@ -170,6 +174,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - #56 adds the validation file view: `@the-open-engine/opcore-validation` composes normalized `ValidationRequest` overlays, resolved scope files, and injected workspace `readFile` access so `opcore validate` checks can read hypothetical after-state writes/deletes, before-state comparisons for introduced report mode, and before-state checksums without mutating the worktree. `ValidationFileView.defaultReadState` is check-visible so adapters with out-of-band readers can bind their before/after source mode to the runner pass. `checksumBefore` conflicts return refused/conflict before checks run. - #26 adds the validation-owned GraphProvider consumer boundary: `ValidationGraphProviderClient`, cached `ValidationGraphQuerySession`, graph requirement preloading, status/query failure mapping, and helper access for metadata, file checksums, IMPORTS_FROM, CALLS, and TESTED_BY facts. - #57 adds the TypeScript validation adapter: `@the-open-engine/opcore-validation-typescript` exports package-owned syntax, type, import-graph, dead-code, and relevant-tests check definitions. Syntax/type checks use the validation file view and an overlay-aware compiler host, including tsconfig path aliases for repo files and deterministic node_modules/package declaration resolution for external package imports; graph checks require #26 graph sessions and batched IMPORTS_FROM, CALLS, and TESTED_BY fact requirements. +- Opcore self-validation records file-level `TESTED_BY` evidence when a conventional TS/JS test imports a source file, while retaining symbol-level call evidence. The TypeScript relevant-test adapter emits diagnostics only for missing evidence; positive evidence is a clean check result. The real `tests/source-package-contracts.test.ts` suite imports workspace facades through root tsconfig source aliases and runs in normal local/CI tests - WHY: unsupported `.mjs` runtime suites and built `dist` imports cannot prove source-level relevant-test coverage, and successful evidence is not a finding. - #20 adds the Rust validation adapter: `@the-open-engine/opcore-validation-rust` exports package-owned source hygiene, fmt, cargo-check, clippy, rustdoc, import-graph, dead-code, graph-signals, unused-deps, file-length, and function-metrics checks. `rust.graph-signals` is graph-provider-backed evidence from `ValidationGraphProviderClient` only (untested public Rust surface, dead public exports, module orphans/cycles), while mechanical evidence remains owned by Cargo and the configured native tools. Missing `rustdoc`, `cargo-depgraph`, `cargo-udeps`, or `rust-code-analysis-cli` stays degraded or unsupported with `requiredTool`. Rust checks materialize temporary workspaces from `ValidationCheckContext.fileView` after-state content for Cargo tools and add no validation daemon or hidden cache. #138 shares cargo-check JSON with dead-code, forbids a second dead-code cargo compile or `-Ddead_code` cache-busting flags, and injects a persistent `CARGO_TARGET_DIR` outside the worktree through `runTool`; cache keys must derive from generic repo, scope, overlay, platform, and toolchain inputs and cleanup must remain TTL/size bounded - WHY: Rust validation must be fast without leaking hypothetical after-state into real worktrees or tuning behavior to one repository. - Rust Cargo/native checks share one environment-keyed materialized workspace per validation file-view state through runner-owned disposable resources; the runner removes it on every exit - WHY: exact staged/tree/overlay semantics require state isolation, while per-check whole-repo copies create unbounded filesystem-event churn. - #151/#214 adds clone detection through the existing `opcore-graph-core` binary as a `clone` subcommand plus `@the-open-engine/opcore-validation-clone`. `CloneAnalysisRequest`/`CloneAnalysisResult` use `opcore.clone.v1`; committed analysis may refresh `.opcore/clone/clone.db`, while scoped or hypothetical analysis is ephemeral. The sparse request shape sends committed candidates as `paths`/`sourcePaths` plus `sourceReadMode`/`sourceTreeRef`, with full content only in write overlays. `clone.duplication` is a validation adapter check with injected native invocation, no graph requirement, no line-level identity, no daemon, and no SAST/security or score claim. @@ -182,7 +187,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - #131 adds `packages/opcore/src/init.ts` and the `opcoreInit` router payload. `opcore init` detects existing `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.github/copilot-instructions.md`, `.codex/AGENTS.md`, and `.opencode/AGENTS.md`, runs a read-only scan without `.opcore/report.json` or history writes, emits scan/settings/interaction/timing payloads, presents a plan before writing, prompts on TTY only, and now chooses repo/global write-gate scope by flag or interactive Git prompt. Approved repo init upserts a single `` block, writes additive `.opcore/config`, appends one managed `.opcore/` `.gitignore` line only in Git repos that do not already ignore it, installs `.opcore/hooks/opcore-agent-gate.mjs`, and merges Claude Code plus Codex PreToolUse hook settings without clobbering existing hooks. Approved global init installs the same adapter under `~/.opcore/hooks/`, merges `~/.claude/settings.json` and `~/.codex/hooks.json`, and records undo under `~/.opcore/init-undo.json`. The adapter maps Write/Edit/MultiEdit and Codex apply_patch payloads to hypothetical validation overlays, calls `opcore validate pre-write --request-file --timeout-ms 30000 --json`, exits 2 on non-ok receipts or validation command failure, and fail-opens only when the adapter cannot parse/map the harness payload. Approved writes refuse symlink targets or symlink ancestors for repo and global paths. Undo refuses metadata whose recorded root does not match or whose entries are outside Opcore-owned config/hooks/agent guidance paths plus the managed `.gitignore` line; `.gitignore` undo removes only that managed line and deletes an init-created `.gitignore` only when empty. The managed `.opcore/` ignore covers `.opcore/telemetry.jsonl`. The guidance must tell agents to run `opcore check --changed`, preserve existing safeguards, report unsupported/degraded coverage honestly, and not rely on ASP host authority for direct Opcore. - #133 adds `packages/opcore/src/try.ts` and the `opcoreTry` router payload. `opcore try` uses generated local sample repos only, keeps output coverage-first with named findings/deltas, includes unsupported-file census evidence, and records clean-room launch proof without public announcement or package publishing. - #22 adds the edit-core library foundation: `@the-open-engine/opcore-edit` owns deterministic exact, multi-edit, and literal search-replace planners, edit checksums, plan hashes, validation overlay construction, preview mode, repo path policy, Node workspaces, and all-or-nothing atomic apply/rollback. #59 adds canonical `opcore edit exact`, `multi`, `search-replace`, `check`, and `apply` parsing inside the edit package, typed `editPlan`/`editResult` router payloads, and search-replace uniqueness unless `replaceAll` is true. #60 adds canonical `opcore edit patch` and `tree`: raw unified diff patch input through `--stdin`/`--request-file` or `{patch}` JSON, tree payloads `{repo?,validation?,fileContains?,files:[{path,content,checksumBefore?}|{path,delete:true,checksumBefore?}]}`, patch/tree-only forbidden target policy for absolute paths, parent traversal, UNC paths, symlink escapes, `.gitignore` targets, generated/private roots, and binary content, plus `editResult.rollback` state for atomic apply failures. #24 routes non-empty `opcore edit` apply/check plans through an injected validation runner before writes, rejects validation bypass plans, preserves full `ValidationResult` envelopes in edit results, and keeps `--dry-run` as a non-validating preview. #23 implements canonical `opcore edit rename`, `move`, and `signature` routes as graph-backed, validation-required edit plans: GraphProvider contract status/query/search evidence is required for targeting/freshness, TypeScript/JavaScript language-service materialization stays edit-owned, and apply/check refuses graph freshness changes before validation or writes. -- #149 adds the shared inspect/edit ts-morph Project construction seam: cold inspect signatures and graph-backed implementations default to target import-closure loading; references and symbol edits load the target import closure plus reverse importers instead of whole-repo Projects when that preserves correctness; graphless implementation paths keep whole-repo fallback where reverse dependents cannot be bounded safely; and both inspect/edit accept injected Project plus snapshot/revert hooks for future warm ASP reuse without introducing a daemon. +- #149 makes `packages/edit/src/typescript-project/` the shared owner of ts-morph project discovery, tsconfig selection, import resolution, source listing, scope materialization, and injected-project snapshot composition consumed by edit, CLI-owned inspect, and warm ASP sessions. Cold inspect signatures and graph-backed implementations default to target import-closure loading; references and symbol edits load the target import closure plus reverse importers instead of whole-repo Projects when that preserves correctness; graphless implementation paths keep whole-repo fallback where reverse dependents cannot be bounded safely; and both inspect/edit accept injected Project plus snapshot/revert hooks without introducing a daemon - WHY: inspect and warm sessions must not maintain a second TypeScript project scanner or import resolver that diverges from symbol edits. - #153 adds the hidden `opcore asp serve --stdio` warm ASP session: host-launched only, process-local singleton/idle lifecycle under `.opcore/asp`, warm injected whole-repo ts-morph Project for `inspect/references` and `edit/rename` preview, unchanged delegated `check/evaluate`, `session/shutdown`, no source writes, no public help/manifest command group, no auto-spawned daemon, and no change to the cold `opcore-asp-provider --stdio` check-only capability. - #17 adds the graph release readiness receipt gate: `npm run graph-release:check` proves canonical graph commands, direct SQLite queries, serve transport, package inspection, provenance/license receipts, benchmark metrics, and handoff data for #7/#28/#29. - #28 adds the aggregate Opcore managed descriptor contract and artifact: `opcore` packages `dist/descriptors/opcore.managed-tool.json`, generated from `packages/opcore/src/advanced/descriptor.ts` after build. Descriptors list only the canonical `opcore` bin, command groups graph/inspect/edit/check/validate/status/doctor, package-relative artifact/checksum paths, GraphProvider query/search/native capabilities, edit validation dependency, validation graph optional/required modes, and deferred optional surfaces as metadata. @@ -200,7 +205,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - Keep release hygiene, conformance metadata, and package packlist gates executable when changing package or release surfaces - WHY: maintainer release receipts must fail before public alpha assumptions drift. - Add #29 negative fixtures for release evidence regressions: high-confidence secrets, unexpected package files, descriptor artifact drift, and missing native checksum evidence. -- Add #30 negative fixtures for cutover regressions: advertised placeholder command receipts, missing cutover command +- Add #30 negative fixtures for cutover regressions: advertised unimplemented command receipts, missing cutover command receipts, and invalid installed package/bin surfaces. - Local proof for agent work is `npm run ci:local`; for source/package/native/release changes it runs `npm run ci` and then `npm run opcore:self-check`. Docs/agent-guidance-only changes use the repository-native fast path described above. diff --git a/crates/graph-core/src/clone/mod.rs b/crates/graph-core/src/clone/mod.rs index 02b4e98..565e085 100644 --- a/crates/graph-core/src/clone/mod.rs +++ b/crates/graph-core/src/clone/mod.rs @@ -257,17 +257,25 @@ fn validate_positive_option(label: &str, value: Option) -> Result<(), Clo } fn validate_request_paths(request: &CloneAnalysisRequest) -> Result<(), CloneError> { - for path in &request.paths { - normalize_repo_relative_path(path, "clone request path") - .map_err(|message| CloneError::InvalidRequest(message.to_string()))?; - } + validate_normalized_paths(&request.paths, "clone request path")?; if let Some(paths) = &request.source_paths { - for path in paths { - normalize_repo_relative_path(path, "clone request source path") - .map_err(|message| CloneError::InvalidRequest(message.to_string()))?; - } + validate_normalized_paths(paths, "clone request source path")?; + } + validate_partitions(&request.partitions)?; + validate_patterns(&request.exclude, "clone exclude pattern")?; + validate_overlay_paths(&request.overlays) +} + +fn validate_normalized_paths(paths: &[String], label: &str) -> Result<(), CloneError> { + for path in paths { + normalize_repo_relative_path(path, label) + .map_err(|message| CloneError::InvalidRequest(message.to_string()))?; } - for (index, partition) in request.partitions.iter().enumerate() { + Ok(()) +} + +fn validate_partitions(partitions: &[Vec]) -> Result<(), CloneError> { + for (index, partition) in partitions.iter().enumerate() { if partition.is_empty() { return Err(CloneError::InvalidRequest(format!( "partitions[{index}] must not be empty" @@ -277,10 +285,18 @@ fn validate_request_paths(request: &CloneAnalysisRequest) -> Result<(), CloneErr validate_clone_pattern(pattern, "clone partition pattern")?; } } - for pattern in &request.exclude { - validate_clone_pattern(pattern, "clone exclude pattern")?; + Ok(()) +} + +fn validate_patterns(patterns: &[String], label: &str) -> Result<(), CloneError> { + for pattern in patterns { + validate_clone_pattern(pattern, label)?; } - for overlay in &request.overlays { + Ok(()) +} + +fn validate_overlay_paths(overlays: &[CloneOverlay]) -> Result<(), CloneError> { + for overlay in overlays { normalize_repo_relative_path(overlay.path(), "clone overlay path") .map_err(|message| CloneError::InvalidRequest(message.to_string()))?; } diff --git a/crates/graph-core/src/clone/store.rs b/crates/graph-core/src/clone/store.rs index 8bb7689..443060c 100644 --- a/crates/graph-core/src/clone/store.rs +++ b/crates/graph-core/src/clone/store.rs @@ -1,6 +1,6 @@ use super::analysis::{CloneClass, CloneSource}; use super::{CloneError, CLONE_PROTOCOL, CLONE_STORE_SCHEMA_VERSION}; -use rusqlite::{params, Connection}; +use rusqlite::{params, Connection, Transaction}; use std::path::{Path, PathBuf}; pub(super) fn persist_clone_index( @@ -14,9 +14,22 @@ pub(super) fn persist_clone_index( let mut connection = Connection::open(&db_path)?; initialize_clone_schema(&connection)?; let transaction = connection.transaction()?; + reset_clone_index(&transaction)?; + write_clone_metadata(&transaction)?; + write_clone_sources(&transaction, sources)?; + write_clone_classes(&transaction, classes)?; + transaction.commit()?; + Ok(db_path) +} + +fn reset_clone_index(transaction: &Transaction<'_>) -> Result<(), CloneError> { transaction.execute("delete from clone_occurrences", [])?; transaction.execute("delete from clone_files", [])?; transaction.execute("delete from clone_metadata", [])?; + Ok(()) +} + +fn write_clone_metadata(transaction: &Transaction<'_>) -> Result<(), CloneError> { transaction.execute( "insert into clone_metadata(key, value) values ('protocol', ?1)", params![CLONE_PROTOCOL], @@ -25,12 +38,26 @@ pub(super) fn persist_clone_index( "insert into clone_metadata(key, value) values ('schema_version', ?1)", params![CLONE_STORE_SCHEMA_VERSION.to_string()], )?; + Ok(()) +} + +fn write_clone_sources( + transaction: &Transaction<'_>, + sources: &[CloneSource], +) -> Result<(), CloneError> { for source in sources { transaction.execute( "insert into clone_files(path, language, sha256) values (?1, ?2, ?3)", params![source.path, source.language, source.sha256], )?; } + Ok(()) +} + +fn write_clone_classes( + transaction: &Transaction<'_>, + classes: &[CloneClass], +) -> Result<(), CloneError> { for class in classes { for occurrence in &class.occurrences { transaction.execute( @@ -45,8 +72,7 @@ pub(super) fn persist_clone_index( )?; } } - transaction.commit()?; - Ok(db_path) + Ok(()) } fn initialize_clone_schema(connection: &Connection) -> Result<(), CloneError> { diff --git a/crates/graph-core/src/daemon/tests.rs b/crates/graph-core/src/daemon/tests.rs index c05782d..2865389 100644 --- a/crates/graph-core/src/daemon/tests.rs +++ b/crates/graph-core/src/daemon/tests.rs @@ -3,9 +3,10 @@ use crate::protocol::{ GraphFactQueryKind, GraphFactQueryRequest, GraphFactQuerySelector, GraphImpactRequest, GraphProviderMode, GraphSearchRequest, RepoIdentity, }; +use crate::test_support::wave1_fixture_root; use serde_json::{json, Value}; use std::fs; -use std::path::{Path, PathBuf}; +use std::path::Path; use std::time::Duration; use tempfile::TempDir; @@ -383,12 +384,6 @@ fn copied_wave1_fixture() -> Result { Ok(destination) } -fn wave1_fixture_root() -> Result { - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("../../packages/fixtures/source-extraction/wave1") - .canonicalize() -} - fn copy_dir(source: &Path, destination: &Path) -> Result<(), std::io::Error> { let mut pending = vec![(source.to_path_buf(), destination.to_path_buf())]; while let Some((from_dir, to_dir)) = pending.pop() { diff --git a/crates/graph-core/src/extraction/facts.rs b/crates/graph-core/src/extraction/facts.rs index a82770c..fca4006 100644 --- a/crates/graph-core/src/extraction/facts.rs +++ b/crates/graph-core/src/extraction/facts.rs @@ -11,8 +11,10 @@ use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; mod collector; mod python; +mod resolution; mod rust; +use resolution::{FactResolution, Resolution}; use std::collections::{BTreeMap, BTreeSet}; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -100,7 +102,7 @@ pub fn file_node(source: &DiscoveredSource) -> GraphFactNode { | SourceLanguage::JavaScriptJsx => "oxc_parser", }; GraphFactNode { - id: file_id(&source.relative_path), + id: Resolution::file_id(&source.relative_path), kind: "File".to_string(), path: Some(source.relative_path.clone()), name: None, @@ -157,7 +159,7 @@ pub fn finalize_facts( tsconfig: Option<&TsConfig>, diagnostics: &mut Vec, ) -> (Vec, Vec) { - let known_files = known_files(file_facts); + let known_files = Resolution::known_files(file_facts); let mut imports = ImportResolutionContext { known_files: &known_files, tsconfig, @@ -167,7 +169,7 @@ pub fn finalize_facts( finalizer.resolve_imports(file_facts, &mut imports); finalizer.resolve_re_exports(file_facts); finalizer.resolve_links(file_facts); - append_path_traversal_blocker(imports.diagnostics); + Resolution::append_path_traversal_blocker(imports.diagnostics); finalizer.into_parts() } @@ -179,9 +181,9 @@ struct ImportResolutionContext<'a> { impl ImportResolutionContext<'_> { fn resolve(&mut self, specifier: &str, path: &str) -> Option { - let resolution = if is_python_source_path(path) { + let resolution = if Resolution::is_python_source_path(path) { python_imports::resolve_import(specifier, path, self.known_files) - } else if is_rust_source_path(path) { + } else if Resolution::is_rust_source_path(path) { rust::resolve_import(specifier, path, self.known_files) } else { resolve_import(specifier, path, self.known_files, self.tsconfig) @@ -209,6 +211,36 @@ fn python_submodule_specifier(specifier: &str, imported: &str) -> Option Some(format!("{specifier}{separator}{imported}")) } +fn is_conventional_script_test_path(path: &str) -> bool { + let Some((stem, extension)) = path.rsplit_once('.') else { + return false; + }; + let is_script = matches!( + extension, + "ts" | "tsx" | "mts" | "cts" | "js" | "jsx" | "mjs" | "cjs" + ); + is_script + && (path.split('/').any(|segment| segment == "__tests__") + || stem.ends_with(".test") + || stem.ends_with(".spec")) +} + +fn set_node_attribute(node: &mut GraphFactNode, key: &str, value: Value) { + node_attributes_object(node).insert(key.to_string(), value); +} + +fn node_attributes_object(node: &mut GraphFactNode) -> &mut serde_json::Map { + let attributes = node + .attributes + .get_or_insert_with(|| Value::Object(serde_json::Map::new())); + loop { + if let Value::Object(object) = attributes { + return object; + } + *attributes = Value::Object(serde_json::Map::new()); + } +} + struct FactFinalizer { nodes: BTreeMap, edges: BTreeMap, @@ -246,7 +278,7 @@ impl FactFinalizer { fn resolve_imports(&mut self, file_facts: &[FileFacts], imports: &mut ImportResolutionContext) { for facts in file_facts { for import in &facts.imports { - if is_python_source_path(&facts.path) + if Resolution::is_python_source_path(&facts.path) && self.register_python_import(facts, import, imports) { continue; @@ -264,8 +296,10 @@ impl FactFinalizer { self.register_import_binding( &facts.path, binding, - target_path.clone(), - binding.imported.clone(), + ImportTarget { + path: target_path.clone(), + imported: binding.imported.clone(), + }, ); } } @@ -284,7 +318,14 @@ impl FactFinalizer { &binding.imported, &facts.path, ) { - self.register_import_binding(&facts.path, binding, target_path, "*".to_string()); + self.register_import_binding( + &facts.path, + binding, + ImportTarget { + path: target_path, + imported: "*".to_string(), + }, + ); handled_submodule = true; } else { fallback_bindings.push(binding); @@ -299,8 +340,10 @@ impl FactFinalizer { self.register_import_binding( &facts.path, binding, - target_path.clone(), - binding.imported.clone(), + ImportTarget { + path: target_path.clone(), + imported: binding.imported.clone(), + }, ); } } @@ -312,27 +355,25 @@ impl FactFinalizer { &mut self, facts_path: &str, binding: &ImportBinding, - target_path: String, - imported: String, + target: ImportTarget, ) { - self.register_import_edge(facts_path, &target_path); + self.register_import_edge(facts_path, &target.path); self.imports_by_file .entry(facts_path.to_string()) .or_default() - .insert( - binding.local.clone(), - ImportTarget { - path: target_path, - imported, - }, - ); + .insert(binding.local.clone(), target); } fn register_import_edge(&mut self, facts_path: &str, target_path: &str) { - let from = file_id(facts_path); - let to = file_id(target_path); - insert_edge(&mut self.edges, EdgeDraft::new("IMPORTS_FROM", &from, &to)); - insert_edge(&mut self.edges, EdgeDraft::new("DEPENDS_ON", &from, &to)); + let from = Resolution::file_id(facts_path); + let to = Resolution::file_id(target_path); + Resolution::insert_edge(&mut self.edges, EdgeDraft::new("IMPORTS_FROM", &from, &to)); + Resolution::insert_edge(&mut self.edges, EdgeDraft::new("DEPENDS_ON", &from, &to)); + if is_conventional_script_test_path(facts_path) + && !is_conventional_script_test_path(target_path) + { + Resolution::insert_edge(&mut self.edges, EdgeDraft::new("TESTED_BY", &to, &from)); + } } fn resolve_re_exports(&mut self, file_facts: &[FileFacts]) { @@ -350,7 +391,7 @@ impl FactFinalizer { }; let resolved = { let context = self.name_resolution_context(); - resolve_imported_name(&target.path, &target.imported, &context) + Resolution::resolve_imported_name(&target.path, &target.imported, &context) }; let Some(resolved) = resolved else { continue; @@ -373,7 +414,7 @@ impl FactFinalizer { } fn mark_file_export_supported(&mut self, path: &str, re_export: &ReExportFact) { - let Some(file_node) = self.nodes.get_mut(&file_id(path)) else { + let Some(file_node) = self.nodes.get_mut(&Resolution::file_id(path)) else { return; }; let Some(attributes) = file_node.attributes.as_mut().and_then(Value::as_object_mut) else { @@ -407,10 +448,10 @@ impl FactFinalizer { for heritage in &facts.heritage { let target = { let context = self.name_resolution_context(); - resolve_name(&facts.path, &heritage.name, &context) + Resolution::resolve_name(&facts.path, &heritage.name, &context) }; if let Some(target) = target { - insert_edge( + Resolution::insert_edge( &mut self.edges, EdgeDraft::new(&heritage.kind, &heritage.from, &target), ); @@ -422,15 +463,15 @@ impl FactFinalizer { for reference in &facts.references { let target = { let context = self.name_resolution_context(); - resolve_name(&facts.path, &reference.name, &context) + Resolution::resolve_name(&facts.path, &reference.name, &context) }; if let Some(target) = target { - insert_edge( + Resolution::insert_edge( &mut self.edges, EdgeDraft::new("CALLS", &reference.from, &target), ); if reference.is_test || reference.from.starts_with("test:") { - insert_edge( + Resolution::insert_edge( &mut self.edges, EdgeDraft::new("TESTED_BY", &target, &reference.from), ); @@ -454,182 +495,3 @@ impl FactFinalizer { ) } } - -fn known_files(file_facts: &[FileFacts]) -> BTreeSet { - file_facts - .iter() - .map(|facts| facts.path.clone()) - .collect::>() -} - -fn append_path_traversal_blocker(diagnostics: &mut Vec) { - let has_path_traversal = diagnostics - .iter() - .any(|diagnostic| diagnostic.category == GraphExtractionDiagnosticCategory::PathTraversal); - if has_path_traversal { - diagnostics.push(error( - GraphExtractionDiagnosticCategory::PathTraversal, - "path traversal diagnostics blocked graph availability", - None, - None, - )); - } -} - -pub fn file_id(path: &str) -> String { - format!("file:{path}") -} - -fn insert_edge(edges: &mut BTreeMap, edge: EdgeDraft<'_>) { - if edge.from == edge.to { - return; - } - let id = format!("edge:{}:{}->{}", edge.kind, edge.from, edge.to); - edges.entry(id.clone()).or_insert_with(|| GraphFactEdge { - id: Some(id), - kind: edge.kind.to_string(), - from: edge.from.to_string(), - to: edge.to.to_string(), - attributes: None, - }); -} - -fn resolve_name( - file_path: &str, - name: &str, - context: &NameResolutionContext<'_>, -) -> Option { - if let Some(local) = context - .declarations_by_file - .get(file_path) - .and_then(|declarations| declarations.get(name)) - { - return Some(local.clone()); - } - if name.contains('.') { - return resolve_dotted_name(file_path, name, context); - } - if let Some(target) = context - .imports_by_file - .get(file_path) - .and_then(|imports| imports.get(name)) - { - if target.imported != "*" { - if let Some(target) = resolve_imported_name(&target.path, &target.imported, context) { - return Some(target); - } - } - } - None -} - -fn resolve_dotted_name( - file_path: &str, - name: &str, - context: &NameResolutionContext<'_>, -) -> Option { - let parts = name.split('.').collect::>(); - let head = parts.first()?; - let target = context - .imports_by_file - .get(file_path) - .and_then(|imports| imports.get(*head))?; - if target.imported == "*" { - for candidate in namespace_import_member_candidates(&parts, &target.path) { - if let Some(target) = resolve_imported_name(&target.path, &candidate, context) { - return Some(target); - } - } - return None; - } - resolve_imported_name(&target.path, &target.imported, context) -} - -fn namespace_import_member_candidates(parts: &[&str], target_path: &str) -> Vec { - let mut candidates = Vec::new(); - let module_parts = module_parts_for_path(target_path); - if let Some(consumed) = longest_module_prefix_match(parts, &module_parts) { - if let Some(candidate) = parts.get(consumed..) { - candidates.push(candidate.join(".")); - } - } - if let Some(candidate) = parts.get(1..) { - candidates.push(candidate.join(".")); - } - if let Some(last) = parts.last() { - candidates.push((*last).to_string()); - } - deduplicate(candidates) -} - -fn longest_module_prefix_match(parts: &[&str], module_parts: &[String]) -> Option { - let max_len = parts.len().min(module_parts.len()); - (1..=max_len).rev().find(|len| { - let len = *len; - let Some(start) = module_parts.len().checked_sub(len) else { - return false; - }; - let Some(module_suffix) = module_parts.get(start..) else { - return false; - }; - let Some(parts_prefix) = parts.get(..len) else { - return false; - }; - module_suffix - .iter() - .map(String::as_str) - .eq(parts_prefix.iter().copied()) - }) -} - -fn module_parts_for_path(path: &str) -> Vec { - let without_extension = path - .strip_suffix(".py") - .or_else(|| path.strip_suffix(".pyi")) - .or_else(|| path.strip_suffix(".mts")) - .or_else(|| path.strip_suffix(".cts")) - .or_else(|| path.strip_suffix(".ts")) - .or_else(|| path.strip_suffix(".tsx")) - .or_else(|| path.strip_suffix(".js")) - .or_else(|| path.strip_suffix(".jsx")) - .or_else(|| path.strip_suffix(".rs")) - .unwrap_or(path); - let mut parts = without_extension - .split('/') - .filter(|part| !part.is_empty()) - .map(ToString::to_string) - .collect::>(); - if parts.last().is_some_and(|part| part == "__init__") { - parts.pop(); - } - parts -} - -fn deduplicate(values: Vec) -> Vec { - values.into_iter().fold(Vec::new(), |mut unique, value| { - if !value.is_empty() && !unique.contains(&value) { - unique.push(value); - } - unique - }) -} - -fn resolve_imported_name( - target_path: &str, - imported: &str, - context: &NameResolutionContext<'_>, -) -> Option { - context - .export_aliases_by_file - .get(target_path) - .and_then(|aliases| aliases.get(imported)) - .cloned() -} - -fn is_python_source_path(path: &str) -> bool { - path.ends_with(".py") || path.ends_with(".pyi") -} - -fn is_rust_source_path(path: &str) -> bool { - path.ends_with(".rs") -} diff --git a/crates/graph-core/src/extraction/facts/collector.rs b/crates/graph-core/src/extraction/facts/collector.rs index 2c89de8..dd3c4f8 100644 --- a/crates/graph-core/src/extraction/facts/collector.rs +++ b/crates/graph-core/src/extraction/facts/collector.rs @@ -1,6 +1,6 @@ use super::{ - file_id, insert_edge, EdgeDraft, FileFacts, HeritageFact, ImportBinding, ImportFact, - ReExportFact, ReferenceFact, + node_attributes_object, set_node_attribute, EdgeDraft, FactResolution, FileFacts, HeritageFact, + ImportBinding, ImportFact, ReExportFact, ReferenceFact, Resolution, }; use crate::protocol::{GraphFactEdge, GraphFactNode}; use oxc_ast::ast::{ @@ -15,13 +15,19 @@ use oxc_syntax::scope::ScopeFlags; use serde_json::{json, Value}; use std::collections::{btree_map::Entry, BTreeMap}; +mod helpers; +mod visit; + +use helpers::{CollectorHelpers, Helpers}; +use visit::CollectProgram; + pub(super) fn collect_file_facts( path: String, file_node: GraphFactNode, program: &oxc_ast::ast::Program<'_>, ) -> FileFacts { let mut collector = FileFactCollector::new(path, file_node); - collector.visit_program(program); + collector.collect_program(program); collector.finish() } @@ -95,7 +101,7 @@ impl FileFactCollector { fn finish(mut self) -> FileFacts { self.reconcile_file_exports(); if !self.file_exports.is_empty() { - set_attribute( + set_node_attribute( &mut self.file_node, "exports", Value::Array(self.file_exports.clone()), @@ -147,13 +153,13 @@ impl FileFactCollector { }; let register_default_alias = export .as_ref() - .is_some_and(|export| registers_default_export_alias(export, name)); + .is_some_and(|export| Helpers::registers_default_export_alias(export, name)); match self.nodes.entry(id.clone()) { Entry::Occupied(mut entry) => { if let Some(export) = &export { - apply_export_attributes(entry.get_mut(), export, name); + Helpers::apply_export_attributes(entry.get_mut(), export, name); } else { - ensure_export_attribute(entry.get_mut()); + Helpers::ensure_export_attribute(entry.get_mut()); } } Entry::Vacant(entry) => { @@ -167,7 +173,7 @@ impl FileFactCollector { })), }; if let Some(export) = &export { - apply_export_attributes(&mut node, export, name); + Helpers::apply_export_attributes(&mut node, export, name); } entry.insert(node); } @@ -187,8 +193,8 @@ impl FileFactCollector { .insert("default".to_string(), id.clone()); } } - let file = file_id(&self.path); - insert_edge(&mut self.edges, EdgeDraft::new("CONTAINS", &file, &id)); + let file = Resolution::file_id(&self.path); + Resolution::insert_edge(&mut self.edges, EdgeDraft::new("CONTAINS", &file, &id)); id } @@ -197,7 +203,7 @@ impl FileFactCollector { let id = format!("{prefix}:{}#{name}", self.path); match self.nodes.entry(id.clone()) { Entry::Occupied(mut entry) => { - apply_export_attributes(entry.get_mut(), &ExportContext::default(), name); + Helpers::apply_export_attributes(entry.get_mut(), &ExportContext::default(), name); } Entry::Vacant(entry) => { let mut node = GraphFactNode { @@ -209,7 +215,7 @@ impl FileFactCollector { "exported": false })), }; - apply_export_attributes(&mut node, &ExportContext::default(), name); + Helpers::apply_export_attributes(&mut node, &ExportContext::default(), name); entry.insert(node); } } @@ -217,8 +223,8 @@ impl FileFactCollector { self.top_level_declarations .insert(name.to_string(), id.clone()); self.register_export_alias(&ExportContext::default(), name, &id); - let file = file_id(&self.path); - insert_edge(&mut self.edges, EdgeDraft::new("CONTAINS", &file, &id)); + let file = Resolution::file_id(&self.path); + Resolution::insert_edge(&mut self.edges, EdgeDraft::new("CONTAINS", &file, &id)); id } @@ -233,8 +239,8 @@ impl FileFactCollector { name: Some(name.to_string()), attributes: None, }); - let file = file_id(&self.path); - insert_edge(&mut self.edges, EdgeDraft::new("CONTAINS", &file, &id)); + let file = Resolution::file_id(&self.path); + Resolution::insert_edge(&mut self.edges, EdgeDraft::new("CONTAINS", &file, &id)); id } @@ -265,9 +271,9 @@ impl FileFactCollector { fn mark_exported(&mut self, local: &str, export: ExportContext) { if let Some(id) = self.top_level_declarations.get(local).cloned() { if let Some(node) = self.nodes.get_mut(&id) { - apply_export_attributes(node, &export, local); + Helpers::apply_export_attributes(node, &export, local); } - if registers_default_export_alias(&export, local) { + if Helpers::registers_default_export_alias(&export, local) { self.declarations.insert("default".to_string(), id.clone()); self.top_level_declarations .insert("default".to_string(), id); @@ -309,459 +315,3 @@ struct ImportBackedLocal { source: String, imported: String, } - -impl<'a> Visit<'a> for FileFactCollector { - fn visit_import_declaration(&mut self, import: &ImportDeclaration<'a>) { - self.imports.push(ImportFact { - specifier: import.source.value.to_string(), - bindings: import_bindings(import.specifiers.as_ref()), - }); - } - - fn visit_import_expression(&mut self, import: &ImportExpression<'a>) { - if let Expression::StringLiteral(source) = &import.source { - self.imports.push(ImportFact { - specifier: source.value.to_string(), - bindings: Vec::new(), - }); - } - walk::walk_import_expression(self, import); - } - - fn visit_ts_import_type(&mut self, import: &TSImportType<'a>) { - self.imports.push(ImportFact { - specifier: import.source.value.to_string(), - bindings: Vec::new(), - }); - walk::walk_ts_import_type(self, import); - } - - fn visit_export_named_declaration(&mut self, export: &ExportNamedDeclaration<'a>) { - let source = export - .source - .as_ref() - .map(|source| source.value.to_string()); - if let Some(source) = &source { - self.imports.push(ImportFact { - specifier: source.clone(), - bindings: export - .specifiers - .iter() - .map(|specifier| ImportBinding { - local: module_export_name(&specifier.exported), - imported: module_export_name(&specifier.local), - }) - .collect(), - }); - } - if let Some(declaration) = &export.declaration { - self.with_export(ExportContext::named(None), |collector| { - collector.visit_declaration(declaration) - }); - } - for specifier in &export.specifiers { - let local = module_export_name(&specifier.local); - let exported = module_export_name(&specifier.exported); - if let Some(source) = &source { - self.re_exports.push(ReExportFact { - specifier: source.clone(), - imported: local.clone(), - exported: exported.clone(), - }); - self.record_file_export(json!({ - "kind": "named", - "local": local, - "exported": exported, - "source": source, - "imported": module_export_name(&specifier.local), - "supportedSymbol": false - })); - } else { - if !self.top_level_declarations.contains_key(&local) { - if let Some(imported) = self.import_backed_local(&local) { - self.record_file_export(json!({ - "kind": "named", - "local": local, - "exported": exported, - "source": imported.source, - "imported": imported.imported, - "supportedSymbol": false - })); - continue; - } - } - self.mark_exported(&local, ExportContext::named(Some(exported.clone()))); - self.record_file_export(json!({ - "kind": "named", - "local": local, - "exported": exported, - "source": null, - "supportedSymbol": true - })); - } - } - } - - fn visit_export_all_declaration(&mut self, export: &ExportAllDeclaration<'a>) { - self.imports.push(ImportFact { - specifier: export.source.value.to_string(), - bindings: Vec::new(), - }); - let source = export.source.value.to_string(); - if let Some(exported) = &export.exported { - self.record_file_export(json!({ - "kind": "namespace", - "exported": module_export_name(exported), - "source": source, - "supportedSymbol": false - })); - } else { - self.record_file_export(json!({ - "kind": "all", - "exported": "*", - "source": source, - "supportedSymbol": false - })); - } - } - - fn visit_export_default_declaration(&mut self, export: &ExportDefaultDeclaration<'a>) { - match &export.declaration { - ExportDefaultDeclarationKind::FunctionDeclaration(function) => { - self.record_file_export(json!({ - "kind": "default", - "local": function_name(function).unwrap_or_else(|| "default".to_string()), - "exported": "default", - "source": null, - "supportedSymbol": true - })); - self.with_export(ExportContext::default(), |collector| { - collector.visit_function(function, ScopeFlags::Function) - }); - } - ExportDefaultDeclarationKind::ClassDeclaration(class) => { - self.record_file_export(json!({ - "kind": "default", - "local": class_name(class).unwrap_or_else(|| "default".to_string()), - "exported": "default", - "source": null, - "supportedSymbol": true - })); - self.with_export(ExportContext::default(), |collector| { - collector.visit_class(class) - }); - } - ExportDefaultDeclarationKind::TSInterfaceDeclaration(declaration) => { - self.record_file_export(json!({ - "kind": "default", - "local": declaration.id.name.as_ref(), - "exported": "default", - "source": null, - "supportedSymbol": true - })); - self.with_export(ExportContext::default(), |collector| { - collector.visit_ts_interface_declaration(declaration) - }); - } - _ => { - let expression = export.declaration.to_expression(); - let local = default_export_local(expression); - if let Some(local) = &local { - if !self.top_level_declarations.contains_key(local) { - if let Some(imported) = self.import_backed_local(local) { - self.record_file_export(json!({ - "kind": "default", - "local": local, - "exported": "default", - "source": imported.source, - "imported": imported.imported, - "supportedSymbol": false - })); - self.visit_expression(expression); - return; - } - } - self.mark_exported(local, ExportContext::default()); - } - self.record_file_export(json!({ - "kind": "default", - "local": local, - "exported": "default", - "source": null, - "supportedSymbol": local.is_some() - })); - self.visit_expression(expression); - } - } - } - - fn visit_function(&mut self, function: &Function<'a>, flags: ScopeFlags) { - if let Some(name) = function_name(function) { - let id = self.add_declaration("function", "Function", &name); - self.with_context(id, |collector| { - walk::walk_function(collector, function, flags) - }); - } else if self - .current_export - .as_ref() - .is_some_and(|export| export.export_kind == "default") - { - let id = self.add_default_declaration("function", "Function"); - self.with_context(id, |collector| { - walk::walk_function(collector, function, flags) - }); - } else { - walk::walk_function(self, function, flags); - } - } - - fn visit_class(&mut self, class: &Class<'a>) { - if let Some(name) = class_name(class) { - let id = self.add_declaration("class", "Class", &name); - if let Some(super_class) = class.super_class.as_ref().and_then(expression_name) { - self.heritage.push(HeritageFact { - from: id.clone(), - name: super_class, - kind: "INHERITS".to_string(), - }); - } - for implemented in &class.implements { - if let Some(name) = ts_type_name(&implemented.expression) { - self.heritage.push(HeritageFact { - from: id.clone(), - name, - kind: "IMPLEMENTS".to_string(), - }); - } - } - self.with_context(id, |collector| walk::walk_class(collector, class)); - } else if self - .current_export - .as_ref() - .is_some_and(|export| export.export_kind == "default") - { - let id = self.add_default_declaration("class", "Class"); - self.with_context(id, |collector| walk::walk_class(collector, class)); - } else { - walk::walk_class(self, class); - } - } - - fn visit_ts_type_alias_declaration(&mut self, declaration: &TSTypeAliasDeclaration<'a>) { - self.add_declaration("type", "Type", declaration.id.name.as_ref()); - walk::walk_ts_type_alias_declaration(self, declaration); - } - - fn visit_ts_interface_declaration(&mut self, declaration: &TSInterfaceDeclaration<'a>) { - let id = self.add_declaration("type", "Type", declaration.id.name.as_ref()); - for extended in &declaration.extends { - if let Some(name) = expression_name(&extended.expression) { - self.heritage.push(HeritageFact { - from: id.clone(), - name, - kind: "INHERITS".to_string(), - }); - } - } - walk::walk_ts_interface_declaration(self, declaration); - } - - fn visit_variable_declaration(&mut self, declaration: &VariableDeclaration<'a>) { - for declarator in &declaration.declarations { - if let Some(type_annotation) = &declarator.type_annotation { - self.visit_ts_type_annotation(type_annotation); - } - if let Some(name) = binding_name(&declarator.id) { - if self.current_context.is_none() { - let Some(init) = declarator.init.as_ref() else { - self.add_declaration("variable", "Variable", &name); - continue; - }; - if is_function_like(init) { - let id = self.add_declaration("function", "Function", &name); - self.with_context(id, |collector| collector.visit_expression(init)); - } else { - let id = self.add_declaration("variable", "Variable", &name); - self.with_context(id, |collector| collector.visit_expression(init)); - } - continue; - } - } - if let Some(init) = declarator.init.as_ref() { - self.visit_expression(init); - } - } - } - - fn visit_call_expression(&mut self, call: &CallExpression<'a>) { - if let Some(callee) = expression_name(&call.callee) { - if callee == "test" || callee == "it" { - let Some(test_name) = first_string_argument(&call.arguments) else { - walk::walk_call_expression(self, call); - return; - }; - let test_id = self.add_test(&test_name); - self.with_context(test_id, |collector| { - walk::walk_call_expression(collector, call) - }); - return; - } - if callee != "describe" && callee != "test" && callee != "it" { - self.add_reference(callee); - } - } - walk::walk_call_expression(self, call); - } - - fn visit_new_expression(&mut self, expression: &NewExpression<'a>) { - if let Some(callee) = expression_name(&expression.callee) { - self.add_reference(callee); - } - walk::walk_new_expression(self, expression); - } -} - -fn import_bindings( - specifiers: Option<&oxc_allocator::Vec<'_, ImportDeclarationSpecifier<'_>>>, -) -> Vec { - specifiers - .into_iter() - .flat_map(|items| items.iter()) - .map(|specifier| match specifier { - ImportDeclarationSpecifier::ImportSpecifier(specifier) => ImportBinding { - local: specifier.local.name.to_string(), - imported: module_export_name(&specifier.imported), - }, - ImportDeclarationSpecifier::ImportDefaultSpecifier(specifier) => ImportBinding { - local: specifier.local.name.to_string(), - imported: "default".to_string(), - }, - ImportDeclarationSpecifier::ImportNamespaceSpecifier(specifier) => ImportBinding { - local: specifier.local.name.to_string(), - imported: "*".to_string(), - }, - }) - .collect() -} - -fn module_export_name(name: &ModuleExportName<'_>) -> String { - match name { - ModuleExportName::IdentifierName(name) => name.name.to_string(), - ModuleExportName::IdentifierReference(name) => name.name.to_string(), - ModuleExportName::StringLiteral(literal) => literal.value.to_string(), - } -} - -fn binding_name(pattern: &BindingPattern<'_>) -> Option { - match pattern { - BindingPattern::BindingIdentifier(identifier) => Some(identifier.name.to_string()), - _ => None, - } -} - -fn is_function_like(expression: &Expression<'_>) -> bool { - matches!( - expression, - Expression::ArrowFunctionExpression(_) | Expression::FunctionExpression(_) - ) -} - -fn first_string_argument(arguments: &oxc_allocator::Vec<'_, Argument<'_>>) -> Option { - arguments.first().and_then(|argument| match argument { - Argument::StringLiteral(literal) => Some(literal.value.to_string()), - _ => None, - }) -} - -fn expression_name(expression: &Expression<'_>) -> Option { - let expression = unwrapped_expression(expression); - match expression { - Expression::Identifier(identifier) => Some(identifier.name.to_string()), - Expression::StaticMemberExpression(member) => static_member_name(member), - _ => None, - } -} - -fn unwrapped_expression<'a>(expression: &'a Expression<'a>) -> &'a Expression<'a> { - let mut current = expression; - loop { - current = match current { - Expression::ParenthesizedExpression(expression) => &expression.expression, - Expression::TSAsExpression(expression) => &expression.expression, - Expression::TSSatisfiesExpression(expression) => &expression.expression, - Expression::TSNonNullExpression(expression) => &expression.expression, - Expression::TSInstantiationExpression(expression) => &expression.expression, - _ => return current, - }; - } -} - -fn static_member_name(member: &oxc_ast::ast::StaticMemberExpression<'_>) -> Option { - let property = member.property.name.to_string(); - match &member.object { - Expression::Identifier(object) => Some(format!("{}.{}", object.name, property)), - _ => Some(property), - } -} - -fn ts_type_name(name: &TSTypeName<'_>) -> Option { - match name { - TSTypeName::IdentifierReference(identifier) => Some(identifier.name.to_string()), - TSTypeName::QualifiedName(_) | TSTypeName::ThisExpression(_) => None, - } -} - -fn function_name(function: &Function<'_>) -> Option { - function.id.as_ref().map(|id| id.name.to_string()) -} - -fn class_name(class: &Class<'_>) -> Option { - class.id.as_ref().map(|id| id.name.to_string()) -} - -fn default_export_local(expression: &Expression<'_>) -> Option { - match unwrapped_expression(expression) { - Expression::Identifier(identifier) => Some(identifier.name.to_string()), - _ => None, - } -} - -fn ensure_export_attribute(node: &mut GraphFactNode) { - let attributes = attributes_object(node); - attributes - .entry("exported".to_string()) - .or_insert_with(|| Value::Bool(false)); -} - -fn apply_export_attributes(node: &mut GraphFactNode, export: &ExportContext, local_name: &str) { - let attributes = attributes_object(node); - attributes.insert("exported".to_string(), Value::Bool(true)); - attributes.insert( - "exportKind".to_string(), - Value::String(export.export_kind.clone()), - ); - attributes.insert( - "exportName".to_string(), - Value::String(export.export_name_for(local_name)), - ); -} - -fn registers_default_export_alias(export: &ExportContext, local_name: &str) -> bool { - export.export_kind == "default" || export.export_name_for(local_name) == "default" -} - -fn set_attribute(node: &mut GraphFactNode, key: &str, value: Value) { - attributes_object(node).insert(key.to_string(), value); -} - -fn attributes_object(node: &mut GraphFactNode) -> &mut serde_json::Map { - let attributes = node - .attributes - .get_or_insert_with(|| Value::Object(serde_json::Map::new())); - loop { - if let Value::Object(object) = attributes { - return object; - } - *attributes = Value::Object(serde_json::Map::new()); - } -} diff --git a/crates/graph-core/src/extraction/facts/collector/helpers.rs b/crates/graph-core/src/extraction/facts/collector/helpers.rs new file mode 100644 index 0000000..d433aa3 --- /dev/null +++ b/crates/graph-core/src/extraction/facts/collector/helpers.rs @@ -0,0 +1,154 @@ +use super::*; + +pub(super) struct Helpers; + +pub(super) trait CollectorHelpers { + fn import_bindings( + specifiers: Option<&oxc_allocator::Vec<'_, ImportDeclarationSpecifier<'_>>>, + ) -> Vec; + fn module_export_name(name: &ModuleExportName<'_>) -> String; + fn binding_name(pattern: &BindingPattern<'_>) -> Option; + fn is_function_like(expression: &Expression<'_>) -> bool; + fn first_string_argument(arguments: &oxc_allocator::Vec<'_, Argument<'_>>) -> Option; + fn expression_name(expression: &Expression<'_>) -> Option; + fn unwrapped_expression<'a>(expression: &'a Expression<'a>) -> &'a Expression<'a>; + fn static_member_name(member: &oxc_ast::ast::StaticMemberExpression<'_>) -> Option; + fn ts_type_name(name: &TSTypeName<'_>) -> Option; + fn function_name(function: &Function<'_>) -> Option; + fn class_name(class: &Class<'_>) -> Option; + fn default_export_local(expression: &Expression<'_>) -> Option; + fn ensure_export_attribute(node: &mut GraphFactNode); + fn apply_export_attributes(node: &mut GraphFactNode, export: &ExportContext, local_name: &str); + fn registers_default_export_alias(export: &ExportContext, local_name: &str) -> bool; +} + +impl CollectorHelpers for Helpers { + fn import_bindings( + specifiers: Option<&oxc_allocator::Vec<'_, ImportDeclarationSpecifier<'_>>>, + ) -> Vec { + specifiers + .into_iter() + .flat_map(|items| items.iter()) + .map(|specifier| match specifier { + ImportDeclarationSpecifier::ImportSpecifier(specifier) => ImportBinding { + local: specifier.local.name.to_string(), + imported: Self::module_export_name(&specifier.imported), + }, + ImportDeclarationSpecifier::ImportDefaultSpecifier(specifier) => ImportBinding { + local: specifier.local.name.to_string(), + imported: "default".to_string(), + }, + ImportDeclarationSpecifier::ImportNamespaceSpecifier(specifier) => ImportBinding { + local: specifier.local.name.to_string(), + imported: "*".to_string(), + }, + }) + .collect() + } + + fn module_export_name(name: &ModuleExportName<'_>) -> String { + match name { + ModuleExportName::IdentifierName(name) => name.name.to_string(), + ModuleExportName::IdentifierReference(name) => name.name.to_string(), + ModuleExportName::StringLiteral(literal) => literal.value.to_string(), + } + } + + fn binding_name(pattern: &BindingPattern<'_>) -> Option { + match pattern { + BindingPattern::BindingIdentifier(identifier) => Some(identifier.name.to_string()), + _ => None, + } + } + + fn is_function_like(expression: &Expression<'_>) -> bool { + matches!( + expression, + Expression::ArrowFunctionExpression(_) | Expression::FunctionExpression(_) + ) + } + + fn first_string_argument(arguments: &oxc_allocator::Vec<'_, Argument<'_>>) -> Option { + arguments.first().and_then(|argument| match argument { + Argument::StringLiteral(literal) => Some(literal.value.to_string()), + _ => None, + }) + } + + fn expression_name(expression: &Expression<'_>) -> Option { + let expression = Self::unwrapped_expression(expression); + match expression { + Expression::Identifier(identifier) => Some(identifier.name.to_string()), + Expression::StaticMemberExpression(member) => Self::static_member_name(member), + _ => None, + } + } + + fn unwrapped_expression<'a>(expression: &'a Expression<'a>) -> &'a Expression<'a> { + let mut current = expression; + loop { + current = match current { + Expression::ParenthesizedExpression(expression) => &expression.expression, + Expression::TSAsExpression(expression) => &expression.expression, + Expression::TSSatisfiesExpression(expression) => &expression.expression, + Expression::TSNonNullExpression(expression) => &expression.expression, + Expression::TSInstantiationExpression(expression) => &expression.expression, + _ => return current, + }; + } + } + + fn static_member_name(member: &oxc_ast::ast::StaticMemberExpression<'_>) -> Option { + let property = member.property.name.to_string(); + match &member.object { + Expression::Identifier(object) => Some(format!("{}.{}", object.name, property)), + _ => Some(property), + } + } + + fn ts_type_name(name: &TSTypeName<'_>) -> Option { + match name { + TSTypeName::IdentifierReference(identifier) => Some(identifier.name.to_string()), + TSTypeName::QualifiedName(_) | TSTypeName::ThisExpression(_) => None, + } + } + + fn function_name(function: &Function<'_>) -> Option { + function.id.as_ref().map(|id| id.name.to_string()) + } + + fn class_name(class: &Class<'_>) -> Option { + class.id.as_ref().map(|id| id.name.to_string()) + } + + fn default_export_local(expression: &Expression<'_>) -> Option { + match Self::unwrapped_expression(expression) { + Expression::Identifier(identifier) => Some(identifier.name.to_string()), + _ => None, + } + } + + fn ensure_export_attribute(node: &mut GraphFactNode) { + let attributes = node_attributes_object(node); + attributes + .entry("exported".to_string()) + .or_insert_with(|| Value::Bool(false)); + } + + fn apply_export_attributes(node: &mut GraphFactNode, export: &ExportContext, local_name: &str) { + let attributes = node_attributes_object(node); + attributes.insert("exported".to_string(), Value::Bool(true)); + attributes.insert( + "exportKind".to_string(), + Value::String(export.export_kind.clone()), + ); + attributes.insert( + "exportName".to_string(), + Value::String(export.export_name_for(local_name)), + ); + } + + fn registers_default_export_alias(export: &ExportContext, local_name: &str) -> bool { + export.export_kind == "default" || export.export_name_for(local_name) == "default" + } +} diff --git a/crates/graph-core/src/extraction/facts/collector/visit.rs b/crates/graph-core/src/extraction/facts/collector/visit.rs new file mode 100644 index 0000000..062a552 --- /dev/null +++ b/crates/graph-core/src/extraction/facts/collector/visit.rs @@ -0,0 +1,326 @@ +use super::*; + +pub(super) trait CollectProgram<'a> { + fn collect_program(&mut self, program: &oxc_ast::ast::Program<'a>); +} + +impl<'a> CollectProgram<'a> for FileFactCollector { + fn collect_program(&mut self, program: &oxc_ast::ast::Program<'a>) { + self.visit_program(program); + } +} + +impl<'a> Visit<'a> for FileFactCollector { + fn visit_import_declaration(&mut self, import: &ImportDeclaration<'a>) { + self.imports.push(ImportFact { + specifier: import.source.value.to_string(), + bindings: Helpers::import_bindings(import.specifiers.as_ref()), + }); + } + + fn visit_import_expression(&mut self, import: &ImportExpression<'a>) { + if let Expression::StringLiteral(source) = &import.source { + self.imports.push(ImportFact { + specifier: source.value.to_string(), + bindings: Vec::new(), + }); + } + walk::walk_import_expression(self, import); + } + + fn visit_ts_import_type(&mut self, import: &TSImportType<'a>) { + self.imports.push(ImportFact { + specifier: import.source.value.to_string(), + bindings: Vec::new(), + }); + walk::walk_ts_import_type(self, import); + } + + fn visit_export_named_declaration(&mut self, export: &ExportNamedDeclaration<'a>) { + let source = export + .source + .as_ref() + .map(|source| source.value.to_string()); + if let Some(source) = &source { + self.imports.push(ImportFact { + specifier: source.clone(), + bindings: export + .specifiers + .iter() + .map(|specifier| ImportBinding { + local: Helpers::module_export_name(&specifier.exported), + imported: Helpers::module_export_name(&specifier.local), + }) + .collect(), + }); + } + if let Some(declaration) = &export.declaration { + self.with_export(ExportContext::named(None), |collector| { + collector.visit_declaration(declaration) + }); + } + for specifier in &export.specifiers { + let local = Helpers::module_export_name(&specifier.local); + let exported = Helpers::module_export_name(&specifier.exported); + if let Some(source) = &source { + self.re_exports.push(ReExportFact { + specifier: source.clone(), + imported: local.clone(), + exported: exported.clone(), + }); + self.record_file_export(json!({ + "kind": "named", + "local": local, + "exported": exported, + "source": source, + "imported": Helpers::module_export_name(&specifier.local), + "supportedSymbol": false + })); + } else { + if !self.top_level_declarations.contains_key(&local) { + if let Some(imported) = self.import_backed_local(&local) { + self.record_file_export(json!({ + "kind": "named", + "local": local, + "exported": exported, + "source": imported.source, + "imported": imported.imported, + "supportedSymbol": false + })); + continue; + } + } + self.mark_exported(&local, ExportContext::named(Some(exported.clone()))); + self.record_file_export(json!({ + "kind": "named", + "local": local, + "exported": exported, + "source": null, + "supportedSymbol": true + })); + } + } + } + + fn visit_export_all_declaration(&mut self, export: &ExportAllDeclaration<'a>) { + self.imports.push(ImportFact { + specifier: export.source.value.to_string(), + bindings: Vec::new(), + }); + let source = export.source.value.to_string(); + if let Some(exported) = &export.exported { + self.record_file_export(json!({ + "kind": "namespace", + "exported": Helpers::module_export_name(exported), + "source": source, + "supportedSymbol": false + })); + } else { + self.record_file_export(json!({ + "kind": "all", + "exported": "*", + "source": source, + "supportedSymbol": false + })); + } + } + + fn visit_export_default_declaration(&mut self, export: &ExportDefaultDeclaration<'a>) { + match &export.declaration { + ExportDefaultDeclarationKind::FunctionDeclaration(function) => { + self.record_file_export(json!({ + "kind": "default", + "local": Helpers::function_name(function).unwrap_or_else(|| "default".to_string()), + "exported": "default", + "source": null, + "supportedSymbol": true + })); + self.with_export(ExportContext::default(), |collector| { + collector.visit_function(function, ScopeFlags::Function) + }); + } + ExportDefaultDeclarationKind::ClassDeclaration(class) => { + self.record_file_export(json!({ + "kind": "default", + "local": Helpers::class_name(class).unwrap_or_else(|| "default".to_string()), + "exported": "default", + "source": null, + "supportedSymbol": true + })); + self.with_export(ExportContext::default(), |collector| { + collector.visit_class(class) + }); + } + ExportDefaultDeclarationKind::TSInterfaceDeclaration(declaration) => { + self.record_file_export(json!({ + "kind": "default", + "local": declaration.id.name.as_ref(), + "exported": "default", + "source": null, + "supportedSymbol": true + })); + self.with_export(ExportContext::default(), |collector| { + collector.visit_ts_interface_declaration(declaration) + }); + } + _ => { + let expression = export.declaration.to_expression(); + let local = Helpers::default_export_local(expression); + if let Some(local) = &local { + if !self.top_level_declarations.contains_key(local) { + if let Some(imported) = self.import_backed_local(local) { + self.record_file_export(json!({ + "kind": "default", + "local": local, + "exported": "default", + "source": imported.source, + "imported": imported.imported, + "supportedSymbol": false + })); + self.visit_expression(expression); + return; + } + } + self.mark_exported(local, ExportContext::default()); + } + self.record_file_export(json!({ + "kind": "default", + "local": local, + "exported": "default", + "source": null, + "supportedSymbol": local.is_some() + })); + self.visit_expression(expression); + } + } + } + + fn visit_function(&mut self, function: &Function<'a>, flags: ScopeFlags) { + if let Some(name) = Helpers::function_name(function) { + let id = self.add_declaration("function", "Function", &name); + self.with_context(id, |collector| { + walk::walk_function(collector, function, flags) + }); + } else if self + .current_export + .as_ref() + .is_some_and(|export| export.export_kind == "default") + { + let id = self.add_default_declaration("function", "Function"); + self.with_context(id, |collector| { + walk::walk_function(collector, function, flags) + }); + } else { + walk::walk_function(self, function, flags); + } + } + + fn visit_class(&mut self, class: &Class<'a>) { + if let Some(name) = Helpers::class_name(class) { + let id = self.add_declaration("class", "Class", &name); + if let Some(super_class) = class + .super_class + .as_ref() + .and_then(Helpers::expression_name) + { + self.heritage.push(HeritageFact { + from: id.clone(), + name: super_class, + kind: "INHERITS".to_string(), + }); + } + for implemented in &class.implements { + if let Some(name) = Helpers::ts_type_name(&implemented.expression) { + self.heritage.push(HeritageFact { + from: id.clone(), + name, + kind: "IMPLEMENTS".to_string(), + }); + } + } + self.with_context(id, |collector| walk::walk_class(collector, class)); + } else if self + .current_export + .as_ref() + .is_some_and(|export| export.export_kind == "default") + { + let id = self.add_default_declaration("class", "Class"); + self.with_context(id, |collector| walk::walk_class(collector, class)); + } else { + walk::walk_class(self, class); + } + } + + fn visit_ts_type_alias_declaration(&mut self, declaration: &TSTypeAliasDeclaration<'a>) { + self.add_declaration("type", "Type", declaration.id.name.as_ref()); + walk::walk_ts_type_alias_declaration(self, declaration); + } + + fn visit_ts_interface_declaration(&mut self, declaration: &TSInterfaceDeclaration<'a>) { + let id = self.add_declaration("type", "Type", declaration.id.name.as_ref()); + for extended in &declaration.extends { + if let Some(name) = Helpers::expression_name(&extended.expression) { + self.heritage.push(HeritageFact { + from: id.clone(), + name, + kind: "INHERITS".to_string(), + }); + } + } + walk::walk_ts_interface_declaration(self, declaration); + } + + fn visit_variable_declaration(&mut self, declaration: &VariableDeclaration<'a>) { + for declarator in &declaration.declarations { + if let Some(type_annotation) = &declarator.type_annotation { + self.visit_ts_type_annotation(type_annotation); + } + if let Some(name) = Helpers::binding_name(&declarator.id) { + if self.current_context.is_none() { + let Some(init) = declarator.init.as_ref() else { + self.add_declaration("variable", "Variable", &name); + continue; + }; + if Helpers::is_function_like(init) { + let id = self.add_declaration("function", "Function", &name); + self.with_context(id, |collector| collector.visit_expression(init)); + } else { + let id = self.add_declaration("variable", "Variable", &name); + self.with_context(id, |collector| collector.visit_expression(init)); + } + continue; + } + } + if let Some(init) = declarator.init.as_ref() { + self.visit_expression(init); + } + } + } + + fn visit_call_expression(&mut self, call: &CallExpression<'a>) { + if let Some(callee) = Helpers::expression_name(&call.callee) { + if callee == "test" || callee == "it" { + let Some(test_name) = Helpers::first_string_argument(&call.arguments) else { + walk::walk_call_expression(self, call); + return; + }; + let test_id = self.add_test(&test_name); + self.with_context(test_id, |collector| { + walk::walk_call_expression(collector, call) + }); + return; + } + if callee != "describe" && callee != "test" && callee != "it" { + self.add_reference(callee); + } + } + walk::walk_call_expression(self, call); + } + + fn visit_new_expression(&mut self, expression: &NewExpression<'a>) { + if let Some(callee) = Helpers::expression_name(&expression.callee) { + self.add_reference(callee); + } + walk::walk_new_expression(self, expression); + } +} diff --git a/crates/graph-core/src/extraction/facts/python.rs b/crates/graph-core/src/extraction/facts/python.rs index fa899a6..465ed8b 100644 --- a/crates/graph-core/src/extraction/facts/python.rs +++ b/crates/graph-core/src/extraction/facts/python.rs @@ -1,6 +1,6 @@ use super::{ - file_id, insert_edge, EdgeDraft, FileFacts, HeritageFact, ImportBinding, ImportFact, - ReExportFact, ReferenceFact, + set_node_attribute, EdgeDraft, FactResolution, FileFacts, HeritageFact, ImportBinding, + ImportFact, ReExportFact, ReferenceFact, Resolution, }; use crate::protocol::{GraphFactEdge, GraphFactNode}; use serde_json::{json, Value}; @@ -8,6 +8,10 @@ use std::collections::{btree_map::Entry, BTreeMap, BTreeSet}; use std::path::Path; use tree_sitter::{Node, Tree}; +mod syntax; + +use syntax::{PythonSyntax, Syntax}; + pub(super) fn collect_file_facts( path: String, file_node: GraphFactNode, @@ -15,7 +19,7 @@ pub(super) fn collect_file_facts( tree: &Tree, ) -> FileFacts { let root = tree.root_node(); - let explicit_exports = collect_explicit_exports(root, source_text); + let explicit_exports = Syntax::collect_explicit_exports(root, source_text); let mut collector = PythonFileFactCollector::new(path, file_node, source_text, explicit_exports); collector.visit_module(root); @@ -49,7 +53,7 @@ impl<'a> PythonFileFactCollector<'a> { source_text: &'a str, explicit_exports: Option>, ) -> Self { - let module_name = module_name_for_path(&path); + let module_name = Syntax::module_name_for_path(&path); let module_id = format!("module:{path}#{module_name}"); let mut nodes = BTreeMap::new(); nodes.insert( @@ -63,9 +67,9 @@ impl<'a> PythonFileFactCollector<'a> { }, ); let mut edges = BTreeMap::new(); - insert_edge( + Resolution::insert_edge( &mut edges, - EdgeDraft::new("CONTAINS", &file_id(&path), &module_id), + EdgeDraft::new("CONTAINS", &Resolution::file_id(&path), &module_id), ); Self { path, @@ -106,7 +110,7 @@ impl<'a> PythonFileFactCollector<'a> { } } if self.explicit_exports.is_some() || !self.file_exports.is_empty() { - set_attribute( + set_node_attribute( &mut self.file_node, "exports", Value::Array(self.file_exports.clone()), @@ -127,22 +131,22 @@ impl<'a> PythonFileFactCollector<'a> { } fn visit_module(&mut self, node: Node<'_>) { - for child in named_children(node) { + for child in Syntax::named_children(node) { self.visit_statement(child, Vec::new()); } } fn visit_statement(&mut self, node: Node<'_>, decorators: Vec) { + if self.visit_definition(node, decorators) { + return; + } match node.kind() { - "decorated_definition" => self.visit_decorated_definition(node), - "class_definition" => self.visit_class(node, decorators), - "function_definition" => self.visit_function(node, decorators), "import_statement" => self.visit_import_statement(node), "import_from_statement" => self.visit_import_from_statement(node), "assignment" => self.visit_assignment(node), "expression_statement" => self.visit_expression_statement(node), "block" | "module" => { - for child in named_children(node) { + for child in Syntax::named_children(node) { self.visit_statement(child, Vec::new()); } } @@ -151,11 +155,21 @@ impl<'a> PythonFileFactCollector<'a> { } } + fn visit_definition(&mut self, node: Node<'_>, decorators: Vec) -> bool { + match node.kind() { + "decorated_definition" => self.visit_decorated_definition(node), + "class_definition" => self.visit_class(node, decorators), + "function_definition" => self.visit_function(node, decorators), + _ => return false, + } + true + } + fn visit_decorated_definition(&mut self, node: Node<'_>) { - let decorators = named_children(node) + let decorators = Syntax::named_children(node) .into_iter() .filter(|child| child.kind() == "decorator") - .filter_map(|child| decorator_name(child, self.source_text)) + .filter_map(|child| Syntax::decorator_name(child, self.source_text)) .collect::>(); if let Some(definition) = node.child_by_field_name("definition") { self.visit_statement(definition, decorators); @@ -163,21 +177,21 @@ impl<'a> PythonFileFactCollector<'a> { } fn visit_class(&mut self, node: Node<'_>, decorators: Vec) { - let Some(name) = field_text(node, "name", self.source_text) else { + let Some(name) = Syntax::field_text(node, "name", self.source_text) else { self.visit_children_for_references(node); return; }; - let bases = class_bases(node, self.source_text); - let is_test = is_test_class(&name, &bases); - let id = self.add_declaration( - "class", - "Class", - &name, - json!({ + let bases = Syntax::class_bases(node, self.source_text); + let is_test = Syntax::is_test_class(&name, &bases); + let id = self.add_declaration(PythonDeclarationDraft { + prefix: "class", + kind: "Class", + name: &name, + extra_attributes: json!({ "decorators": decorators, "isTest": is_test }), - ); + }); for base in bases { self.heritage.push(HeritageFact { from: id.clone(), @@ -186,7 +200,7 @@ impl<'a> PythonFileFactCollector<'a> { }); } let body = node.child_by_field_name("body"); - self.with_parent(id, name, is_test, |collector| { + self.with_parent(ParentScope::new(id, name, is_test), |collector| { if let Some(body) = body { collector.visit_statement(body, Vec::new()); } @@ -194,26 +208,26 @@ impl<'a> PythonFileFactCollector<'a> { } fn visit_function(&mut self, node: Node<'_>, decorators: Vec) { - let Some(name) = field_text(node, "name", self.source_text) else { + let Some(name) = Syntax::field_text(node, "name", self.source_text) else { self.visit_children_for_references(node); return; }; - let is_async = node_text(node, self.source_text) + let is_async = Syntax::node_text(node, self.source_text) .trim_start() .starts_with("async def"); - let is_test = is_test_function(&self.path, &name, self.test_class_depth > 0); - let id = self.add_declaration( - "function", - "Function", - &name, - json!({ + let is_test = Syntax::is_test_function(&self.path, &name, self.test_class_depth > 0); + let id = self.add_declaration(PythonDeclarationDraft { + prefix: "function", + kind: "Function", + name: &name, + extra_attributes: json!({ "async": is_async, "decorators": decorators, "isTest": is_test }), - ); + }); let body = node.child_by_field_name("body"); - self.with_parent(id, name, false, |collector| { + self.with_parent(ParentScope::new(id, name, false), |collector| { if let Some(body) = body { collector.visit_statement(body, Vec::new()); } @@ -222,18 +236,22 @@ impl<'a> PythonFileFactCollector<'a> { fn visit_import_statement(&mut self, node: Node<'_>) { self.imports - .extend(parse_import_statement(&node_text(node, self.source_text))); + .extend(Syntax::parse_import_statement(&Syntax::node_text( + node, + self.source_text, + ))); } fn visit_import_from_statement(&mut self, node: Node<'_>) { - self.imports.extend(parse_from_import_statement(&node_text( - node, - self.source_text, - ))); + self.imports + .extend(Syntax::parse_from_import_statement(&Syntax::node_text( + node, + self.source_text, + ))); } fn visit_expression_statement(&mut self, node: Node<'_>) { - if let Some(assignment) = named_children(node) + if let Some(assignment) = Syntax::named_children(node) .into_iter() .find(|child| child.kind() == "assignment") { @@ -247,9 +265,16 @@ impl<'a> PythonFileFactCollector<'a> { let left = node.child_by_field_name("left"); let right = node.child_by_field_name("right"); if self.current_parent == self.module_id { - if let Some(name) = left.and_then(|left| assignment_name(left, self.source_text)) { + if let Some(name) = + left.and_then(|left| Syntax::assignment_name(left, self.source_text)) + { if name != "__all__" { - let id = self.add_declaration("variable", "Variable", &name, json!({})); + let id = self.add_declaration(PythonDeclarationDraft { + prefix: "variable", + kind: "Variable", + name: &name, + extra_attributes: json!({}), + }); if let Some(right) = right { self.with_existing_parent(id, |collector| { collector.visit_children_for_references(right) @@ -266,8 +291,8 @@ impl<'a> PythonFileFactCollector<'a> { fn visit_call(&mut self, node: Node<'_>) { if let Some(function) = node.child_by_field_name("function") { - if let Some(name) = expression_name(function, self.source_text) { - if !is_builtin_reference(&name) { + if let Some(name) = Syntax::expression_name(function, self.source_text) { + if !Syntax::is_builtin_reference(&name) { self.references.push(ReferenceFact { from: self.current_parent.clone(), name, @@ -286,22 +311,17 @@ impl<'a> PythonFileFactCollector<'a> { self.visit_call(node); return; } - for child in named_children(node) { + for child in Syntax::named_children(node) { self.visit_statement(child, Vec::new()); } } - fn add_declaration( - &mut self, - prefix: &str, - kind: &str, - name: &str, - extra_attributes: Value, - ) -> String { - let qualifier = self.qualified_name(name); - let id = format!("{prefix}:{}#{qualifier}", self.path); + fn add_declaration(&mut self, draft: PythonDeclarationDraft<'_>) -> String { + let qualifier = self.qualified_name(draft.name); + let id = format!("{}:{}#{qualifier}", draft.prefix, self.path); let is_top_level = self.current_parent == self.module_id; - let export = export_policy(name, is_top_level, self.explicit_exports.as_ref()); + let export = + Syntax::export_policy(draft.name, is_top_level, self.explicit_exports.as_ref()); let mut attributes = serde_json::Map::new(); attributes.insert("exported".to_string(), Value::Bool(export.exported)); attributes.insert( @@ -310,9 +330,12 @@ impl<'a> PythonFileFactCollector<'a> { ); if export.exported { attributes.insert("exportKind".to_string(), Value::String("named".to_string())); - attributes.insert("exportName".to_string(), Value::String(name.to_string())); + attributes.insert( + "exportName".to_string(), + Value::String(draft.name.to_string()), + ); } - if let Value::Object(extra) = extra_attributes { + if let Value::Object(extra) = draft.extra_attributes { for (key, value) in extra { attributes.insert(key, value); } @@ -325,31 +348,32 @@ impl<'a> PythonFileFactCollector<'a> { Entry::Vacant(entry) => { entry.insert(GraphFactNode { id: id.clone(), - kind: kind.to_string(), + kind: draft.kind.to_string(), path: Some(self.path.clone()), - name: Some(name.to_string()), + name: Some(draft.name.to_string()), attributes: Some(Value::Object(attributes)), }); } } - self.declarations.insert(name.to_string(), id.clone()); + self.declarations.insert(draft.name.to_string(), id.clone()); self.declarations.insert(qualifier, id.clone()); if is_top_level { self.top_level_declarations - .insert(name.to_string(), id.clone()); + .insert(draft.name.to_string(), id.clone()); if export.exported { - self.export_aliases.insert(name.to_string(), id.clone()); + self.export_aliases + .insert(draft.name.to_string(), id.clone()); self.file_exports.push(json!({ "kind": "named", - "local": name, - "exported": name, + "local": draft.name, + "exported": draft.name, "source": null, "supportedSymbol": true, "policy": export.policy })); } } - insert_edge( + Resolution::insert_edge( &mut self.edges, EdgeDraft::new("CONTAINS", &self.current_parent, &id), ); @@ -363,20 +387,14 @@ impl<'a> PythonFileFactCollector<'a> { format!("{}.{}", self.qualifier.join("."), name) } - fn with_parent( - &mut self, - parent: String, - name: String, - is_test_class: bool, - visit: impl FnOnce(&mut Self), - ) { - let previous_parent = std::mem::replace(&mut self.current_parent, parent); - self.qualifier.push(name); - if is_test_class { + fn with_parent(&mut self, scope: ParentScope, visit: impl FnOnce(&mut Self)) { + let previous_parent = std::mem::replace(&mut self.current_parent, scope.parent); + self.qualifier.push(scope.name); + if scope.is_test_class { self.test_class_depth += 1; } visit(self); - if is_test_class { + if scope.is_test_class { self.test_class_depth = self.test_class_depth.saturating_sub(1); } self.qualifier.pop(); @@ -399,314 +417,25 @@ impl<'a> PythonFileFactCollector<'a> { } } -struct ExportPolicy<'a> { - exported: bool, - policy: &'a str, -} - -fn export_policy( - name: &str, - is_top_level: bool, - explicit_exports: Option<&BTreeSet>, -) -> ExportPolicy<'static> { - if !is_top_level { - return ExportPolicy { - exported: false, - policy: "not_module_level", - }; - } - if let Some(exports) = explicit_exports { - return ExportPolicy { - exported: exports.contains(name), - policy: "__all__", - }; - } - ExportPolicy { - exported: !name.starts_with('_'), - policy: "underscore_convention", - } -} - -fn collect_explicit_exports(root: Node<'_>, source_text: &str) -> Option> { - let mut exports = BTreeSet::new(); - let mut found = false; - for statement in named_children(root) { - let Some(node) = module_level_assignment(statement) else { - continue; - }; - let left = node.child_by_field_name("left"); - if left - .and_then(|left| assignment_name(left, source_text)) - .as_deref() - != Some("__all__") - { - continue; - } - found = true; - if let Some(right) = node.child_by_field_name("right") { - for string_node in descendant_nodes(right) { - if string_node.kind() == "string" { - if let Some(value) = parse_string_literal(&node_text(string_node, source_text)) - { - exports.insert(value); - } - } - } - } - } - found.then_some(exports) -} - -fn module_level_assignment(node: Node<'_>) -> Option> { - if node.kind() == "assignment" { - return Some(node); - } - if node.kind() != "expression_statement" { - return None; - } - named_children(node) - .into_iter() - .find(|child| child.kind() == "assignment") -} - -fn class_bases(node: Node<'_>, source_text: &str) -> Vec { - let Some(superclasses) = node.child_by_field_name("superclasses") else { - return Vec::new(); - }; - named_children(superclasses) - .into_iter() - .filter_map(|child| expression_name(child, source_text)) - .collect() -} - -fn is_test_class(name: &str, bases: &[String]) -> bool { - name.starts_with("Test") || bases.iter().any(|base| base == "unittest.TestCase") -} - -fn is_test_function(path: &str, name: &str, in_test_class: bool) -> bool { - (is_test_file(path) && name.starts_with("test_")) || in_test_class && name.starts_with("test_") -} - -fn is_test_file(path: &str) -> bool { - let file_name = Path::new(path) - .file_name() - .and_then(|name| name.to_str()) - .unwrap_or(path); - file_name.starts_with("test_") || file_name.ends_with("_test.py") -} - -fn parse_import_statement(text: &str) -> Vec { - let Some(imports) = text.trim().strip_prefix("import ") else { - return Vec::new(); - }; - imports - .split(',') - .filter_map(|entry| parse_import_entry(entry.trim())) - .map(|(specifier, local)| ImportFact { - specifier, - bindings: vec![ImportBinding { - local, - imported: "*".to_string(), - }], - }) - .collect() -} - -fn parse_import_entry(entry: &str) -> Option<(String, String)> { - let (module, alias) = split_alias(entry); - if module.is_empty() { - return None; - } - let local = alias - .map(ToString::to_string) - .or_else(|| module.split('.').next().map(ToString::to_string))?; - Some((module.to_string(), local)) -} - -fn parse_from_import_statement(text: &str) -> Vec { - let text = text.trim(); - let Some(rest) = text.strip_prefix("from ") else { - return Vec::new(); - }; - let Some((module, imports)) = rest.split_once(" import ") else { - return Vec::new(); - }; - let module = module.trim(); - let imports = imports.trim().trim_start_matches('(').trim_end_matches(')'); - if imports == "*" { - return vec![ImportFact { - specifier: module.to_string(), - bindings: vec![ImportBinding { - local: "*".to_string(), - imported: "*".to_string(), - }], - }]; - } - imports - .split(',') - .filter_map(|entry| parse_from_import_entry(module, entry.trim())) - .collect() -} - -fn parse_from_import_entry(module: &str, entry: &str) -> Option { - let (imported, alias) = split_alias(entry); - if imported.is_empty() { - return None; - } - let local = alias.unwrap_or(imported).to_string(); - let (specifier, imported_name) = if module.chars().all(|character| character == '.') { - (format!("{module}{imported}"), "*".to_string()) - } else { - (module.to_string(), imported.to_string()) - }; - Some(ImportFact { - specifier, - bindings: vec![ImportBinding { - local, - imported: imported_name, - }], - }) -} - -fn split_alias(entry: &str) -> (&str, Option<&str>) { - if let Some((left, right)) = entry.split_once(" as ") { - (left.trim(), Some(right.trim())) - } else { - (entry.trim(), None) - } -} - -fn decorator_name(node: Node<'_>, source_text: &str) -> Option { - let text = node_text(node, source_text); - text.trim() - .strip_prefix('@') - .map(str::trim) - .filter(|name| !name.is_empty()) - .map(ToString::to_string) -} - -fn assignment_name(node: Node<'_>, source_text: &str) -> Option { - match node.kind() { - "identifier" => Some(node_text(node, source_text)), - _ => None, - } -} - -fn expression_name(node: Node<'_>, source_text: &str) -> Option { - match node.kind() { - "identifier" => Some(node_text(node, source_text)), - "attribute" => { - let object = node - .child_by_field_name("object") - .and_then(|object| expression_name(object, source_text))?; - let attribute = field_text(node, "attribute", source_text)?; - Some(format!("{object}.{attribute}")) - } - "call" => node - .child_by_field_name("function") - .and_then(|function| expression_name(function, source_text)), - "dotted_name" => Some(node_text(node, source_text)), - _ => named_children(node) - .into_iter() - .find_map(|child| expression_name(child, source_text)), - } -} - -fn is_builtin_reference(name: &str) -> bool { - matches!( - name, - "super" - | "len" - | "str" - | "int" - | "float" - | "bool" - | "list" - | "dict" - | "set" - | "tuple" - | "print" - | "range" - ) || name.starts_with("self.") - || name.starts_with("cls.") -} - -fn field_text(node: Node<'_>, field: &str, source_text: &str) -> Option { - node.child_by_field_name(field) - .map(|child| node_text(child, source_text)) -} - -fn node_text(node: Node<'_>, source_text: &str) -> String { - node.utf8_text(source_text.as_bytes()) - .map(ToString::to_string) - .unwrap_or_default() -} - -fn named_children(node: Node<'_>) -> Vec> { - let mut cursor = node.walk(); - node.named_children(&mut cursor).collect() -} - -fn descendant_nodes(node: Node<'_>) -> Vec> { - let mut nodes = Vec::new(); - let mut stack = vec![node]; - while let Some(current) = stack.pop() { - nodes.push(current); - for child in named_children(current) { - stack.push(child); - } - } - nodes -} - -fn parse_string_literal(text: &str) -> Option { - let trimmed = text.trim(); - let quote_index = trimmed.find(['"', '\''])?; - let quoted = trimmed.get(quote_index..)?; - let quote = quoted.chars().next()?; - let triple = format!("{quote}{quote}{quote}"); - if let Some(body) = quoted - .strip_prefix(&triple) - .and_then(|body| body.strip_suffix(&triple)) - { - return Some(body.to_string()); - } - quoted - .strip_prefix(quote) - .and_then(|body| body.strip_suffix(quote)) - .map(ToString::to_string) -} - -fn module_name_for_path(path: &str) -> String { - let without_extension = path - .strip_suffix(".py") - .or_else(|| path.strip_suffix(".pyi")) - .unwrap_or(path); - let mut parts = without_extension - .split('/') - .filter(|part| !part.is_empty()) - .collect::>(); - if parts.last().is_some_and(|part| *part == "__init__") { - parts.pop(); - } - if parts.is_empty() { - return "__init__".to_string(); - } - parts.join(".") +struct PythonDeclarationDraft<'a> { + prefix: &'a str, + kind: &'a str, + name: &'a str, + extra_attributes: Value, } -fn set_attribute(node: &mut GraphFactNode, key: &str, value: Value) { - attributes_object(node).insert(key.to_string(), value); +struct ParentScope { + parent: String, + name: String, + is_test_class: bool, } -fn attributes_object(node: &mut GraphFactNode) -> &mut serde_json::Map { - let attributes = node - .attributes - .get_or_insert_with(|| Value::Object(serde_json::Map::new())); - loop { - if let Value::Object(object) = attributes { - return object; +impl ParentScope { + fn new(parent: String, name: String, is_test_class: bool) -> Self { + Self { + parent, + name, + is_test_class, } - *attributes = Value::Object(serde_json::Map::new()); } } diff --git a/crates/graph-core/src/extraction/facts/python/syntax.rs b/crates/graph-core/src/extraction/facts/python/syntax.rs new file mode 100644 index 0000000..dd73fcd --- /dev/null +++ b/crates/graph-core/src/extraction/facts/python/syntax.rs @@ -0,0 +1,335 @@ +use super::*; + +pub(super) struct ExportPolicy<'a> { + pub(super) exported: bool, + pub(super) policy: &'a str, +} + +pub(super) struct Syntax; + +pub(super) trait PythonSyntax { + fn export_policy( + name: &str, + is_top_level: bool, + explicit_exports: Option<&BTreeSet>, + ) -> ExportPolicy<'static>; + fn collect_explicit_exports(root: Node<'_>, source_text: &str) -> Option>; + fn module_level_assignment(node: Node<'_>) -> Option>; + fn class_bases(node: Node<'_>, source_text: &str) -> Vec; + fn is_test_class(name: &str, bases: &[String]) -> bool; + fn is_test_function(path: &str, name: &str, in_test_class: bool) -> bool; + fn is_test_file(path: &str) -> bool; + fn parse_import_statement(text: &str) -> Vec; + fn parse_import_entry(entry: &str) -> Option<(String, String)>; + fn parse_from_import_statement(text: &str) -> Vec; + fn parse_from_import_entry(module: &str, entry: &str) -> Option; + fn split_alias(entry: &str) -> (&str, Option<&str>); + fn decorator_name(node: Node<'_>, source_text: &str) -> Option; + fn assignment_name(node: Node<'_>, source_text: &str) -> Option; + fn expression_name(node: Node<'_>, source_text: &str) -> Option; + fn is_builtin_reference(name: &str) -> bool; + fn field_text(node: Node<'_>, field: &str, source_text: &str) -> Option; + fn node_text(node: Node<'_>, source_text: &str) -> String; + fn named_children(node: Node<'_>) -> Vec>; + fn descendant_nodes(node: Node<'_>) -> Vec>; + fn parse_string_literal(text: &str) -> Option; + fn module_name_for_path(path: &str) -> String; +} + +impl PythonSyntax for Syntax { + fn export_policy( + name: &str, + is_top_level: bool, + explicit_exports: Option<&BTreeSet>, + ) -> ExportPolicy<'static> { + if !is_top_level { + return ExportPolicy { + exported: false, + policy: "not_module_level", + }; + } + if let Some(exports) = explicit_exports { + return ExportPolicy { + exported: exports.contains(name), + policy: "__all__", + }; + } + ExportPolicy { + exported: !name.starts_with('_'), + policy: "underscore_convention", + } + } + + fn collect_explicit_exports(root: Node<'_>, source_text: &str) -> Option> { + let mut exports = BTreeSet::new(); + let mut found = false; + for statement in Self::named_children(root) { + let Some(node) = Self::module_level_assignment(statement) else { + continue; + }; + let left = node.child_by_field_name("left"); + if left + .and_then(|left| Self::assignment_name(left, source_text)) + .as_deref() + != Some("__all__") + { + continue; + } + found = true; + let Some(right) = node.child_by_field_name("right") else { + continue; + }; + for string_node in Self::descendant_nodes(right) { + if string_node.kind() != "string" { + continue; + } + let Some(value) = + Self::parse_string_literal(&Self::node_text(string_node, source_text)) + else { + continue; + }; + exports.insert(value); + } + } + found.then_some(exports) + } + + fn module_level_assignment(node: Node<'_>) -> Option> { + if node.kind() == "assignment" { + return Some(node); + } + if node.kind() != "expression_statement" { + return None; + } + Self::named_children(node) + .into_iter() + .find(|child| child.kind() == "assignment") + } + + fn class_bases(node: Node<'_>, source_text: &str) -> Vec { + let Some(superclasses) = node.child_by_field_name("superclasses") else { + return Vec::new(); + }; + Self::named_children(superclasses) + .into_iter() + .filter_map(|child| Self::expression_name(child, source_text)) + .collect() + } + + fn is_test_class(name: &str, bases: &[String]) -> bool { + name.starts_with("Test") || bases.iter().any(|base| base == "unittest.TestCase") + } + + fn is_test_function(path: &str, name: &str, in_test_class: bool) -> bool { + (Self::is_test_file(path) && name.starts_with("test_")) + || in_test_class && name.starts_with("test_") + } + + fn is_test_file(path: &str) -> bool { + let file_name = Path::new(path) + .file_name() + .and_then(|name| name.to_str()) + .unwrap_or(path); + file_name.starts_with("test_") || file_name.ends_with("_test.py") + } + + fn parse_import_statement(text: &str) -> Vec { + let Some(imports) = text.trim().strip_prefix("import ") else { + return Vec::new(); + }; + imports + .split(',') + .filter_map(|entry| Self::parse_import_entry(entry.trim())) + .map(|(specifier, local)| ImportFact { + specifier, + bindings: vec![ImportBinding { + local, + imported: "*".to_string(), + }], + }) + .collect() + } + + fn parse_import_entry(entry: &str) -> Option<(String, String)> { + let (module, alias) = Self::split_alias(entry); + if module.is_empty() { + return None; + } + let local = alias + .map(ToString::to_string) + .or_else(|| module.split('.').next().map(ToString::to_string))?; + Some((module.to_string(), local)) + } + + fn parse_from_import_statement(text: &str) -> Vec { + let text = text.trim(); + let Some(rest) = text.strip_prefix("from ") else { + return Vec::new(); + }; + let Some((module, imports)) = rest.split_once(" import ") else { + return Vec::new(); + }; + let module = module.trim(); + let imports = imports.trim().trim_start_matches('(').trim_end_matches(')'); + if imports == "*" { + return vec![ImportFact { + specifier: module.to_string(), + bindings: vec![ImportBinding { + local: "*".to_string(), + imported: "*".to_string(), + }], + }]; + } + imports + .split(',') + .filter_map(|entry| Self::parse_from_import_entry(module, entry.trim())) + .collect() + } + + fn parse_from_import_entry(module: &str, entry: &str) -> Option { + let (imported, alias) = Self::split_alias(entry); + if imported.is_empty() { + return None; + } + let local = alias.unwrap_or(imported).to_string(); + let (specifier, imported_name) = if module.chars().all(|character| character == '.') { + (format!("{module}{imported}"), "*".to_string()) + } else { + (module.to_string(), imported.to_string()) + }; + Some(ImportFact { + specifier, + bindings: vec![ImportBinding { + local, + imported: imported_name, + }], + }) + } + + fn split_alias(entry: &str) -> (&str, Option<&str>) { + if let Some((left, right)) = entry.split_once(" as ") { + (left.trim(), Some(right.trim())) + } else { + (entry.trim(), None) + } + } + + fn decorator_name(node: Node<'_>, source_text: &str) -> Option { + let text = Self::node_text(node, source_text); + text.trim() + .strip_prefix('@') + .map(str::trim) + .filter(|name| !name.is_empty()) + .map(ToString::to_string) + } + + fn assignment_name(node: Node<'_>, source_text: &str) -> Option { + match node.kind() { + "identifier" => Some(Self::node_text(node, source_text)), + _ => None, + } + } + + fn expression_name(node: Node<'_>, source_text: &str) -> Option { + match node.kind() { + "identifier" => Some(Self::node_text(node, source_text)), + "attribute" => { + let object = node + .child_by_field_name("object") + .and_then(|object| Self::expression_name(object, source_text))?; + let attribute = Self::field_text(node, "attribute", source_text)?; + Some(format!("{object}.{attribute}")) + } + "call" => node + .child_by_field_name("function") + .and_then(|function| Self::expression_name(function, source_text)), + "dotted_name" => Some(Self::node_text(node, source_text)), + _ => Self::named_children(node) + .into_iter() + .find_map(|child| Self::expression_name(child, source_text)), + } + } + + fn is_builtin_reference(name: &str) -> bool { + matches!( + name, + "super" + | "len" + | "str" + | "int" + | "float" + | "bool" + | "list" + | "dict" + | "set" + | "tuple" + | "print" + | "range" + ) || name.starts_with("self.") + || name.starts_with("cls.") + } + + fn field_text(node: Node<'_>, field: &str, source_text: &str) -> Option { + node.child_by_field_name(field) + .map(|child| Self::node_text(child, source_text)) + } + + fn node_text(node: Node<'_>, source_text: &str) -> String { + node.utf8_text(source_text.as_bytes()) + .map(ToString::to_string) + .unwrap_or_default() + } + + fn named_children(node: Node<'_>) -> Vec> { + let mut cursor = node.walk(); + node.named_children(&mut cursor).collect() + } + + fn descendant_nodes(node: Node<'_>) -> Vec> { + let mut nodes = Vec::new(); + let mut stack = vec![node]; + while let Some(current) = stack.pop() { + nodes.push(current); + for child in Self::named_children(current) { + stack.push(child); + } + } + nodes + } + + fn parse_string_literal(text: &str) -> Option { + let trimmed = text.trim(); + let quote_index = trimmed.find(['"', '\''])?; + let quoted = trimmed.get(quote_index..)?; + let quote = quoted.chars().next()?; + let triple = format!("{quote}{quote}{quote}"); + if let Some(body) = quoted + .strip_prefix(&triple) + .and_then(|body| body.strip_suffix(&triple)) + { + return Some(body.to_string()); + } + quoted + .strip_prefix(quote) + .and_then(|body| body.strip_suffix(quote)) + .map(ToString::to_string) + } + + fn module_name_for_path(path: &str) -> String { + let without_extension = path + .strip_suffix(".py") + .or_else(|| path.strip_suffix(".pyi")) + .unwrap_or(path); + let mut parts = without_extension + .split('/') + .filter(|part| !part.is_empty()) + .collect::>(); + if parts.last().is_some_and(|part| *part == "__init__") { + parts.pop(); + } + if parts.is_empty() { + return "__init__".to_string(); + } + parts.join(".") + } +} diff --git a/crates/graph-core/src/extraction/facts/resolution.rs b/crates/graph-core/src/extraction/facts/resolution.rs new file mode 100644 index 0000000..19b2dee --- /dev/null +++ b/crates/graph-core/src/extraction/facts/resolution.rs @@ -0,0 +1,207 @@ +use super::*; + +pub(super) struct Resolution; + +pub(super) trait FactResolution { + fn file_id(path: &str) -> String; + fn insert_edge(edges: &mut BTreeMap, edge: EdgeDraft<'_>); + fn resolve_name( + file_path: &str, + name: &str, + context: &NameResolutionContext<'_>, + ) -> Option; + fn resolve_imported_name( + target_path: &str, + imported: &str, + context: &NameResolutionContext<'_>, + ) -> Option; + fn is_python_source_path(path: &str) -> bool; + fn is_rust_source_path(path: &str) -> bool; + fn known_files(file_facts: &[FileFacts]) -> BTreeSet; + fn append_path_traversal_blocker(diagnostics: &mut Vec); +} + +impl FactResolution for Resolution { + fn file_id(path: &str) -> String { + format!("file:{path}") + } + + fn insert_edge(edges: &mut BTreeMap, edge: EdgeDraft<'_>) { + if edge.from == edge.to { + return; + } + let id = format!("edge:{}:{}->{}", edge.kind, edge.from, edge.to); + edges.entry(id.clone()).or_insert_with(|| GraphFactEdge { + id: Some(id), + kind: edge.kind.to_string(), + from: edge.from.to_string(), + to: edge.to.to_string(), + attributes: None, + }); + } + + fn resolve_name( + file_path: &str, + name: &str, + context: &NameResolutionContext<'_>, + ) -> Option { + if let Some(local) = context + .declarations_by_file + .get(file_path) + .and_then(|declarations| declarations.get(name)) + { + return Some(local.clone()); + } + if name.contains('.') { + return resolve_dotted_name(file_path, name, context); + } + if let Some(target) = context + .imports_by_file + .get(file_path) + .and_then(|imports| imports.get(name)) + { + if target.imported != "*" { + if let Some(target) = + Self::resolve_imported_name(&target.path, &target.imported, context) + { + return Some(target); + } + } + } + None + } + + fn resolve_imported_name( + target_path: &str, + imported: &str, + context: &NameResolutionContext<'_>, + ) -> Option { + context + .export_aliases_by_file + .get(target_path) + .and_then(|aliases| aliases.get(imported)) + .cloned() + } + + fn is_python_source_path(path: &str) -> bool { + path.ends_with(".py") || path.ends_with(".pyi") + } + + fn is_rust_source_path(path: &str) -> bool { + path.ends_with(".rs") + } + + fn known_files(file_facts: &[FileFacts]) -> BTreeSet { + file_facts + .iter() + .map(|facts| facts.path.clone()) + .collect::>() + } + + fn append_path_traversal_blocker(diagnostics: &mut Vec) { + let has_path_traversal = diagnostics.iter().any(|diagnostic| { + diagnostic.category == GraphExtractionDiagnosticCategory::PathTraversal + }); + if has_path_traversal { + diagnostics.push(error( + GraphExtractionDiagnosticCategory::PathTraversal, + "path traversal diagnostics blocked graph availability", + None, + None, + )); + } + } +} + +fn resolve_dotted_name( + file_path: &str, + name: &str, + context: &NameResolutionContext<'_>, +) -> Option { + let parts = name.split('.').collect::>(); + let head = parts.first()?; + let target = context + .imports_by_file + .get(file_path) + .and_then(|imports| imports.get(*head))?; + if target.imported == "*" { + for candidate in namespace_import_member_candidates(&parts, &target.path) { + if let Some(target) = + Resolution::resolve_imported_name(&target.path, &candidate, context) + { + return Some(target); + } + } + return None; + } + Resolution::resolve_imported_name(&target.path, &target.imported, context) +} + +fn namespace_import_member_candidates(parts: &[&str], target_path: &str) -> Vec { + let mut candidates = Vec::new(); + let module_parts = module_parts_for_path(target_path); + if let Some(consumed) = longest_module_prefix_match(parts, &module_parts) { + if let Some(candidate) = parts.get(consumed..) { + candidates.push(candidate.join(".")); + } + } + if let Some(candidate) = parts.get(1..) { + candidates.push(candidate.join(".")); + } + if let Some(last) = parts.last() { + candidates.push((*last).to_string()); + } + deduplicate(candidates) +} + +fn longest_module_prefix_match(parts: &[&str], module_parts: &[String]) -> Option { + let max_len = parts.len().min(module_parts.len()); + (1..=max_len).rev().find(|len| { + let len = *len; + let Some(start) = module_parts.len().checked_sub(len) else { + return false; + }; + let Some(module_suffix) = module_parts.get(start..) else { + return false; + }; + let Some(parts_prefix) = parts.get(..len) else { + return false; + }; + module_suffix + .iter() + .map(String::as_str) + .eq(parts_prefix.iter().copied()) + }) +} + +fn module_parts_for_path(path: &str) -> Vec { + let without_extension = path + .strip_suffix(".py") + .or_else(|| path.strip_suffix(".pyi")) + .or_else(|| path.strip_suffix(".mts")) + .or_else(|| path.strip_suffix(".cts")) + .or_else(|| path.strip_suffix(".ts")) + .or_else(|| path.strip_suffix(".tsx")) + .or_else(|| path.strip_suffix(".js")) + .or_else(|| path.strip_suffix(".jsx")) + .or_else(|| path.strip_suffix(".rs")) + .unwrap_or(path); + let mut parts = without_extension + .split('/') + .filter(|part| !part.is_empty()) + .map(ToString::to_string) + .collect::>(); + if parts.last().is_some_and(|part| part == "__init__") { + parts.pop(); + } + parts +} + +fn deduplicate(values: Vec) -> Vec { + values.into_iter().fold(Vec::new(), |mut unique, value| { + if !value.is_empty() && !unique.contains(&value) { + unique.push(value); + } + unique + }) +} diff --git a/crates/graph-core/src/extraction/facts/rust.rs b/crates/graph-core/src/extraction/facts/rust.rs index f844d74..49df02f 100644 --- a/crates/graph-core/src/extraction/facts/rust.rs +++ b/crates/graph-core/src/extraction/facts/rust.rs @@ -1,6 +1,6 @@ use super::{ - file_id, insert_edge, EdgeDraft, FileFacts, HeritageFact, ImportBinding, ImportFact, - ReExportFact, ReferenceFact, + set_node_attribute, EdgeDraft, FactResolution, FileFacts, HeritageFact, ImportBinding, + ImportFact, ReExportFact, ReferenceFact, Resolution, }; use crate::protocol::{GraphFactEdge, GraphFactNode}; use quote::ToTokens; @@ -15,6 +15,14 @@ use syn::{ Path as SynPath, Type, UseTree, Visibility, }; +mod imports; +mod references; +mod syntax; + +use imports::{Imports, RustImports}; +use references::{References, RustReferences}; +use syntax::{ModuleScope, RustSyntax, Syntax}; + type ImportResolution = super::super::tsconfig::ImportResolution; pub(super) fn collect_file_facts( @@ -33,7 +41,7 @@ pub(super) fn resolve_import( known_files: &BTreeSet, ) -> ImportResolution { ImportResolution { - resolved_path: resolve_rust_import_path(specifier, from_path, known_files), + resolved_path: Imports::resolve_rust_import_path(specifier, from_path, known_files), diagnostics: Vec::new(), } } @@ -82,7 +90,7 @@ struct GraphNodeDraft<'a> { impl RustFileFactCollector { fn new(path: String, file_node: GraphFactNode) -> Self { - let module_name = file_module_name(&path); + let module_name = Imports::file_module_name(&path); let module_id = format!("module:{path}#{module_name}"); let mut nodes = BTreeMap::new(); nodes.insert( @@ -100,9 +108,9 @@ impl RustFileFactCollector { }, ); let mut edges = BTreeMap::new(); - insert_edge( + Resolution::insert_edge( &mut edges, - EdgeDraft::new("CONTAINS", &file_id(&path), &module_id), + EdgeDraft::new("CONTAINS", &Resolution::file_id(&path), &module_id), ); Self { path, @@ -123,7 +131,7 @@ impl RustFileFactCollector { fn finish(mut self) -> FileFacts { if !self.file_exports.is_empty() { - set_attribute( + set_node_attribute( &mut self.file_node, "exports", Value::Array(self.file_exports.clone()), @@ -151,11 +159,19 @@ impl RustFileFactCollector { fn visit_item(&mut self, item: &Item) { match item { - Item::Use(item) => self.imports.extend(imports_from_use_tree(&item.tree)), + Item::Use(item) => self + .imports + .extend(Imports::imports_from_use_tree(&item.tree)), Item::Mod(item) => self.visit_module(item), Item::Struct(item) => self.visit_struct(item), Item::Enum(item) => self.visit_enum(item), Item::Trait(item) => self.visit_trait(item), + _ => self.visit_remaining_item(item), + } + } + + fn visit_remaining_item(&mut self, item: &Item) { + match item { Item::Impl(item) => self.visit_impl(item), Item::Fn(item) => self.visit_function(item), Item::Type(item) => self.visit_type_alias(item), @@ -168,17 +184,18 @@ impl RustFileFactCollector { fn visit_module(&mut self, item: &ItemMod) { let name = item.ident.to_string(); - let exported = is_exported(&item.vis); - let is_test = has_cfg_test(&item.attrs) || self.test_module_depth > 0; + let exported = Syntax::is_exported(&item.vis); + let is_test = Syntax::has_cfg_test(&item.attrs) || self.test_module_depth > 0; let qualified_name = self.module_child_name(&name); let id = format!("module:{}#{qualified_name}", self.path); - let mut attributes = base_attributes( + let mut attributes = Syntax::base_attributes( exported, &qualified_name, - Some(signature_for_module(item)), + Some(Syntax::signature_for_module(item)), Some(item.span()), ); - attributes_object(&mut attributes).insert("isTest".to_string(), Value::Bool(is_test)); + Syntax::attributes_object(&mut attributes) + .insert("isTest".to_string(), Value::Bool(is_test)); self.insert_node(GraphNodeDraft { id: id.clone(), kind: "Module", @@ -186,11 +203,18 @@ impl RustFileFactCollector { attributes, exported, }); - self.with_module(id, name, is_test, |collector| { - if let Some((_, items)) = &item.content { - collector.visit_items(items); - } - }); + self.with_module( + ModuleScope { + parent: id, + name, + is_test, + }, + |collector| { + if let Some((_, items)) = &item.content { + collector.visit_items(items); + } + }, + ); } fn visit_struct(&mut self, item: &ItemStruct) { @@ -199,7 +223,7 @@ impl RustFileFactCollector { kind: "Struct", name: &item.ident.to_string(), visibility: &item.vis, - signature: signature_for_item(item), + signature: Syntax::signature_for_item(item), span: item.span(), }); } @@ -210,7 +234,7 @@ impl RustFileFactCollector { kind: "Enum", name: &item.ident.to_string(), visibility: &item.vis, - signature: signature_for_item(item), + signature: Syntax::signature_for_item(item), span: item.span(), }); } @@ -221,20 +245,21 @@ impl RustFileFactCollector { kind: "Trait", name: &item.ident.to_string(), visibility: &item.vis, - signature: signature_for_item(item), + signature: Syntax::signature_for_item(item), span: item.span(), }); } fn visit_impl(&mut self, item: &ItemImpl) { - let self_type = type_name(&item.self_ty).unwrap_or_else(|| "Self".to_string()); + let self_type = Syntax::type_name(&item.self_ty).unwrap_or_else(|| "Self".to_string()); let trait_name = item .trait_ .as_ref() - .and_then(|(_, path, _)| path_last_segment(path)); - let name = impl_name(trait_name.as_deref(), &self_type); + .and_then(|(_, path, _)| Syntax::path_last_segment(path)); + let name = Syntax::impl_name(trait_name.as_deref(), &self_type); let id = format!("impl:{}#{name}", self.path); - let attributes = base_attributes(false, &name, Some(name.clone()), Some(item.span())); + let attributes = + Syntax::base_attributes(false, &name, Some(name.clone()), Some(item.span())); self.insert_node(GraphNodeDraft { id: id.clone(), kind: "Impl", @@ -255,11 +280,11 @@ impl RustFileFactCollector { let name = method.sig.ident.to_string(); let qualified_name = format!("{self_type}::{name}"); let id = format!("method:{}#{qualified_name}", self.path); - let exported = is_exported(&method.vis); - let attributes = base_attributes( + let exported = Syntax::is_exported(&method.vis); + let attributes = Syntax::base_attributes( exported, &qualified_name, - Some(signature_for_method(method)), + Some(Syntax::signature_for_method(method)), Some(method.span()), ); self.insert_node(GraphNodeDraft { @@ -277,19 +302,20 @@ impl RustFileFactCollector { fn visit_function(&mut self, item: &ItemFn) { let name = item.sig.ident.to_string(); - let is_test = has_test_attr(&item.attrs) || has_cfg_test(&item.attrs); + let is_test = Syntax::has_test_attr(&item.attrs) || Syntax::has_cfg_test(&item.attrs); let qualified_name = self.item_qualified_name(&name); let prefix = if is_test { "test" } else { "function" }; let kind = if is_test { "Test" } else { "Function" }; let id = format!("{prefix}:{}#{qualified_name}", self.path); - let exported = is_exported(&item.vis); - let mut attributes = base_attributes( + let exported = Syntax::is_exported(&item.vis); + let mut attributes = Syntax::base_attributes( exported, &qualified_name, - Some(signature_for_function(item)), + Some(Syntax::signature_for_function(item)), Some(item.span()), ); - attributes_object(&mut attributes).insert("isTest".to_string(), Value::Bool(is_test)); + Syntax::attributes_object(&mut attributes) + .insert("isTest".to_string(), Value::Bool(is_test)); self.insert_node(GraphNodeDraft { id: id.clone(), kind, @@ -306,7 +332,7 @@ impl RustFileFactCollector { kind: "TypeAlias", name: &item.ident.to_string(), visibility: &item.vis, - signature: signature_for_item(item), + signature: Syntax::signature_for_item(item), span: item.span(), }); } @@ -317,7 +343,7 @@ impl RustFileFactCollector { kind: "Const", name: &item.ident.to_string(), visibility: &item.vis, - signature: signature_for_item(item), + signature: Syntax::signature_for_item(item), span: item.span(), }); } @@ -328,7 +354,7 @@ impl RustFileFactCollector { kind: "Static", name: &item.ident.to_string(), visibility: &item.vis, - signature: signature_for_item(item), + signature: Syntax::signature_for_item(item), span: item.span(), }); } @@ -355,7 +381,7 @@ impl RustFileFactCollector { kind: "Macro", name: ¯o_name, exported: false, - signature: signature_for_item(item), + signature: Syntax::signature_for_item(item), span: item.span(), }); } @@ -365,7 +391,7 @@ impl RustFileFactCollector { prefix: draft.prefix, kind: draft.kind, name: draft.name, - exported: is_exported(draft.visibility), + exported: Syntax::is_exported(draft.visibility), signature: draft.signature, span: draft.span, }) @@ -374,7 +400,7 @@ impl RustFileFactCollector { fn add_named_node(&mut self, draft: NamedNodeDraft<'_>) -> String { let qualified_name = self.item_qualified_name(draft.name); let id = format!("{}:{}#{qualified_name}", draft.prefix, self.path); - let attributes = base_attributes( + let attributes = Syntax::base_attributes( draft.exported, &qualified_name, Some(draft.signature), @@ -424,32 +450,25 @@ impl RustFileFactCollector { })); } } - insert_edge( + Resolution::insert_edge( &mut self.edges, EdgeDraft::new("CONTAINS", &self.current_parent, &draft.id), ); } fn collect_references_from_block(&mut self, from: &str, block: &syn::Block, is_test: bool) { - let mut visitor = RustReferenceVisitor::new(from.to_string(), is_test); - visitor.visit_block(block); - self.references.extend(visitor.references); + self.references + .extend(References::collect(from.to_string(), block, is_test)); } - fn with_module( - &mut self, - parent: String, - name: String, - is_test: bool, - visit: impl FnOnce(&mut Self), - ) { - let previous_parent = std::mem::replace(&mut self.current_parent, parent); - self.module_stack.push(name); - if is_test { + fn with_module(&mut self, scope: ModuleScope, visit: impl FnOnce(&mut Self)) { + let previous_parent = std::mem::replace(&mut self.current_parent, scope.parent); + self.module_stack.push(scope.name); + if scope.is_test { self.test_module_depth += 1; } visit(self); - if is_test { + if scope.is_test { self.test_module_depth = self.test_module_depth.saturating_sub(1); } self.module_stack.pop(); @@ -476,447 +495,3 @@ impl RustFileFactCollector { format!("{}::{name}", parts.join(".")) } } - -struct RustReferenceVisitor { - from: String, - is_test: bool, - references: Vec, -} - -impl RustReferenceVisitor { - fn new(from: String, is_test: bool) -> Self { - Self { - from, - is_test, - references: Vec::new(), - } - } - - fn push_path_reference(&mut self, path: &SynPath) { - if let Some(name) = reference_name_for_path(path) { - self.references.push(ReferenceFact { - from: self.from.clone(), - name, - is_test: self.is_test, - }); - } - } -} - -impl<'ast> Visit<'ast> for RustReferenceVisitor { - fn visit_expr_call(&mut self, node: &'ast ExprCall) { - if let syn::Expr::Path(path) = node.func.as_ref() { - self.push_path_reference(&path.path); - } - visit::visit_expr_call(self, node); - } - - fn visit_expr_method_call(&mut self, node: &'ast ExprMethodCall) { - self.references.push(ReferenceFact { - from: self.from.clone(), - name: node.method.to_string(), - is_test: self.is_test, - }); - visit::visit_expr_method_call(self, node); - } - - fn visit_expr_macro(&mut self, node: &'ast ExprMacro) { - self.push_path_reference(&node.mac.path); - visit::visit_expr_macro(self, node); - } -} - -fn imports_from_use_tree(tree: &UseTree) -> Vec { - let mut imports = Vec::new(); - collect_use_tree(tree, Vec::new(), &mut imports); - imports -} - -fn collect_use_tree(tree: &UseTree, prefix: Vec, imports: &mut Vec) { - match tree { - UseTree::Path(path) => { - let mut next_prefix = prefix; - next_prefix.push(path.ident.to_string()); - collect_use_tree(&path.tree, next_prefix, imports); - } - UseTree::Name(name) => push_use_binding(imports, &prefix, &name.ident.to_string(), None), - UseTree::Rename(rename) => push_use_binding( - imports, - &prefix, - &rename.ident.to_string(), - Some(rename.rename.to_string()), - ), - UseTree::Glob(_) => { - imports.push(ImportFact { - specifier: prefix.join("::"), - bindings: vec![ImportBinding { - local: "*".to_string(), - imported: "*".to_string(), - }], - }); - } - UseTree::Group(group) => { - for item in &group.items { - collect_use_tree(item, prefix.clone(), imports); - } - } - } -} - -fn push_use_binding( - imports: &mut Vec, - prefix: &[String], - imported_name: &str, - renamed: Option, -) { - let local = renamed.unwrap_or_else(|| imported_name.to_string()); - let module_import = is_probable_module_import(prefix, imported_name); - let specifier = if module_import { - path_with_tail(prefix, imported_name) - } else { - prefix.join("::") - }; - let imported = if module_import { - "*".to_string() - } else { - imported_name.to_string() - }; - imports.push(ImportFact { - specifier, - bindings: vec![ImportBinding { local, imported }], - }); -} - -fn is_probable_module_import(prefix: &[String], imported_name: &str) -> bool { - prefix - .last() - .is_some_and(|part| part == "crate" || part == "self" || part == "super") - && imported_name.chars().next().is_some_and(char::is_lowercase) -} - -fn path_with_tail(prefix: &[String], tail: &str) -> String { - let mut parts = prefix.to_vec(); - parts.push(tail.to_string()); - parts.join("::") -} - -fn resolve_rust_import_path( - specifier: &str, - from_path: &str, - known_files: &BTreeSet, -) -> Option { - let parts = specifier - .split("::") - .filter(|part| !part.is_empty()) - .map(ToString::to_string) - .collect::>(); - match parts.first().map(String::as_str) { - Some("crate") => resolve_crate_path(from_path, parts.iter().skip(1), known_files), - Some("self") => resolve_relative_path(from_path, parts.iter().skip(1), known_files), - Some("super") => resolve_super_path(from_path, parts.iter().skip(1), known_files), - Some(_) => resolve_relative_path(from_path, parts.iter(), known_files), - None => crate_root_file_for(from_path, known_files), - } -} - -fn resolve_crate_path<'a>( - from_path: &str, - module_parts: impl Iterator, - known_files: &BTreeSet, -) -> Option { - let source_dir = crate_source_dir_for(from_path, known_files)?; - let module_parts = module_parts.cloned().collect::>(); - if module_parts.is_empty() { - return crate_root_file_in_source_dir(&source_dir, known_files); - } - resolve_module_candidates(&source_dir, &module_parts, known_files) -} - -fn resolve_relative_path<'a>( - from_path: &str, - module_parts: impl Iterator, - known_files: &BTreeSet, -) -> Option { - let module_parts = module_parts.cloned().collect::>(); - if module_parts.is_empty() { - return Some(from_path.to_string()); - } - let base_dir = module_dir_for_path(from_path); - resolve_module_candidates(&base_dir, &module_parts, known_files) -} - -fn resolve_super_path<'a>( - from_path: &str, - module_parts: impl Iterator, - known_files: &BTreeSet, -) -> Option { - let parent = Path::new(from_path) - .parent() - .and_then(Path::parent) - .map(path_to_string) - .unwrap_or_default(); - let module_parts = module_parts.cloned().collect::>(); - if module_parts.is_empty() { - return None; - } - let base_dir = parent_module_dir_for_path(from_path).unwrap_or(parent); - resolve_module_candidates(&base_dir, &module_parts, known_files) -} - -fn resolve_module_candidates( - base_dir: &str, - module_parts: &[String], - known_files: &BTreeSet, -) -> Option { - module_file_candidates(base_dir, module_parts) - .into_iter() - .find(|candidate| known_files.contains(candidate)) -} - -fn module_file_candidates(base_dir: &str, module_parts: &[String]) -> Vec { - let mut module_path = PathBuf::from(base_dir); - for part in module_parts { - module_path.push(part); - } - let file_candidate = format!("{}.rs", path_to_string(&module_path)); - let mut mod_candidate = module_path; - mod_candidate.push("mod.rs"); - vec![file_candidate, path_to_string(&mod_candidate)] -} - -fn crate_root_file_for(from_path: &str, known_files: &BTreeSet) -> Option { - let source_dir = crate_source_dir_for(from_path, known_files)?; - crate_root_file_in_source_dir(&source_dir, known_files) -} - -fn crate_source_dir_for(from_path: &str, known_files: &BTreeSet) -> Option { - let mut current = Path::new(from_path).parent(); - while let Some(directory) = current { - let source_dir = path_to_string(directory); - if crate_root_file_in_source_dir(&source_dir, known_files).is_some() { - return Some(source_dir); - } - current = directory.parent(); - } - None -} - -fn crate_root_file_in_source_dir( - source_dir: &str, - known_files: &BTreeSet, -) -> Option { - let lib = join_path(source_dir, "lib.rs"); - if known_files.contains(&lib) { - return Some(lib); - } - let main = join_path(source_dir, "main.rs"); - if known_files.contains(&main) { - return Some(main); - } - None -} - -fn module_dir_for_path(path: &str) -> String { - let path = Path::new(path); - let parent = path.parent().map(path_to_string).unwrap_or_default(); - let Some(stem) = path - .file_stem() - .map(|stem| stem.to_string_lossy().to_string()) - else { - return parent; - }; - if stem == "lib" || stem == "main" || stem == "mod" { - return parent; - } - join_path(&parent, &stem) -} - -fn parent_module_dir_for_path(path: &str) -> Option { - Path::new(&module_dir_for_path(path)) - .parent() - .map(path_to_string) -} - -fn join_path(parent: &str, child: &str) -> String { - if parent.is_empty() { - child.to_string() - } else { - format!("{parent}/{child}") - } -} - -fn file_module_name(path: &str) -> String { - let file_name = Path::new(path) - .file_stem() - .map(|name| name.to_string_lossy().to_string()) - .unwrap_or_else(|| "crate".to_string()); - if file_name == "lib" || file_name == "main" { - return "crate".to_string(); - } - if file_name == "mod" { - return Path::new(path) - .parent() - .and_then(Path::file_name) - .map(|name| name.to_string_lossy().to_string()) - .unwrap_or(file_name); - } - file_name -} - -fn is_exported(visibility: &Visibility) -> bool { - !matches!(visibility, Visibility::Inherited) -} - -fn has_test_attr(attributes: &[Attribute]) -> bool { - attributes - .iter() - .any(|attribute| attribute.path().is_ident("test")) -} - -fn has_cfg_test(attributes: &[Attribute]) -> bool { - attributes.iter().any(|attribute| { - attribute.path().is_ident("cfg") - && attribute - .meta - .to_token_stream() - .to_string() - .contains("test") - }) -} - -fn signature_for_module(item: &ItemMod) -> String { - format!("{}mod {}", visibility_tokens(&item.vis), item.ident) - .trim() - .to_string() -} - -fn signature_for_function(item: &ItemFn) -> String { - format!( - "{}{}", - visibility_tokens(&item.vis), - item.sig.to_token_stream() - ) - .trim() - .to_string() -} - -fn signature_for_method(item: &syn::ImplItemFn) -> String { - format!( - "{}{}", - visibility_tokens(&item.vis), - item.sig.to_token_stream() - ) - .trim() - .to_string() -} - -fn signature_for_item(item: &impl ToTokens) -> String { - item.to_token_stream().to_string() -} - -fn visibility_tokens(visibility: &Visibility) -> String { - let tokens = visibility.to_token_stream().to_string(); - if tokens.is_empty() { - tokens - } else { - format!("{tokens} ") - } -} - -fn type_name(ty: &Type) -> Option { - match ty { - Type::Path(path) => path_last_segment(&path.path), - _ => None, - } -} - -fn path_last_segment(path: &SynPath) -> Option { - path.segments - .last() - .map(|segment| segment.ident.to_string()) -} - -fn impl_name(trait_name: Option<&str>, self_type: &str) -> String { - match trait_name { - Some(trait_name) => format!("impl {trait_name} for {self_type}"), - None => format!("impl {self_type}"), - } -} - -fn reference_name_for_path(path: &SynPath) -> Option { - let parts = path - .segments - .iter() - .map(|segment| segment.ident.to_string()) - .collect::>(); - if parts.is_empty() { - return None; - } - if parts - .iter() - .any(|part| part == "self" || part == "Self" || part == "super" || part == "crate") - { - return None; - } - let separator = if parts.len() > 1 { "." } else { "" }; - if separator.is_empty() { - parts.first().cloned() - } else { - Some(parts.join(separator)) - } -} - -fn base_attributes( - exported: bool, - qualified_name: &str, - signature: Option, - span: Option, -) -> Value { - let mut attributes = serde_json::Map::new(); - attributes.insert("language".to_string(), Value::String("rust".to_string())); - attributes.insert("exported".to_string(), Value::Bool(exported)); - attributes.insert( - "qualifiedName".to_string(), - Value::String(qualified_name.to_string()), - ); - if exported { - attributes.insert("exportKind".to_string(), Value::String("named".to_string())); - attributes.insert( - "exportName".to_string(), - Value::String(qualified_name.to_string()), - ); - } - if let Some(signature) = signature { - attributes.insert("signature".to_string(), Value::String(signature)); - } - if let Some(span) = span { - let start = span.start(); - let end = span.end(); - attributes.insert("lineStart".to_string(), json!(start.line)); - attributes.insert("lineEnd".to_string(), json!(end.line)); - attributes.insert("columnStart".to_string(), json!(start.column)); - attributes.insert("columnEnd".to_string(), json!(end.column)); - } - Value::Object(attributes) -} - -fn set_attribute(node: &mut GraphFactNode, key: &str, value: Value) { - attributes_object( - node.attributes - .get_or_insert_with(|| Value::Object(serde_json::Map::new())), - ) - .insert(key.to_string(), value); -} - -fn attributes_object(value: &mut Value) -> &mut serde_json::Map { - loop { - if let Value::Object(object) = value { - return object; - } - *value = Value::Object(serde_json::Map::new()); - } -} - -fn path_to_string(path: impl AsRef) -> String { - path.as_ref().to_string_lossy().replace('\\', "/") -} diff --git a/crates/graph-core/src/extraction/facts/rust/imports.rs b/crates/graph-core/src/extraction/facts/rust/imports.rs new file mode 100644 index 0000000..10505ea --- /dev/null +++ b/crates/graph-core/src/extraction/facts/rust/imports.rs @@ -0,0 +1,255 @@ +use super::*; + +pub(super) struct Imports; + +pub(super) trait RustImports { + fn imports_from_use_tree(tree: &UseTree) -> Vec; + fn resolve_rust_import_path( + specifier: &str, + from_path: &str, + known_files: &BTreeSet, + ) -> Option; + fn file_module_name(path: &str) -> String; +} + +impl RustImports for Imports { + fn imports_from_use_tree(tree: &UseTree) -> Vec { + let mut imports = Vec::new(); + collect_use_tree(tree, Vec::new(), &mut imports); + imports + } + + fn resolve_rust_import_path( + specifier: &str, + from_path: &str, + known_files: &BTreeSet, + ) -> Option { + let parts = specifier + .split("::") + .filter(|part| !part.is_empty()) + .map(ToString::to_string) + .collect::>(); + match parts.first().map(String::as_str) { + Some("crate") => resolve_crate_path(from_path, parts.iter().skip(1), known_files), + Some("self") => resolve_relative_path(from_path, parts.iter().skip(1), known_files), + Some("super") => resolve_super_path(from_path, parts.iter().skip(1), known_files), + Some(_) => resolve_relative_path(from_path, parts.iter(), known_files), + None => crate_root_file_for(from_path, known_files), + } + } + + fn file_module_name(path: &str) -> String { + let file_name = Path::new(path) + .file_stem() + .map(|name| name.to_string_lossy().to_string()) + .unwrap_or_else(|| "crate".to_string()); + if file_name == "lib" || file_name == "main" { + return "crate".to_string(); + } + if file_name == "mod" { + return Path::new(path) + .parent() + .and_then(Path::file_name) + .map(|name| name.to_string_lossy().to_string()) + .unwrap_or(file_name); + } + file_name + } +} + +fn collect_use_tree(tree: &UseTree, prefix: Vec, imports: &mut Vec) { + match tree { + UseTree::Path(path) => { + let mut next_prefix = prefix; + next_prefix.push(path.ident.to_string()); + collect_use_tree(&path.tree, next_prefix, imports); + } + UseTree::Name(name) => push_use_binding(imports, &prefix, &name.ident.to_string(), None), + UseTree::Rename(rename) => push_use_binding( + imports, + &prefix, + &rename.ident.to_string(), + Some(rename.rename.to_string()), + ), + UseTree::Glob(_) => { + imports.push(ImportFact { + specifier: prefix.join("::"), + bindings: vec![ImportBinding { + local: "*".to_string(), + imported: "*".to_string(), + }], + }); + } + UseTree::Group(group) => { + for item in &group.items { + collect_use_tree(item, prefix.clone(), imports); + } + } + } +} + +fn push_use_binding( + imports: &mut Vec, + prefix: &[String], + imported_name: &str, + renamed: Option, +) { + let local = renamed.unwrap_or_else(|| imported_name.to_string()); + let module_import = is_probable_module_import(prefix, imported_name); + let specifier = if module_import { + path_with_tail(prefix, imported_name) + } else { + prefix.join("::") + }; + let imported = if module_import { + "*".to_string() + } else { + imported_name.to_string() + }; + imports.push(ImportFact { + specifier, + bindings: vec![ImportBinding { local, imported }], + }); +} + +fn is_probable_module_import(prefix: &[String], imported_name: &str) -> bool { + prefix + .last() + .is_some_and(|part| part == "crate" || part == "self" || part == "super") + && imported_name.chars().next().is_some_and(char::is_lowercase) +} + +fn path_with_tail(prefix: &[String], tail: &str) -> String { + let mut parts = prefix.to_vec(); + parts.push(tail.to_string()); + parts.join("::") +} + +fn resolve_crate_path<'a>( + from_path: &str, + module_parts: impl Iterator, + known_files: &BTreeSet, +) -> Option { + let source_dir = crate_source_dir_for(from_path, known_files)?; + let module_parts = module_parts.cloned().collect::>(); + if module_parts.is_empty() { + return crate_root_file_in_source_dir(&source_dir, known_files); + } + resolve_module_candidates(&source_dir, &module_parts, known_files) +} + +fn resolve_relative_path<'a>( + from_path: &str, + module_parts: impl Iterator, + known_files: &BTreeSet, +) -> Option { + let module_parts = module_parts.cloned().collect::>(); + if module_parts.is_empty() { + return Some(from_path.to_string()); + } + let base_dir = module_dir_for_path(from_path); + resolve_module_candidates(&base_dir, &module_parts, known_files) +} + +fn resolve_super_path<'a>( + from_path: &str, + module_parts: impl Iterator, + known_files: &BTreeSet, +) -> Option { + let parent = Path::new(from_path) + .parent() + .and_then(Path::parent) + .map(Syntax::path_to_string) + .unwrap_or_default(); + let module_parts = module_parts.cloned().collect::>(); + if module_parts.is_empty() { + return None; + } + let base_dir = parent_module_dir_for_path(from_path).unwrap_or(parent); + resolve_module_candidates(&base_dir, &module_parts, known_files) +} + +fn resolve_module_candidates( + base_dir: &str, + module_parts: &[String], + known_files: &BTreeSet, +) -> Option { + module_file_candidates(base_dir, module_parts) + .into_iter() + .find(|candidate| known_files.contains(candidate)) +} + +fn module_file_candidates(base_dir: &str, module_parts: &[String]) -> Vec { + let mut module_path = PathBuf::from(base_dir); + for part in module_parts { + module_path.push(part); + } + let file_candidate = format!("{}.rs", Syntax::path_to_string(&module_path)); + let mut mod_candidate = module_path; + mod_candidate.push("mod.rs"); + vec![file_candidate, Syntax::path_to_string(&mod_candidate)] +} + +fn crate_root_file_for(from_path: &str, known_files: &BTreeSet) -> Option { + let source_dir = crate_source_dir_for(from_path, known_files)?; + crate_root_file_in_source_dir(&source_dir, known_files) +} + +fn crate_source_dir_for(from_path: &str, known_files: &BTreeSet) -> Option { + let mut current = Path::new(from_path).parent(); + while let Some(directory) = current { + let source_dir = Syntax::path_to_string(directory); + if crate_root_file_in_source_dir(&source_dir, known_files).is_some() { + return Some(source_dir); + } + current = directory.parent(); + } + None +} + +fn crate_root_file_in_source_dir( + source_dir: &str, + known_files: &BTreeSet, +) -> Option { + let lib = join_path(source_dir, "lib.rs"); + if known_files.contains(&lib) { + return Some(lib); + } + let main = join_path(source_dir, "main.rs"); + if known_files.contains(&main) { + return Some(main); + } + None +} + +fn module_dir_for_path(path: &str) -> String { + let path = Path::new(path); + let parent = path + .parent() + .map(Syntax::path_to_string) + .unwrap_or_default(); + let Some(stem) = path + .file_stem() + .map(|stem| stem.to_string_lossy().to_string()) + else { + return parent; + }; + if stem == "lib" || stem == "main" || stem == "mod" { + return parent; + } + join_path(&parent, &stem) +} + +fn parent_module_dir_for_path(path: &str) -> Option { + Path::new(&module_dir_for_path(path)) + .parent() + .map(Syntax::path_to_string) +} + +fn join_path(parent: &str, child: &str) -> String { + if parent.is_empty() { + child.to_string() + } else { + format!("{parent}/{child}") + } +} diff --git a/crates/graph-core/src/extraction/facts/rust/references.rs b/crates/graph-core/src/extraction/facts/rust/references.rs new file mode 100644 index 0000000..01a61e9 --- /dev/null +++ b/crates/graph-core/src/extraction/facts/rust/references.rs @@ -0,0 +1,60 @@ +use super::*; + +pub(super) struct References; + +pub(super) trait RustReferences { + fn collect(from: String, block: &syn::Block, is_test: bool) -> Vec; +} + +impl RustReferences for References { + fn collect(from: String, block: &syn::Block, is_test: bool) -> Vec { + let mut visitor = RustReferenceVisitor { + from, + is_test, + references: Vec::new(), + }; + visitor.visit_block(block); + visitor.references + } +} + +struct RustReferenceVisitor { + from: String, + is_test: bool, + references: Vec, +} + +impl RustReferenceVisitor { + fn push_path_reference(&mut self, path: &SynPath) { + if let Some(name) = Syntax::reference_name_for_path(path) { + self.references.push(ReferenceFact { + from: self.from.clone(), + name, + is_test: self.is_test, + }); + } + } +} + +impl<'ast> Visit<'ast> for RustReferenceVisitor { + fn visit_expr_call(&mut self, node: &'ast ExprCall) { + if let syn::Expr::Path(path) = node.func.as_ref() { + self.push_path_reference(&path.path); + } + visit::visit_expr_call(self, node); + } + + fn visit_expr_method_call(&mut self, node: &'ast ExprMethodCall) { + self.references.push(ReferenceFact { + from: self.from.clone(), + name: node.method.to_string(), + is_test: self.is_test, + }); + visit::visit_expr_method_call(self, node); + } + + fn visit_expr_macro(&mut self, node: &'ast ExprMacro) { + self.push_path_reference(&node.mac.path); + visit::visit_expr_macro(self, node); + } +} diff --git a/crates/graph-core/src/extraction/facts/rust/syntax.rs b/crates/graph-core/src/extraction/facts/rust/syntax.rs new file mode 100644 index 0000000..86868a1 --- /dev/null +++ b/crates/graph-core/src/extraction/facts/rust/syntax.rs @@ -0,0 +1,184 @@ +use super::*; + +pub(super) struct ModuleScope { + pub(super) parent: String, + pub(super) name: String, + pub(super) is_test: bool, +} + +pub(super) struct Syntax; + +pub(super) trait RustSyntax { + fn is_exported(visibility: &Visibility) -> bool; + fn has_test_attr(attributes: &[Attribute]) -> bool; + fn has_cfg_test(attributes: &[Attribute]) -> bool; + fn signature_for_module(item: &ItemMod) -> String; + fn signature_for_function(item: &ItemFn) -> String; + fn signature_for_method(item: &syn::ImplItemFn) -> String; + fn signature_for_item(item: &impl ToTokens) -> String; + fn visibility_tokens(visibility: &Visibility) -> String; + fn type_name(ty: &Type) -> Option; + fn path_last_segment(path: &SynPath) -> Option; + fn impl_name(trait_name: Option<&str>, self_type: &str) -> String; + fn reference_name_for_path(path: &SynPath) -> Option; + fn base_attributes( + exported: bool, + qualified_name: &str, + signature: Option, + span: Option, + ) -> Value; + fn attributes_object(value: &mut Value) -> &mut serde_json::Map; + fn path_to_string(path: impl AsRef) -> String; +} + +impl RustSyntax for Syntax { + fn is_exported(visibility: &Visibility) -> bool { + !matches!(visibility, Visibility::Inherited) + } + + fn has_test_attr(attributes: &[Attribute]) -> bool { + attributes + .iter() + .any(|attribute| attribute.path().is_ident("test")) + } + + fn has_cfg_test(attributes: &[Attribute]) -> bool { + attributes.iter().any(|attribute| { + attribute.path().is_ident("cfg") + && attribute + .meta + .to_token_stream() + .to_string() + .contains("test") + }) + } + + fn signature_for_module(item: &ItemMod) -> String { + format!("{}mod {}", Self::visibility_tokens(&item.vis), item.ident) + .trim() + .to_string() + } + + fn signature_for_function(item: &ItemFn) -> String { + format!( + "{}{}", + Self::visibility_tokens(&item.vis), + item.sig.to_token_stream() + ) + .trim() + .to_string() + } + + fn signature_for_method(item: &syn::ImplItemFn) -> String { + format!( + "{}{}", + Self::visibility_tokens(&item.vis), + item.sig.to_token_stream() + ) + .trim() + .to_string() + } + + fn signature_for_item(item: &impl ToTokens) -> String { + item.to_token_stream().to_string() + } + + fn visibility_tokens(visibility: &Visibility) -> String { + let tokens = visibility.to_token_stream().to_string(); + if tokens.is_empty() { + tokens + } else { + format!("{tokens} ") + } + } + + fn type_name(ty: &Type) -> Option { + match ty { + Type::Path(path) => Self::path_last_segment(&path.path), + _ => None, + } + } + + fn path_last_segment(path: &SynPath) -> Option { + path.segments + .last() + .map(|segment| segment.ident.to_string()) + } + + fn impl_name(trait_name: Option<&str>, self_type: &str) -> String { + match trait_name { + Some(trait_name) => format!("impl {trait_name} for {self_type}"), + None => format!("impl {self_type}"), + } + } + + fn reference_name_for_path(path: &SynPath) -> Option { + let parts = path + .segments + .iter() + .map(|segment| segment.ident.to_string()) + .collect::>(); + if parts.is_empty() { + return None; + } + if parts + .iter() + .any(|part| part == "self" || part == "Self" || part == "super" || part == "crate") + { + return None; + } + let separator = if parts.len() > 1 { "." } else { "" }; + if separator.is_empty() { + parts.first().cloned() + } else { + Some(parts.join(separator)) + } + } + + fn base_attributes( + exported: bool, + qualified_name: &str, + signature: Option, + span: Option, + ) -> Value { + let mut attributes = serde_json::Map::new(); + attributes.insert("language".to_string(), Value::String("rust".to_string())); + attributes.insert("exported".to_string(), Value::Bool(exported)); + attributes.insert( + "qualifiedName".to_string(), + Value::String(qualified_name.to_string()), + ); + if exported { + attributes.insert("exportKind".to_string(), Value::String("named".to_string())); + attributes.insert( + "exportName".to_string(), + Value::String(qualified_name.to_string()), + ); + } + if let Some(signature) = signature { + attributes.insert("signature".to_string(), Value::String(signature)); + } + if let Some(span) = span { + let start = span.start(); + let end = span.end(); + attributes.insert("lineStart".to_string(), json!(start.line)); + attributes.insert("lineEnd".to_string(), json!(end.line)); + attributes.insert("columnStart".to_string(), json!(start.column)); + attributes.insert("columnEnd".to_string(), json!(end.column)); + } + Value::Object(attributes) + } + + fn attributes_object(value: &mut Value) -> &mut serde_json::Map { + loop { + if let Value::Object(object) = value { + return object; + } + *value = Value::Object(serde_json::Map::new()); + } + } + + fn path_to_string(path: impl AsRef) -> String { + path.as_ref().to_string_lossy().replace('\\', "/") + } +} diff --git a/crates/graph-core/src/extraction/mod.rs b/crates/graph-core/src/extraction/mod.rs index 084b83a..84267fc 100644 --- a/crates/graph-core/src/extraction/mod.rs +++ b/crates/graph-core/src/extraction/mod.rs @@ -16,7 +16,8 @@ use crate::protocol::{ use crate::{GRAPH_PROVIDER_NAME, GRAPH_SCHEMA_VERSION}; use diagnostics::has_error; use serde::{Deserialize, Serialize}; -use std::path::PathBuf; +use std::collections::BTreeMap; +use std::path::{Component, Path, PathBuf}; use time::format_description::well_known::Rfc3339; use time::OffsetDateTime; @@ -112,6 +113,42 @@ pub fn source_file_hashes(sources: &[DiscoveredSource]) -> Vec { .collect() } +pub(crate) struct SourcePaths; + +pub(crate) trait SourcePathOps { + fn source_hashes_by_path(hashes: &[SourceFileHash]) -> BTreeMap<&str, &str>; + fn normalize_relative_path(path: &Path) -> Result; +} + +impl SourcePathOps for SourcePaths { + fn source_hashes_by_path(hashes: &[SourceFileHash]) -> BTreeMap<&str, &str> { + hashes + .iter() + .map(|hash| (hash.relative_path.as_str(), hash.sha256.as_str())) + .collect() + } + + fn normalize_relative_path(path: &Path) -> Result { + let mut parts = Vec::new(); + for component in path.components() { + match component { + Component::CurDir => {} + Component::Normal(part) => parts.push(part.to_string_lossy().to_string()), + Component::ParentDir => { + if parts.pop().is_none() { + return Err(()); + } + } + Component::RootDir | Component::Prefix(_) => return Err(()), + } + } + if parts.is_empty() { + return Err(()); + } + Ok(parts.join("/")) + } +} + fn load_tsconfig_if_ready( repo_root: &std::path::Path, diagnostics: &mut Vec, diff --git a/crates/graph-core/src/extraction/python_imports.rs b/crates/graph-core/src/extraction/python_imports.rs index 59499fb..59c76db 100644 --- a/crates/graph-core/src/extraction/python_imports.rs +++ b/crates/graph-core/src/extraction/python_imports.rs @@ -1,8 +1,9 @@ use super::diagnostics::{error, warning}; use super::tsconfig::ImportResolution; +use super::{SourcePathOps, SourcePaths}; use crate::protocol::GraphExtractionDiagnosticCategory; use std::collections::BTreeSet; -use std::path::{Component, Path, PathBuf}; +use std::path::{Path, PathBuf}; pub fn resolve_import( specifier: &str, @@ -93,7 +94,7 @@ fn resolve_path_candidate( candidate: PathBuf, known_files: &BTreeSet, ) -> Result, ()> { - let normalized = normalize_relative(&candidate)?; + let normalized = SourcePaths::normalize_relative_path(&candidate)?; Ok(resolve_module_suffix(&normalized, known_files)) } @@ -186,26 +187,6 @@ fn unresolved_import(specifier: &str, from_path: &str) -> ImportResolution { } } -fn normalize_relative(path: &Path) -> Result { - let mut parts = Vec::new(); - for component in path.components() { - match component { - Component::CurDir => {} - Component::Normal(part) => parts.push(part.to_string_lossy().to_string()), - Component::ParentDir => { - if parts.pop().is_none() { - return Err(()); - } - } - Component::RootDir | Component::Prefix(_) => return Err(()), - } - } - if parts.is_empty() { - return Err(()); - } - Ok(parts.join("/")) -} - fn is_python_source_path(path: &str) -> bool { path.ends_with(".py") || path.ends_with(".pyi") } diff --git a/crates/graph-core/src/extraction/tests.rs b/crates/graph-core/src/extraction/tests.rs index 8b1abbd..3725ca0 100644 --- a/crates/graph-core/src/extraction/tests.rs +++ b/crates/graph-core/src/extraction/tests.rs @@ -1,1760 +1,22 @@ -use super::{ - discover_sources_for_options, extract_sources, DiscoveryResult, ExtractionOptions, - ExtractionResult, -}; -use crate::protocol::{ - GraphExtractionDiagnosticCategory as Category, GraphExtractionDiagnosticSeverity as Severity, - GraphFactNode, -}; -use serde_json::{json, Value}; -use std::fs; -use std::path::PathBuf; -use tempfile::TempDir; - -type TestResult = Result<(), Box>; - -#[test] -fn wave1_fixture_extracts_contract_facts() -> TestResult { - let fixture_root = wave1_fixture_root()?; - let expected: Value = serde_json::from_str(&fs::read_to_string( - fixture_root.join("wave1.expected.json"), - )?)?; - - let result = extract_sources(ExtractionOptions::new(&fixture_root)); - - assert!( - !result - .diagnostics - .iter() - .any(|diagnostic| diagnostic.severity == Severity::Error), - "{:?}", - result.diagnostics - ); - assert_eq!( - sorted(result.nodes.iter().map(|node| node.id.clone()).collect()), - value_strings(&expected, "nodeIds")? - ); - assert_eq!( - sorted(result.metadata.node_kinds), - value_strings(&expected, "nodeKinds")? - ); - assert_eq!( - sorted(result.metadata.edge_kinds), - value_strings(&expected, "edgeKinds")? - ); - assert_eq!( - edge_triples(&result.edges), - value_triples(&expected, "edgeTriples")? - ); - assert_eq!( - node_attributes(&result.nodes), - value_object(&expected, "nodeAttributes")? - ); - assert_eq!( - file_exports(&result.nodes), - value_object(&expected, "fileExports")? - ); - Ok(()) -} - -#[test] -fn export_metadata_marks_supported_ts_js_declarations() -> TestResult { - let repo = repo_with_tsconfig()?; - write_export_metadata_fixture(&repo)?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - - assert!( - !result - .diagnostics - .iter() - .any(|diagnostic| diagnostic.severity == Severity::Error), - "{:?}", - result.diagnostics - ); - assert_exported_symbol_attributes(&result.nodes)?; - assert_non_exported_symbol_attributes(&result.nodes)?; - assert_index_file_export_metadata(&result.nodes)?; - Ok(()) -} - -#[test] -fn import_backed_barrel_exports_are_unsupported_reexport_metadata() -> TestResult { - let repo = repo_with_tsconfig()?; - write( - &repo, - "src/source.ts", - "export default function inner() { return 1; }\nexport const named = 1;", - )?; - write( - &repo, - "src/barrel.ts", - "import inner, { named } from './source';\nexport { named };\nexport default inner;", - )?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - let exports = required_exports(&result.nodes, "file:src/barrel.ts")?; - - for expected in [ - json!({"kind": "named", "local": "named", "exported": "named", "source": "./source", "imported": "named", "supportedSymbol": false}), - json!({"kind": "default", "local": "inner", "exported": "default", "source": "./source", "imported": "default", "supportedSymbol": false}), - ] { - assert!(exports.contains(&expected), "{expected}"); - } - assert_missing_node(&result.nodes, "variable:src/barrel.ts#named")?; - assert_missing_node(&result.nodes, "function:src/barrel.ts#inner")?; - Ok(()) -} - -#[test] -fn unresolved_local_exports_are_unsupported_file_metadata() -> TestResult { - let repo = repo_with_tsconfig()?; - write( - &repo, - "src/index.ts", - "export { missing as renamed };\nfunction internal(){return 1;}\n", - )?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - let exports = required_exports(&result.nodes, "file:src/index.ts")?; - - assert!(exports.contains(&json!({ - "kind": "named", - "local": "missing", - "exported": "renamed", - "source": null, - "supportedSymbol": false - }))); - assert_eq!( - required_attributes(&result.nodes, "function:src/index.ts#internal")?, - json!({"exported": false}) - ); - assert_missing_node(&result.nodes, "function:src/index.ts#missing")?; - Ok(()) -} - -#[test] -fn nested_local_exports_are_unsupported_file_metadata() -> TestResult { - let repo = repo_with_tsconfig()?; - write( - &repo, - "src/index.ts", - "export { laterNested as exportedLaterNested };\nfunction container(){ function laterNested(){ return 1; } return laterNested(); }\n", - )?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - let exports = required_exports(&result.nodes, "file:src/index.ts")?; - - assert!(exports.contains(&json!({ - "kind": "named", - "local": "laterNested", - "exported": "exportedLaterNested", - "source": null, - "supportedSymbol": false - }))); - assert_eq!( - required_attributes(&result.nodes, "function:src/index.ts#laterNested")?, - json!({"exported": false}) - ); - Ok(()) -} - -fn write_export_metadata_fixture(repo: &TempDir) -> TestResult { - write_export_metadata_index_fixture(repo)?; - write_export_metadata_supporting_modules(repo)?; - write_export_metadata_default_modules(repo)?; - write_export_metadata_jsx_modules(repo)?; - Ok(()) -} - -fn write_export_metadata_index_fixture(repo: &TempDir) -> TestResult { - write( - repo, - "src/index.ts", - r#" - export interface Renderable { render(): string; } - export type Payload = { label: string }; - export class ExportedClass implements Renderable { render() { return "ok"; } } - class InternalClass {} - export function exportedFunction() { return new ExportedClass(); } - function internalFunction() { return new InternalClass(); } - export const exportedValue = 1; - const internalValue = 2; - export const exportedArrow = () => internalFunction(); - const internalArrow = () => exportedFunction(); - export function exportedWithNested() { - function nestedLocal() { return 1; } - return nestedLocal(); - } - export { laterNested as exportedLaterNested }; - function container() { - function laterNested() { return 1; } - return laterNested(); - } - const aliasTarget = 3; - export { aliasTarget as renamedAlias }; - export { externalThing as renamedExternal } from "./external"; - export { default as externalDefault } from "./defaulted"; - export * from "./barrel"; - export * as namespaceExport from "./namespace"; - const defaultValue = exportedValue; - export default defaultValue; - "#, - )?; - Ok(()) -} - -fn write_export_metadata_supporting_modules(repo: &TempDir) -> TestResult { - write(repo, "src/external.ts", "export const externalThing = 1;")?; - write( - repo, - "src/defaulted.ts", - "export default function defaulted() { return 1; }", - )?; - write(repo, "src/barrel.ts", "export const barrelValue = 1;")?; - write(repo, "src/namespace.ts", "export const namespaced = 1;")?; - Ok(()) -} - -fn write_export_metadata_default_modules(repo: &TempDir) -> TestResult { - write( - repo, - "src/default-function.ts", - "export default function defaultFunction() { return 1; }", - )?; - write( - repo, - "src/default-class.ts", - "export default class DefaultClass {}", - )?; - write( - repo, - "src/default-class-with-method.ts", - r#" - export default class DefaultClassWithMethod { - render() { return "ok"; } - } - "#, - )?; - write( - repo, - "src/default-interface.ts", - "export default interface DefaultInterface {}", - )?; - Ok(()) -} - -fn write_export_metadata_jsx_modules(repo: &TempDir) -> TestResult { - write( - repo, - "src/js-cases.js", - r#" - export function jsFunction() { return jsValue; } - export const jsValue = 1; - const jsInternal = 2; - "#, - )?; - write( - repo, - "src/view.tsx", - r#" - export function View() { return
; } - export const TsxArrow = () => ; - "#, - )?; - write( - repo, - "src/widget.jsx", - r#" - export default function Widget() { return
; } - export const WidgetHelper = () => ; - "#, - )?; - Ok(()) -} - -fn assert_exported_symbol_attributes(nodes: &[GraphFactNode]) -> TestResult { - for (id, expected) in [ - ( - "function:src/index.ts#exportedFunction", - json!({"exported": true, "exportKind": "named", "exportName": "exportedFunction"}), - ), - ( - "class:src/index.ts#ExportedClass", - json!({"exported": true, "exportKind": "named", "exportName": "ExportedClass"}), - ), - ( - "type:src/index.ts#Renderable", - json!({"exported": true, "exportKind": "named", "exportName": "Renderable"}), - ), - ( - "type:src/index.ts#Payload", - json!({"exported": true, "exportKind": "named", "exportName": "Payload"}), - ), - ( - "variable:src/index.ts#exportedValue", - json!({"exported": true, "exportKind": "named", "exportName": "exportedValue"}), - ), - ( - "function:src/index.ts#exportedArrow", - json!({"exported": true, "exportKind": "named", "exportName": "exportedArrow"}), - ), - ( - "function:src/index.ts#exportedWithNested", - json!({"exported": true, "exportKind": "named", "exportName": "exportedWithNested"}), - ), - ( - "variable:src/index.ts#aliasTarget", - json!({"exported": true, "exportKind": "named", "exportName": "renamedAlias"}), - ), - ( - "function:src/default-function.ts#defaultFunction", - json!({"exported": true, "exportKind": "default", "exportName": "default"}), - ), - ( - "class:src/default-class.ts#DefaultClass", - json!({"exported": true, "exportKind": "default", "exportName": "default"}), - ), - ( - "class:src/default-class-with-method.ts#DefaultClassWithMethod", - json!({"exported": true, "exportKind": "default", "exportName": "default"}), - ), - ( - "type:src/default-interface.ts#DefaultInterface", - json!({"exported": true, "exportKind": "default", "exportName": "default"}), - ), - ( - "function:src/js-cases.js#jsFunction", - json!({"exported": true, "exportKind": "named", "exportName": "jsFunction"}), - ), - ( - "variable:src/js-cases.js#jsValue", - json!({"exported": true, "exportKind": "named", "exportName": "jsValue"}), - ), - ( - "function:src/view.tsx#View", - json!({"exported": true, "exportKind": "named", "exportName": "View"}), - ), - ( - "function:src/view.tsx#TsxArrow", - json!({"exported": true, "exportKind": "named", "exportName": "TsxArrow"}), - ), - ( - "function:src/widget.jsx#Widget", - json!({"exported": true, "exportKind": "default", "exportName": "default"}), - ), - ( - "function:src/widget.jsx#WidgetHelper", - json!({"exported": true, "exportKind": "named", "exportName": "WidgetHelper"}), - ), - ] { - assert_eq!(required_attributes(nodes, id)?, expected, "{id}"); - } - Ok(()) -} - -fn assert_non_exported_symbol_attributes(nodes: &[GraphFactNode]) -> TestResult { - for id in [ - "class:src/index.ts#InternalClass", - "function:src/index.ts#internalFunction", - "variable:src/index.ts#internalValue", - "function:src/index.ts#internalArrow", - "function:src/index.ts#container", - "function:src/index.ts#nestedLocal", - "function:src/index.ts#laterNested", - "variable:src/js-cases.js#jsInternal", - ] { - assert_eq!( - required_attributes(nodes, id)?, - json!({"exported": false}), - "{id}" - ); - } - assert_missing_node(nodes, "function:src/default-class-with-method.ts#default")?; - Ok(()) -} - -fn assert_index_file_export_metadata(nodes: &[GraphFactNode]) -> TestResult { - let index_exports = required_exports(nodes, "file:src/index.ts")?; - for expected in [ - json!({"kind": "named", "local": "externalThing", "exported": "renamedExternal", "source": "./external", "imported": "externalThing", "supportedSymbol": true}), - json!({"kind": "named", "local": "default", "exported": "externalDefault", "source": "./defaulted", "imported": "default", "supportedSymbol": true}), - json!({"kind": "all", "exported": "*", "source": "./barrel", "supportedSymbol": false}), - json!({"kind": "namespace", "exported": "namespaceExport", "source": "./namespace", "supportedSymbol": false}), - json!({"kind": "default", "local": "defaultValue", "exported": "default", "source": null, "supportedSymbol": true}), - json!({"kind": "named", "local": "laterNested", "exported": "exportedLaterNested", "source": null, "supportedSymbol": false}), - ] { - assert!(index_exports.contains(&expected), "{expected}"); - } - Ok(()) -} - -#[test] -fn tsconfig_path_aliases_resolve_to_repo_relative_files() -> TestResult { - let result = extract_sources(ExtractionOptions::new(wave1_fixture_root()?)); - let triples = edge_triples(&result.edges); - - assert!(triples.contains(&vec![ - "IMPORTS_FROM".to_string(), - "file:src/__tests__/greeting.test.ts".to_string(), - "file:src/math.js".to_string() - ])); - assert!(triples.contains(&vec![ - "IMPORTS_FROM".to_string(), - "file:src/legacy-widget.jsx".to_string(), - "file:src/components/GreetingCard.tsx".to_string() - ])); - Ok(()) -} - -#[test] -fn unimported_cross_file_symbols_do_not_create_edges() -> TestResult { - let repo = temp_repo()?; - write( - &repo, - "tsconfig.json", - r#"{"compilerOptions":{"baseUrl":"."}}"#, - )?; - write( - &repo, - "src/a.ts", - r#" - export function caller() { return target(); } - export class Child extends Base implements Shape {} - "#, - )?; - write( - &repo, - "src/b.ts", - r#" - export function target() { return 1; } - export class Base {} - export interface Shape {} - "#, - )?; - write( - &repo, - "src/c.ts", - r#" - export function localCaller() { return sameName(); } - export function sameName() { return 1; } - "#, - )?; - write( - &repo, - "src/d.ts", - "export function sameName() { return 2; }", - )?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - let triples = edge_triples(&result.edges); - - assert!(!result - .diagnostics - .iter() - .any(|diagnostic| diagnostic.severity == Severity::Error)); - assert!(!triples.contains(&vec![ - "CALLS".to_string(), - "function:src/a.ts#caller".to_string(), - "function:src/b.ts#target".to_string() - ])); - assert!(!triples.contains(&vec![ - "INHERITS".to_string(), - "class:src/a.ts#Child".to_string(), - "class:src/b.ts#Base".to_string() - ])); - assert!(!triples.contains(&vec![ - "IMPLEMENTS".to_string(), - "class:src/a.ts#Child".to_string(), - "type:src/b.ts#Shape".to_string() - ])); - assert!(triples.contains(&vec![ - "CALLS".to_string(), - "function:src/c.ts#localCaller".to_string(), - "function:src/c.ts#sameName".to_string() - ])); - Ok(()) -} - -#[test] -fn default_imports_resolve_to_default_exported_symbols() -> TestResult { - let repo = repo_with_tsconfig()?; - write( - &repo, - "src/default-function.ts", - "export default function usedDefault() { return 1; }", - )?; - write( - &repo, - "src/default-value.ts", - "const usedValue = () => 1; export default usedValue;", - )?; - write( - &repo, - "src/index.ts", - r#" - import usedDefault from "./default-function"; - import usedValue from "./default-value"; - export function run() { - return usedDefault() + usedValue(); - } - "#, - )?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - let triples = edge_triples(&result.edges); - - assert!(!result - .diagnostics - .iter() - .any(|diagnostic| diagnostic.severity == Severity::Error)); - assert!(triples.contains(&vec![ - "CALLS".to_string(), - "function:src/index.ts#run".to_string(), - "function:src/default-function.ts#usedDefault".to_string() - ])); - assert!(triples.contains(&vec![ - "CALLS".to_string(), - "function:src/index.ts#run".to_string(), - "function:src/default-value.ts#usedValue".to_string() - ])); - Ok(()) -} - -#[test] -fn named_export_alias_imports_resolve_to_local_exported_symbols() -> TestResult { - let repo = repo_with_tsconfig()?; - write( - &repo, - "src/dep.ts", - "function localName() { return 1; }\nexport { localName as publicName };", - )?; - write( - &repo, - "src/index.ts", - r#" - import { publicName } from "./dep"; - export function run() { - return publicName(); - } - "#, - )?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - let triples = edge_triples(&result.edges); - - assert!(!result - .diagnostics - .iter() - .any(|diagnostic| diagnostic.severity == Severity::Error)); - assert!(triples.contains(&vec![ - "CALLS".to_string(), - "function:src/index.ts#run".to_string(), - "function:src/dep.ts#localName".to_string() - ])); - Ok(()) -} - -#[test] -fn source_re_export_alias_imports_resolve_to_source_exported_symbols() -> TestResult { - let repo = repo_with_tsconfig()?; - write( - &repo, - "src/source.ts", - "export function add() { return 1; }", - )?; - write( - &repo, - "src/barrel.ts", - "export { add as addFromBarrel } from './source';", - )?; - write( - &repo, - "src/index.ts", - r#" - import { addFromBarrel } from "./barrel"; - export function run() { - return addFromBarrel(); - } - "#, - )?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - let triples = edge_triples(&result.edges); - let exports = required_exports(&result.nodes, "file:src/barrel.ts")?; - - assert!( - !result - .diagnostics - .iter() - .any(|diagnostic| diagnostic.severity == Severity::Error), - "{:?}", - result.diagnostics - ); - assert!(triples.contains(&vec![ - "CALLS".to_string(), - "function:src/index.ts#run".to_string(), - "function:src/source.ts#add".to_string() - ])); - assert!(exports.contains(&json!({ - "kind": "named", - "local": "add", - "exported": "addFromBarrel", - "source": "./source", - "imported": "add", - "supportedSymbol": true - }))); - Ok(()) -} - -#[test] -fn oxc_parse_errors_are_typed_warnings_and_non_fatal() -> TestResult { - let repo = repo_with_tsconfig()?; - write(&repo, "src/broken.ts", "export function broken(")?; - write( - &repo, - "src/valid.ts", - "export function valid() { return 1; }", - )?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - - assert!( - result - .diagnostics - .iter() - .any(|diagnostic| diagnostic.category == Category::ParseError - && diagnostic.severity == Severity::Warning), - "{:?}", - result.diagnostics - ); - assert!( - !result - .diagnostics - .iter() - .any(|diagnostic| diagnostic.category == Category::ParseError - && diagnostic.severity == Severity::Error), - "{:?}", - result.diagnostics - ); - required_attributes(&result.nodes, "file:src/broken.ts")?; - required_attributes(&result.nodes, "function:src/valid.ts#valid")?; - Ok(()) -} - -#[test] -fn missing_parser_errors_are_typed_and_block_empty_success() -> TestResult { - let repo = repo_with_tsconfig()?; - write(&repo, "src/a.ts", "export function a() { return 1; }")?; - let mut options = ExtractionOptions::new(repo.path()); - options.force_missing_parser = true; - assert_error_category(extract_sources(options), Category::MissingParser); - Ok(()) -} - -#[test] -fn malformed_tsconfig_errors_are_typed_and_block_empty_success() -> TestResult { - let repo = temp_repo()?; - let malformed_json = char::from(123).to_string(); - write(&repo, "tsconfig.json", &malformed_json)?; - write(&repo, "src/a.ts", "export function a() { return 1; }")?; - assert_error_category( - extract_sources(ExtractionOptions::new(repo.path())), - Category::MalformedTsconfig, - ); - Ok(()) -} - -#[test] -fn malformed_tsconfig_paths_are_typed_and_block_empty_success() -> TestResult { - let repo = temp_repo()?; - write( - &repo, - "tsconfig.json", - r#"{"compilerOptions":{"baseUrl":".","paths":{"@bad/*":"src/*"}}}"#, - )?; - write( - &repo, - "src/a.ts", - "import { b } from '@bad/b'; export function a() { return b(); }", - )?; - write(&repo, "src/b.ts", "export function b() { return 1; }")?; - assert_error_category( - extract_sources(ExtractionOptions::new(repo.path())), - Category::MalformedTsconfig, - ); - Ok(()) -} - -#[test] -fn max_file_errors_are_typed_and_block_empty_success() -> TestResult { - let repo = repo_with_tsconfig()?; - write(&repo, "src/a.ts", "export function a() { return 1; }")?; - let mut options = ExtractionOptions::new(repo.path()); - options.max_files = 0; - assert_error_category(extract_sources(options), Category::MaxFilesExceeded); - Ok(()) -} - -#[test] -fn default_discovery_has_no_legacy_four_thousand_file_ceiling() -> TestResult { - let repo = repo_with_tsconfig()?; - for index in 0..4_001 { - write( - &repo, - &format!("src/generated/file_{index:04}.ts"), - "export const value = 1;\n", - )?; - } - - let discovery = discover_sources_for_options(&ExtractionOptions::new(repo.path())); - - assert_eq!(discovery.sources.len(), 4_001); - assert!( - !discovery - .diagnostics - .iter() - .any(|diagnostic| diagnostic.category == Category::MaxFilesExceeded), - "{:?}", - discovery.diagnostics - ); - Ok(()) -} - -#[test] -fn max_depth_errors_are_typed_and_block_empty_success() -> TestResult { - let repo = repo_with_tsconfig()?; - write(&repo, "src/deep/a.ts", "export function a() { return 1; }")?; - let mut options = ExtractionOptions::new(repo.path()); - options.max_depth = 1; - assert_error_category(extract_sources(options), Category::MaxDepthExceeded); - Ok(()) -} - -#[test] -fn path_traversal_errors_are_typed_and_block_empty_success() -> TestResult { - let repo = temp_repo()?; - write( - &repo, - "tsconfig.json", - r#"{"compilerOptions":{"baseUrl":".","paths":{"@outside/*":["../outside/*"]}}}"#, - )?; - write( - &repo, - "src/a.ts", - "import { out } from '@outside/out'; export function a() { return out(); }", - )?; - assert_error_category( - extract_sources(ExtractionOptions::new(repo.path())), - Category::PathTraversal, - ); - Ok(()) -} - -#[test] -fn unsupported_and_missing_tsconfig_are_typed_warnings() -> TestResult { - let repo = temp_repo()?; - write(&repo, "src/a.ts", "export function a() { return 1; }")?; - write(&repo, "src/view.vue", "")?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - - assert!(result - .diagnostics - .iter() - .any( - |diagnostic| diagnostic.category == Category::MissingTsconfig - && diagnostic.severity == Severity::Warning - )); - assert!(result - .diagnostics - .iter() - .any( - |diagnostic| diagnostic.category == Category::UnsupportedLanguage - && diagnostic.severity == Severity::Warning - )); - assert!(!result - .diagnostics - .iter() - .any(|diagnostic| diagnostic.severity == Severity::Error)); - assert!(!result.nodes.is_empty()); - Ok(()) -} - -#[test] -fn rust_sources_are_discovered_and_extracted() -> TestResult { - let repo = repo_with_tsconfig()?; - write_rust_graph_fixture(&repo)?; - - let discovery = discover_sources_for_options(&ExtractionOptions::new(repo.path())); - let result = extract_sources(ExtractionOptions::new(repo.path())); - - assert_rust_discovery_sources(&discovery); - assert_rust_extraction_nodes(&result); - assert_rust_extraction_edges(&result); - assert_rust_extraction_attributes(&result)?; - Ok(()) -} - -fn assert_rust_discovery_sources(discovery: &DiscoveryResult) { - let sources = discovery - .sources - .iter() - .map(|source| (source.relative_path.as_str(), source.language.as_str())) - .collect::>(); - assert_eq!( - sources, - vec![ - ("src/helpers.rs", "rust"), - ("src/lib.rs", "rust"), - ("src/user.rs", "rust") - ] - ); - assert!(!discovery - .diagnostics - .iter() - .any(|diagnostic| diagnostic.category == Category::UnsupportedLanguage)); -} - -fn assert_rust_extraction_nodes(result: &ExtractionResult) { - assert!( - !result - .diagnostics - .iter() - .any(|diagnostic| diagnostic.severity == Severity::Error), - "{:?}", - result.diagnostics - ); - let node_ids = sorted(result.nodes.iter().map(|node| node.id.clone()).collect()); - for id in [ - "file:src/lib.rs", - "module:src/lib.rs#crate", - "module:src/user.rs#user", - "module:src/user.rs#user.tests", - "struct:src/lib.rs#Widget", - "enum:src/lib.rs#Mode", - "trait:src/lib.rs#Service", - "impl:src/lib.rs#impl Service for Widget", - "method:src/lib.rs#Widget::handle", - "type:src/lib.rs#Alias", - "const:src/lib.rs#LIMIT", - "static:src/lib.rs#NAME", - "macro:src/lib.rs#trace", - "function:src/helpers.rs#helpers::assist", - "function:src/user.rs#user::run", - "test:src/user.rs#user.tests::test_run", - ] { - assert!(node_ids.contains(&id.to_string()), "{id}"); - } -} - -fn assert_rust_extraction_edges(result: &ExtractionResult) { - let triples = edge_triples(&result.edges); - for triple in rust_expected_edge_triples() { - assert!(triples.contains(&triple), "{triple:?}"); - } -} - -fn assert_rust_extraction_attributes(result: &ExtractionResult) -> TestResult { - assert_eq!( - required_attributes(&result.nodes, "struct:src/lib.rs#Widget")? - .get("exported") - .and_then(Value::as_bool), - Some(true) - ); - assert_eq!( - required_attributes(&result.nodes, "struct:src/lib.rs#Widget")? - .get("language") - .and_then(Value::as_str), - Some("rust") - ); - assert_rust_run_attributes(result)?; - assert!(result.metadata.node_kinds.contains(&"Struct".to_string())); - assert!(result - .metadata - .edge_kinds - .contains(&"IMPLEMENTS".to_string())); - Ok(()) -} - -fn assert_rust_run_attributes(result: &ExtractionResult) -> TestResult { - let attributes = required_attributes(&result.nodes, "function:src/user.rs#user::run")?; - assert_eq!( - attributes.get("qualifiedName").and_then(Value::as_str), - Some("user::run") - ); - assert!(attributes - .get("signature") - .and_then(Value::as_str) - .is_some_and(|signature| signature.starts_with("pub fn run"))); - assert!(attributes - .get("lineStart") - .and_then(Value::as_u64) - .is_some()); - Ok(()) -} - -fn rust_expected_edge_triples() -> Vec> { - [ - ("CONTAINS", "file:src/user.rs", "module:src/user.rs#user"), - ( - "CONTAINS", - "module:src/user.rs#user", - "function:src/user.rs#user::run", - ), - ( - "CONTAINS", - "module:src/user.rs#user.tests", - "test:src/user.rs#user.tests::test_run", - ), - ("IMPORTS_FROM", "file:src/user.rs", "file:src/helpers.rs"), - ( - "CALLS", - "function:src/user.rs#user::run", - "function:src/helpers.rs#helpers::assist", - ), - ( - "CALLS", - "test:src/user.rs#user.tests::test_run", - "function:src/user.rs#user::run", - ), - ( - "TESTED_BY", - "function:src/user.rs#user::run", - "test:src/user.rs#user.tests::test_run", - ), - ( - "IMPLEMENTS", - "impl:src/lib.rs#impl Service for Widget", - "trait:src/lib.rs#Service", - ), - ] - .into_iter() - .map(|(kind, from, to)| vec![kind.to_string(), from.to_string(), to.to_string()]) - .collect() -} - -#[test] -fn rust_crate_use_prefers_module_file_over_lib_declaration_stub() -> TestResult { - let repo = repo_with_tsconfig()?; - write(&repo, "src/lib.rs", "pub mod helpers;\nmod user;\n")?; - write(&repo, "src/helpers.rs", "pub fn assist() -> usize { 1 }\n")?; - write( - &repo, - "src/user.rs", - "use crate::helpers;\npub fn run() { helpers::assist(); }\n", - )?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - let triples = edge_triples(&result.edges); - - assert!(triples.contains(&vec![ - "IMPORTS_FROM".to_string(), - "file:src/user.rs".to_string(), - "file:src/helpers.rs".to_string() - ])); - assert!(!triples.contains(&vec![ - "IMPORTS_FROM".to_string(), - "file:src/user.rs".to_string(), - "file:src/lib.rs".to_string() - ])); - Ok(()) -} - -#[test] -fn rust_crate_use_resolves_within_nearest_workspace_crate() -> TestResult { - let repo = repo_with_tsconfig()?; - write( - &repo, - "crates/app/src/lib.rs", - "pub mod helpers;\nmod user;\n", - )?; - write( - &repo, - "crates/app/src/helpers.rs", - "pub fn assist() -> usize { 1 }\n", - )?; - write( - &repo, - "crates/app/src/user.rs", - "use crate::helpers;\npub fn run() { helpers::assist(); }\n", - )?; - write( - &repo, - "crates/other/src/lib.rs", - "pub mod helpers;\npub fn unrelated() {}\n", - )?; - write( - &repo, - "crates/other/src/helpers.rs", - "pub fn assist() -> usize { 2 }\n", - )?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - let triples = edge_triples(&result.edges); - - assert!(triples.contains(&vec![ - "IMPORTS_FROM".to_string(), - "file:crates/app/src/user.rs".to_string(), - "file:crates/app/src/helpers.rs".to_string() - ])); - assert!(!triples.contains(&vec![ - "IMPORTS_FROM".to_string(), - "file:crates/app/src/user.rs".to_string(), - "file:crates/other/src/helpers.rs".to_string() - ])); - Ok(()) -} - -#[test] -fn rust_parse_errors_are_typed_and_block_empty_success() -> TestResult { - let repo = repo_with_tsconfig()?; - write(&repo, "src/broken.rs", "pub fn broken(")?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - - assert_error_category(result.clone(), Category::ParseError); - assert!(result.nodes.is_empty(), "{:?}", result.nodes); - assert!(result.edges.is_empty(), "{:?}", result.edges); - Ok(()) -} - -#[test] -fn python_ast_extracts_contract_facts() -> TestResult { - let repo = repo_with_tsconfig()?; - write_python_graph_fixture(&repo)?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - let node_ids = sorted(result.nodes.iter().map(|node| node.id.clone()).collect()); - let triples = edge_triples(&result.edges); - - assert!( - !result - .diagnostics - .iter() - .any(|diagnostic| diagnostic.severity == Severity::Error), - "{:?}", - result.diagnostics - ); - for id in [ - "file:src/pkg/models.py", - "module:src/pkg/models.py#src.pkg.models", - "class:src/pkg/models.py#PublicModel", - "function:src/pkg/models.py#PublicModel.from_value", - "function:src/pkg/models.py#make_model", - "function:src/pkg/models.py#_hidden", - "variable:src/pkg/models.py#_private", - "function:tests/test_models.py#test_make_model", - "function:tests/test_models.py#TestPublicModel.test_render", - ] { - assert!(node_ids.contains(&id.to_string()), "{id}"); - } - for triple in [ - vec![ - "CONTAINS".to_string(), - "file:src/pkg/models.py".to_string(), - "module:src/pkg/models.py#src.pkg.models".to_string(), - ], - vec![ - "CONTAINS".to_string(), - "module:src/pkg/models.py#src.pkg.models".to_string(), - "class:src/pkg/models.py#PublicModel".to_string(), - ], - vec![ - "CONTAINS".to_string(), - "class:src/pkg/models.py#PublicModel".to_string(), - "function:src/pkg/models.py#PublicModel.from_value".to_string(), - ], - vec![ - "CALLS".to_string(), - "function:src/pkg/models.py#make_model".to_string(), - "class:src/pkg/models.py#PublicModel".to_string(), - ], - vec![ - "INHERITS".to_string(), - "class:src/pkg/models.py#PublicModel".to_string(), - "class:src/pkg/base.py#BaseModel".to_string(), - ], - vec![ - "TESTED_BY".to_string(), - "class:src/pkg/models.py#PublicModel".to_string(), - "function:tests/test_models.py#test_make_model".to_string(), - ], - ] { - assert!(triples.contains(&triple), "{triple:?}"); - } - assert!(result.metadata.node_kinds.contains(&"Module".to_string())); - Ok(()) -} - -#[test] -fn python_import_resolution_handles_absolute_relative_package_and_unresolved() -> TestResult { - let repo = repo_with_tsconfig()?; - write_python_graph_fixture(&repo)?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - let triples = edge_triples(&result.edges); - - for triple in [ - vec![ - "IMPORTS_FROM".to_string(), - "file:src/pkg/models.py".to_string(), - "file:src/pkg/base.py".to_string(), - ], - vec![ - "IMPORTS_FROM".to_string(), - "file:src/pkg/models.py".to_string(), - "file:src/pkg/helpers.py".to_string(), - ], - vec![ - "IMPORTS_FROM".to_string(), - "file:tests/test_models.py".to_string(), - "file:src/pkg/models.py".to_string(), - ], - vec![ - "IMPORTS_FROM".to_string(), - "file:tests/test_models.py".to_string(), - "file:src/pkg/__init__.py".to_string(), - ], - vec![ - "IMPORTS_FROM".to_string(), - "file:src/pkg/uses_stub.py".to_string(), - "file:src/pkg/stubs.pyi".to_string(), - ], - ] { - assert!(triples.contains(&triple), "{triple:?}"); - } - assert!(result.diagnostics.iter().any(|diagnostic| { - diagnostic.category == Category::UnresolvedImport - && diagnostic.severity == Severity::Warning - && diagnostic.path.as_deref() == Some("src/pkg/models.py") - })); - assert!(!result - .diagnostics - .iter() - .any(|diagnostic| diagnostic.severity == Severity::Error)); - Ok(()) -} - -#[test] -fn python_exports_are_best_effort_and_documented() -> TestResult { - let repo = repo_with_tsconfig()?; - write_python_graph_fixture(&repo)?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - - assert_eq!( - required_attributes(&result.nodes, "class:src/pkg/models.py#PublicModel")?, - json!({"decorators":[],"exportKind":"named","exportName":"PublicModel","exportPolicy":"__all__","exported":true,"isTest":false}) - ); - assert_eq!( - required_attributes(&result.nodes, "function:src/pkg/models.py#_hidden")?, - json!({"async":false,"decorators":[],"exportPolicy":"__all__","exported":false,"isTest":false}) - ); - assert_eq!( - required_attributes(&result.nodes, "function:src/pkg/helpers.py#build_name")?, - json!({"async":false,"decorators":[],"exportKind":"named","exportName":"build_name","exportPolicy":"underscore_convention","exported":true,"isTest":false}) - ); - let exports = required_exports(&result.nodes, "file:src/pkg/models.py")?; - for expected in [ - json!({"kind":"named","local":"PublicModel","exported":"PublicModel","source":null,"supportedSymbol":true,"policy":"__all__"}), - json!({"kind":"named","local":"make_model","exported":"make_model","source":null,"supportedSymbol":true,"policy":"__all__"}), - ] { - assert!(exports.contains(&expected), "{expected}"); - } - Ok(()) -} - -#[test] -fn python_module_level_all_wins_even_when_empty() -> TestResult { - let repo = repo_with_tsconfig()?; - write( - &repo, - "pkg/api.py", - r#" -__all__ = [] - -def exposed(): - return True - -class Public: - pass -"#, - )?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - - assert_eq!( - required_attributes(&result.nodes, "function:pkg/api.py#exposed")?, - json!({"async":false,"decorators":[],"exportPolicy":"__all__","exported":false,"isTest":false}) - ); - assert_eq!( - required_attributes(&result.nodes, "class:pkg/api.py#Public")?, - json!({"decorators":[],"exportPolicy":"__all__","exported":false,"isTest":false}) - ); - assert_eq!( - required_exports(&result.nodes, "file:pkg/api.py")?, - Vec::::new() - ); - Ok(()) -} - -#[test] -fn python_nested_all_does_not_control_module_exports() -> TestResult { - let repo = repo_with_tsconfig()?; - write( - &repo, - "pkg/api.py", - r#" -def leaked(): - __all__ = ["_hidden"] - return True - -def _hidden(): - return True -"#, - )?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - - assert_eq!( - required_attributes(&result.nodes, "function:pkg/api.py#leaked")?, - json!({"async":false,"decorators":[],"exportKind":"named","exportName":"leaked","exportPolicy":"underscore_convention","exported":true,"isTest":false}) - ); - assert_eq!( - required_attributes(&result.nodes, "function:pkg/api.py#_hidden")?, - json!({"async":false,"decorators":[],"exportPolicy":"underscore_convention","exported":false,"isTest":false}) - ); - assert_eq!( - required_exports(&result.nodes, "file:pkg/api.py")?, - vec![ - json!({"kind":"named","local":"leaked","exported":"leaked","source":null,"supportedSymbol":true,"policy":"underscore_convention"}) - ] - ); - Ok(()) -} - -#[test] -fn python_dotted_import_module_calls_resolve_to_exported_member() -> TestResult { - let repo = repo_with_tsconfig()?; - write( - &repo, - "pkg/sub.py", - r#" -def target(): - return True -"#, - )?; - write( - &repo, - "app.py", - r#" -import pkg.sub - -def run(): - return pkg.sub.target() -"#, - )?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - let triples = edge_triples(&result.edges); - - assert!(triples.contains(&vec![ - "IMPORTS_FROM".to_string(), - "file:app.py".to_string(), - "file:pkg/sub.py".to_string() - ])); - assert!(triples.contains(&vec![ - "CALLS".to_string(), - "function:app.py#run".to_string(), - "function:pkg/sub.py#target".to_string() - ])); - Ok(()) -} - -#[test] -fn python_package_from_import_submodule_calls_resolve_to_submodule_member() -> TestResult { - let repo = repo_with_tsconfig()?; - write(&repo, "pkg/__init__.py", "")?; - write( - &repo, - "pkg/mod.py", - r#" -def f(): - return True -"#, - )?; - write( - &repo, - "app.py", - r#" -from pkg import mod - -def g(): - return mod.f() -"#, - )?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - let triples = edge_triples(&result.edges); - - assert!(triples.contains(&vec![ - "IMPORTS_FROM".to_string(), - "file:app.py".to_string(), - "file:pkg/mod.py".to_string() - ])); - assert!(triples.contains(&vec![ - "CALLS".to_string(), - "function:app.py#g".to_string(), - "function:pkg/mod.py#f".to_string() - ])); - Ok(()) -} - -#[test] -fn python_parse_errors_are_typed_warnings_and_non_fatal() -> TestResult { - let repo = repo_with_tsconfig()?; - write(&repo, "src/broken.py", "def broken(:\n return True\n")?; - - let result = extract_sources(ExtractionOptions::new(repo.path())); - - assert!(result.diagnostics.iter().any(|diagnostic| { - diagnostic.category == Category::ParseError && diagnostic.severity == Severity::Warning - })); - assert!(!result - .diagnostics - .iter() - .any(|diagnostic| diagnostic.severity == Severity::Error)); - assert!(result - .nodes - .iter() - .any(|node| node.id == "file:src/broken.py")); - Ok(()) -} - -#[test] -fn python_sources_are_discovered_and_extracted() -> TestResult { - let repo = repo_with_tsconfig()?; - write(&repo, "src/app.ts", "export const app = true;\n")?; - write(&repo, "src/tool.py", "def run():\n return True\n")?; - write(&repo, "src/typings.pyi", "def run() -> bool: ...\n")?; - - let discovery = discover_sources_for_options(&ExtractionOptions::new(repo.path())); - let sources = discovery - .sources - .iter() - .map(|source| (source.relative_path.as_str(), source.language.as_str())) - .collect::>(); - assert_eq!( - sources, - vec![ - ("src/app.ts", "typescript"), - ("src/tool.py", "python"), - ("src/typings.pyi", "python") - ] - ); - assert!(!discovery - .diagnostics - .iter() - .any(|diagnostic| diagnostic.category == Category::UnsupportedLanguage)); - - let result = extract_sources(ExtractionOptions::new(repo.path())); - assert!(!result - .diagnostics - .iter() - .any(|diagnostic| diagnostic.severity == Severity::Error)); - assert_eq!( - sorted( - result - .file_hashes - .iter() - .map(|hash| format!("{}:{}", hash.relative_path, hash.language)) - .collect() - ), - vec![ - "src/app.ts:typescript".to_string(), - "src/tool.py:python".to_string(), - "src/typings.pyi:python".to_string() - ] - ); - assert!(result - .nodes - .iter() - .any(|node| node.id == "file:src/tool.py")); - assert!(result - .nodes - .iter() - .any(|node| node.id == "file:src/typings.pyi")); - Ok(()) -} - -#[test] -fn python_generated_private_and_dependency_paths_are_ignored() -> TestResult { - let repo = repo_with_tsconfig()?; - write(&repo, "src/app.ts", "export const app = true;\n")?; - write(&repo, "src/tool.py", "def run():\n return True\n")?; - for path in [ - ".venv/lib/python3.12/site-packages/pkg/ignored.py", - "venv/lib/python3.12/site-packages/pkg/ignored.py", - "env/lib/python3.12/site-packages/pkg/ignored.py", - ".agents/runtime/ignored.ts", - ".claude/runtime/ignored.ts", - ".codex/runtime/ignored.ts", - ".gemini/runtime/ignored.ts", - ".opencode/runtime/ignored.ts", - "src/__pycache__/ignored.py", - ".eggs/pkg/ignored.py", - "build/lib/ignored.py", - ".tox/py/ignored.py", - ".mypy_cache/ignored.py", - ".pytest_cache/ignored.py", - ".ruff_cache/ignored.py", - "pkg.egg-info/ignored.py", - "pkg.dist-info/ignored.py", - "lib/site-packages/pkg/ignored.py", - ] { - write(&repo, path, "def ignored():\n return True\n")?; - } - - let discovery = discover_sources_for_options(&ExtractionOptions::new(repo.path())); - let source_paths = sorted( - discovery - .sources - .iter() - .map(|source| source.relative_path.clone()) - .collect(), - ); - - assert_eq!(source_paths, vec!["src/app.ts", "src/tool.py"]); - assert!(!discovery - .diagnostics - .iter() - .any(|diagnostic| diagnostic.category == Category::UnsupportedLanguage)); - Ok(()) -} - -fn assert_error_category(result: super::ExtractionResult, category: Category) { - assert!( - result - .diagnostics - .iter() - .any(|diagnostic| diagnostic.category == category - && diagnostic.severity == Severity::Error), - "{:?}", - result.diagnostics - ); - assert!(result.nodes.is_empty()); - assert!(result.edges.is_empty()); -} - -fn wave1_fixture_root() -> Result { - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("../../packages/fixtures/source-extraction/wave1") - .canonicalize() -} - -fn temp_repo() -> Result { - tempfile::tempdir() -} - -fn repo_with_tsconfig() -> Result { - let repo = temp_repo()?; - write( - &repo, - "tsconfig.json", - r#"{"compilerOptions":{"baseUrl":"."}}"#, - )?; - Ok(repo) -} - -fn write_python_graph_fixture(repo: &TempDir) -> TestResult { - write_python_package_files(repo)?; - write_python_model_files(repo)?; - write_python_stub_and_tests(repo)?; - Ok(()) -} - -fn write_rust_graph_fixture(repo: &TempDir) -> TestResult { - write( - repo, - "src/lib.rs", - r#" -pub mod helpers; -mod user; - -pub trait Service { - fn handle(&self); -} - -pub struct Widget; - -pub enum Mode { - Fast, -} - -impl Service for Widget { - fn handle(&self) { - helpers::assist(); - } -} - -pub type Alias = Widget; -pub const LIMIT: usize = 1; -pub static NAME: &str = "widget"; - -macro_rules! trace { - () => {}; -} -"#, - )?; - write(repo, "src/helpers.rs", "pub fn assist() -> usize { 1 }\n")?; - write( - repo, - "src/user.rs", - r#" -use crate::helpers; -use crate::{Service, Widget}; - -pub fn run() { - helpers::assist(); - let widget = Widget; - widget.handle(); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_run() { - run(); - } -} -"#, - )?; - Ok(()) -} - -fn write_python_package_files(repo: &TempDir) -> TestResult { - write( - repo, - "src/pkg/__init__.py", - r#" -from .models import PublicModel - -PACKAGE_VALUE = PublicModel() -__all__ = ["PublicModel", "PACKAGE_VALUE"] -"#, - )?; - write( - repo, - "src/pkg/base.py", - r#" -class BaseModel: - pass -"#, - )?; - write( - repo, - "src/pkg/helpers.py", - r#" -def build_name(): - return "public" - "#, - )?; - Ok(()) -} - -fn write_python_model_files(repo: &TempDir) -> TestResult { - write( - repo, - "src/pkg/models.py", - r#" -from .base import BaseModel -from .helpers import build_name -from .missing import MissingLocal - -_private = 1 -__all__ = ["PublicModel", "make_model"] - -class PublicModel(BaseModel): - @classmethod - def from_value(cls): - return build_name() - - def render(self): - return build_name() - -def make_model(): - return PublicModel() - -def _hidden(): - return PublicModel() - "#, - )?; - Ok(()) -} - -fn write_python_stub_and_tests(repo: &TempDir) -> TestResult { - write(repo, "src/pkg/stubs.pyi", "def stubbed() -> str: ...\n")?; - write( - repo, - "src/pkg/uses_stub.py", - r#" -from .stubs import stubbed - -def call_stub(): - return stubbed() -"#, - )?; - write( - repo, - "tests/test_models.py", - r#" -from src.pkg import PACKAGE_VALUE -from src.pkg.models import PublicModel, make_model - -def test_make_model(): - make_model() - PublicModel.from_value() - return PACKAGE_VALUE - -class TestPublicModel: - def test_render(self): - return PublicModel().render() -"#, - )?; - Ok(()) -} - -fn write(repo: &TempDir, path: &str, contents: &str) -> Result<(), std::io::Error> { - let path = repo.path().join(path); - if let Some(parent) = path.parent() { - fs::create_dir_all(parent)?; - } - fs::write(path, contents) -} - -fn edge_triples(edges: &[crate::protocol::GraphFactEdge]) -> Vec> { - sorted( - edges - .iter() - .map(|edge| vec![edge.kind.clone(), edge.from.clone(), edge.to.clone()]) - .collect(), - ) -} - -fn value_strings(value: &Value, key: &str) -> Result, std::io::Error> { - let entries = value - .get(key) - .and_then(Value::as_array) - .ok_or_else(|| std::io::Error::other(format!("missing string array {key}")))?; - entries - .iter() - .map(|entry| { - entry - .as_str() - .map(ToString::to_string) - .ok_or_else(|| std::io::Error::other(format!("non-string entry in {key}"))) - }) - .collect::, _>>() - .map(sorted) -} - -fn value_triples(value: &Value, key: &str) -> Result>, std::io::Error> { - let entries = value - .get(key) - .and_then(Value::as_array) - .ok_or_else(|| std::io::Error::other(format!("missing triple array {key}")))?; - entries - .iter() - .map(|entry| { - let parts = entry - .as_array() - .ok_or_else(|| std::io::Error::other(format!("non-array triple in {key}")))?; - parts - .iter() - .map(|part| { - part.as_str().map(ToString::to_string).ok_or_else(|| { - std::io::Error::other(format!("non-string triple part in {key}")) - }) - }) - .collect::, _>>() - }) - .collect::, _>>() - .map(sorted) -} - -fn value_object(value: &Value, key: &str) -> Result { - value - .get(key) - .cloned() - .ok_or_else(|| std::io::Error::other(format!("missing object {key}"))) -} - -fn node_attributes(nodes: &[GraphFactNode]) -> Value { - let mut attributes = serde_json::Map::new(); - for node in nodes { - if node.kind == "File" { - continue; - } - attributes.insert( - node.id.clone(), - node.attributes.clone().unwrap_or_else(|| json!({})), - ); - } - Value::Object(attributes) -} - -fn file_exports(nodes: &[GraphFactNode]) -> Value { - let mut exports_by_file = serde_json::Map::new(); - for node in nodes { - if node.kind != "File" { - continue; - } - if let Some(exports) = node - .attributes - .as_ref() - .and_then(|attributes| attributes.get("exports")) - { - exports_by_file.insert(node.id.clone(), exports.clone()); - } - } - Value::Object(exports_by_file) -} - -fn required_attributes(nodes: &[GraphFactNode], id: &str) -> Result { - let node = nodes - .iter() - .find(|node| node.id == id) - .ok_or_else(|| std::io::Error::other(format!("missing node {id}")))?; - node.attributes - .clone() - .ok_or_else(|| std::io::Error::other(format!("missing attributes for {id}"))) -} - -fn required_exports(nodes: &[GraphFactNode], id: &str) -> Result, std::io::Error> { - let node = nodes - .iter() - .find(|node| node.id == id) - .ok_or_else(|| std::io::Error::other(format!("missing node {id}")))?; - node.attributes - .as_ref() - .and_then(|attributes| attributes.get("exports")) - .and_then(Value::as_array) - .cloned() - .ok_or_else(|| std::io::Error::other(format!("missing exports for {id}"))) -} - -fn assert_missing_node(nodes: &[GraphFactNode], id: &str) -> Result<(), std::io::Error> { - if nodes.iter().any(|node| node.id == id) { - Err(std::io::Error::other(format!("unexpected node {id}"))) - } else { - Ok(()) - } -} - -fn sorted(mut values: Vec) -> Vec { - values.sort(); - values -} +mod diagnostics; +mod python; +mod rust; +mod support; +mod typescript_exports; +mod typescript_resolution; + +use diagnostics::DiagnosticsTests; +use python::PythonTests; +use rust::RustTests; +use typescript_exports::TypeScriptExportTests; +use typescript_resolution::TypeScriptResolutionTests; + +type SplitTestModules = ( + DiagnosticsTests, + PythonTests, + RustTests, + TypeScriptExportTests, + TypeScriptResolutionTests, +); + +const _: usize = std::mem::size_of::(); diff --git a/crates/graph-core/src/extraction/tests/diagnostics.rs b/crates/graph-core/src/extraction/tests/diagnostics.rs new file mode 100644 index 0000000..5321be7 --- /dev/null +++ b/crates/graph-core/src/extraction/tests/diagnostics.rs @@ -0,0 +1,184 @@ +use super::support::*; +use crate::extraction::{discover_sources_for_options, extract_sources, ExtractionOptions}; +use crate::protocol::{ + GraphExtractionDiagnosticCategory as Category, GraphExtractionDiagnosticSeverity as Severity, +}; + +type TestResult = Result<(), Box>; + +#[cfg(test)] +pub(super) struct DiagnosticsTests; + +#[test] +fn oxc_parse_errors_are_typed_warnings_and_non_fatal() -> TestResult { + let repo = repo_with_tsconfig()?; + write(&repo, "src/broken.ts", "export function broken(")?; + write( + &repo, + "src/valid.ts", + "export function valid() { return 1; }", + )?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + + assert!( + result + .diagnostics + .iter() + .any(|diagnostic| diagnostic.category == Category::ParseError + && diagnostic.severity == Severity::Warning), + "{:?}", + result.diagnostics + ); + assert!( + !result + .diagnostics + .iter() + .any(|diagnostic| diagnostic.category == Category::ParseError + && diagnostic.severity == Severity::Error), + "{:?}", + result.diagnostics + ); + required_attributes(&result.nodes, "file:src/broken.ts")?; + required_attributes(&result.nodes, "function:src/valid.ts#valid")?; + Ok(()) +} + +#[test] +fn missing_parser_errors_are_typed_and_block_empty_success() -> TestResult { + let repo = repo_with_tsconfig()?; + write(&repo, "src/a.ts", "export function a() { return 1; }")?; + let mut options = ExtractionOptions::new(repo.path()); + options.force_missing_parser = true; + assert_error_category(extract_sources(options), Category::MissingParser); + Ok(()) +} + +#[test] +fn malformed_tsconfig_errors_are_typed_and_block_empty_success() -> TestResult { + let repo = temp_repo()?; + let malformed_json = char::from(123).to_string(); + write(&repo, "tsconfig.json", &malformed_json)?; + write(&repo, "src/a.ts", "export function a() { return 1; }")?; + assert_error_category( + extract_sources(ExtractionOptions::new(repo.path())), + Category::MalformedTsconfig, + ); + Ok(()) +} + +#[test] +fn malformed_tsconfig_paths_are_typed_and_block_empty_success() -> TestResult { + let repo = temp_repo()?; + write( + &repo, + "tsconfig.json", + r#"{"compilerOptions":{"baseUrl":".","paths":{"@bad/*":"src/*"}}}"#, + )?; + write( + &repo, + "src/a.ts", + "import { b } from '@bad/b'; export function a() { return b(); }", + )?; + write(&repo, "src/b.ts", "export function b() { return 1; }")?; + assert_error_category( + extract_sources(ExtractionOptions::new(repo.path())), + Category::MalformedTsconfig, + ); + Ok(()) +} + +#[test] +fn max_file_errors_are_typed_and_block_empty_success() -> TestResult { + let repo = repo_with_tsconfig()?; + write(&repo, "src/a.ts", "export function a() { return 1; }")?; + let mut options = ExtractionOptions::new(repo.path()); + options.max_files = 0; + assert_error_category(extract_sources(options), Category::MaxFilesExceeded); + Ok(()) +} + +#[test] +fn default_discovery_has_no_legacy_four_thousand_file_ceiling() -> TestResult { + let repo = repo_with_tsconfig()?; + for index in 0..4_001 { + write( + &repo, + &format!("src/generated/file_{index:04}.ts"), + "export const value = 1;\n", + )?; + } + + let discovery = discover_sources_for_options(&ExtractionOptions::new(repo.path())); + + assert_eq!(discovery.sources.len(), 4_001); + assert!( + !discovery + .diagnostics + .iter() + .any(|diagnostic| diagnostic.category == Category::MaxFilesExceeded), + "{:?}", + discovery.diagnostics + ); + Ok(()) +} + +#[test] +fn max_depth_errors_are_typed_and_block_empty_success() -> TestResult { + let repo = repo_with_tsconfig()?; + write(&repo, "src/deep/a.ts", "export function a() { return 1; }")?; + let mut options = ExtractionOptions::new(repo.path()); + options.max_depth = 1; + assert_error_category(extract_sources(options), Category::MaxDepthExceeded); + Ok(()) +} + +#[test] +fn path_traversal_errors_are_typed_and_block_empty_success() -> TestResult { + let repo = temp_repo()?; + write( + &repo, + "tsconfig.json", + r#"{"compilerOptions":{"baseUrl":".","paths":{"@outside/*":["../outside/*"]}}}"#, + )?; + write( + &repo, + "src/a.ts", + "import { out } from '@outside/out'; export function a() { return out(); }", + )?; + assert_error_category( + extract_sources(ExtractionOptions::new(repo.path())), + Category::PathTraversal, + ); + Ok(()) +} + +#[test] +fn unsupported_and_missing_tsconfig_are_typed_warnings() -> TestResult { + let repo = temp_repo()?; + write(&repo, "src/a.ts", "export function a() { return 1; }")?; + write(&repo, "src/view.vue", "")?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + + assert!(result + .diagnostics + .iter() + .any( + |diagnostic| diagnostic.category == Category::MissingTsconfig + && diagnostic.severity == Severity::Warning + )); + assert!(result + .diagnostics + .iter() + .any( + |diagnostic| diagnostic.category == Category::UnsupportedLanguage + && diagnostic.severity == Severity::Warning + )); + assert!(!result + .diagnostics + .iter() + .any(|diagnostic| diagnostic.severity == Severity::Error)); + assert!(!result.nodes.is_empty()); + Ok(()) +} diff --git a/crates/graph-core/src/extraction/tests/python.rs b/crates/graph-core/src/extraction/tests/python.rs new file mode 100644 index 0000000..f192885 --- /dev/null +++ b/crates/graph-core/src/extraction/tests/python.rs @@ -0,0 +1,479 @@ +use super::support::*; +use crate::extraction::{discover_sources_for_options, extract_sources, ExtractionOptions}; +use crate::protocol::{ + GraphExtractionDiagnosticCategory as Category, GraphExtractionDiagnosticSeverity as Severity, +}; +use serde_json::{json, Value}; + +type TestResult = Result<(), Box>; + +#[cfg(test)] +pub(super) struct PythonTests; + +#[test] +fn python_ast_extracts_contract_facts() -> TestResult { + let repo = repo_with_tsconfig()?; + write_python_graph_fixture(&repo)?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + let node_ids = sorted(result.nodes.iter().map(|node| node.id.clone()).collect()); + let triples = edge_triples(&result.edges); + + assert!( + !result + .diagnostics + .iter() + .any(|diagnostic| diagnostic.severity == Severity::Error), + "{:?}", + result.diagnostics + ); + for id in [ + "file:src/pkg/models.py", + "module:src/pkg/models.py#src.pkg.models", + "class:src/pkg/models.py#PublicModel", + "function:src/pkg/models.py#PublicModel.from_value", + "function:src/pkg/models.py#make_model", + "function:src/pkg/models.py#_hidden", + "variable:src/pkg/models.py#_private", + "function:tests/test_models.py#test_make_model", + "function:tests/test_models.py#TestPublicModel.test_render", + ] { + assert!(node_ids.contains(&id.to_string()), "{id}"); + } + for triple in [ + vec![ + "CONTAINS".to_string(), + "file:src/pkg/models.py".to_string(), + "module:src/pkg/models.py#src.pkg.models".to_string(), + ], + vec![ + "CONTAINS".to_string(), + "module:src/pkg/models.py#src.pkg.models".to_string(), + "class:src/pkg/models.py#PublicModel".to_string(), + ], + vec![ + "CONTAINS".to_string(), + "class:src/pkg/models.py#PublicModel".to_string(), + "function:src/pkg/models.py#PublicModel.from_value".to_string(), + ], + vec![ + "CALLS".to_string(), + "function:src/pkg/models.py#make_model".to_string(), + "class:src/pkg/models.py#PublicModel".to_string(), + ], + vec![ + "INHERITS".to_string(), + "class:src/pkg/models.py#PublicModel".to_string(), + "class:src/pkg/base.py#BaseModel".to_string(), + ], + vec![ + "TESTED_BY".to_string(), + "class:src/pkg/models.py#PublicModel".to_string(), + "function:tests/test_models.py#test_make_model".to_string(), + ], + ] { + assert!(triples.contains(&triple), "{triple:?}"); + } + assert!(result.metadata.node_kinds.contains(&"Module".to_string())); + Ok(()) +} + +#[test] +fn python_import_resolution_handles_absolute_relative_package_and_unresolved() -> TestResult { + let repo = repo_with_tsconfig()?; + write_python_graph_fixture(&repo)?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + let triples = edge_triples(&result.edges); + + for triple in [ + vec![ + "IMPORTS_FROM".to_string(), + "file:src/pkg/models.py".to_string(), + "file:src/pkg/base.py".to_string(), + ], + vec![ + "IMPORTS_FROM".to_string(), + "file:src/pkg/models.py".to_string(), + "file:src/pkg/helpers.py".to_string(), + ], + vec![ + "IMPORTS_FROM".to_string(), + "file:tests/test_models.py".to_string(), + "file:src/pkg/models.py".to_string(), + ], + vec![ + "IMPORTS_FROM".to_string(), + "file:tests/test_models.py".to_string(), + "file:src/pkg/__init__.py".to_string(), + ], + vec![ + "IMPORTS_FROM".to_string(), + "file:src/pkg/uses_stub.py".to_string(), + "file:src/pkg/stubs.pyi".to_string(), + ], + ] { + assert!(triples.contains(&triple), "{triple:?}"); + } + assert!(result.diagnostics.iter().any(|diagnostic| { + diagnostic.category == Category::UnresolvedImport + && diagnostic.severity == Severity::Warning + && diagnostic.path.as_deref() == Some("src/pkg/models.py") + })); + assert!(!result + .diagnostics + .iter() + .any(|diagnostic| diagnostic.severity == Severity::Error)); + Ok(()) +} + +#[test] +fn python_exports_are_best_effort_and_documented() -> TestResult { + let repo = repo_with_tsconfig()?; + write_python_graph_fixture(&repo)?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + + assert_eq!( + required_attributes(&result.nodes, "class:src/pkg/models.py#PublicModel")?, + json!({ + "decorators": [], + "exportKind": "named", + "exportName": "PublicModel", + "exportPolicy": "__all__", + "exported": true, + "isTest": false + }) + ); + assert_eq!( + required_attributes(&result.nodes, "function:src/pkg/models.py#_hidden")?, + json!({ + "async": false, + "decorators": [], + "exportPolicy": "__all__", + "exported": false, + "isTest": false + }) + ); + assert_eq!( + required_attributes(&result.nodes, "function:src/pkg/helpers.py#build_name")?, + json!({ + "async": false, + "decorators": [], + "exportKind": "named", + "exportName": "build_name", + "exportPolicy": "underscore_convention", + "exported": true, + "isTest": false + }) + ); + let exports = required_exports(&result.nodes, "file:src/pkg/models.py")?; + for expected in [ + json!({ + "kind": "named", + "local": "PublicModel", + "exported": "PublicModel", + "source": null, + "supportedSymbol": true, + "policy": "__all__" + }), + json!({ + "kind": "named", + "local": "make_model", + "exported": "make_model", + "source": null, + "supportedSymbol": true, + "policy": "__all__" + }), + ] { + assert!(exports.contains(&expected), "{expected}"); + } + Ok(()) +} + +#[test] +fn python_module_level_all_wins_even_when_empty() -> TestResult { + let repo = repo_with_tsconfig()?; + write( + &repo, + "pkg/api.py", + r#" +__all__ = [] + +def exposed(): + return True + +class Public: + pass +"#, + )?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + + assert_eq!( + required_attributes(&result.nodes, "function:pkg/api.py#exposed")?, + json!({"async":false,"decorators":[],"exportPolicy":"__all__","exported":false,"isTest":false}) + ); + assert_eq!( + required_attributes(&result.nodes, "class:pkg/api.py#Public")?, + json!({"decorators":[],"exportPolicy":"__all__","exported":false,"isTest":false}) + ); + assert_eq!( + required_exports(&result.nodes, "file:pkg/api.py")?, + Vec::::new() + ); + Ok(()) +} + +#[test] +fn python_nested_all_does_not_control_module_exports() -> TestResult { + let repo = repo_with_tsconfig()?; + write( + &repo, + "pkg/api.py", + r#" +def leaked(): + __all__ = ["_hidden"] + return True + +def _hidden(): + return True +"#, + )?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + + assert_eq!( + required_attributes(&result.nodes, "function:pkg/api.py#leaked")?, + json!({ + "async": false, + "decorators": [], + "exportKind": "named", + "exportName": "leaked", + "exportPolicy": "underscore_convention", + "exported": true, + "isTest": false + }) + ); + assert_eq!( + required_attributes(&result.nodes, "function:pkg/api.py#_hidden")?, + json!({ + "async": false, + "decorators": [], + "exportPolicy": "underscore_convention", + "exported": false, + "isTest": false + }) + ); + assert_eq!( + required_exports(&result.nodes, "file:pkg/api.py")?, + vec![json!({ + "kind": "named", + "local": "leaked", + "exported": "leaked", + "source": null, + "supportedSymbol": true, + "policy": "underscore_convention" + })] + ); + Ok(()) +} + +#[test] +fn python_dotted_import_module_calls_resolve_to_exported_member() -> TestResult { + let repo = repo_with_tsconfig()?; + write( + &repo, + "pkg/sub.py", + r#" +def target(): + return True +"#, + )?; + write( + &repo, + "app.py", + r#" +import pkg.sub + +def run(): + return pkg.sub.target() +"#, + )?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + let triples = edge_triples(&result.edges); + + assert!(triples.contains(&vec![ + "IMPORTS_FROM".to_string(), + "file:app.py".to_string(), + "file:pkg/sub.py".to_string() + ])); + assert!(triples.contains(&vec![ + "CALLS".to_string(), + "function:app.py#run".to_string(), + "function:pkg/sub.py#target".to_string() + ])); + Ok(()) +} + +#[test] +fn python_package_from_import_submodule_calls_resolve_to_submodule_member() -> TestResult { + let repo = repo_with_tsconfig()?; + write(&repo, "pkg/__init__.py", "")?; + write( + &repo, + "pkg/mod.py", + r#" +def f(): + return True +"#, + )?; + write( + &repo, + "app.py", + r#" +from pkg import mod + +def g(): + return mod.f() +"#, + )?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + let triples = edge_triples(&result.edges); + + assert!(triples.contains(&vec![ + "IMPORTS_FROM".to_string(), + "file:app.py".to_string(), + "file:pkg/mod.py".to_string() + ])); + assert!(triples.contains(&vec![ + "CALLS".to_string(), + "function:app.py#g".to_string(), + "function:pkg/mod.py#f".to_string() + ])); + Ok(()) +} + +#[test] +fn python_parse_errors_are_typed_warnings_and_non_fatal() -> TestResult { + let repo = repo_with_tsconfig()?; + write(&repo, "src/broken.py", "def broken(:\n return True\n")?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + + assert!(result.diagnostics.iter().any(|diagnostic| { + diagnostic.category == Category::ParseError && diagnostic.severity == Severity::Warning + })); + assert!(!result + .diagnostics + .iter() + .any(|diagnostic| diagnostic.severity == Severity::Error)); + assert!(result + .nodes + .iter() + .any(|node| node.id == "file:src/broken.py")); + Ok(()) +} + +#[test] +fn python_sources_are_discovered_and_extracted() -> TestResult { + let repo = repo_with_tsconfig()?; + write(&repo, "src/app.ts", "export const app = true;\n")?; + write(&repo, "src/tool.py", "def run():\n return True\n")?; + write(&repo, "src/typings.pyi", "def run() -> bool: ...\n")?; + + let discovery = discover_sources_for_options(&ExtractionOptions::new(repo.path())); + let sources = discovery + .sources + .iter() + .map(|source| (source.relative_path.as_str(), source.language.as_str())) + .collect::>(); + assert_eq!( + sources, + vec![ + ("src/app.ts", "typescript"), + ("src/tool.py", "python"), + ("src/typings.pyi", "python") + ] + ); + assert!(!discovery + .diagnostics + .iter() + .any(|diagnostic| diagnostic.category == Category::UnsupportedLanguage)); + + let result = extract_sources(ExtractionOptions::new(repo.path())); + assert!(!result + .diagnostics + .iter() + .any(|diagnostic| diagnostic.severity == Severity::Error)); + assert_eq!( + sorted( + result + .file_hashes + .iter() + .map(|hash| format!("{}:{}", hash.relative_path, hash.language)) + .collect() + ), + vec![ + "src/app.ts:typescript".to_string(), + "src/tool.py:python".to_string(), + "src/typings.pyi:python".to_string() + ] + ); + assert!(result + .nodes + .iter() + .any(|node| node.id == "file:src/tool.py")); + assert!(result + .nodes + .iter() + .any(|node| node.id == "file:src/typings.pyi")); + Ok(()) +} + +#[test] +fn python_generated_private_and_dependency_paths_are_ignored() -> TestResult { + let repo = repo_with_tsconfig()?; + write(&repo, "src/app.ts", "export const app = true;\n")?; + write(&repo, "src/tool.py", "def run():\n return True\n")?; + for path in [ + ".venv/lib/python3.12/site-packages/pkg/ignored.py", + "venv/lib/python3.12/site-packages/pkg/ignored.py", + "env/lib/python3.12/site-packages/pkg/ignored.py", + ".agents/runtime/ignored.ts", + ".claude/runtime/ignored.ts", + ".codex/runtime/ignored.ts", + ".gemini/runtime/ignored.ts", + ".opencode/runtime/ignored.ts", + "src/__pycache__/ignored.py", + ".eggs/pkg/ignored.py", + "build/lib/ignored.py", + ".tox/py/ignored.py", + ".mypy_cache/ignored.py", + ".pytest_cache/ignored.py", + ".ruff_cache/ignored.py", + "pkg.egg-info/ignored.py", + "pkg.dist-info/ignored.py", + "lib/site-packages/pkg/ignored.py", + ] { + write(&repo, path, "def ignored():\n return True\n")?; + } + + let discovery = discover_sources_for_options(&ExtractionOptions::new(repo.path())); + let source_paths = sorted( + discovery + .sources + .iter() + .map(|source| source.relative_path.clone()) + .collect(), + ); + + assert_eq!(source_paths, vec!["src/app.ts", "src/tool.py"]); + assert!(!discovery + .diagnostics + .iter() + .any(|diagnostic| diagnostic.category == Category::UnsupportedLanguage)); + Ok(()) +} diff --git a/crates/graph-core/src/extraction/tests/rust.rs b/crates/graph-core/src/extraction/tests/rust.rs new file mode 100644 index 0000000..2b53b4d --- /dev/null +++ b/crates/graph-core/src/extraction/tests/rust.rs @@ -0,0 +1,252 @@ +use super::support::*; +use crate::extraction::{ + discover_sources_for_options, extract_sources, DiscoveryResult, ExtractionOptions, + ExtractionResult, +}; +use crate::protocol::{ + GraphExtractionDiagnosticCategory as Category, GraphExtractionDiagnosticSeverity as Severity, +}; + +#[cfg(test)] +pub(super) struct RustTests; +use serde_json::Value; + +type TestResult = Result<(), Box>; + +#[test] +fn rust_sources_are_discovered_and_extracted() -> TestResult { + let repo = repo_with_tsconfig()?; + write_rust_graph_fixture(&repo)?; + + let discovery = discover_sources_for_options(&ExtractionOptions::new(repo.path())); + let result = extract_sources(ExtractionOptions::new(repo.path())); + + assert_rust_discovery_sources(&discovery); + assert_rust_extraction_nodes(&result); + assert_rust_extraction_edges(&result); + assert_rust_extraction_attributes(&result)?; + Ok(()) +} + +fn assert_rust_discovery_sources(discovery: &DiscoveryResult) { + let sources = discovery + .sources + .iter() + .map(|source| (source.relative_path.as_str(), source.language.as_str())) + .collect::>(); + assert_eq!( + sources, + vec![ + ("src/helpers.rs", "rust"), + ("src/lib.rs", "rust"), + ("src/user.rs", "rust") + ] + ); + assert!(!discovery + .diagnostics + .iter() + .any(|diagnostic| diagnostic.category == Category::UnsupportedLanguage)); +} + +fn assert_rust_extraction_nodes(result: &ExtractionResult) { + assert!( + !result + .diagnostics + .iter() + .any(|diagnostic| diagnostic.severity == Severity::Error), + "{:?}", + result.diagnostics + ); + let node_ids = sorted(result.nodes.iter().map(|node| node.id.clone()).collect()); + for id in [ + "file:src/lib.rs", + "module:src/lib.rs#crate", + "module:src/user.rs#user", + "module:src/user.rs#user.tests", + "struct:src/lib.rs#Widget", + "enum:src/lib.rs#Mode", + "trait:src/lib.rs#Service", + "impl:src/lib.rs#impl Service for Widget", + "method:src/lib.rs#Widget::handle", + "type:src/lib.rs#Alias", + "const:src/lib.rs#LIMIT", + "static:src/lib.rs#NAME", + "macro:src/lib.rs#trace", + "function:src/helpers.rs#helpers::assist", + "function:src/user.rs#user::run", + "test:src/user.rs#user.tests::test_run", + ] { + assert!(node_ids.contains(&id.to_string()), "{id}"); + } +} + +fn assert_rust_extraction_edges(result: &ExtractionResult) { + let triples = edge_triples(&result.edges); + for triple in rust_expected_edge_triples() { + assert!(triples.contains(&triple), "{triple:?}"); + } +} + +fn assert_rust_extraction_attributes(result: &ExtractionResult) -> TestResult { + assert_eq!( + required_attributes(&result.nodes, "struct:src/lib.rs#Widget")? + .get("exported") + .and_then(Value::as_bool), + Some(true) + ); + assert_eq!( + required_attributes(&result.nodes, "struct:src/lib.rs#Widget")? + .get("language") + .and_then(Value::as_str), + Some("rust") + ); + assert_rust_run_attributes(result)?; + assert!(result.metadata.node_kinds.contains(&"Struct".to_string())); + assert!(result + .metadata + .edge_kinds + .contains(&"IMPLEMENTS".to_string())); + Ok(()) +} + +fn assert_rust_run_attributes(result: &ExtractionResult) -> TestResult { + let attributes = required_attributes(&result.nodes, "function:src/user.rs#user::run")?; + assert_eq!( + attributes.get("qualifiedName").and_then(Value::as_str), + Some("user::run") + ); + assert!(attributes + .get("signature") + .and_then(Value::as_str) + .is_some_and(|signature| signature.starts_with("pub fn run"))); + assert!(attributes + .get("lineStart") + .and_then(Value::as_u64) + .is_some()); + Ok(()) +} + +fn rust_expected_edge_triples() -> Vec> { + [ + ("CONTAINS", "file:src/user.rs", "module:src/user.rs#user"), + ( + "CONTAINS", + "module:src/user.rs#user", + "function:src/user.rs#user::run", + ), + ( + "CONTAINS", + "module:src/user.rs#user.tests", + "test:src/user.rs#user.tests::test_run", + ), + ("IMPORTS_FROM", "file:src/user.rs", "file:src/helpers.rs"), + ( + "CALLS", + "function:src/user.rs#user::run", + "function:src/helpers.rs#helpers::assist", + ), + ( + "CALLS", + "test:src/user.rs#user.tests::test_run", + "function:src/user.rs#user::run", + ), + ( + "TESTED_BY", + "function:src/user.rs#user::run", + "test:src/user.rs#user.tests::test_run", + ), + ( + "IMPLEMENTS", + "impl:src/lib.rs#impl Service for Widget", + "trait:src/lib.rs#Service", + ), + ] + .into_iter() + .map(|(kind, from, to)| vec![kind.to_string(), from.to_string(), to.to_string()]) + .collect() +} + +#[test] +fn rust_crate_use_prefers_module_file_over_lib_declaration_stub() -> TestResult { + let repo = repo_with_tsconfig()?; + write(&repo, "src/lib.rs", "pub mod helpers;\nmod user;\n")?; + write(&repo, "src/helpers.rs", "pub fn assist() -> usize { 1 }\n")?; + write( + &repo, + "src/user.rs", + "use crate::helpers;\npub fn run() { helpers::assist(); }\n", + )?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + let triples = edge_triples(&result.edges); + + assert!(triples.contains(&vec![ + "IMPORTS_FROM".to_string(), + "file:src/user.rs".to_string(), + "file:src/helpers.rs".to_string() + ])); + assert!(!triples.contains(&vec![ + "IMPORTS_FROM".to_string(), + "file:src/user.rs".to_string(), + "file:src/lib.rs".to_string() + ])); + Ok(()) +} + +#[test] +fn rust_crate_use_resolves_within_nearest_workspace_crate() -> TestResult { + let repo = repo_with_tsconfig()?; + write( + &repo, + "crates/app/src/lib.rs", + "pub mod helpers;\nmod user;\n", + )?; + write( + &repo, + "crates/app/src/helpers.rs", + "pub fn assist() -> usize { 1 }\n", + )?; + write( + &repo, + "crates/app/src/user.rs", + "use crate::helpers;\npub fn run() { helpers::assist(); }\n", + )?; + write( + &repo, + "crates/other/src/lib.rs", + "pub mod helpers;\npub fn unrelated() {}\n", + )?; + write( + &repo, + "crates/other/src/helpers.rs", + "pub fn assist() -> usize { 2 }\n", + )?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + let triples = edge_triples(&result.edges); + + assert!(triples.contains(&vec![ + "IMPORTS_FROM".to_string(), + "file:crates/app/src/user.rs".to_string(), + "file:crates/app/src/helpers.rs".to_string() + ])); + assert!(!triples.contains(&vec![ + "IMPORTS_FROM".to_string(), + "file:crates/app/src/user.rs".to_string(), + "file:crates/other/src/helpers.rs".to_string() + ])); + Ok(()) +} + +#[test] +fn rust_parse_errors_are_typed_and_block_empty_success() -> TestResult { + let repo = repo_with_tsconfig()?; + write(&repo, "src/broken.rs", "pub fn broken(")?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + + assert_error_category(result.clone(), Category::ParseError); + assert!(result.nodes.is_empty(), "{:?}", result.nodes); + assert!(result.edges.is_empty(), "{:?}", result.edges); + Ok(()) +} diff --git a/crates/graph-core/src/extraction/tests/support.rs b/crates/graph-core/src/extraction/tests/support.rs new file mode 100644 index 0000000..62eb394 --- /dev/null +++ b/crates/graph-core/src/extraction/tests/support.rs @@ -0,0 +1,360 @@ +use crate::extraction::ExtractionResult; +use crate::protocol::{ + GraphExtractionDiagnosticCategory as Category, GraphExtractionDiagnosticSeverity as Severity, + GraphFactNode, +}; +use serde_json::{json, Value}; +use std::fs; +use std::path::PathBuf; +use tempfile::TempDir; + +type TestResult = Result<(), Box>; + +#[cfg(test)] +pub(super) fn assert_error_category(result: ExtractionResult, category: Category) { + assert!( + result + .diagnostics + .iter() + .any(|diagnostic| diagnostic.category == category + && diagnostic.severity == Severity::Error), + "{:?}", + result.diagnostics + ); + assert!(result.nodes.is_empty()); + assert!(result.edges.is_empty()); +} + +#[cfg(test)] +pub(super) fn wave1_fixture_root() -> Result { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../packages/fixtures/source-extraction/wave1") + .canonicalize() +} + +#[cfg(test)] +pub(super) fn temp_repo() -> Result { + tempfile::tempdir() +} + +#[cfg(test)] +pub(super) fn repo_with_tsconfig() -> Result { + let repo = temp_repo()?; + write( + &repo, + "tsconfig.json", + r#"{"compilerOptions":{"baseUrl":"."}}"#, + )?; + Ok(repo) +} + +#[cfg(test)] +pub(super) fn write_python_graph_fixture(repo: &TempDir) -> TestResult { + write_python_package_files(repo)?; + write_python_model_files(repo)?; + write_python_stub_and_tests(repo)?; + Ok(()) +} + +#[cfg(test)] +pub(super) fn write_rust_graph_fixture(repo: &TempDir) -> TestResult { + write( + repo, + "src/lib.rs", + r#" +pub mod helpers; +mod user; + +pub trait Service { + fn handle(&self); +} + +pub struct Widget; + +pub enum Mode { + Fast, +} + +impl Service for Widget { + fn handle(&self) { + helpers::assist(); + } +} + +pub type Alias = Widget; +pub const LIMIT: usize = 1; +pub static NAME: &str = "widget"; + +macro_rules! trace { + () => {}; +} +"#, + )?; + write(repo, "src/helpers.rs", "pub fn assist() -> usize { 1 }\n")?; + write( + repo, + "src/user.rs", + r#" +use crate::helpers; +use crate::{Service, Widget}; + +pub fn run() { + helpers::assist(); + let widget = Widget; + widget.handle(); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_run() { + run(); + } +} +"#, + )?; + Ok(()) +} + +fn write_python_package_files(repo: &TempDir) -> TestResult { + write( + repo, + "src/pkg/__init__.py", + r#" +from .models import PublicModel + +PACKAGE_VALUE = PublicModel() +__all__ = ["PublicModel", "PACKAGE_VALUE"] +"#, + )?; + write( + repo, + "src/pkg/base.py", + r#" +class BaseModel: + pass +"#, + )?; + write( + repo, + "src/pkg/helpers.py", + r#" +def build_name(): + return "public" + "#, + )?; + Ok(()) +} + +fn write_python_model_files(repo: &TempDir) -> TestResult { + write( + repo, + "src/pkg/models.py", + r#" +from .base import BaseModel +from .helpers import build_name +from .missing import MissingLocal + +_private = 1 +__all__ = ["PublicModel", "make_model"] + +class PublicModel(BaseModel): + @classmethod + def from_value(cls): + return build_name() + + def render(self): + return build_name() + +def make_model(): + return PublicModel() + +def _hidden(): + return PublicModel() + "#, + )?; + Ok(()) +} + +fn write_python_stub_and_tests(repo: &TempDir) -> TestResult { + write(repo, "src/pkg/stubs.pyi", "def stubbed() -> str: ...\n")?; + write( + repo, + "src/pkg/uses_stub.py", + r#" +from .stubs import stubbed + +def call_stub(): + return stubbed() +"#, + )?; + write( + repo, + "tests/test_models.py", + r#" +from src.pkg import PACKAGE_VALUE +from src.pkg.models import PublicModel, make_model + +def test_make_model(): + make_model() + PublicModel.from_value() + return PACKAGE_VALUE + +class TestPublicModel: + def test_render(self): + return PublicModel().render() +"#, + )?; + Ok(()) +} + +#[cfg(test)] +pub(super) fn write(repo: &TempDir, path: &str, contents: &str) -> Result<(), std::io::Error> { + let path = repo.path().join(path); + if let Some(parent) = path.parent() { + fs::create_dir_all(parent)?; + } + fs::write(path, contents) +} + +#[cfg(test)] +pub(super) fn edge_triples(edges: &[crate::protocol::GraphFactEdge]) -> Vec> { + sorted( + edges + .iter() + .map(|edge| vec![edge.kind.clone(), edge.from.clone(), edge.to.clone()]) + .collect(), + ) +} + +#[cfg(test)] +pub(super) fn value_strings(value: &Value, key: &str) -> Result, std::io::Error> { + let entries = value + .get(key) + .and_then(Value::as_array) + .ok_or_else(|| std::io::Error::other(format!("missing string array {key}")))?; + entries + .iter() + .map(|entry| { + entry + .as_str() + .map(ToString::to_string) + .ok_or_else(|| std::io::Error::other(format!("non-string entry in {key}"))) + }) + .collect::, _>>() + .map(sorted) +} + +#[cfg(test)] +pub(super) fn value_triples(value: &Value, key: &str) -> Result>, std::io::Error> { + let entries = value + .get(key) + .and_then(Value::as_array) + .ok_or_else(|| std::io::Error::other(format!("missing triple array {key}")))?; + entries + .iter() + .map(|entry| { + let parts = entry + .as_array() + .ok_or_else(|| std::io::Error::other(format!("non-array triple in {key}")))?; + parts + .iter() + .map(|part| { + part.as_str().map(ToString::to_string).ok_or_else(|| { + std::io::Error::other(format!("non-string triple part in {key}")) + }) + }) + .collect::, _>>() + }) + .collect::, _>>() + .map(sorted) +} + +#[cfg(test)] +pub(super) fn value_object(value: &Value, key: &str) -> Result { + value + .get(key) + .cloned() + .ok_or_else(|| std::io::Error::other(format!("missing object {key}"))) +} + +#[cfg(test)] +pub(super) fn node_attributes(nodes: &[GraphFactNode]) -> Value { + let mut attributes = serde_json::Map::new(); + for node in nodes { + if node.kind == "File" { + continue; + } + attributes.insert( + node.id.clone(), + node.attributes.clone().unwrap_or_else(|| json!({})), + ); + } + Value::Object(attributes) +} + +#[cfg(test)] +pub(super) fn file_exports(nodes: &[GraphFactNode]) -> Value { + let mut exports_by_file = serde_json::Map::new(); + for node in nodes { + if node.kind != "File" { + continue; + } + if let Some(exports) = node + .attributes + .as_ref() + .and_then(|attributes| attributes.get("exports")) + { + exports_by_file.insert(node.id.clone(), exports.clone()); + } + } + Value::Object(exports_by_file) +} + +#[cfg(test)] +pub(super) fn required_attributes( + nodes: &[GraphFactNode], + id: &str, +) -> Result { + let node = nodes + .iter() + .find(|node| node.id == id) + .ok_or_else(|| std::io::Error::other(format!("missing node {id}")))?; + node.attributes + .clone() + .ok_or_else(|| std::io::Error::other(format!("missing attributes for {id}"))) +} + +#[cfg(test)] +pub(super) fn required_exports( + nodes: &[GraphFactNode], + id: &str, +) -> Result, std::io::Error> { + let node = nodes + .iter() + .find(|node| node.id == id) + .ok_or_else(|| std::io::Error::other(format!("missing node {id}")))?; + node.attributes + .as_ref() + .and_then(|attributes| attributes.get("exports")) + .and_then(Value::as_array) + .cloned() + .ok_or_else(|| std::io::Error::other(format!("missing exports for {id}"))) +} + +#[cfg(test)] +pub(super) fn assert_missing_node(nodes: &[GraphFactNode], id: &str) -> Result<(), std::io::Error> { + if nodes.iter().any(|node| node.id == id) { + Err(std::io::Error::other(format!("unexpected node {id}"))) + } else { + Ok(()) + } +} + +#[cfg(test)] +pub(super) fn sorted(mut values: Vec) -> Vec { + values.sort(); + values +} diff --git a/crates/graph-core/src/extraction/tests/typescript_exports.rs b/crates/graph-core/src/extraction/tests/typescript_exports.rs new file mode 100644 index 0000000..4d21d95 --- /dev/null +++ b/crates/graph-core/src/extraction/tests/typescript_exports.rs @@ -0,0 +1,430 @@ +use super::support::*; +use crate::extraction::{extract_sources, ExtractionOptions}; +use crate::protocol::{GraphExtractionDiagnosticSeverity as Severity, GraphFactNode}; +use serde_json::{json, Value}; +use std::fs; +use tempfile::TempDir; + +type TestResult = Result<(), Box>; + +#[cfg(test)] +pub(super) struct TypeScriptExportTests; + +#[test] +fn wave1_fixture_extracts_contract_facts() -> TestResult { + let fixture_root = wave1_fixture_root()?; + let expected: Value = serde_json::from_str(&fs::read_to_string( + fixture_root.join("wave1.expected.json"), + )?)?; + + let result = extract_sources(ExtractionOptions::new(&fixture_root)); + + assert!( + !result + .diagnostics + .iter() + .any(|diagnostic| diagnostic.severity == Severity::Error), + "{:?}", + result.diagnostics + ); + assert_eq!( + sorted(result.nodes.iter().map(|node| node.id.clone()).collect()), + value_strings(&expected, "nodeIds")? + ); + assert_eq!( + sorted(result.metadata.node_kinds), + value_strings(&expected, "nodeKinds")? + ); + assert_eq!( + sorted(result.metadata.edge_kinds), + value_strings(&expected, "edgeKinds")? + ); + assert_eq!( + edge_triples(&result.edges), + value_triples(&expected, "edgeTriples")? + ); + assert_eq!( + node_attributes(&result.nodes), + value_object(&expected, "nodeAttributes")? + ); + assert_eq!( + file_exports(&result.nodes), + value_object(&expected, "fileExports")? + ); + Ok(()) +} + +#[test] +fn export_metadata_marks_supported_ts_js_declarations() -> TestResult { + let repo = repo_with_tsconfig()?; + write_export_metadata_fixture(&repo)?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + + assert!( + !result + .diagnostics + .iter() + .any(|diagnostic| diagnostic.severity == Severity::Error), + "{:?}", + result.diagnostics + ); + assert_exported_symbol_attributes(&result.nodes)?; + assert_non_exported_symbol_attributes(&result.nodes)?; + assert_index_file_export_metadata(&result.nodes)?; + Ok(()) +} + +#[test] +fn import_backed_barrel_exports_are_unsupported_reexport_metadata() -> TestResult { + let repo = repo_with_tsconfig()?; + write( + &repo, + "src/source.ts", + "export default function inner() { return 1; }\nexport const named = 1;", + )?; + write( + &repo, + "src/barrel.ts", + "import inner, { named } from './source';\nexport { named };\nexport default inner;", + )?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + let exports = required_exports(&result.nodes, "file:src/barrel.ts")?; + + for expected in [ + json!({ + "kind": "named", + "local": "named", + "exported": "named", + "source": "./source", + "imported": "named", + "supportedSymbol": false + }), + json!({ + "kind": "default", + "local": "inner", + "exported": "default", + "source": "./source", + "imported": "default", + "supportedSymbol": false + }), + ] { + assert!(exports.contains(&expected), "{expected}"); + } + assert_missing_node(&result.nodes, "variable:src/barrel.ts#named")?; + assert_missing_node(&result.nodes, "function:src/barrel.ts#inner")?; + Ok(()) +} + +#[test] +fn unresolved_local_exports_are_unsupported_file_metadata() -> TestResult { + let repo = repo_with_tsconfig()?; + write( + &repo, + "src/index.ts", + "export { missing as renamed };\nfunction internal(){return 1;}\n", + )?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + let exports = required_exports(&result.nodes, "file:src/index.ts")?; + + assert!(exports.contains(&json!({ + "kind": "named", + "local": "missing", + "exported": "renamed", + "source": null, + "supportedSymbol": false + }))); + assert_eq!( + required_attributes(&result.nodes, "function:src/index.ts#internal")?, + json!({"exported": false}) + ); + assert_missing_node(&result.nodes, "function:src/index.ts#missing")?; + Ok(()) +} + +#[test] +fn nested_local_exports_are_unsupported_file_metadata() -> TestResult { + let repo = repo_with_tsconfig()?; + write( + &repo, + "src/index.ts", + concat!( + "export { laterNested as exportedLaterNested };\n", + "function container(){ function laterNested(){ return 1; } return laterNested(); }\n", + ), + )?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + let exports = required_exports(&result.nodes, "file:src/index.ts")?; + + assert!(exports.contains(&json!({ + "kind": "named", + "local": "laterNested", + "exported": "exportedLaterNested", + "source": null, + "supportedSymbol": false + }))); + assert_eq!( + required_attributes(&result.nodes, "function:src/index.ts#laterNested")?, + json!({"exported": false}) + ); + Ok(()) +} + +fn write_export_metadata_fixture(repo: &TempDir) -> TestResult { + write_export_metadata_index_fixture(repo)?; + write_export_metadata_supporting_modules(repo)?; + write_export_metadata_default_modules(repo)?; + write_export_metadata_jsx_modules(repo)?; + Ok(()) +} + +fn write_export_metadata_index_fixture(repo: &TempDir) -> TestResult { + write( + repo, + "src/index.ts", + r#" + export interface Renderable { render(): string; } + export type Payload = { label: string }; + export class ExportedClass implements Renderable { render() { return "ok"; } } + class InternalClass {} + export function exportedFunction() { return new ExportedClass(); } + function internalFunction() { return new InternalClass(); } + export const exportedValue = 1; + const internalValue = 2; + export const exportedArrow = () => internalFunction(); + const internalArrow = () => exportedFunction(); + export function exportedWithNested() { + function nestedLocal() { return 1; } + return nestedLocal(); + } + export { laterNested as exportedLaterNested }; + function container() { + function laterNested() { return 1; } + return laterNested(); + } + const aliasTarget = 3; + export { aliasTarget as renamedAlias }; + export { externalThing as renamedExternal } from "./external"; + export { default as externalDefault } from "./defaulted"; + export * from "./barrel"; + export * as namespaceExport from "./namespace"; + const defaultValue = exportedValue; + export default defaultValue; + "#, + )?; + Ok(()) +} + +fn write_export_metadata_supporting_modules(repo: &TempDir) -> TestResult { + write(repo, "src/external.ts", "export const externalThing = 1;")?; + write( + repo, + "src/defaulted.ts", + "export default function defaulted() { return 1; }", + )?; + write(repo, "src/barrel.ts", "export const barrelValue = 1;")?; + write(repo, "src/namespace.ts", "export const namespaced = 1;")?; + Ok(()) +} + +fn write_export_metadata_default_modules(repo: &TempDir) -> TestResult { + write( + repo, + "src/default-function.ts", + "export default function defaultFunction() { return 1; }", + )?; + write( + repo, + "src/default-class.ts", + "export default class DefaultClass {}", + )?; + write( + repo, + "src/default-class-with-method.ts", + r#" + export default class DefaultClassWithMethod { + render() { return "ok"; } + } + "#, + )?; + write( + repo, + "src/default-interface.ts", + "export default interface DefaultInterface {}", + )?; + Ok(()) +} + +fn write_export_metadata_jsx_modules(repo: &TempDir) -> TestResult { + write( + repo, + "src/js-cases.js", + r#" + export function jsFunction() { return jsValue; } + export const jsValue = 1; + const jsInternal = 2; + "#, + )?; + write( + repo, + "src/view.tsx", + r#" + export function View() { return
; } + export const TsxArrow = () => ; + "#, + )?; + write( + repo, + "src/widget.jsx", + r#" + export default function Widget() { return
; } + export const WidgetHelper = () => ; + "#, + )?; + Ok(()) +} + +fn assert_exported_symbol_attributes(nodes: &[GraphFactNode]) -> TestResult { + for (id, expected) in [ + ( + "function:src/index.ts#exportedFunction", + json!({"exported": true, "exportKind": "named", "exportName": "exportedFunction"}), + ), + ( + "class:src/index.ts#ExportedClass", + json!({"exported": true, "exportKind": "named", "exportName": "ExportedClass"}), + ), + ( + "type:src/index.ts#Renderable", + json!({"exported": true, "exportKind": "named", "exportName": "Renderable"}), + ), + ( + "type:src/index.ts#Payload", + json!({"exported": true, "exportKind": "named", "exportName": "Payload"}), + ), + ( + "variable:src/index.ts#exportedValue", + json!({"exported": true, "exportKind": "named", "exportName": "exportedValue"}), + ), + ( + "function:src/index.ts#exportedArrow", + json!({"exported": true, "exportKind": "named", "exportName": "exportedArrow"}), + ), + ( + "function:src/index.ts#exportedWithNested", + json!({"exported": true, "exportKind": "named", "exportName": "exportedWithNested"}), + ), + ( + "variable:src/index.ts#aliasTarget", + json!({"exported": true, "exportKind": "named", "exportName": "renamedAlias"}), + ), + ( + "function:src/default-function.ts#defaultFunction", + json!({"exported": true, "exportKind": "default", "exportName": "default"}), + ), + ( + "class:src/default-class.ts#DefaultClass", + json!({"exported": true, "exportKind": "default", "exportName": "default"}), + ), + ( + "class:src/default-class-with-method.ts#DefaultClassWithMethod", + json!({"exported": true, "exportKind": "default", "exportName": "default"}), + ), + ( + "type:src/default-interface.ts#DefaultInterface", + json!({"exported": true, "exportKind": "default", "exportName": "default"}), + ), + ( + "function:src/js-cases.js#jsFunction", + json!({"exported": true, "exportKind": "named", "exportName": "jsFunction"}), + ), + ( + "variable:src/js-cases.js#jsValue", + json!({"exported": true, "exportKind": "named", "exportName": "jsValue"}), + ), + ( + "function:src/view.tsx#View", + json!({"exported": true, "exportKind": "named", "exportName": "View"}), + ), + ( + "function:src/view.tsx#TsxArrow", + json!({"exported": true, "exportKind": "named", "exportName": "TsxArrow"}), + ), + ( + "function:src/widget.jsx#Widget", + json!({"exported": true, "exportKind": "default", "exportName": "default"}), + ), + ( + "function:src/widget.jsx#WidgetHelper", + json!({"exported": true, "exportKind": "named", "exportName": "WidgetHelper"}), + ), + ] { + assert_eq!(required_attributes(nodes, id)?, expected, "{id}"); + } + Ok(()) +} + +fn assert_non_exported_symbol_attributes(nodes: &[GraphFactNode]) -> TestResult { + for id in [ + "class:src/index.ts#InternalClass", + "function:src/index.ts#internalFunction", + "variable:src/index.ts#internalValue", + "function:src/index.ts#internalArrow", + "function:src/index.ts#container", + "function:src/index.ts#nestedLocal", + "function:src/index.ts#laterNested", + "variable:src/js-cases.js#jsInternal", + ] { + assert_eq!( + required_attributes(nodes, id)?, + json!({"exported": false}), + "{id}" + ); + } + assert_missing_node(nodes, "function:src/default-class-with-method.ts#default")?; + Ok(()) +} + +fn assert_index_file_export_metadata(nodes: &[GraphFactNode]) -> TestResult { + let index_exports = required_exports(nodes, "file:src/index.ts")?; + for expected in [ + json!({ + "kind": "named", + "local": "externalThing", + "exported": "renamedExternal", + "source": "./external", + "imported": "externalThing", + "supportedSymbol": true + }), + json!({ + "kind": "named", + "local": "default", + "exported": "externalDefault", + "source": "./defaulted", + "imported": "default", + "supportedSymbol": true + }), + json!({"kind": "all", "exported": "*", "source": "./barrel", "supportedSymbol": false}), + json!({"kind": "namespace", "exported": "namespaceExport", "source": "./namespace", "supportedSymbol": false}), + json!({ + "kind": "default", + "local": "defaultValue", + "exported": "default", + "source": null, + "supportedSymbol": true + }), + json!({ + "kind": "named", + "local": "laterNested", + "exported": "exportedLaterNested", + "source": null, + "supportedSymbol": false + }), + ] { + assert!(index_exports.contains(&expected), "{expected}"); + } + Ok(()) +} diff --git a/crates/graph-core/src/extraction/tests/typescript_resolution.rs b/crates/graph-core/src/extraction/tests/typescript_resolution.rs new file mode 100644 index 0000000..1f5f3cd --- /dev/null +++ b/crates/graph-core/src/extraction/tests/typescript_resolution.rs @@ -0,0 +1,268 @@ +use super::support::*; +use crate::extraction::{extract_sources, ExtractionOptions}; +use crate::protocol::GraphExtractionDiagnosticSeverity as Severity; +use serde_json::json; + +type TestResult = Result<(), Box>; + +#[cfg(test)] +pub(super) struct TypeScriptResolutionTests; + +#[test] +fn tsconfig_path_aliases_resolve_to_repo_relative_files() -> TestResult { + let result = extract_sources(ExtractionOptions::new(wave1_fixture_root()?)); + let triples = edge_triples(&result.edges); + + assert!(triples.contains(&vec![ + "IMPORTS_FROM".to_string(), + "file:src/__tests__/greeting.test.ts".to_string(), + "file:src/math.js".to_string() + ])); + assert!(triples.contains(&vec![ + "IMPORTS_FROM".to_string(), + "file:src/legacy-widget.jsx".to_string(), + "file:src/components/GreetingCard.tsx".to_string() + ])); + Ok(()) +} + +#[test] +fn script_test_imports_emit_file_level_tested_by_evidence() -> TestResult { + let repo = temp_repo()?; + write( + &repo, + "tsconfig.json", + r#"{"compilerOptions":{"baseUrl":".","paths":{"@example/pkg":["src/index.ts"]}}}"#, + )?; + write( + &repo, + "src/index.ts", + "export { publicValue } from './value.js';", + )?; + write(&repo, "src/value.ts", "export const publicValue = 1;")?; + write( + &repo, + "tests/package-contract.test.ts", + "import { publicValue } from '@example/pkg'; test('public value', () => publicValue);", + )?; + write( + &repo, + "src/consumer.ts", + "import { publicValue } from '@example/pkg'; export const consumed = publicValue;", + )?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + let triples = edge_triples(&result.edges); + + assert!(triples.contains(&vec![ + "TESTED_BY".to_string(), + "file:src/index.ts".to_string(), + "file:tests/package-contract.test.ts".to_string() + ])); + assert!(!triples.contains(&vec![ + "TESTED_BY".to_string(), + "file:src/index.ts".to_string(), + "file:src/consumer.ts".to_string() + ])); + Ok(()) +} + +#[test] +fn unimported_cross_file_symbols_do_not_create_edges() -> TestResult { + let repo = temp_repo()?; + write( + &repo, + "tsconfig.json", + r#"{"compilerOptions":{"baseUrl":"."}}"#, + )?; + write( + &repo, + "src/a.ts", + r#" + export function caller() { return target(); } + export class Child extends Base implements Shape {} + "#, + )?; + write( + &repo, + "src/b.ts", + r#" + export function target() { return 1; } + export class Base {} + export interface Shape {} + "#, + )?; + write( + &repo, + "src/c.ts", + r#" + export function localCaller() { return sameName(); } + export function sameName() { return 1; } + "#, + )?; + write( + &repo, + "src/d.ts", + "export function sameName() { return 2; }", + )?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + let triples = edge_triples(&result.edges); + + assert!(!result + .diagnostics + .iter() + .any(|diagnostic| diagnostic.severity == Severity::Error)); + assert!(!triples.contains(&vec![ + "CALLS".to_string(), + "function:src/a.ts#caller".to_string(), + "function:src/b.ts#target".to_string() + ])); + assert!(!triples.contains(&vec![ + "INHERITS".to_string(), + "class:src/a.ts#Child".to_string(), + "class:src/b.ts#Base".to_string() + ])); + assert!(!triples.contains(&vec![ + "IMPLEMENTS".to_string(), + "class:src/a.ts#Child".to_string(), + "type:src/b.ts#Shape".to_string() + ])); + assert!(triples.contains(&vec![ + "CALLS".to_string(), + "function:src/c.ts#localCaller".to_string(), + "function:src/c.ts#sameName".to_string() + ])); + Ok(()) +} + +#[test] +fn default_imports_resolve_to_default_exported_symbols() -> TestResult { + let repo = repo_with_tsconfig()?; + write( + &repo, + "src/default-function.ts", + "export default function usedDefault() { return 1; }", + )?; + write( + &repo, + "src/default-value.ts", + "const usedValue = () => 1; export default usedValue;", + )?; + write( + &repo, + "src/index.ts", + r#" + import usedDefault from "./default-function"; + import usedValue from "./default-value"; + export function run() { + return usedDefault() + usedValue(); + } + "#, + )?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + let triples = edge_triples(&result.edges); + + assert!(!result + .diagnostics + .iter() + .any(|diagnostic| diagnostic.severity == Severity::Error)); + assert!(triples.contains(&vec![ + "CALLS".to_string(), + "function:src/index.ts#run".to_string(), + "function:src/default-function.ts#usedDefault".to_string() + ])); + assert!(triples.contains(&vec![ + "CALLS".to_string(), + "function:src/index.ts#run".to_string(), + "function:src/default-value.ts#usedValue".to_string() + ])); + Ok(()) +} + +#[test] +fn named_export_alias_imports_resolve_to_local_exported_symbols() -> TestResult { + let repo = repo_with_tsconfig()?; + write( + &repo, + "src/dep.ts", + "function localName() { return 1; }\nexport { localName as publicName };", + )?; + write( + &repo, + "src/index.ts", + r#" + import { publicName } from "./dep"; + export function run() { + return publicName(); + } + "#, + )?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + let triples = edge_triples(&result.edges); + + assert!(!result + .diagnostics + .iter() + .any(|diagnostic| diagnostic.severity == Severity::Error)); + assert!(triples.contains(&vec![ + "CALLS".to_string(), + "function:src/index.ts#run".to_string(), + "function:src/dep.ts#localName".to_string() + ])); + Ok(()) +} + +#[test] +fn source_re_export_alias_imports_resolve_to_source_exported_symbols() -> TestResult { + let repo = repo_with_tsconfig()?; + write( + &repo, + "src/source.ts", + "export function add() { return 1; }", + )?; + write( + &repo, + "src/barrel.ts", + "export { add as addFromBarrel } from './source';", + )?; + write( + &repo, + "src/index.ts", + r#" + import { addFromBarrel } from "./barrel"; + export function run() { + return addFromBarrel(); + } + "#, + )?; + + let result = extract_sources(ExtractionOptions::new(repo.path())); + let triples = edge_triples(&result.edges); + let exports = required_exports(&result.nodes, "file:src/barrel.ts")?; + + assert!( + !result + .diagnostics + .iter() + .any(|diagnostic| diagnostic.severity == Severity::Error), + "{:?}", + result.diagnostics + ); + assert!(triples.contains(&vec![ + "CALLS".to_string(), + "function:src/index.ts#run".to_string(), + "function:src/source.ts#add".to_string() + ])); + assert!(exports.contains(&json!({ + "kind": "named", + "local": "add", + "exported": "addFromBarrel", + "source": "./source", + "imported": "add", + "supportedSymbol": true + }))); + Ok(()) +} diff --git a/crates/graph-core/src/extraction/tsconfig.rs b/crates/graph-core/src/extraction/tsconfig.rs index b17a1e0..49a915f 100644 --- a/crates/graph-core/src/extraction/tsconfig.rs +++ b/crates/graph-core/src/extraction/tsconfig.rs @@ -1,8 +1,9 @@ use super::diagnostics::{error, warning}; +use super::{SourcePathOps, SourcePaths}; use crate::protocol::{GraphExtractionDiagnostic, GraphExtractionDiagnosticCategory}; use serde_json::{Map, Value}; use std::collections::BTreeSet; -use std::path::{Component, Path, PathBuf}; +use std::path::{Path, PathBuf}; #[derive(Debug, Clone)] pub struct TsConfig { @@ -324,7 +325,7 @@ fn resolve_candidate( specifier: &str, from_path: &str, ) -> ImportResolution { - let normalized = match normalize_relative(&candidate) { + let normalized = match SourcePaths::normalize_relative_path(&candidate) { Ok(path) => path, Err(()) => { return ImportResolution { @@ -401,23 +402,3 @@ fn replace_extension(path: &str, extension: &str) -> String { .to_string_lossy() .replace('\\', "/") } - -fn normalize_relative(path: &Path) -> Result { - let mut parts = Vec::new(); - for component in path.components() { - match component { - Component::CurDir => {} - Component::Normal(part) => parts.push(part.to_string_lossy().to_string()), - Component::ParentDir => { - if parts.pop().is_none() { - return Err(()); - } - } - Component::RootDir | Component::Prefix(_) => return Err(()), - } - } - if parts.is_empty() { - return Err(()); - } - Ok(parts.join("/")) -} diff --git a/crates/graph-core/src/lib.rs b/crates/graph-core/src/lib.rs index d32067d..3d5dd86 100644 --- a/crates/graph-core/src/lib.rs +++ b/crates/graph-core/src/lib.rs @@ -9,5 +9,8 @@ pub mod search; pub mod store; pub mod watch; +#[cfg(test)] +mod test_support; + pub const GRAPH_PROVIDER_NAME: &str = "opcore-graph"; pub const GRAPH_SCHEMA_VERSION: u32 = 1; diff --git a/crates/graph-core/src/pipeline.rs b/crates/graph-core/src/pipeline.rs index e3b0e41..de85879 100644 --- a/crates/graph-core/src/pipeline.rs +++ b/crates/graph-core/src/pipeline.rs @@ -17,6 +17,10 @@ use std::time::Instant; use time::format_description::well_known::Rfc3339; use time::OffsetDateTime; +mod delta; + +use delta::{Delta, SourceDeltaOps}; + #[derive(Debug, Clone)] pub struct GraphPipelineOptions { pub repo_root: PathBuf, @@ -221,7 +225,7 @@ fn incremental_file_facts( diagnostics: &mut Vec, ) -> Result { let store = GraphStore::open(StorePaths::for_repo_root(&context.options.repo_root))?; - let delta = source_delta(&store.file_hashes()?, context.current_hashes); + let delta = Delta::source_delta(&store.file_hashes()?, context.current_hashes); let changed_sources = sources_for_paths(&context.discovery.sources, &delta.changed_files); let changed_extractable_sources = graph_extractable_sources(&changed_sources); let parsed = timed("extraction", || { @@ -384,44 +388,6 @@ fn with_file_count( timing } -#[derive(Debug)] -struct SourceDelta { - changed_files: Vec, - deleted_files: Vec, -} - -fn source_delta( - stored: &[crate::extraction::SourceFileHash], - current: &[crate::extraction::SourceFileHash], -) -> SourceDelta { - let stored_by_path = stored - .iter() - .map(|hash| (hash.relative_path.as_str(), hash.sha256.as_str())) - .collect::>(); - let current_by_path = current - .iter() - .map(|hash| (hash.relative_path.as_str(), hash.sha256.as_str())) - .collect::>(); - let mut changed_files = current_by_path - .iter() - .filter_map(|(path, sha)| match stored_by_path.get(path) { - Some(stored_sha) if stored_sha == sha => None, - _ => Some((*path).to_string()), - }) - .collect::>(); - let mut deleted_files = stored_by_path - .keys() - .filter(|path| !current_by_path.contains_key(**path)) - .map(|path| (*path).to_string()) - .collect::>(); - changed_files.sort(); - deleted_files.sort(); - SourceDelta { - changed_files, - deleted_files, - } -} - fn sources_for_paths(sources: &[DiscoveredSource], paths: &[String]) -> Vec { let wanted = paths.iter().map(String::as_str).collect::>(); sources diff --git a/crates/graph-core/src/pipeline/delta.rs b/crates/graph-core/src/pipeline/delta.rs new file mode 100644 index 0000000..b32615c --- /dev/null +++ b/crates/graph-core/src/pipeline/delta.rs @@ -0,0 +1,37 @@ +use crate::extraction::{SourceFileHash, SourcePathOps, SourcePaths}; + +pub(super) struct SourceDelta { + pub(super) changed_files: Vec, + pub(super) deleted_files: Vec, +} + +pub(super) struct Delta; + +pub(super) trait SourceDeltaOps { + fn source_delta(stored: &[SourceFileHash], current: &[SourceFileHash]) -> SourceDelta; +} + +impl SourceDeltaOps for Delta { + fn source_delta(stored: &[SourceFileHash], current: &[SourceFileHash]) -> SourceDelta { + let stored_by_path = SourcePaths::source_hashes_by_path(stored); + let current_by_path = SourcePaths::source_hashes_by_path(current); + let mut changed_files = current_by_path + .iter() + .filter_map(|(path, sha)| match stored_by_path.get(path) { + Some(stored_sha) if stored_sha == sha => None, + _ => Some((*path).to_string()), + }) + .collect::>(); + let mut deleted_files = stored_by_path + .keys() + .filter(|path| !current_by_path.contains_key(**path)) + .map(|path| (*path).to_string()) + .collect::>(); + changed_files.sort(); + deleted_files.sort(); + SourceDelta { + changed_files, + deleted_files, + } + } +} diff --git a/crates/graph-core/src/query/tests.rs b/crates/graph-core/src/query/tests.rs index 18160a4..ce95a20 100644 --- a/crates/graph-core/src/query/tests.rs +++ b/crates/graph-core/src/query/tests.rs @@ -3,6 +3,10 @@ use crate::protocol::{GraphFactQueryKind, GraphFreshness, GraphProviderMode, Rep use serde_json::json; use std::collections::BTreeSet; +mod fixtures; + +use fixtures::*; + #[test] fn impact_traverses_reverse_file_dependencies_and_tests() { let snapshot = fixture_snapshot(); @@ -444,339 +448,3 @@ fn review_context_impacts_renamed_source_paths() { .tests .contains(&"src/__tests__/greeting.test.ts".to_string())); } - -fn fixture_snapshot() -> StoreQueryOutput { - StoreQueryOutput { - metadata: GraphSnapshotMetadata { - schema_version: 1, - provider: "opcore-graph".to_string(), - repo: repo(), - generated_at: "2026-06-04T00:00:00.000Z".to_string(), - freshness: GraphFreshness { - generated_at: "2026-06-04T00:00:00.000Z".to_string(), - age_ms: 0, - max_age_ms: None, - stale: false, - reason: None, - }, - node_kinds: vec![ - "File".to_string(), - "Function".to_string(), - "Test".to_string(), - ], - edge_kinds: vec![ - "CONTAINS".to_string(), - "DEPENDS_ON".to_string(), - "TESTED_BY".to_string(), - ], - }, - nodes: vec![ - node("file:src/models.ts", "File", Some("src/models.ts")), - node( - "file:src/components/GreetingCard.tsx", - "File", - Some("src/components/GreetingCard.tsx"), - ), - node( - "file:src/__tests__/greeting.test.ts", - "File", - Some("src/__tests__/greeting.test.ts"), - ), - node("function:src/models.ts#formatGreeting", "Function", None), - node( - "test:src/__tests__/greeting.test.ts#renders greeting cards", - "Test", - None, - ), - ], - edges: vec![ - edge( - "CONTAINS", - "file:src/models.ts", - "function:src/models.ts#formatGreeting", - ), - edge( - "DEPENDS_ON", - "file:src/components/GreetingCard.tsx", - "file:src/models.ts", - ), - edge( - "DEPENDS_ON", - "file:src/__tests__/greeting.test.ts", - "file:src/components/GreetingCard.tsx", - ), - edge( - "TESTED_BY", - "function:src/models.ts#formatGreeting", - "test:src/__tests__/greeting.test.ts#renders greeting cards", - ), - ], - diagnostics: Vec::new(), - } -} - -fn inheritance_snapshot() -> StoreQueryOutput { - StoreQueryOutput { - metadata: GraphSnapshotMetadata { - schema_version: 1, - provider: "opcore-graph".to_string(), - repo: repo(), - generated_at: "2026-06-04T00:00:00.000Z".to_string(), - freshness: GraphFreshness { - generated_at: "2026-06-04T00:00:00.000Z".to_string(), - age_ms: 0, - max_age_ms: None, - stale: false, - reason: None, - }, - node_kinds: vec!["Class".to_string()], - edge_kinds: vec!["INHERITS".to_string()], - }, - nodes: vec![ - node( - "class:src/models.ts#BaseModel", - "Class", - Some("src/models.ts"), - ), - node( - "class:src/models.ts#DirectModel", - "Class", - Some("src/models.ts"), - ), - node( - "class:src/models.ts#IndirectModel", - "Class", - Some("src/models.ts"), - ), - node( - "class:src/other.ts#UnrelatedModel", - "Class", - Some("src/other.ts"), - ), - ], - edges: vec![ - edge( - "INHERITS", - "class:src/models.ts#DirectModel", - "class:src/models.ts#BaseModel", - ), - edge( - "INHERITS", - "class:src/models.ts#IndirectModel", - "class:src/models.ts#DirectModel", - ), - ], - diagnostics: Vec::new(), - } -} - -fn cycle_snapshot() -> StoreQueryOutput { - StoreQueryOutput { - metadata: GraphSnapshotMetadata { - schema_version: 1, - provider: "opcore-graph".to_string(), - repo: repo(), - generated_at: "2026-06-04T00:00:00.000Z".to_string(), - freshness: GraphFreshness { - generated_at: "2026-06-04T00:00:00.000Z".to_string(), - age_ms: 0, - max_age_ms: None, - stale: false, - reason: None, - }, - node_kinds: vec!["File".to_string()], - edge_kinds: vec!["DEPENDS_ON".to_string()], - }, - nodes: vec![ - node("file:src/c.ts", "File", Some("src/c.ts")), - node("file:src/a.ts", "File", Some("src/a.ts")), - node("file:src/b.ts", "File", Some("src/b.ts")), - ], - edges: vec![ - edge("DEPENDS_ON", "file:src/c.ts", "file:src/a.ts"), - edge("DEPENDS_ON", "file:src/b.ts", "file:src/a.ts"), - edge("DEPENDS_ON", "file:src/a.ts", "file:src/b.ts"), - ], - diagnostics: Vec::new(), - } -} - -fn python_test_snapshot() -> StoreQueryOutput { - StoreQueryOutput { - metadata: python_test_metadata(), - nodes: python_test_nodes(), - edges: python_test_edges(), - diagnostics: Vec::new(), - } -} - -fn python_test_metadata() -> GraphSnapshotMetadata { - GraphSnapshotMetadata { - schema_version: 1, - provider: "opcore-graph".to_string(), - repo: repo(), - generated_at: "2026-06-04T00:00:00.000Z".to_string(), - freshness: GraphFreshness { - generated_at: "2026-06-04T00:00:00.000Z".to_string(), - age_ms: 0, - max_age_ms: None, - stale: false, - reason: None, - }, - node_kinds: vec![ - "File".to_string(), - "Module".to_string(), - "Class".to_string(), - "Function".to_string(), - ], - edge_kinds: vec![ - "CONTAINS".to_string(), - "IMPORTS_FROM".to_string(), - "TESTED_BY".to_string(), - ], - } -} - -fn python_test_nodes() -> Vec { - vec![ - node("file:src/pkg/models.py", "File", Some("src/pkg/models.py")), - node( - "module:src/pkg/models.py#src.pkg.models", - "Module", - Some("src/pkg/models.py"), - ), - node( - "class:src/pkg/models.py#PublicModel", - "Class", - Some("src/pkg/models.py"), - ), - node( - "function:src/pkg/models.py#make_model", - "Function", - Some("src/pkg/models.py"), - ), - node( - "file:tests/test_models.py", - "File", - Some("tests/test_models.py"), - ), - node( - "module:tests/test_models.py#tests.test_models", - "Module", - Some("tests/test_models.py"), - ), - node_with_attributes( - "function:tests/test_models.py#test_make_model", - "Function", - Some("tests/test_models.py"), - json!({"isTest": true}), - ), - ] -} - -fn python_test_edges() -> Vec { - vec![ - edge( - "CONTAINS", - "file:src/pkg/models.py", - "module:src/pkg/models.py#src.pkg.models", - ), - edge( - "CONTAINS", - "module:src/pkg/models.py#src.pkg.models", - "class:src/pkg/models.py#PublicModel", - ), - edge( - "CONTAINS", - "module:src/pkg/models.py#src.pkg.models", - "function:src/pkg/models.py#make_model", - ), - edge( - "CONTAINS", - "file:tests/test_models.py", - "module:tests/test_models.py#tests.test_models", - ), - edge( - "CONTAINS", - "module:tests/test_models.py#tests.test_models", - "function:tests/test_models.py#test_make_model", - ), - edge( - "IMPORTS_FROM", - "file:tests/test_models.py", - "file:src/pkg/models.py", - ), - edge( - "TESTED_BY", - "class:src/pkg/models.py#PublicModel", - "function:tests/test_models.py#test_make_model", - ), - edge( - "TESTED_BY", - "function:src/pkg/models.py#make_model", - "function:tests/test_models.py#test_make_model", - ), - ] -} - -fn repo() -> RepoIdentity { - RepoIdentity { - repo_id: Some("fixture".to_string()), - repo_root: None, - remote_url: None, - commit_sha: None, - } -} - -fn node(id: &str, kind: &str, path: Option<&str>) -> GraphFactNode { - GraphFactNode { - id: id.to_string(), - kind: kind.to_string(), - path: path.map(str::to_string), - name: None, - attributes: None, - } -} - -fn node_with_attributes( - id: &str, - kind: &str, - path: Option<&str>, - attributes: serde_json::Value, -) -> GraphFactNode { - GraphFactNode { - attributes: Some(attributes), - ..node(id, kind, path) - } -} - -fn edge(kind: &str, from: &str, to: &str) -> GraphFactEdge { - GraphFactEdge { - id: Some(format!("{kind}:{from}->{to}")), - kind: kind.to_string(), - from: from.to_string(), - to: to.to_string(), - attributes: None, - } -} - -fn hash(path: &str, sha: &str) -> SourceFileHash { - SourceFileHash { - relative_path: path.to_string(), - absolute_path: format!("/repo/{path}"), - language: "typescript".to_string(), - sha256: sha.to_string(), - } -} - -fn assert_limited_without_dangling(nodes: &[GraphFactNode], edges: &[GraphFactEdge], limit: usize) { - assert!(nodes.len() <= limit); - let node_ids = nodes - .iter() - .map(|node| node.id.as_str()) - .collect::>(); - for edge in edges { - assert!(node_ids.contains(edge.from.as_str())); - assert!(node_ids.contains(edge.to.as_str())); - } -} diff --git a/crates/graph-core/src/query/tests/fixtures.rs b/crates/graph-core/src/query/tests/fixtures.rs new file mode 100644 index 0000000..c62db6d --- /dev/null +++ b/crates/graph-core/src/query/tests/fixtures.rs @@ -0,0 +1,349 @@ +use super::*; + +#[cfg(test)] +pub(super) fn fixture_snapshot() -> StoreQueryOutput { + StoreQueryOutput { + metadata: GraphSnapshotMetadata { + schema_version: 1, + provider: "opcore-graph".to_string(), + repo: repo(), + generated_at: "2026-06-04T00:00:00.000Z".to_string(), + freshness: GraphFreshness { + generated_at: "2026-06-04T00:00:00.000Z".to_string(), + age_ms: 0, + max_age_ms: None, + stale: false, + reason: None, + }, + node_kinds: vec![ + "File".to_string(), + "Function".to_string(), + "Test".to_string(), + ], + edge_kinds: vec![ + "CONTAINS".to_string(), + "DEPENDS_ON".to_string(), + "TESTED_BY".to_string(), + ], + }, + nodes: vec![ + node("file:src/models.ts", "File", Some("src/models.ts")), + node( + "file:src/components/GreetingCard.tsx", + "File", + Some("src/components/GreetingCard.tsx"), + ), + node( + "file:src/__tests__/greeting.test.ts", + "File", + Some("src/__tests__/greeting.test.ts"), + ), + node("function:src/models.ts#formatGreeting", "Function", None), + node( + "test:src/__tests__/greeting.test.ts#renders greeting cards", + "Test", + None, + ), + ], + edges: vec![ + edge( + "CONTAINS", + "file:src/models.ts", + "function:src/models.ts#formatGreeting", + ), + edge( + "DEPENDS_ON", + "file:src/components/GreetingCard.tsx", + "file:src/models.ts", + ), + edge( + "DEPENDS_ON", + "file:src/__tests__/greeting.test.ts", + "file:src/components/GreetingCard.tsx", + ), + edge( + "TESTED_BY", + "function:src/models.ts#formatGreeting", + "test:src/__tests__/greeting.test.ts#renders greeting cards", + ), + ], + diagnostics: Vec::new(), + } +} + +#[cfg(test)] +pub(super) fn inheritance_snapshot() -> StoreQueryOutput { + StoreQueryOutput { + metadata: GraphSnapshotMetadata { + schema_version: 1, + provider: "opcore-graph".to_string(), + repo: repo(), + generated_at: "2026-06-04T00:00:00.000Z".to_string(), + freshness: GraphFreshness { + generated_at: "2026-06-04T00:00:00.000Z".to_string(), + age_ms: 0, + max_age_ms: None, + stale: false, + reason: None, + }, + node_kinds: vec!["Class".to_string()], + edge_kinds: vec!["INHERITS".to_string()], + }, + nodes: vec![ + node( + "class:src/models.ts#BaseModel", + "Class", + Some("src/models.ts"), + ), + node( + "class:src/models.ts#DirectModel", + "Class", + Some("src/models.ts"), + ), + node( + "class:src/models.ts#IndirectModel", + "Class", + Some("src/models.ts"), + ), + node( + "class:src/other.ts#UnrelatedModel", + "Class", + Some("src/other.ts"), + ), + ], + edges: vec![ + edge( + "INHERITS", + "class:src/models.ts#DirectModel", + "class:src/models.ts#BaseModel", + ), + edge( + "INHERITS", + "class:src/models.ts#IndirectModel", + "class:src/models.ts#DirectModel", + ), + ], + diagnostics: Vec::new(), + } +} + +#[cfg(test)] +pub(super) fn cycle_snapshot() -> StoreQueryOutput { + StoreQueryOutput { + metadata: GraphSnapshotMetadata { + schema_version: 1, + provider: "opcore-graph".to_string(), + repo: repo(), + generated_at: "2026-06-04T00:00:00.000Z".to_string(), + freshness: GraphFreshness { + generated_at: "2026-06-04T00:00:00.000Z".to_string(), + age_ms: 0, + max_age_ms: None, + stale: false, + reason: None, + }, + node_kinds: vec!["File".to_string()], + edge_kinds: vec!["DEPENDS_ON".to_string()], + }, + nodes: vec![ + node("file:src/c.ts", "File", Some("src/c.ts")), + node("file:src/a.ts", "File", Some("src/a.ts")), + node("file:src/b.ts", "File", Some("src/b.ts")), + ], + edges: vec![ + edge("DEPENDS_ON", "file:src/c.ts", "file:src/a.ts"), + edge("DEPENDS_ON", "file:src/b.ts", "file:src/a.ts"), + edge("DEPENDS_ON", "file:src/a.ts", "file:src/b.ts"), + ], + diagnostics: Vec::new(), + } +} + +#[cfg(test)] +pub(super) fn python_test_snapshot() -> StoreQueryOutput { + StoreQueryOutput { + metadata: python_test_metadata(), + nodes: python_test_nodes(), + edges: python_test_edges(), + diagnostics: Vec::new(), + } +} + +fn python_test_metadata() -> GraphSnapshotMetadata { + GraphSnapshotMetadata { + schema_version: 1, + provider: "opcore-graph".to_string(), + repo: repo(), + generated_at: "2026-06-04T00:00:00.000Z".to_string(), + freshness: GraphFreshness { + generated_at: "2026-06-04T00:00:00.000Z".to_string(), + age_ms: 0, + max_age_ms: None, + stale: false, + reason: None, + }, + node_kinds: vec![ + "File".to_string(), + "Module".to_string(), + "Class".to_string(), + "Function".to_string(), + ], + edge_kinds: vec![ + "CONTAINS".to_string(), + "IMPORTS_FROM".to_string(), + "TESTED_BY".to_string(), + ], + } +} + +fn python_test_nodes() -> Vec { + vec![ + node("file:src/pkg/models.py", "File", Some("src/pkg/models.py")), + node( + "module:src/pkg/models.py#src.pkg.models", + "Module", + Some("src/pkg/models.py"), + ), + node( + "class:src/pkg/models.py#PublicModel", + "Class", + Some("src/pkg/models.py"), + ), + node( + "function:src/pkg/models.py#make_model", + "Function", + Some("src/pkg/models.py"), + ), + node( + "file:tests/test_models.py", + "File", + Some("tests/test_models.py"), + ), + node( + "module:tests/test_models.py#tests.test_models", + "Module", + Some("tests/test_models.py"), + ), + node_with_attributes( + "function:tests/test_models.py#test_make_model", + "Function", + Some("tests/test_models.py"), + json!({"isTest": true}), + ), + ] +} + +fn python_test_edges() -> Vec { + vec![ + edge( + "CONTAINS", + "file:src/pkg/models.py", + "module:src/pkg/models.py#src.pkg.models", + ), + edge( + "CONTAINS", + "module:src/pkg/models.py#src.pkg.models", + "class:src/pkg/models.py#PublicModel", + ), + edge( + "CONTAINS", + "module:src/pkg/models.py#src.pkg.models", + "function:src/pkg/models.py#make_model", + ), + edge( + "CONTAINS", + "file:tests/test_models.py", + "module:tests/test_models.py#tests.test_models", + ), + edge( + "CONTAINS", + "module:tests/test_models.py#tests.test_models", + "function:tests/test_models.py#test_make_model", + ), + edge( + "IMPORTS_FROM", + "file:tests/test_models.py", + "file:src/pkg/models.py", + ), + edge( + "TESTED_BY", + "class:src/pkg/models.py#PublicModel", + "function:tests/test_models.py#test_make_model", + ), + edge( + "TESTED_BY", + "function:src/pkg/models.py#make_model", + "function:tests/test_models.py#test_make_model", + ), + ] +} + +#[cfg(test)] +pub(super) fn repo() -> RepoIdentity { + RepoIdentity { + repo_id: Some("fixture".to_string()), + repo_root: None, + remote_url: None, + commit_sha: None, + } +} + +fn node(id: &str, kind: &str, path: Option<&str>) -> GraphFactNode { + GraphFactNode { + id: id.to_string(), + kind: kind.to_string(), + path: path.map(str::to_string), + name: None, + attributes: None, + } +} + +fn node_with_attributes( + id: &str, + kind: &str, + path: Option<&str>, + attributes: serde_json::Value, +) -> GraphFactNode { + GraphFactNode { + attributes: Some(attributes), + ..node(id, kind, path) + } +} + +#[cfg(test)] +pub(super) fn edge(kind: &str, from: &str, to: &str) -> GraphFactEdge { + GraphFactEdge { + id: Some(format!("{kind}:{from}->{to}")), + kind: kind.to_string(), + from: from.to_string(), + to: to.to_string(), + attributes: None, + } +} + +#[cfg(test)] +pub(super) fn hash(path: &str, sha: &str) -> SourceFileHash { + SourceFileHash { + relative_path: path.to_string(), + absolute_path: format!("/repo/{path}"), + language: "typescript".to_string(), + sha256: sha.to_string(), + } +} + +#[cfg(test)] +pub(super) fn assert_limited_without_dangling( + nodes: &[GraphFactNode], + edges: &[GraphFactEdge], + limit: usize, +) { + assert!(nodes.len() <= limit); + let node_ids = nodes + .iter() + .map(|node| node.id.as_str()) + .collect::>(); + for edge in edges { + assert!(node_ids.contains(edge.from.as_str())); + assert!(node_ids.contains(edge.to.as_str())); + } +} diff --git a/crates/graph-core/src/search.rs b/crates/graph-core/src/search.rs index c9a7f2f..2d01ea9 100644 --- a/crates/graph-core/src/search.rs +++ b/crates/graph-core/src/search.rs @@ -2,7 +2,10 @@ use crate::protocol::{ GraphFactEdge, GraphFactNode, GraphSearchMode, GraphSearchRequest, GraphSearchResultEntry, GraphSearchSummary, }; -use crate::store::{StoreError, StoreResult}; +use crate::store::{ + read::{CollectRows, Rows}, + StoreError, StoreResult, +}; use rusqlite::{params, Connection, OptionalExtension}; mod dependents; mod scoring; @@ -348,7 +351,7 @@ fn limit_to_usize(limit: u32) -> usize { fn indexed_node_kinds(connection: &Connection) -> StoreResult> { let mut statement = connection.prepare("select distinct kind from nodes_fts order by kind")?; let rows = statement.query_map([], |row| row.get::<_, String>(0))?; - collect_rows(rows) + Rows::collect(rows) } fn project_node(node: &GraphFactNode) -> SearchIndexRow { @@ -426,17 +429,6 @@ fn sorted_strings(values: Vec) -> Vec { .collect() } -fn collect_rows(rows: rusqlite::MappedRows<'_, F>) -> StoreResult> -where - F: FnMut(&rusqlite::Row<'_>) -> rusqlite::Result, -{ - let mut values = Vec::new(); - for row in rows { - values.push(row?); - } - Ok(values) -} - pub fn boundary_name() -> &'static str { "GraphProvider FTS5 search boundary" } diff --git a/crates/graph-core/src/search/scoring.rs b/crates/graph-core/src/search/scoring.rs index 5c7a518..6bc2512 100644 --- a/crates/graph-core/src/search/scoring.rs +++ b/crates/graph-core/src/search/scoring.rs @@ -1,4 +1,4 @@ -use super::{collect_rows, limit_to_usize, searchable_text}; +use super::{limit_to_usize, searchable_text, CollectRows, Rows}; use crate::store::StoreResult; use rusqlite::{params, Connection}; use std::collections::BTreeSet; @@ -30,7 +30,7 @@ pub(super) fn search_rows( "#, )?; let rows = statement.query_map(params![spec.fts_query], |row| map_search_row(row, &spec))?; - let mut rows = collect_rows(rows)?; + let mut rows = Rows::collect(rows)?; rows.sort_by(|left, right| { right .score diff --git a/crates/graph-core/src/store.rs b/crates/graph-core/src/store.rs index 0e9f0e7..390c98c 100644 --- a/crates/graph-core/src/store.rs +++ b/crates/graph-core/src/store.rs @@ -16,7 +16,7 @@ use time::format_description::well_known::Rfc3339; use time::OffsetDateTime; mod metadata; -mod read; +pub(crate) mod read; mod schema; mod types; mod write; @@ -29,12 +29,12 @@ pub use types::{ }; use metadata::{ - collect_rows, current_source_hashes, freshness_age_ms, hash_mismatch_reason, - missing_metadata_freshness, missing_watch_root_reason, read_optional_json, - scoped_source_hashes, source_hash_discovery_failed, stale_freshness, stale_metadata_freshness, + current_source_hashes, freshness_age_ms, hash_mismatch_reason, missing_metadata_freshness, + missing_watch_root_reason, read_optional_json, scoped_source_hashes, + source_hash_discovery_failed, stale_freshness, stale_metadata_freshness, stale_metadata_freshness_with_age, }; -use read::{read_edge_row, read_node_row}; +use read::{read_edge_row, read_node_row, CollectRows, Rows}; use schema::{configure_sqlite, migrate_or_validate, validate_schema}; #[cfg(test)] use schema::{require_index, require_table, STORE_INDEX_NAMES}; @@ -410,7 +410,7 @@ impl GraphStore { sha256: row.get(3)?, }) })?; - collect_rows(rows) + Rows::collect(rows) } fn read_nodes(&self) -> StoreResult> { @@ -418,7 +418,7 @@ impl GraphStore { .connection .prepare("select id, kind, path, name, extra from nodes order by id")?; let rows = statement.query_map([], read_node_row)?; - collect_rows(rows) + Rows::collect(rows) } fn read_edges(&self) -> StoreResult> { @@ -426,7 +426,7 @@ impl GraphStore { "select id, kind, source_qualified, target_qualified, extra from edges order by kind, source_qualified, target_qualified", )?; let rows = statement.query_map([], read_edge_row)?; - collect_rows(rows) + Rows::collect(rows) } fn read_kind_counts(&self, table: &str) -> StoreResult> { @@ -441,7 +441,7 @@ impl GraphStore { }; let mut statement = self.connection.prepare(sql)?; let rows = statement.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))?; - Ok(collect_rows(rows)?.into_iter().collect()) + Ok(Rows::collect(rows)?.into_iter().collect()) } } diff --git a/crates/graph-core/src/store/metadata.rs b/crates/graph-core/src/store/metadata.rs index b064239..2902525 100644 --- a/crates/graph-core/src/store/metadata.rs +++ b/crates/graph-core/src/store/metadata.rs @@ -1,14 +1,17 @@ use super::schema::user_version; use super::types::FreshnessState; -use super::{StoreError, StoreResult}; +use super::{ + read::{CollectRows, Rows}, + StoreError, StoreResult, +}; use crate::extraction::{ - collect_source_file_hashes, ExtractionOptions, SourceFileHash, EXTRACTION_GENERATED_AT, + collect_source_file_hashes, ExtractionOptions, SourceFileHash, SourcePathOps, SourcePaths, + EXTRACTION_GENERATED_AT, }; use crate::protocol::{GraphExtractionDiagnostic, GraphFreshness, GraphSnapshotMetadata}; use crate::{GRAPH_PROVIDER_NAME, GRAPH_SCHEMA_VERSION}; use rusqlite::{params, Connection, OptionalExtension}; use serde::de::DeserializeOwned; -use std::collections::BTreeMap; use std::path::Path; use time::format_description::well_known::Rfc3339; use time::OffsetDateTime; @@ -46,7 +49,7 @@ fn validate_metadata_table_json( metadata_key, diagnostics_key, }; - for (key, value) in collect_rows(rows)? { + for (key, value) in Rows::collect(rows)? { validate_metadata_row(&check, &key, &value)?; } Ok(()) @@ -152,17 +155,6 @@ fn parse_json(value: &str) -> Result serde_json::from_str(value) } -pub(super) fn collect_rows(rows: rusqlite::MappedRows<'_, F>) -> StoreResult> -where - F: FnMut(&rusqlite::Row<'_>) -> rusqlite::Result, -{ - let mut values = Vec::new(); - for row in rows { - values.push(row?); - } - Ok(values) -} - pub(super) fn optional_json(value: &Option) -> StoreResult> { value .as_ref() @@ -227,14 +219,8 @@ pub(super) fn hash_mismatch_reason( stored: &[SourceFileHash], current: &[SourceFileHash], ) -> Option { - let stored_by_path = stored - .iter() - .map(|hash| (hash.relative_path.as_str(), hash.sha256.as_str())) - .collect::>(); - let current_by_path = current - .iter() - .map(|hash| (hash.relative_path.as_str(), hash.sha256.as_str())) - .collect::>(); + let stored_by_path = SourcePaths::source_hashes_by_path(stored); + let current_by_path = SourcePaths::source_hashes_by_path(current); for path in stored_by_path.keys() { if !current_by_path.contains_key(path) { return Some(format!("source file {path} was removed")); diff --git a/crates/graph-core/src/store/read.rs b/crates/graph-core/src/store/read.rs index 3582b03..d5818bc 100644 --- a/crates/graph-core/src/store/read.rs +++ b/crates/graph-core/src/store/read.rs @@ -1,5 +1,26 @@ use crate::protocol::{GraphFactEdge, GraphFactNode}; +pub(crate) struct Rows; + +pub(crate) trait CollectRows { + fn collect(rows: rusqlite::MappedRows<'_, F>) -> super::StoreResult> + where + F: FnMut(&rusqlite::Row<'_>) -> rusqlite::Result; +} + +impl CollectRows for Rows { + fn collect(rows: rusqlite::MappedRows<'_, F>) -> super::StoreResult> + where + F: FnMut(&rusqlite::Row<'_>) -> rusqlite::Result, + { + let mut values = Vec::new(); + for row in rows { + values.push(row?); + } + Ok(values) + } +} + pub(super) fn read_node_row(row: &rusqlite::Row<'_>) -> rusqlite::Result { if let Some(extra) = row.get::<_, Option>(4)? { return parse_canonical_row(&extra, 4); diff --git a/crates/graph-core/src/store/schema.rs b/crates/graph-core/src/store/schema.rs index c4daef8..0c22482 100644 --- a/crates/graph-core/src/store/schema.rs +++ b/crates/graph-core/src/store/schema.rs @@ -1,11 +1,11 @@ -use super::metadata::{collect_rows, validate_metadata_json}; +use super::metadata::validate_metadata_json; +use super::read::{CollectRows, Rows}; use super::{ now_rfc3339, search, StoreError, StoreResult, STORE_SCHEMA_VERSION, WAL_AUTOCHECKPOINT_PAGES, }; use rusqlite::{params, Connection, OptionalExtension}; use std::collections::BTreeSet; -#[cfg_attr(not(test), allow(dead_code))] pub(super) const STORE_INDEX_NAMES: [&str; 8] = [ "idx_nodes_file", "idx_nodes_kind", @@ -318,7 +318,7 @@ pub(super) fn require_table(connection: &Connection, table: &str) -> StoreResult fn require_columns(connection: &Connection, table: &str, required: &[&str]) -> StoreResult<()> { let mut statement = connection.prepare(&format!("pragma table_info({table})"))?; let rows = statement.query_map([], |row| row.get::<_, String>(1))?; - let columns = collect_rows(rows)?.into_iter().collect::>(); + let columns = Rows::collect(rows)?.into_iter().collect::>(); for column in required { if !columns.contains(*column) { return Err(StoreError::SchemaMismatch { diff --git a/crates/graph-core/src/store/tests.rs b/crates/graph-core/src/store/tests.rs index be50de8..d7a23c4 100644 --- a/crates/graph-core/src/store/tests.rs +++ b/crates/graph-core/src/store/tests.rs @@ -1,6 +1,7 @@ use super::*; use crate::extraction::{extract_sources, ExtractionOptions}; use crate::protocol::{GraphFactQueryKind, GraphFactQuerySelector, GraphPipelineSummary}; +use crate::test_support::wave1_fixture_root; use crate::GRAPH_SCHEMA_VERSION; use std::fs; use std::path::PathBuf; @@ -430,12 +431,6 @@ fn copied_wave1_fixture() -> Result { Ok(destination) } -fn wave1_fixture_root() -> Result { - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("../../packages/fixtures/source-extraction/wave1") - .canonicalize() -} - fn copy_dir(source: &Path, destination: &Path) -> Result<(), std::io::Error> { fs::create_dir_all(destination)?; for entry in fs::read_dir(source)? { diff --git a/crates/graph-core/src/test_support.rs b/crates/graph-core/src/test_support.rs new file mode 100644 index 0000000..23ef610 --- /dev/null +++ b/crates/graph-core/src/test_support.rs @@ -0,0 +1,8 @@ +use std::path::PathBuf; + +#[cfg(test)] +pub(crate) fn wave1_fixture_root() -> Result { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../packages/fixtures/source-extraction/wave1") + .canonicalize() +} diff --git a/docs/architecture/graph-hub-inventory.md b/docs/architecture/graph-hub-inventory.md new file mode 100644 index 0000000..a47e7e6 --- /dev/null +++ b/docs/architecture/graph-hub-inventory.md @@ -0,0 +1,356 @@ +# Graph Hub Inventory + +This inventory records source files currently classified as graph hubs by +`docs.hub-coverage`. It is grouped by ownership track so architectural context +remains discoverable after module splits. Update the inventory when graph +topology adds or removes a hub; inclusion documents location, not public API +status or cross-package ownership. + +## crates/graph-core + +```text +crates/graph-core/src/clone/analysis.rs +crates/graph-core/src/daemon/lifecycle.rs +crates/graph-core/src/daemon/session.rs +crates/graph-core/src/extraction/diagnostics.rs +crates/graph-core/src/extraction/discovery.rs +crates/graph-core/src/extraction/language.rs +crates/graph-core/src/extraction/tests/support.rs +crates/graph-core/src/extraction/tsconfig.rs +crates/graph-core/src/lib.rs +crates/graph-core/src/pipeline.rs +crates/graph-core/src/protocol.rs +crates/graph-core/src/protocol/daemon.rs +crates/graph-core/src/protocol/facts.rs +crates/graph-core/src/protocol/provider.rs +crates/graph-core/src/query.rs +crates/graph-core/src/query/common.rs +crates/graph-core/src/query/index.rs +crates/graph-core/src/search.rs +crates/graph-core/src/store/metadata.rs +crates/graph-core/src/store/read.rs +crates/graph-core/src/store/schema.rs +crates/graph-core/src/store/types.rs +crates/graph-core/src/test_support.rs +crates/graph-core/src/watch.rs +``` + +## packages/asp-provider + +```text +packages/asp-provider/src/json-rpc.ts +packages/asp-provider/src/protocol.ts +``` + +## packages/contracts + +```text +packages/contracts/src/command/adapter-validator.ts +packages/contracts/src/command/contracts.ts +packages/contracts/src/command/helper-validators.ts +packages/contracts/src/command/router-02.ts +packages/contracts/src/command/router-contracts.ts +packages/contracts/src/command/validators.ts +packages/contracts/src/command/vocabulary.ts +packages/contracts/src/edit/contracts.ts +packages/contracts/src/edit/refusal-validator.ts +packages/contracts/src/edit/validators.ts +packages/contracts/src/edit/vocabulary.ts +packages/contracts/src/graph/daemon-validators-01.ts +packages/contracts/src/graph/daemon-validators-02.ts +packages/contracts/src/graph/helper-validators.ts +packages/contracts/src/graph/payload-validators.ts +packages/contracts/src/graph/pipeline-contracts.ts +packages/contracts/src/graph/protocol-validators.ts +packages/contracts/src/graph/provider-contracts-01.ts +packages/contracts/src/graph/provider-contracts-02.ts +packages/contracts/src/graph/provider-validators.ts +packages/contracts/src/graph/query-contracts-01.ts +packages/contracts/src/graph/query-contracts-02.ts +packages/contracts/src/graph/query-validators.ts +packages/contracts/src/graph/search-contracts.ts +packages/contracts/src/graph/search-validators.ts +packages/contracts/src/graph/vocabulary-01.ts +packages/contracts/src/graph/vocabulary-02.ts +packages/contracts/src/inspect/contracts-01.ts +packages/contracts/src/inspect/contracts-02.ts +packages/contracts/src/inspect/helper-validators-01.ts +packages/contracts/src/inspect/validators.ts +packages/contracts/src/managed/contracts.ts +packages/contracts/src/managed/helper-validators.ts +packages/contracts/src/managed/validators-01.ts +packages/contracts/src/managed/validators-02.ts +packages/contracts/src/managed/validators-03.ts +packages/contracts/src/product/init-contracts.ts +packages/contracts/src/product/init-validators-01.ts +packages/contracts/src/product/latency-contracts.ts +packages/contracts/src/product/metrics-contracts-01.ts +packages/contracts/src/product/metrics-contracts-02.ts +packages/contracts/src/product/metrics-coverage-validators.ts +packages/contracts/src/product/metrics-validators-01.ts +packages/contracts/src/product/metrics-validators-02.ts +packages/contracts/src/product/metrics-validators-03.ts +packages/contracts/src/product/metrics-validators-04.ts +packages/contracts/src/product/metrics-validators-05.ts +packages/contracts/src/product/status-contracts.ts +packages/contracts/src/product/status-validators.ts +packages/contracts/src/release/asp-contracts-01.ts +packages/contracts/src/release/asp-validators-02.ts +packages/contracts/src/release/cutover-contracts.ts +packages/contracts/src/release/cutover-validators-02.ts +packages/contracts/src/release/graph-contracts.ts +packages/contracts/src/release/graph-optional-validators.ts +packages/contracts/src/release/graph-validators-02.ts +packages/contracts/src/release/graph-validators-03.ts +packages/contracts/src/release/graph-vocabulary-01.ts +packages/contracts/src/release/graph-vocabulary-02.ts +packages/contracts/src/release/receipt-contracts-01.ts +packages/contracts/src/release/receipt-contracts-02.ts +packages/contracts/src/release/receipt-validators-01.ts +packages/contracts/src/release/receipt-validators-02.ts +packages/contracts/src/release/receipt-validators-03.ts +packages/contracts/src/release/vocabulary-01.ts +packages/contracts/src/release/vocabulary-02.ts +packages/contracts/src/shared/json.ts +packages/contracts/src/shared/path-validators.ts +packages/contracts/src/shared/primitives.ts +packages/contracts/src/shared/validators-01.ts +packages/contracts/src/shared/validators-02.ts +packages/contracts/src/validation/capability-contracts.ts +packages/contracts/src/validation/diagnostic-contracts.ts +packages/contracts/src/validation/prewrite-status-validators-01.ts +packages/contracts/src/validation/python-project-contracts-01.ts +packages/contracts/src/validation/python-project-contracts-02.ts +packages/contracts/src/validation/python-project-validators-01.ts +packages/contracts/src/validation/python-project-validators-02.ts +packages/contracts/src/validation/python-pytest-validators-02.ts +packages/contracts/src/validation/python-ruff-validators-03.ts +packages/contracts/src/validation/python-types-validators.ts +packages/contracts/src/validation/python-validator-primitives.ts +packages/contracts/src/validation/request-contracts.ts +packages/contracts/src/validation/request-validators-01.ts +packages/contracts/src/validation/request-validators-02.ts +packages/contracts/src/validation/result-validator.ts +packages/contracts/src/validation/status-contracts.ts +packages/contracts/src/validation/vocabulary-01.ts +packages/contracts/src/validation/vocabulary-02.ts +``` + +## packages/edit + +```text +packages/edit/src/atomic-writer.ts +packages/edit/src/codex-patch-parser.ts +packages/edit/src/command-parser.ts +packages/edit/src/content-policy.ts +packages/edit/src/hash.ts +packages/edit/src/language-service.ts +packages/edit/src/operations.ts +packages/edit/src/patch-parser.ts +packages/edit/src/patch-tree-command.ts +packages/edit/src/path-policy.ts +packages/edit/src/planner.ts +packages/edit/src/symbol-command.ts +packages/edit/src/symbol-graph.ts +packages/edit/src/symbol-preview.ts +packages/edit/src/symbol-requests.ts +packages/edit/src/tree-planner.ts +packages/edit/src/typescript-project/filesystem-discovery.ts +packages/edit/src/typescript-project/path-policy.ts +packages/edit/src/typescript-project/source-discovery.ts +packages/edit/src/typescript-project/tsconfig.ts +packages/edit/src/typescript-project/types.ts +packages/edit/src/validated-apply.ts +packages/edit/src/validation-request.ts +packages/edit/src/validation.ts +``` + +## packages/fixtures + +Fixture roots are excluded from repository self-validation, so their graph hubs +are recorded by basename without claiming file-view freshness evidence. + +```text +helpers.py +models.py +relative_target.py +stubs.pyi +math.js +``` + +## packages/graph + +```text +packages/graph/src/artifact.ts +packages/graph/src/ephemeral-snapshot.ts +packages/graph/src/native-targets.ts +packages/graph/src/sidecar.ts +``` + +## packages/opcore + +```text +packages/opcore/src/advanced/asp-warm/asp-warm-lifecycle.ts +packages/opcore/src/advanced/asp-warm/warm-project-registry.ts +packages/opcore/src/advanced/inspect-language-service.ts +packages/opcore/src/advanced/inspect-typescript-project.ts +packages/opcore/src/advanced/router.ts +packages/opcore/src/agent-gate.ts +packages/opcore/src/doctor.ts +packages/opcore/src/init-action-helpers.ts +packages/opcore/src/init-actions.ts +packages/opcore/src/init-apply.ts +packages/opcore/src/init-constants.ts +packages/opcore/src/init-context-payload.ts +packages/opcore/src/init-data.ts +packages/opcore/src/init-files.ts +packages/opcore/src/init-format.ts +packages/opcore/src/init-gitignore.ts +packages/opcore/src/init-guidance.ts +packages/opcore/src/init-hooks.ts +packages/opcore/src/init-messages.ts +packages/opcore/src/init-paths.ts +packages/opcore/src/init-payloads.ts +packages/opcore/src/init-plan.ts +packages/opcore/src/init-prompts.ts +packages/opcore/src/init-result.ts +packages/opcore/src/init-timing.ts +packages/opcore/src/init-types.ts +packages/opcore/src/init-undo-metadata.ts +packages/opcore/src/init-undo-plan.ts +packages/opcore/src/init-wizard-render.ts +packages/opcore/src/install-wizard.ts +packages/opcore/src/json-output.ts +packages/opcore/src/plate.ts +packages/opcore/src/repo-paths.ts +packages/opcore/src/repo-validation-policy.ts +packages/opcore/src/runtime-info.ts +packages/opcore/src/scan-presentation.ts +packages/opcore/src/scan-validation-preview.ts +packages/opcore/src/scan.ts +packages/opcore/src/serve-telemetry.ts +packages/opcore/src/source-policy.ts +packages/opcore/src/status-errors.ts +packages/opcore/src/status-git.ts +packages/opcore/src/status-repo.ts +packages/opcore/src/status-state.ts +packages/opcore/src/status-validation.ts +packages/opcore/src/status.ts +packages/opcore/src/stream-output.ts +packages/opcore/src/timing.ts +packages/opcore/src/validation-graph-session.ts +``` + +## packages/validation-clone + +```text +packages/validation-clone/src/check-constants.ts +packages/validation-clone/src/check-ids.ts +packages/validation-clone/src/source-files.ts +``` + +## packages/validation-docs + +```text +packages/validation-docs/src/check-constants.ts +packages/validation-docs/src/check-definition.ts +packages/validation-docs/src/check-ids.ts +packages/validation-docs/src/check-results.ts +packages/validation-docs/src/diagnostics.ts +packages/validation-docs/src/history.ts +packages/validation-docs/src/options.ts +packages/validation-docs/src/snapshot.ts +``` + +## packages/validation-policy + +```text +packages/validation-policy/src/check-packs.ts +packages/validation-policy/src/config.ts +packages/validation-policy/src/path-policy.ts +packages/validation-policy/src/types.ts +``` + +## packages/validation-python + +```text +packages/validation-python/src/check-constants.ts +packages/validation-python/src/check-ids.ts +packages/validation-python/src/diagnostics.ts +packages/validation-python/src/environment-resolution.ts +packages/validation-python/src/graph-requirements.ts +packages/validation-python/src/import-analysis.ts +packages/validation-python/src/ini-config.ts +packages/validation-python/src/mypy-config-values.ts +packages/validation-python/src/mypy-runner-types.ts +packages/validation-python/src/process.ts +packages/validation-python/src/project-config-files.ts +packages/validation-python/src/project-context.ts +packages/validation-python/src/project-fingerprint.ts +packages/validation-python/src/project-groups.ts +packages/validation-python/src/project-workspace.ts +packages/validation-python/src/pyright-config-values.ts +packages/validation-python/src/pytest-result.ts +packages/validation-python/src/pytest-types.ts +packages/validation-python/src/pytest-workspace.ts +packages/validation-python/src/python-context-result.ts +packages/validation-python/src/ruff-capability-run.ts +packages/validation-python/src/ruff-check-definition.ts +packages/validation-python/src/ruff-check-shared.ts +packages/validation-python/src/ruff-execution.ts +packages/validation-python/src/ruff-invocation-failure.ts +packages/validation-python/src/source-files.ts +packages/validation-python/src/source-types.ts +packages/validation-python/src/strict-json.ts +packages/validation-python/src/toml-config.ts +packages/validation-python/src/toolchain.ts +packages/validation-python/src/type-authority.ts +packages/validation-python/src/type-capability-run.ts +packages/validation-python/src/type-result.ts +packages/validation-python/src/type-runner-runtime.ts +packages/validation-python/src/type-runner-types.ts +packages/validation-python/src/version-constraint.ts +``` + +## packages/validation-rust + +```text +packages/validation-rust/src/cargo-metadata.ts +packages/validation-rust/src/cargo-target-cache.ts +packages/validation-rust/src/check-constants.ts +packages/validation-rust/src/check-ids.ts +packages/validation-rust/src/diagnostics.ts +packages/validation-rust/src/import-graph-check.ts +packages/validation-rust/src/materialize.ts +packages/validation-rust/src/process.ts +packages/validation-rust/src/retained-compatibility.ts +packages/validation-rust/src/source-files.ts +packages/validation-rust/src/toolchain.ts +``` + +## packages/validation-typescript + +```text +packages/validation-typescript/src/check-constants.ts +packages/validation-typescript/src/check-ids.ts +packages/validation-typescript/src/compiler-host.ts +packages/validation-typescript/src/dead-code-entrypoints.ts +packages/validation-typescript/src/diagnostics.ts +packages/validation-typescript/src/graph-requirements.ts +packages/validation-typescript/src/lint-helpers.ts +packages/validation-typescript/src/script-kind.ts +packages/validation-typescript/src/source-files.ts +packages/validation-typescript/src/test-paths.ts +``` + +## packages/validation + +```text +packages/validation/src/aggregation.ts +packages/validation/src/command-options.ts +packages/validation/src/registry.ts +packages/validation/src/request.ts +packages/validation/src/resources.ts +packages/validation/src/runner.ts +packages/validation/src/scope.ts +``` diff --git a/docs/architecture/runtime-cli-ard.md b/docs/architecture/runtime-cli-ard.md index b00665d..2532834 100644 --- a/docs/architecture/runtime-cli-ard.md +++ b/docs/architecture/runtime-cli-ard.md @@ -54,11 +54,11 @@ The ownership invariant is: do not collapse graph, edit, and policy ownership in | `opcore measure` | runtime | Read `.opcore/report.json` and `.opcore/history.jsonl` and return read-only metric deltas. | | `opcore try` | runtime | Generate local TS, Rust, mixed, and unsupported-file sample repos, run the launch loop, and publish nothing. | -Canonical `opcore graph`, `opcore edit`, `opcore check`, and `opcore validate` commands dispatch directly to public adapters owned by `packages/graph`, `packages/edit`, and `packages/validation`. Top-level `opcore inspect symbols|definition|references|signature|implementations|search` is CLI-owned read-only routing backed by public GraphProvider query/search APIs; `opcore inspect references --line [--column ]` adds #72 inspect-owned TypeScript/JavaScript language-service resolution for semantic reference evidence. `opcore inspect signature --line [--column ]` and `opcore inspect signature ` add #101 inspect-owned TypeScript/JavaScript language-service signature parity for functions, methods, constructors, classes, interfaces, type aliases, overloads, imported/aliased symbols, path aliases, TS/TSX, JS, and JSX after fresh GraphProvider evidence is available. `opcore inspect implementations --line [--column ]` and node-id targets add #102 read-only implementation evidence for TypeScript/TSX class implements, class extends, and interface extends relationships over fresh graph facts plus language-service materialization. Unimplemented graph inspect routes are not advertised as release behavior. `opcore edit` implements `exact`, `multi`, `search-replace`, `patch`, `tree`, `rename`, `move`, `signature`, `check`, and `apply` over edit-core, edit-owned patch/tree APIs, and graph-backed symbol planning. `opcore edit patch` accepts unified diffs and Codex `apply_patch` documents; unified `--3way` is explicitly de-scoped for this release and returns a typed refusal instead of attempting dirty-file merges. Symbol routes consume GraphProvider contract status/query/search evidence plus edit-owned TypeScript/JavaScript language-service materialization, then emit normal validation-required edit plans; apply/check refuses graph freshness changes before validation or writes. Search-replace must reject duplicate matches unless `replaceAll` is true. `opcore check` implements `files`, `staged`, `changed`, `tree`, `all`, and `manifest`; tree checks read committed Git tree content from `--tree ` and scope files from `--changed-from `. `opcore validate` implements request-file validation, `hypothetical`, `pre-write`, and `manifest`. Runtime-owned `opcore status` and `opcore doctor` include typed validation status payloads but do not own validation checks. `opcore status` emits a stable `repoState` payload and must remain read-only: no graph build/update/watch, validation check execution, package install, ASP setup, external development-tool execution, or source writes. `opcore` scan and `opcore check` must avoid source edits, hooks, setup, ASP setup, external development-tool execution, sibling checkouts, and package installs; scan may write only `.opcore/report.json`, `.opcore/history.jsonl`, and bounded `.opcore/telemetry.jsonl` capped at 500 records or 1 MiB. `opcore install` is the recommended setup writer in the public product facade; it runs the same read-only scan without report/history writes, emits scan/settings/interaction/timing in `opcoreInit`, prompts in an interactive Git repo to choose repo or global write-gate scope when neither scope nor `--json`/`--yes` is supplied, keeps JSON and non-TTY no-flag runs plan-only, and approved mode is additive/idempotent. Repo scope appends `.opcore/` to `.gitignore` only in Git repos that do not already ignore it, skips `.gitignore` in non-Git repos, writes `.opcore/config`, agent guidance, Opcore agent skill files, `.opcore/hooks/opcore-agent-gate.mjs`, active `.git/hooks/pre-commit` when safe, and merges Claude Code `.claude/settings.json` plus Codex `.codex/hooks.json`; undo uses repo `.opcore/init-undo.json` through `opcore uninstall`. Global scope writes `~/.opcore/hooks/opcore-agent-gate.mjs`, user-level skill files, merges `~/.claude/settings.json` and `~/.codex/hooks.json`, and records undo in `~/.opcore/init-undo.json`. `opcore init` remains a compatibility setup route with explicit `--approve` semantics. The adapter maps harness write/edit payloads to validation overlays, calls `opcore validate pre-write --request-file --timeout-ms 30000 --json`, exits 2 for non-ok receipts and validation command failures, and fail-opens only for adapter parse/mapping errors. Claude Code PreToolUse blocks matching Edit/MultiEdit/Write calls through exit 2. Codex PreToolUse is wired as the strongest current guardrail for supported edit aliases, but Codex hook trust and interception scope remain harness behavior, so public copy must not claim broader enforcement. The separate fail-closed pre-commit hook script remains opt-in through `--fail-closed-hook`. The managed `.opcore/` ignore covers `.opcore/telemetry.jsonl`. The public package exposes exactly the `opcore` and `opcore-asp-provider` bins. +Canonical `opcore graph`, `opcore edit`, `opcore check`, and `opcore validate` commands dispatch directly to public adapters owned by `packages/graph`, `packages/edit`, and `packages/validation`. Top-level `opcore inspect symbols|definition|references|signature|implementations|search` is CLI-owned read-only routing backed by public GraphProvider query/search APIs; `opcore inspect references --line [--column ]` adds #72 inspect-owned TypeScript/JavaScript language-service resolution for semantic reference evidence. `opcore inspect signature --line [--column ]` and `opcore inspect signature ` add #101 inspect-owned TypeScript/JavaScript language-service signature parity for functions, methods, constructors, classes, interfaces, type aliases, overloads, imported/aliased symbols, path aliases, TS/TSX, JS, and JSX after fresh GraphProvider evidence is available. `opcore inspect implementations --line [--column ]` and node-id targets add #102 read-only implementation evidence for TypeScript/TSX class implements, class extends, and interface extends relationships over fresh graph facts plus language-service materialization. Inspect routing and result ownership remain CLI-owned, while `packages/edit/src/typescript-project/` owns the shared ts-morph project discovery, tsconfig selection, import resolution, source listing, scope materialization, and injected-project snapshot seam consumed by cold inspect and warm ASP sessions. Unimplemented graph inspect routes are not advertised as release behavior. `opcore edit` implements `exact`, `multi`, `search-replace`, `patch`, `tree`, `rename`, `move`, `signature`, `check`, and `apply` over edit-core, edit-owned patch/tree APIs, and graph-backed symbol planning. `opcore edit patch` accepts unified diffs and Codex `apply_patch` documents; unified `--3way` is explicitly de-scoped for this release and returns a typed refusal instead of attempting dirty-file merges. Symbol routes consume GraphProvider contract status/query/search evidence plus edit-owned TypeScript/JavaScript language-service materialization, then emit normal validation-required edit plans; apply/check refuses graph freshness changes before validation or writes. Search-replace must reject duplicate matches unless `replaceAll` is true. `opcore check` implements `files`, `staged`, `changed`, `tree`, `all`, and `manifest`; tree checks read committed Git tree content from `--tree ` and scope files from `--changed-from `. `opcore validate` implements request-file validation, `hypothetical`, `pre-write`, and `manifest`. Runtime-owned `opcore status` and `opcore doctor` include typed validation status payloads but do not own validation checks. `opcore status` emits a stable `repoState` payload and must remain read-only: no graph build/update/watch, validation check execution, package install, ASP setup, external development-tool execution, or source writes. `opcore` scan and `opcore check` must avoid source edits, hooks, setup, ASP setup, external development-tool execution, sibling checkouts, and package installs; scan may write only `.opcore/report.json`, `.opcore/history.jsonl`, and bounded `.opcore/telemetry.jsonl` capped at 500 records or 1 MiB. `opcore install` is the recommended setup writer in the public product facade; it runs the same read-only scan without report/history writes, emits scan/settings/interaction/timing in `opcoreInit`, prompts in an interactive Git repo to choose repo or global write-gate scope when neither scope nor `--json`/`--yes` is supplied, keeps JSON and non-TTY no-flag runs plan-only, and approved mode is additive/idempotent. Repo scope appends `.opcore/` to `.gitignore` only in Git repos that do not already ignore it, skips `.gitignore` in non-Git repos, writes `.opcore/config`, agent guidance, Opcore agent skill files, `.opcore/hooks/opcore-agent-gate.mjs`, active `.git/hooks/pre-commit` when safe, and merges Claude Code `.claude/settings.json` plus Codex `.codex/hooks.json`; undo uses repo `.opcore/init-undo.json` through `opcore uninstall`. Global scope writes `~/.opcore/hooks/opcore-agent-gate.mjs`, user-level skill files, merges `~/.claude/settings.json` and `~/.codex/hooks.json`, and records undo in `~/.opcore/init-undo.json`. `opcore init` remains a compatibility setup route with explicit `--approve` semantics. The adapter maps harness write/edit payloads to validation overlays, calls `opcore validate pre-write --request-file --timeout-ms 30000 --json`, exits 2 for non-ok receipts and validation command failures, and fail-opens only for adapter parse/mapping errors. Claude Code PreToolUse blocks matching Edit/MultiEdit/Write calls through exit 2. Codex PreToolUse is wired as the strongest current guardrail for supported edit aliases, but Codex hook trust and interception scope remain harness behavior, so public copy must not claim broader enforcement. The separate fail-closed pre-commit hook script remains opt-in through `--fail-closed-hook`. The managed `.opcore/` ignore covers `.opcore/telemetry.jsonl`. The public package exposes exactly the `opcore` and `opcore-asp-provider` bins. #58 adds `opcore validate pre-write --request-file --timeout-ms 30000 --json` for hook integration. The route is validation-owned, file-based, fail-closed, overlay-only, and emits a typed `PreWriteValidationReceipt` with timing, repo, scope, checks, graph, overlay, status, and failure summary data. -#197 makes hypothetical graph-backed validation state-exact. `packages/validation` creates the before/after `ValidationFileView`, acquires one disposable exact graph session for each state, shares that immutable session across every selected check and streaming/fail-fast path, and disposes it on every exit. `reportMode:"introduced"` uses distinct before and after snapshots. `packages/graph` owns bounded temporary source materialization, one graph-core build, bound queries, and recursive cleanup; product, advanced, and ASP composition inject that implementation through validation-owned types. Exact materialization, build, listing, or query failures are non-pass even under optional persistent-graph policy. ASP continues to obtain all source content through host callbacks and preserves `workspace/listTree.truncated`. The target source tree, persistent `.opcore/graph`, configuration, lockfiles, environments, and caches remain untouched. +#197 makes hypothetical graph-backed validation state-exact. `packages/validation` creates the before/after `ValidationFileView`, acquires one disposable exact graph session for each state, shares that immutable session across every selected check and streaming/fail-fast path, and disposes it on every exit. `reportMode:"introduced"` uses distinct before and after snapshots. `packages/graph` owns bounded temporary source materialization including root `tsconfig.json` alias configuration, one graph-core build, bound queries, and recursive cleanup; product, advanced, and ASP composition inject that implementation through validation-owned types. Exact materialization, build, listing, or query failures are non-pass even under optional persistent-graph policy. ASP continues to obtain all source content through host callbacks and preserves `workspace/listTree.truncated`. The target source tree, persistent `.opcore/graph`, configuration, lockfiles, environments, and caches remain untouched. #30 adds `ReleaseCutoverReceipt` and `npm run cutover:check` as the installed-artifact cutover proof. The gate packs the public release packages, installs them into a clean temporary project, clears current-tool environment resolution, excludes local wrapper/sibling paths, verifies installed canonical bins (`opcore` and `opcore-asp-provider`), binds every command receipt id to its expected canonical command/status/exit, and fails on old-tool/private-path markers or advertised `not_implemented` release commands. @@ -75,7 +75,7 @@ There is no public `opcore asp` router group in this release. ASP Core check int | `packages/contracts` | Public wire contracts, schemas, command payloads, adapter request/result envelopes, validation shapes, graph query contracts, and generated TypeScript types. | | `crates/graph-core` | Planned Rust graph core for source extraction, parser integration, SQLite/WAL persistence, freshness metadata, watch daemon state, hot query execution, and clone index analysis through the existing native binary. | | `packages/graph` | npm facade/package track for graph commands, public graph command adapter, native graph-core loading, graph JSON output, and bounded disposable exact-state source snapshots. It must not duplicate graph-core internals or depend on the aggregate CLI. | -| `packages/edit` | Edit planner, public edit command adapter, patch/tree edits, symbol-aware orchestration, graph-backed discovery integration, and whole-plan validation. It consumes contracts and graph queries, not graph internals or the aggregate CLI. | +| `packages/edit` | Edit planner, public edit command adapter, patch/tree edits, symbol-aware orchestration, graph-backed discovery integration, whole-plan validation, and shared ts-morph project discovery/materialization consumed by edit plus CLI-owned inspect and warm ASP sessions. It consumes contracts and graph queries, not graph internals or the aggregate CLI. | | `packages/validation` | Mechanical checks, public check/validate command adapters, validation manifests, failure policy, hypothetical validation, file-view-state graph-session lifecycle, graph-aware rule orchestration, and `check`/`validate` reporting. It consumes contracts and injected graph adapters, not graph internals, edit internals, or the aggregate CLI. | | `packages/validation-rust` | Rust validation adapter rules, temporary workspace materialization from validation file views, and Cargo/native-tool-backed checks exposed to validation through contracts. It does not own host decisions or current external guardrail replacement policy. | | `packages/validation-typescript` | TypeScript adapter rules, compiler-backed checks, and TypeScript graph-aware validations exposed to validation through contracts. | @@ -103,6 +103,8 @@ Release artifacts must include: #8 adds Wave 1 staged source extraction in Rust graph-core for TS, TSX, JS, and JSX using OXC parser crates. Query responses may now return GraphProvider-compatible File, Class, Function, Type, and Test facts with typed extraction diagnostics. #9 adds the GraphProvider SQLite/WAL store at `.opcore/graph/graph.db`, freshness metadata, deterministic full-snapshot replacement, store-backed nodes/edges/neighbors/symbols selectors, and the #19 direct-reader reference evidence projection. +For TS/JS, graph-core emits file-level `TESTED_BY` from an imported source File to a conventional `.test.*`, `.spec.*`, or `__tests__` File, in addition to symbol-level test-call evidence. Ordinary importers and imports of another test file do not count as test evidence. The TypeScript relevant-test adapter emits diagnostics only when evidence is absent; positive evidence produces a clean passed check. Opcore self-validation exercises this through a real source-facade contract test resolved by root tsconfig aliases and executed by the normal Node test runner. + #10 adds `opcore graph build`, `opcore graph update`, `opcore graph watch`, and `opcore graph status --json` over the Rust graph-core pipeline. Build performs deterministic full-repo discovery, extraction, atomic store replacement, cached FileFacts persistence, phase timing, and WAL budget checks. Update compares stored and current file hashes, reparses changed files, removes deleted-file facts, reuses cached unchanged facts, records changed/deleted file summaries, and marks full rebuilds explicitly when cache state is insufficient. Build/update/status are scoped only by explicit `--paths`; `OPCORE_GRAPH_WATCH_PATHS` is watch-only default. Watch runs a polling Rust daemon with `.opcore/graph/daemon/{pid,state.json,daemon.log}` lifecycle artifacts, ignore-file and watch-path filtering, dirty/deleted reconciliation, graceful shutdown, and WAL checkpoint enforcement. #11 adds read-only store-backed `opcore graph impact`, named `opcore graph query`, `opcore graph review-context`, and `opcore graph detect-changes` envelopes; stale stores, schema mismatches, invalid daemon state, and unsupported query shapes return typed failures without graph payload arrays. #12 adds Rust graph-core FTS5 search with signature projection, `nodes_fts` schema ownership, full and incremental index maintenance, typed search failures, and canonical `opcore graph search` routing through the TypeScript graph adapter. The JSONL sidecar supports `ping` and `health` for lifecycle/status only; long-tail parser coverage remains follow-up graph-core work and must stay behind GraphProvider contracts. Graph pipeline command failures use router status `error` with exit code 1 and the typed GraphProvider failure status; the TypeScript facade must not fabricate pipeline summaries when graph-core returns no pipeline. `status` and `health` are read-only over existing store/lifecycle artifacts and must report missing stores without creating `.opcore/graph/graph.db`. diff --git a/opcore-zero.docs.json b/opcore-zero.docs.json new file mode 100644 index 0000000..6b5ec16 --- /dev/null +++ b/opcore-zero.docs.json @@ -0,0 +1,9 @@ +{ + "schemaVersion": 1, + "bindings": [ + { + "source": "packages/contracts/src/index.ts", + "document": "AGENTS.md" + } + ] +} diff --git a/package.json b/package.json index 798b778..55c63f3 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "rust:test": "cargo test", "setup": "npm ci", "setup:check-clean": "bash ./scripts/ci/check-setup-clean-worktree.sh", - "test": "node --test tests/*.test.mjs", + "test": "node --test tests/*.test.mjs tests/*.test.ts", "test:ci": "node scripts/run-test-ci.mjs", "verify": "bash ./scripts/ci/run-local-ci-equivalent.sh", "lint:eslint": "eslint ." diff --git a/packages/contracts/src/clone/validators.ts b/packages/contracts/src/clone/validators.ts new file mode 100644 index 0000000..3e07e0d --- /dev/null +++ b/packages/contracts/src/clone/validators.ts @@ -0,0 +1,191 @@ +import { validateExactValue, validateOptional, validateRequiredObject } from "../shared/validators-02.js"; +import { includesString } from "../shared/primitives.js"; +import { validateRepoRelativePaths } from "../shared/path-validators.js"; +import { CLONE_PROTOCOL } from "../graph/vocabulary-01.js"; +import { validateRepoIdentity, validateRepoRelativePath } from "../shared/path-validators.js"; +import { + validateNonEmptyString, + validateNonNegativeInteger, + validatePositiveInteger, + validateStringArray, +} from "../shared/validators-01.js"; +import type { + CloneAnalysisRequest, + CloneAnalysisResult, + CloneAnalysisSummary, + CloneFinding, + CloneReportMode, + CloneSourceReadMode} from "../validation/request-contracts.js"; +import { + cloneReportModes, + cloneSourceReadModes, +} from "../validation/request-contracts.js"; +import { validateHypotheticalOverlays } from "../validation/request-validators-01.js"; + +function validateCloneAnalysisRequest(request: CloneAnalysisRequest): CloneAnalysisRequest { + validateRequiredObject(request, "Clone analysis request is required"); + validateExactValue( + request.protocol, + CLONE_PROTOCOL, + `Clone analysis request protocol must be ${CLONE_PROTOCOL}`, + ); + validateOptional(request.requestId, (value) => + validateNonEmptyString(value, "Clone analysis request requestId"), + ); + validateExactValue(request.schemaVersion, 1, "Clone analysis request schemaVersion must be 1"); + validateRepoIdentity(request.repo); + validateCloneReportMode(request.reportMode, "Clone analysis request reportMode"); + validateOptional(request.paths, (value) => validateRepoRelativePaths(value, "Clone analysis request paths")); + validateOptional(request.sourcePaths, (value) => + validateRepoRelativePaths(value, "Clone analysis request sourcePaths"), + ); + validateOptional(request.sourceReadMode, (value) => + validateCloneSourceReadMode(value, "Clone analysis request sourceReadMode"), + ); + validateOptional(request.sourceTreeRef, (value) => + validateNonEmptyString(value, "Clone analysis request sourceTreeRef"), + ); + validateHypotheticalOverlays(request.overlays); + validateOptional(request.windowSize, (value) => + validatePositiveInteger(value, "Clone analysis request windowSize"), + ); + validateOptional(request.minLines, (value) => + validatePositiveInteger(value, "Clone analysis request minLines"), + ); + validateOptional(request.minTokens, (value) => + validatePositiveInteger(value, "Clone analysis request minTokens"), + ); + validateOptional(request.threshold, (value) => + validatePositiveInteger(value, "Clone analysis request threshold"), + ); + validateOptional(request.partitions, validateCloneAnalysisPartitions); + validateOptional(request.exclude, (value) => + validateStringArray(value, "Clone analysis request exclude", { + allowEmpty: true, + }), + ); + validateOptional(request.modes, (value) => + validateStringArray(value, "Clone analysis request modes", { + allowEmpty: true, + }), + ); + return request; +} + +export { validateCloneAnalysisRequest }; + +function validateCloneSourceReadMode(value: unknown, label: string): asserts value is CloneSourceReadMode { + if (!cloneSourceReadModes.includes(value as CloneSourceReadMode)) { + throw new Error(`${label} must be one of ${cloneSourceReadModes.join(", ")}`); + } +} + +export { validateCloneSourceReadMode }; + +function validateCloneAnalysisPartitions(partitions: readonly (readonly string[])[]): void { + if (!Array.isArray(partitions)) throw new Error("Clone analysis request partitions must be an array"); + for (const [index, partition] of partitions.entries()) { + validateStringArray(partition, `Clone analysis request partitions[${index}]`, { allowEmpty: false }); + } +} + +export { validateCloneAnalysisPartitions }; + +function validateCloneAnalysisResult(result: CloneAnalysisResult): CloneAnalysisResult { + validateRequiredObject(result, "Clone analysis result is required"); + if (result.protocol !== CLONE_PROTOCOL) { + throw new Error(`Clone analysis result protocol must be ${CLONE_PROTOCOL}`); + } + if (result.requestId !== undefined) validateNonEmptyString(result.requestId, "Clone analysis result requestId"); + if (result.schemaVersion !== 1) { + throw new Error("Clone analysis result schemaVersion must be 1"); + } + validateRepoIdentity(result.repo); + validateCloneReportMode(result.reportMode, "Clone analysis result reportMode"); + if (result.status !== "passed") { + throw new Error("Clone analysis result status must be passed"); + } + if (typeof result.persisted !== "boolean") { + throw new Error("Clone analysis result persisted must be boolean"); + } + if (result.dbPath !== undefined) validateRepoRelativePath(result.dbPath); + if (!Array.isArray(result.findings)) { + throw new Error("Clone analysis result findings must be an array"); + } + for (const finding of result.findings) validateCloneFinding(finding); + validateCloneAnalysisSummary(result.summary, result.findings.length); + return result; +} + +export { validateCloneAnalysisResult }; + +function validateCloneReportMode(mode: unknown, label: string): CloneReportMode { + if (!includesString(cloneReportModes, mode)) { + throw new Error(`Unknown ${label}: ${String(mode)}`); + } + return mode; +} + +export { validateCloneReportMode }; + +function validateCloneFinding(finding: CloneFinding): CloneFinding { + validateRequiredObject(finding, "Clone finding is required"); + for (const forbidden of ["line", "column", "startLine", "endLine", "startColumn", "endColumn"]) { + if (Object.hasOwn(finding, forbidden)) { + throw new Error("Clone finding identity must stay line-free"); + } + } + validateCloneClassId(finding.cloneClassId, "Clone finding cloneClassId"); + validateSha256Hex(finding.contentHash, "Clone finding contentHash"); + const path = validateRepoRelativePath(finding.path); + const peerPath = validateRepoRelativePath(finding.peerPath); + if (path === peerPath) { + throw new Error("Clone finding path and peerPath must be distinct"); + } + validateRepoRelativePaths(finding.paths, "Clone finding paths"); + if (!finding.paths.includes(path) || !finding.paths.includes(peerPath)) { + throw new Error("Clone finding paths must include path and peerPath"); + } + validatePositiveInteger(finding.lineCount, "Clone finding lineCount"); + validatePositiveInteger(finding.tokenCount, "Clone finding tokenCount"); + if (typeof finding.introduced !== "boolean") { + throw new Error("Clone finding introduced must be boolean"); + } + return finding; +} + +export { validateCloneFinding }; + +function validateCloneAnalysisSummary(summary: CloneAnalysisSummary, findingsLength: number): CloneAnalysisSummary { + validateRequiredObject(summary, "Clone analysis summary is required"); + validateNonNegativeInteger(summary.analyzedFiles, "Clone analysis summary analyzedFiles"); + validateNonNegativeInteger(summary.cloneClassCount, "Clone analysis summary cloneClassCount"); + validateNonNegativeInteger(summary.findingCount, "Clone analysis summary findingCount"); + validateNonNegativeInteger(summary.overlayCount, "Clone analysis summary overlayCount"); + if (summary.findingCount !== findingsLength) { + throw new Error("Clone analysis summary findingCount must equal findings length"); + } + return summary; +} + +export { validateCloneAnalysisSummary }; + +function validateCloneClassId(value: unknown, label: string): string { + const id = validateNonEmptyString(value, label); + if (!/^clone-[a-f0-9]{16}$/u.test(id)) { + throw new Error(`${label} must be a stable clone id`); + } + return id; +} + +export { validateCloneClassId }; + +function validateSha256Hex(value: unknown, label: string): string { + const sha = validateNonEmptyString(value, label); + if (!/^[a-f0-9]{64}$/u.test(sha)) { + throw new Error(`${label} must be a SHA-256 hex digest`); + } + return sha; +} + +export { validateSha256Hex }; diff --git a/packages/contracts/src/command/adapter-validator.ts b/packages/contracts/src/command/adapter-validator.ts new file mode 100644 index 0000000..b7ef2a9 --- /dev/null +++ b/packages/contracts/src/command/adapter-validator.ts @@ -0,0 +1,32 @@ +import { validateRequiredObject } from "../shared/validators-02.js"; +import { validateNonEmptyString, validateStringArray } from "../shared/validators-01.js"; +import type { CommandAdapterRequest } from "./router-contracts.js"; +import { validateManifestGroups } from "./validators.js"; + +function validateCommandAdapterRequest(request: CommandAdapterRequest): CommandAdapterRequest { + validateRequiredObject(request, "Command adapter request is required"); + if (request.schemaVersion !== 1) { + throw new Error("Command adapter request schemaVersion must be 1"); + } + validateNonEmptyString(request.bin, "Command adapter request bin"); + validateStringArray(request.argv, "Command adapter request argv", { + allowEmpty: true, + allowEmptyValues: true, + }); + validateStringArray(request.args, "Command adapter request args", { + allowEmpty: true, + allowEmptyValues: true, + }); + if (typeof request.json !== "boolean") { + throw new Error("Command adapter request json must be boolean"); + } + validateRequiredObject(request.group, "Command adapter request group is required"); + validateManifestGroups([request.group]); + validateStringArray(request.canonicalCommand, "Command adapter request canonicalCommand", { allowEmpty: false }); + if (!request.group.canonicalCommand.every((part, index) => request.canonicalCommand[index] === part)) { + throw new Error("Command adapter request canonicalCommand must start with the group canonicalCommand"); + } + return request; +} + +export { validateCommandAdapterRequest }; diff --git a/packages/contracts/src/command/contracts.ts b/packages/contracts/src/command/contracts.ts new file mode 100644 index 0000000..6cc51a5 --- /dev/null +++ b/packages/contracts/src/command/contracts.ts @@ -0,0 +1,35 @@ +import type { CommandOwner } from "./vocabulary.js"; + +interface CommandExitSemantics { + ok: 0; + error: 1; + notImplemented: 2; + unsupported: 64; + jsonStable: boolean; +} + +export type { CommandExitSemantics }; + +interface CommandGroupContract { + name: string; + owner: CommandOwner; + canonicalCommand: readonly string[]; + commands: readonly string[]; + summary: string; +} + +export type { CommandGroupContract }; + +interface CommandRouterManifest { + schemaVersion: 1; + packageName: "opcore" | (string & {}); + bins: readonly string[]; + exitSemantics: CommandExitSemantics; + ownershipBoundaries: readonly { + owner: CommandOwner; + summary: string; + }[]; + commandGroups: readonly CommandGroupContract[]; +} + +export type { CommandRouterManifest }; diff --git a/packages/contracts/src/command/helper-validators.ts b/packages/contracts/src/command/helper-validators.ts new file mode 100644 index 0000000..7898176 --- /dev/null +++ b/packages/contracts/src/command/helper-validators.ts @@ -0,0 +1,35 @@ +import { includesString } from "../shared/primitives.js"; +import type { + GraphReleaseSurfaceClassification} from "../release/graph-vocabulary-01.js"; +import { + graphReleaseSurfaceClassifications, +} from "../release/graph-vocabulary-01.js"; +import type { CommandOwner, CommandRouteStatus} from "./vocabulary.js"; +import { commandOwners, commandRouteStatuses } from "./vocabulary.js"; + +function validateCommandOwner(owner: unknown): CommandOwner { + if (!includesString(commandOwners, owner)) { + throw new Error(`Unknown command owner: ${String(owner)}`); + } + return owner; +} + +export { validateCommandOwner }; + +function validateCommandRouteStatus(status: unknown): CommandRouteStatus { + if (!includesString(commandRouteStatuses, status)) { + throw new Error(`Unknown command route status: ${String(status)}`); + } + return status; +} + +export { validateCommandRouteStatus }; + +function validateGraphReleaseSurfaceClassification(classification: unknown): GraphReleaseSurfaceClassification { + if (!includesString(graphReleaseSurfaceClassifications, classification)) { + throw new Error(`Unknown graph release surface classification: ${String(classification)}`); + } + return classification; +} + +export { validateGraphReleaseSurfaceClassification }; diff --git a/packages/contracts/src/command/manifest.ts b/packages/contracts/src/command/manifest.ts new file mode 100644 index 0000000..abbff93 --- /dev/null +++ b/packages/contracts/src/command/manifest.ts @@ -0,0 +1,108 @@ +import type { CommandExitSemantics, CommandRouterManifest } from "./contracts.js"; + +const commandExitSemantics: CommandExitSemantics = { + ok: 0, + error: 1, + notImplemented: 2, + unsupported: 64, + jsonStable: true, +}; + +export { commandExitSemantics }; + +const commandRouterManifest: CommandRouterManifest = { + schemaVersion: 1, + packageName: "opcore", + bins: ["opcore"], + exitSemantics: commandExitSemantics, + ownershipBoundaries: [ + { + owner: "graph", + summary: "Graph provider owns extraction, persistent facts, freshness, query, search, and impact contracts.", + }, + { + owner: "inspect", + summary: "Inspect owns read-only code intelligence over graph facts and language-service surfaces.", + }, + { + owner: "edit", + summary: "Edit planner owns symbol-aware rename, move, signature, patch, and tree edit orchestration.", + }, + { + owner: "validation", + summary: "Validation owns checks, hypothetical validation, manifests, failure policy, and check status.", + }, + { + owner: "runtime", + summary: "Runtime owns shared router health, help, and doctor surfaces.", + }, + ], + commandGroups: [ + { + name: "graph", + owner: "graph", + canonicalCommand: ["opcore", "graph"], + commands: [ + "build", + "update", + "watch", + "status", + "query", + "serve", + "impact", + "review-context", + "detect-changes", + "search", + ], + summary: + "GraphProvider build, update, watch, status, query, impact, review context, change detection, " + + "daemon lifecycle, and freshness behavior.", + }, + { + name: "inspect", + owner: "inspect", + canonicalCommand: ["opcore", "inspect"], + commands: ["symbols", "definition", "references", "signature", "implementations", "search"], + summary: "Read-only code intelligence over graph and inspect-owned language services.", + }, + { + name: "edit", + owner: "edit", + canonicalCommand: ["opcore", "edit"], + commands: ["exact", "multi", "search-replace", "check", "apply", "patch", "tree", "rename", "move", "signature"], + summary: + "Exact edit, multi-edit, search-replace, patch/tree, graph-backed symbol rename/move/signature, " + + "preview/check, and apply routes.", + }, + { + name: "check", + owner: "validation", + canonicalCommand: ["opcore", "check"], + commands: ["files", "staged", "changed", "tree", "all", "manifest"], + summary: "Mechanical check execution and check manifest behavior.", + }, + { + name: "validate", + owner: "validation", + canonicalCommand: ["opcore", "validate"], + commands: ["request", "hypothetical", "pre-write", "manifest"], + summary: "Hypothetical, pre-write, and validation request behavior.", + }, + { + name: "status", + owner: "runtime", + canonicalCommand: ["opcore", "status"], + commands: ["status"], + summary: "Shared router and runtime health status.", + }, + { + name: "doctor", + owner: "runtime", + canonicalCommand: ["opcore", "doctor"], + commands: ["doctor"], + summary: "Shared runtime diagnostic summary.", + }, + ], +}; + +export { commandRouterManifest }; diff --git a/packages/contracts/src/command/router-01.ts b/packages/contracts/src/command/router-01.ts new file mode 100644 index 0000000..1132ffc --- /dev/null +++ b/packages/contracts/src/command/router-01.ts @@ -0,0 +1,265 @@ +import { withoutUndefinedProperties } from "../shared/primitives.js"; +import { validateCommandAdapterRequest } from "./adapter-validator.js"; +import type { CommandGroupContract } from "./contracts.js"; +import { commandRouterManifest } from "./manifest.js"; +import { commandGroupByName, commandHelpMessage } from "./router-02.js"; +import type { + CommandAdapterRequest, + CommandRouterResult, + CommandRouterResultInput, + ParsedCommandArgv, + RouteCommandAdapterOptions, + RunCommandAdapterCliOptions, +} from "./router-contracts.js"; +import { validateCommandRouterResult } from "./validators.js"; +import type { CommandRouteStatus } from "./vocabulary.js"; + +declare const process: { + argv: string[]; + stdout: { write(text: string): void }; + stderr: { write(text: string): void }; +}; + +const commandHelpArgs = new Set(["--help", "-h", "help"]); + +export { commandHelpArgs }; + +function parseCommandArgv(argv: readonly string[]): ParsedCommandArgv { + return { + args: argv.filter((arg) => arg !== "--json"), + json: argv.includes("--json"), + }; +} + +export { parseCommandArgv }; + +function normalizeCommandBin(bin: string): string { + const normalized = bin.replaceAll("\\", "/").split("/").at(-1) ?? bin; + return normalized.endsWith(".js") ? "opcore" : normalized; +} + +export { normalizeCommandBin }; + +function commandExitCodeForStatus(status: CommandRouteStatus): number { + if (status === "ok") return commandRouterManifest.exitSemantics.ok; + if (status === "error") return commandRouterManifest.exitSemantics.error; + if (status === "not_implemented") return commandRouterManifest.exitSemantics.notImplemented; + return commandRouterManifest.exitSemantics.unsupported; +} + +export { commandExitCodeForStatus }; + +function createCommandRouterResult(input: CommandRouterResultInput): CommandRouterResult { + return validateCommandRouterResult( + withoutUndefinedProperties({ + schemaVersion: 1, + bin: input.bin, + argv: input.argv, + canonicalCommand: input.canonicalCommand, + owner: input.owner, + status: input.status, + exitCode: commandExitCodeForStatus(input.status), + message: input.message, + json: input.json, + providerStatus: input.providerStatus, + graphPipeline: input.graphPipeline, + graphQuery: input.graphQuery, + graphSearch: input.graphSearch, + inspectResult: input.inspectResult, + graphImpact: input.graphImpact, + graphReviewContext: input.graphReviewContext, + graphChanges: input.graphChanges, + graphServe: input.graphServe, + validationResult: input.validationResult, + validationStatus: input.validationStatus, + receipt: input.receipt, + editPlan: input.editPlan, + editResult: input.editResult, + repoState: input.repoState, + runtimeInfo: input.runtimeInfo, + opcoreDoctor: input.opcoreDoctor, + opcoreInit: input.opcoreInit, + opcoreMeasure: input.opcoreMeasure, + opcoreTry: input.opcoreTry, + timing: input.timing, + }) as CommandRouterResult, + ); +} + +export { createCommandRouterResult }; + +async function routeCommandAdapter(options: RouteCommandAdapterOptions): Promise { + const parsedArgv = parseCommandArgv(options.argv); + const parsed = resolveParsedCommandArgv(options, parsedArgv); + const bin = normalizeCommandBin(options.bin); + const group = commandGroupByName(options.groupName); + if (!group) { + return unsupportedCommandGroupResult(options, bin, parsed.json); + } + + if (shouldShowCommandHelp(options, group, parsed.args)) { + const routeName = parsed.args.find((arg) => !commandHelpArgs.has(arg) && !arg.startsWith("-")); + return commandHelpResult(bin, options.argv, parsed.json, group.name, routeName); + } + + const canonicalCommand = [...group.canonicalCommand, ...parsed.args.map(canonicalCommandArg)]; + const firstRouteArg = parsed.args.find((arg) => !arg.startsWith("-")); + if (isUnsupportedCommandRoute(options, group, firstRouteArg)) { + return unsupportedCommandRouteResult({ options, bin, json: parsed.json, group, canonicalCommand }); + } + + const adapterRequest = validateCommandAdapterRequest({ + schemaVersion: 1, + bin, + argv: options.argv, + args: parsed.args, + json: parsed.json, + group, + canonicalCommand, + }); + return invokeCommandAdapter({ options, adapterRequest, bin, json: parsed.json, group, canonicalCommand }); +} + +export { routeCommandAdapter }; + +function resolveParsedCommandArgv( + options: RouteCommandAdapterOptions, + parsed: ParsedCommandArgv, +): ParsedCommandArgv { + return { + args: options.args ?? parsed.args, + json: options.json ?? parsed.json, + }; +} + +function shouldShowCommandHelp( + options: RouteCommandAdapterOptions, + group: CommandGroupContract, + args: readonly string[], +): boolean { + const showHelpOnEmpty = options.showHelpOnEmpty ?? group.name !== "check"; + return args.some((arg) => commandHelpArgs.has(arg)) || (args.length === 0 && showHelpOnEmpty); +} + +function isUnsupportedCommandRoute( + options: RouteCommandAdapterOptions, + group: CommandGroupContract, + firstRouteArg: string | undefined, +): boolean { + const validateFirstRouteArg = options.validateFirstRouteArg !== false; + return Boolean(validateFirstRouteArg && firstRouteArg && !group.commands.includes(firstRouteArg)); +} + +function unsupportedCommandGroupResult( + options: RouteCommandAdapterOptions, + bin: string, + json: boolean, +): CommandRouterResult { + return createCommandRouterResult({ + bin, + argv: options.argv, + canonicalCommand: ["opcore", options.groupName], + owner: "runtime", + status: "unsupported", + json, + message: `Unsupported opcore command group: ${options.groupName}`, + }); +} + +export { unsupportedCommandGroupResult }; + +interface CommandRouteResultContext { + options: RouteCommandAdapterOptions; + bin: string; + json: boolean; + group: CommandGroupContract; + canonicalCommand: readonly string[]; +} + +function unsupportedCommandRouteResult(context: CommandRouteResultContext): CommandRouterResult { + const { options, bin, json, group, canonicalCommand } = context; + return createCommandRouterResult({ + bin, + argv: options.argv, + canonicalCommand, + owner: group.owner, + status: "unsupported", + json, + message: `${canonicalCommand.join(" ")} is not a supported ${group.name} route.`, + }); +} + +export { unsupportedCommandRouteResult }; + +async function invokeCommandAdapter( + context: CommandRouteResultContext & { adapterRequest: CommandAdapterRequest }, +): Promise { + const { options, adapterRequest, bin, json, group, canonicalCommand } = context; + try { + return validateCommandRouterResult(await options.adapter(adapterRequest)); + } catch (error) { + return createCommandRouterResult({ + bin, + argv: options.argv, + canonicalCommand, + owner: group.owner, + status: "error", + json, + message: `${canonicalCommand.join(" ")} failed: ${errorMessage(error)}`, + }); + } +} + +export { invokeCommandAdapter }; + +async function runCommandAdapterCli(options: RunCommandAdapterCliOptions): Promise { + const stdout = options.stdout ?? ((text: string) => process.stdout.write(text)); + const stderr = options.stderr ?? ((text: string) => process.stderr.write(text)); + const argv = options.argv ?? process.argv.slice(2); + const routed = await routeCommandAdapter({ + ...options, + argv, + }); + const text = routed.json ? JSON.stringify(routed) : routed.message; + const write = routed.json || routed.status === "ok" ? stdout : stderr; + write(`${text}\n`); + return routed.exitCode; +} + +export { runCommandAdapterCli }; + +function commandHelpResult( + ...args: readonly [bin: string, argv: readonly string[], json: boolean, groupName?: string, routeName?: string] +): CommandRouterResult { + const [bin, argv, json, groupName, routeName] = args; + const group = groupName ? commandGroupByName(groupName) : undefined; + const canonicalCommand = + group && routeName + ? [...group.canonicalCommand, routeName, "help"] + : group + ? [...group.canonicalCommand, "help"] + : ["opcore", "help"]; + return createCommandRouterResult({ + bin, + argv, + canonicalCommand, + owner: group?.owner ?? "runtime", + status: "ok", + json, + message: commandHelpMessage(groupName, routeName), + }); +} + +export { commandHelpResult }; + +function canonicalCommandArg(arg: string): string { + return arg.length === 0 ? "" : arg; +} + +export { canonicalCommandArg }; + +function errorMessage(error: unknown): string { + return error instanceof Error ? error.message : String(error); +} + +export { errorMessage }; diff --git a/packages/contracts/src/command/router-02.ts b/packages/contracts/src/command/router-02.ts new file mode 100644 index 0000000..065f8ac --- /dev/null +++ b/packages/contracts/src/command/router-02.ts @@ -0,0 +1,112 @@ +import type { CommandGroupContract } from "./contracts.js"; +import { commandRouterManifest } from "./manifest.js"; + +function commandGroupByName(groupName: string): CommandGroupContract | undefined { + return commandRouterManifest.commandGroups.find((group) => group.name === groupName); +} + +export { commandGroupByName }; + +function commandHelpMessage(groupName?: string, routeName?: string): string { + if (!groupName) { + return [ + "Opcore - local code intelligence and edit safety for coding agents.", + "Groups: graph, inspect, edit, check, validate, status, doctor", + ].join("\n"); + } + const group = commandGroupByName(groupName); + if (!group) return `Unknown opcore command group: ${groupName}`; + const routeHelp = routeName ? commandRouteHelpMessage(groupName, routeName) : undefined; + if (routeHelp !== undefined) return routeHelp; + return [ + `${group.canonicalCommand.join(" ")} - ${group.summary}`, + `Commands: ${group.commands.join(", ")}`, + ...(groupName === "graph" ? [`Syntax: ${contractHelpSyntax(group)}`] : []), + `Example: ${contractHelpExample(groupName)}`, + ].join("\n"); +} + +export { commandHelpMessage }; + +function commandRouteHelpMessage(groupName: string, routeName: string): string | undefined { + if (groupName === "graph" && routeName === "update") { + return [ + "Usage: opcore graph update [--repo ] [--base ] [--paths ] [--json]", + "Flags:", + " --repo Repository root to update.", + " --base Optional base ref for changed-file metadata.", + " --paths Optional repo-relative paths to refresh.", + " --json Emit structured JSON.", + "Defaults:", + " --repo defaults to the current working directory; JSON output is summary-oriented.", + "Examples:", + " opcore graph update --repo . --base HEAD --json", + " opcore graph update --repo . --paths src tests --json", + "Exit codes: 0 updated, 1 update failed, 64 unsupported.", + ].join("\n"); + } + if (groupName === "graph" && routeName === "build") { + return [ + "Usage: opcore graph build [--repo ] [--paths ] [--json]", + "Flags:", + " --repo Repository root to build.", + " --paths Optional repo-relative paths to index.", + " --json Emit structured JSON.", + "Defaults:", + " --repo defaults to the current working directory; JSON output is summary-oriented.", + "Examples:", + " opcore graph build --repo . --json", + "Exit codes: 0 built, 1 build failed, 64 unsupported.", + ].join("\n"); + } + if (groupName === "graph" && routeName === "status") { + return [ + "Usage: opcore graph status [--repo ] [--json]", + "Flags:", + " --repo Repository root to inspect.", + " --json Emit structured JSON.", + "Defaults:", + " --repo defaults to the current working directory.", + "Examples:", + " opcore graph status --repo . --json", + "Exit codes: 0 available or stale status read, 1 status failed, 64 unsupported.", + ].join("\n"); + } + if (groupName === "validate" && routeName === "pre-write") { + return [ + "Usage: opcore validate pre-write --request-file [--timeout-ms ] [--json]", + "Flags:", + " --request-file ValidationRequest JSON payload.", + " --timeout-ms Pre-write timeout in milliseconds.", + " --json Emit structured JSON.", + "Defaults:", + " --timeout-ms defaults to 30000.", + "Examples:", + " opcore validate pre-write --request-file ./validation-request.json --timeout-ms 30000 --json", + "Exit codes: 0 passed, 1 findings or errors, 64 unsupported.", + ].join("\n"); + } + return undefined; +} + +export { commandRouteHelpMessage }; + +function contractHelpSyntax(group: CommandGroupContract): string { + return `${group.canonicalCommand.join(" ")} <${group.commands.join("|")}> --repo . [--json]`; +} + +export { contractHelpSyntax }; + +function contractHelpExample(groupName: string): string { + if (groupName === "graph") return 'opcore graph search "GreetingCard" --repo . --limit 5'; + if (groupName === "inspect") return "opcore inspect definition GreetingCard --repo ."; + if (groupName === "edit") return 'opcore edit exact --path src/a.ts --expected "old" --replacement "new" --json'; + if (groupName === "check") return "opcore check files --files src/index.ts --json"; + if (groupName === "validate") + return "opcore validate pre-write --request-file ./validation-request.json --timeout-ms 30000 --json"; + if (groupName === "status") return "opcore status"; + if (groupName === "doctor") return "opcore doctor --json"; + return `opcore ${groupName} --help`; +} + +export { contractHelpExample }; diff --git a/packages/contracts/src/command/router-contracts.ts b/packages/contracts/src/command/router-contracts.ts new file mode 100644 index 0000000..a6880e9 --- /dev/null +++ b/packages/contracts/src/command/router-contracts.ts @@ -0,0 +1,139 @@ +import type { EditCommandResult, EditPlan } from "../edit/contracts.js"; +import type { GraphPipelineResult, GraphServeTransportStatus } from "../graph/pipeline-contracts.js"; +import type { GraphProviderStatus } from "../graph/provider-contracts-02.js"; +import type { GraphFactQueryResult, GraphNamedQueryResult } from "../graph/query-contracts-01.js"; +import type { + GraphDetectChangesResult, + GraphImpactResult, + GraphReviewContextResult, +} from "../graph/query-contracts-02.js"; +import type { GraphSearchResult } from "../graph/search-contracts.js"; +import type { InspectRouteResult } from "../inspect/contracts-02.js"; +import type { OpcoreInitPlanPayload } from "../product/init-contracts.js"; +import type { CommandTiming } from "../product/latency-contracts.js"; +import type { OpcoreMeasureDelta } from "../product/metrics-contracts-01.js"; +import type { OpcoreTryPayload } from "../product/metrics-contracts-02.js"; +import type { + OpcoreDoctorPayload, + OpcoreRepoStatePayload, + OpcoreRuntimeInfoPayload, +} from "../product/status-contracts.js"; +import type { ValidationResult } from "../validation/capability-contracts.js"; +import type { PreWriteValidationReceipt, ValidationStatusPayload } from "../validation/status-contracts.js"; +import type { CommandGroupContract } from "./contracts.js"; +import type { CommandOwner, CommandRouteStatus } from "./vocabulary.js"; + +interface CommandRouterContext { + bin: string; + argv: readonly string[]; + canonicalCommand: readonly string[]; + owner: CommandOwner; + status: CommandRouteStatus; + message: string; + json: boolean; +} + +export type { CommandRouterContext }; + +interface CommandRouterGraphPayloads { + providerStatus?: GraphProviderStatus; + graphPipeline?: GraphPipelineResult; + graphQuery?: GraphFactQueryResult | GraphNamedQueryResult; + graphSearch?: GraphSearchResult; + inspectResult?: InspectRouteResult; + graphImpact?: GraphImpactResult; + graphReviewContext?: GraphReviewContextResult; + graphChanges?: GraphDetectChangesResult; + graphServe?: GraphServeTransportStatus; +} + +export type { CommandRouterGraphPayloads }; + +interface CommandRouterActionPayloads { + validationResult?: ValidationResult; + validationStatus?: ValidationStatusPayload; + receipt?: PreWriteValidationReceipt; + editPlan?: EditPlan; + editResult?: EditCommandResult; +} + +export type { CommandRouterActionPayloads }; + +interface CommandRouterProductPayloads { + repoState?: OpcoreRepoStatePayload; + runtimeInfo?: OpcoreRuntimeInfoPayload; + opcoreDoctor?: OpcoreDoctorPayload; + opcoreInit?: OpcoreInitPlanPayload; + opcoreMeasure?: OpcoreMeasureDelta; + opcoreTry?: OpcoreTryPayload; + timing?: CommandTiming; +} + +export type { CommandRouterProductPayloads }; + +interface CommandRouterResult + extends CommandRouterContext, + CommandRouterGraphPayloads, + CommandRouterActionPayloads, + CommandRouterProductPayloads { + schemaVersion: 1; + exitCode: number; +} + +export type { CommandRouterResult }; + +interface ParsedCommandArgv { + args: readonly string[]; + json: boolean; +} + +export type { ParsedCommandArgv }; + +interface CommandRouterResultInput + extends CommandRouterContext, + CommandRouterGraphPayloads, + CommandRouterActionPayloads, + CommandRouterProductPayloads {} + +export type { CommandRouterResultInput }; + +interface CommandAdapterRequest { + schemaVersion: 1; + bin: string; + argv: readonly string[]; + args: readonly string[]; + json: boolean; + group: CommandGroupContract; + canonicalCommand: readonly string[]; +} + +export type { CommandAdapterRequest }; + +type CommandAdapter = (request: CommandAdapterRequest) => CommandRouterResult | Promise; + +export type { CommandAdapter }; + +type CommandRouterWriter = (text: string) => void; + +export type { CommandRouterWriter }; + +interface RouteCommandAdapterOptions { + bin: string; + argv: readonly string[]; + groupName: string; + adapter: CommandAdapter; + args?: readonly string[]; + json?: boolean; + showHelpOnEmpty?: boolean; + validateFirstRouteArg?: boolean; +} + +export type { RouteCommandAdapterOptions }; + +interface RunCommandAdapterCliOptions extends Omit { + argv?: readonly string[]; + stdout?: CommandRouterWriter; + stderr?: CommandRouterWriter; +} + +export type { RunCommandAdapterCliOptions }; diff --git a/packages/contracts/src/command/validators.ts b/packages/contracts/src/command/validators.ts new file mode 100644 index 0000000..c632e80 --- /dev/null +++ b/packages/contracts/src/command/validators.ts @@ -0,0 +1,193 @@ +import { + validateBoolean, + validateOptional, + validateRequiredObject, +} from "../shared/validators-02.js"; +import { validateEditCommandResult, validateEditPlanPayload } from "../edit/validators.js"; +import { validateGraphPipelineResult } from "../graph/daemon-validators-01.js"; +import { validateGraphServeTransportStatus } from "../graph/daemon-validators-02.js"; +import { isNamedQueryResult } from "../graph/payload-validators.js"; +import { validateProviderStatus } from "../graph/provider-validators.js"; +import { + validateGraphDetectChangesResult, + validateGraphFactQueryResult, + validateGraphImpactResult, + validateGraphNamedQueryResult, + validateGraphReviewContextResult, +} from "../graph/query-validators.js"; +import { validateGraphSearchResult } from "../graph/search-validators.js"; +import { validateInspectRouteResult } from "../inspect/validators.js"; +import { + validateOpcoreDoctorPayload, + validateOpcoreInitPlanPayload, + validateOpcoreRuntimeInfoPayload, +} from "../product/init-validators-01.js"; +import { validateCommandTiming } from "../product/metrics-validators-01.js"; +import { validateOpcoreMeasureDelta, validateOpcoreTryPayload } from "../product/metrics-validators-03.js"; +import { validateOpcoreRepoStatePayload } from "../product/status-validators.js"; +import { validateExitCodeForStatus, validateNonEmptyString, validateStringArray } from "../shared/validators-01.js"; +import { + validatePreWriteValidationReceipt, + validateValidationStatusPayload, +} from "../validation/prewrite-status-validators-01.js"; +import { validateValidationResultPayload } from "../validation/result-validator.js"; +import type { CommandGroupContract, CommandRouterManifest } from "./contracts.js"; +import { validateCommandOwner, validateCommandRouteStatus } from "./helper-validators.js"; +import type { CommandRouterResult } from "./router-contracts.js"; + +function validateCommandRouterManifestHeader(manifest: CommandRouterManifest): void { + validateRequiredObject(manifest, "Command router manifest is required"); + if (manifest.schemaVersion !== 1) { + throw new Error("Command router manifest schemaVersion must be 1"); + } + if (typeof manifest.packageName !== "string" || manifest.packageName.length === 0) { + throw new Error("Command router manifest packageName must be a non-empty string"); + } +} + +export { validateCommandRouterManifestHeader }; + +function validateManifestBins(bins: readonly string[]): void { + if (!Array.isArray(bins) || bins.length === 0) { + throw new Error("Command router manifest must include bins"); + } + for (const bin of bins) { + if (typeof bin !== "string" || bin.length === 0) { + throw new Error("Command router manifest bins must be non-empty strings"); + } + } +} + +export { validateManifestBins }; + +function validateManifestGroups(commandGroups: readonly CommandGroupContract[]): Set { + const groupNames = new Set(); + for (const group of commandGroups) { + validateCommandOwner(group.owner); + validateNonEmptyString(group.name, "Command group name"); + validateStringArray(group.canonicalCommand, "Command group canonicalCommand", { allowEmpty: false }); + validateStringArray(group.commands, "Command group commands", { + allowEmpty: false, + }); + validateNonEmptyString(group.summary, "Command group summary"); + groupNames.add(group.name); + } + + return groupNames; +} + +export { validateManifestGroups }; + +function validateManifestOwnershipBoundaries(boundaries: CommandRouterManifest["ownershipBoundaries"]): void { + for (const boundary of boundaries) { + validateCommandOwner(boundary.owner); + validateNonEmptyString(boundary.summary, "Command ownership boundary summary"); + } +} + +export { validateManifestOwnershipBoundaries }; + +function validateCommandRouterResult(result: CommandRouterResult): CommandRouterResult { + validateRequiredObject(result, "Command router result is required"); + if (result.schemaVersion !== 1) { + throw new Error("Command router result schemaVersion must be 1"); + } + validateNonEmptyString(result.bin, "Command router result bin"); + validateStringArray(result.argv, "Command router result argv", { + allowEmpty: true, + allowEmptyValues: true, + }); + validateStringArray(result.canonicalCommand, "Command router result canonicalCommand", { allowEmpty: false }); + validateCommandOwner(result.owner); + validateCommandRouteStatus(result.status); + validateExitCodeForStatus(result.exitCode, result.status); + validateNonEmptyString(result.message, "Command router result message"); + validateBoolean(result.json, "Command router result json"); + validateCommandRouterGraphPayloads(result); + validateCommandRouterValidationPayloads(result); + validateCommandRouterProductPayloads(result); + validateCommandRouterPayloadOwners(result); + validateCommandRouterEditMessage(result); + return result; +} + +export { validateCommandRouterResult }; + +function validateCommandRouterGraphPayloads(result: CommandRouterResult): void { + validateOptional(result.providerStatus, validateProviderStatus); + validateOptional(result.graphPipeline, validateGraphPipelineResult); + validateOptional(result.graphQuery, (query) => { + if (isNamedQueryResult(query)) validateGraphNamedQueryResult(query); + else validateGraphFactQueryResult(query); + }); + validateOptional(result.graphSearch, validateGraphSearchResult); + validateOptional(result.inspectResult, validateInspectRouteResult); + validateOptional(result.graphImpact, validateGraphImpactResult); + validateOptional(result.graphReviewContext, validateGraphReviewContextResult); + validateOptional(result.graphChanges, validateGraphDetectChangesResult); + validateOptional(result.graphServe, validateGraphServeTransportStatus); +} + +export { validateCommandRouterGraphPayloads }; + +function validateCommandRouterValidationPayloads(result: CommandRouterResult): void { + validateOptional(result.validationResult, validateValidationResultPayload); + validateOptional(result.validationStatus, validateValidationStatusPayload); + validateOptional(result.receipt, validatePreWriteValidationReceipt); + validateOptional(result.editPlan, validateEditPlanPayload); + validateOptional(result.editResult, validateEditCommandResult); +} + +export { validateCommandRouterValidationPayloads }; + +function validateCommandRouterProductPayloads(result: CommandRouterResult): void { + validateOptional(result.repoState, validateOpcoreRepoStatePayload); + validateOptional(result.runtimeInfo, validateOpcoreRuntimeInfoPayload); + validateOptional(result.opcoreDoctor, validateOpcoreDoctorPayload); + validateOptional(result.opcoreInit, validateOpcoreInitPlanPayload); + validateOptional(result.opcoreMeasure, validateOpcoreMeasureDelta); + validateOptional(result.opcoreTry, validateOpcoreTryPayload); + validateOptional(result.timing, validateCommandTiming); +} + +export { validateCommandRouterProductPayloads }; + +function validateCommandRouterPayloadOwners(result: CommandRouterResult): void { + validatePayloadOwner(result.repoState, result.owner, "runtime", "Opcore repoState payload requires runtime owner"); + validatePayloadOwner( + result.runtimeInfo, + result.owner, + "runtime", + "Opcore runtime info payload requires runtime owner", + ); + validatePayloadOwner(result.opcoreDoctor, result.owner, "runtime", "Opcore doctor payload requires runtime owner"); + validatePayloadOwner(result.opcoreInit, result.owner, "runtime", "Opcore init payload requires runtime owner"); + validatePayloadOwner(result.opcoreMeasure, result.owner, "runtime", "Opcore measure payload requires runtime owner"); + validatePayloadOwner(result.opcoreTry, result.owner, "runtime", "Opcore try payload requires runtime owner"); + validatePayloadOwner(result.editPlan, result.owner, "edit", "Edit router payloads require edit owner"); + validatePayloadOwner(result.editResult, result.owner, "edit", "Edit router payloads require edit owner"); +} + +export { validateCommandRouterPayloadOwners }; + +function validatePayloadOwner( + payload: unknown, + owner: CommandRouterResult["owner"], + expectedOwner: CommandRouterResult["owner"], + message: string, +): void { + if (payload !== undefined && owner !== expectedOwner) throw new Error(message); +} + +export { validatePayloadOwner }; + +function validateCommandRouterEditMessage(result: CommandRouterResult): void { + if (result.owner !== "edit" || result.status !== "ok") return; + if (result.editPlan !== undefined || result.editResult !== undefined) return; + const hiddenPayloadPattern = /"?(editPlan|editResult|planId|changes|afterState)"?\s*[:{[]/; + if (hiddenPayloadPattern.test(result.message)) { + throw new Error("Edit router payloads must use editPlan/editResult fields, not message strings"); + } +} + +export { validateCommandRouterEditMessage }; diff --git a/packages/contracts/src/command/vocabulary.ts b/packages/contracts/src/command/vocabulary.ts new file mode 100644 index 0000000..cc353e6 --- /dev/null +++ b/packages/contracts/src/command/vocabulary.ts @@ -0,0 +1,56 @@ +const commandOwners = ["graph", "inspect", "edit", "validation", "runtime"] as const; + +export { commandOwners }; + +type CommandOwner = (typeof commandOwners)[number]; + +export type { CommandOwner }; + +const commandRouteStatuses = ["ok", "error", "not_implemented", "unsupported"] as const; + +export { commandRouteStatuses }; + +type CommandRouteStatus = (typeof commandRouteStatuses)[number]; + +export type { CommandRouteStatus }; + +const commandTimingProcessStates = ["cold", "warm"] as const; + +export { commandTimingProcessStates }; + +type CommandTimingProcessState = (typeof commandTimingProcessStates)[number]; + +export type { CommandTimingProcessState }; + +const commandTimingDegradationReasons = ["no_source", "no_paths"] as const; + +export { commandTimingDegradationReasons }; + +type CommandTimingDegradationReason = (typeof commandTimingDegradationReasons)[number]; + +export type { CommandTimingDegradationReason }; + +const latencyBudgetResultStatuses = ["pass", "over"] as const; + +export { latencyBudgetResultStatuses }; + +type LatencyBudgetResultStatus = (typeof latencyBudgetResultStatuses)[number]; + +export type { LatencyBudgetResultStatus }; + +const commandLatencyTelemetryBins = ["opcore", "opcore-asp-provider"] as const; + +export { commandLatencyTelemetryBins }; + +type CommandLatencyTelemetryBin = (typeof commandLatencyTelemetryBins)[number]; + +export type { CommandLatencyTelemetryBin }; + +const commandLatencyTelemetryArtifactPolicy = { + path: ".opcore/telemetry.jsonl", + maxRecords: 500, + maxBytes: 1024 * 1024, + rotation: "ring_buffer", +} as const; + +export { commandLatencyTelemetryArtifactPolicy }; diff --git a/packages/contracts/src/edit/contracts.ts b/packages/contracts/src/edit/contracts.ts new file mode 100644 index 0000000..eaa477e --- /dev/null +++ b/packages/contracts/src/edit/contracts.ts @@ -0,0 +1,98 @@ +import type { RepoIdentity } from "../graph/provider-contracts-01.js"; +import type { ValidationResult } from "../validation/capability-contracts.js"; +import type { ValidationRequest } from "../validation/request-contracts.js"; +import type { EditRefusalCategory } from "./vocabulary.js"; + +interface RepoRelativeChangeBase { + path: string; + checksumBefore?: string; + checksumAfter?: string; +} + +export type { RepoRelativeChangeBase }; + +type RepoRelativeChange = + | (RepoRelativeChangeBase & { + kind: "create" | "replace"; + content: string; + }) + | (RepoRelativeChangeBase & { + kind: "delete"; + }) + | { + kind: "rename"; + path: string; + toPath: string; + checksumBefore?: string; + }; + +export type { RepoRelativeChange }; + +interface AtomicApplyMetadata { + strategy: "all_or_nothing"; + planHash?: string; + expectedBaseSha?: string; +} + +export type { AtomicApplyMetadata }; + +interface EditPlanValidationRequirement { + required: boolean; + request: ValidationRequest; +} + +export type { EditPlanValidationRequirement }; + +interface EditPlan { + planId: string; + repo: RepoIdentity; + changes: readonly RepoRelativeChange[]; + atomic: AtomicApplyMetadata; + validation: EditPlanValidationRequirement; +} + +export type { EditPlan }; + +interface EditRefusal { + category: EditRefusalCategory; + message: string; + path?: string; +} + +export type { EditRefusal }; + +interface EditPlanResult { + planId: string; + ok: boolean; + applied: boolean; + appliedAt?: string; + refusal?: EditRefusal; + validation?: ValidationResult; +} + +export type { EditPlanResult }; + +interface EditPlanRollbackState { + completed: boolean; + restoredPaths: readonly string[]; + failedPaths: readonly string[]; + cleanupFailedPaths: readonly string[]; +} + +export type { EditPlanRollbackState }; + +interface EditCommandResult { + ok: boolean; + applied: boolean; + planId?: string; + planHash?: string; + appliedAt?: string; + matchCount?: number; + afterState?: Readonly>; + validationRequest?: ValidationRequest; + validation?: ValidationResult; + refusal?: EditRefusal; + rollback?: EditPlanRollbackState; +} + +export type { EditCommandResult }; diff --git a/packages/contracts/src/edit/refusal-validator.ts b/packages/contracts/src/edit/refusal-validator.ts new file mode 100644 index 0000000..b7e40cf --- /dev/null +++ b/packages/contracts/src/edit/refusal-validator.ts @@ -0,0 +1,17 @@ +import { includesString, validateRequiredObject } from "../shared/primitives.js"; +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { validateNonEmptyString } from "../shared/validators-01.js"; +import type { EditRefusal } from "./contracts.js"; +import { editRefusalCategories } from "./vocabulary.js"; + +function validateEditRefusal(refusal: EditRefusal): EditRefusal { + validateRequiredObject(refusal, "Edit refusal is required"); + if (!includesString(editRefusalCategories, refusal.category)) { + throw new Error(`Unknown edit refusal category: ${String(refusal.category)}`); + } + validateNonEmptyString(refusal.message, "Edit refusal message"); + if (refusal.path !== undefined) validateRepoRelativePath(refusal.path); + return refusal; +} + +export { validateEditRefusal }; diff --git a/packages/contracts/src/edit/validators.ts b/packages/contracts/src/edit/validators.ts new file mode 100644 index 0000000..0924a92 --- /dev/null +++ b/packages/contracts/src/edit/validators.ts @@ -0,0 +1,126 @@ +import { + validateBoolean, + validateOptional, + validateRequiredObject, +} from "../shared/validators-02.js"; +import { + validateRepoIdentity, + validateRepoRelativePath, + validateRepoRelativePaths, +} from "../shared/path-validators.js"; +import { validateNonEmptyString, validateNonNegativeInteger } from "../shared/validators-01.js"; +import { validateValidationRequestPayload, validateValidationResultPayload } from "../validation/result-validator.js"; +import type { EditCommandResult, EditPlan, EditPlanRollbackState, RepoRelativeChange } from "./contracts.js"; +import { validateEditRefusal } from "./refusal-validator.js"; + +function validateEditPlanPayload(plan: EditPlan): EditPlan { + validateRequiredObject(plan, "Edit plan is required"); + validateNonEmptyString(plan.planId, "Edit plan planId"); + validateRepoIdentity(plan.repo); + if (!Array.isArray(plan.changes)) { + throw new Error("Edit plan changes must be an array"); + } + for (const change of plan.changes) validateRepoRelativeChange(change); + validateRequiredObject(plan.atomic, "Edit plan atomic metadata is required"); + if (plan.atomic.strategy !== "all_or_nothing") { + throw new Error("Edit plan atomic strategy must be all_or_nothing"); + } + if (plan.atomic.planHash !== undefined) validateNonEmptyString(plan.atomic.planHash, "Edit plan planHash"); + if (plan.atomic.expectedBaseSha !== undefined) + validateNonEmptyString(plan.atomic.expectedBaseSha, "Edit plan expectedBaseSha"); + validateRequiredObject(plan.validation, "Edit plan validation requirement is required"); + if (typeof plan.validation.required !== "boolean") { + throw new Error("Edit plan validation required must be boolean"); + } + validateValidationRequestPayload(plan.validation.request); + return plan; +} + +export { validateEditPlanPayload }; + +function validateEditCommandResult(result: EditCommandResult): EditCommandResult { + validateRequiredObject(result, "Edit command result is required"); + validateBoolean(result.ok, "Edit command result ok"); + validateBoolean(result.applied, "Edit command result applied"); + validateOptional(result.planId, (value) => validateNonEmptyString(value, "Edit command result planId")); + validateOptional(result.planHash, (value) => validateNonEmptyString(value, "Edit command result planHash")); + validateOptional(result.appliedAt, (value) => validateNonEmptyString(value, "Edit command result appliedAt")); + validateOptional(result.matchCount, (value) => + validateNonNegativeInteger(value, "Edit command result matchCount"), + ); + validateOptional(result.afterState, validateEditAfterState); + validateOptional(result.validationRequest, validateValidationRequestPayload); + validateOptional(result.validation, validateValidationResultPayload); + validateOptional(result.refusal, validateEditRefusal); + validateOptional(result.rollback, validateEditPlanRollbackState); + if (!result.ok && result.refusal === undefined) { + throw new Error("Edit command result refusal is required when ok=false"); + } + if (result.ok && result.refusal !== undefined) { + throw new Error("Edit command result ok=true must not include refusal"); + } + return result; +} + +export { validateEditCommandResult }; + +function validateEditPlanRollbackState(rollback: EditPlanRollbackState): EditPlanRollbackState { + validateRequiredObject(rollback, "Edit rollback state is required"); + if (typeof rollback.completed !== "boolean") { + throw new Error("Edit rollback completed must be boolean"); + } + validateRepoRelativePaths(rollback.restoredPaths, "Edit rollback restoredPaths"); + validateRepoRelativePaths(rollback.failedPaths, "Edit rollback failedPaths"); + if (!Array.isArray(rollback.cleanupFailedPaths)) { + throw new Error("Edit rollback cleanupFailedPaths must be an array"); + } + for (const path of rollback.cleanupFailedPaths) { + validateNonEmptyString(path, "Edit rollback cleanupFailedPaths path"); + } + return rollback; +} + +export { validateEditPlanRollbackState }; + +function validateRepoRelativeChange(change: RepoRelativeChange): RepoRelativeChange { + validateRequiredObject(change, "Repo-relative change is required"); + if (change.kind === "create" || change.kind === "replace") { + validateRepoRelativePath(change.path); + if (typeof change.content !== "string") throw new Error("Repo-relative write change content must be string"); + if (change.checksumBefore !== undefined) + validateNonEmptyString(change.checksumBefore, "Repo-relative change checksumBefore"); + if (change.checksumAfter !== undefined) + validateNonEmptyString(change.checksumAfter, "Repo-relative change checksumAfter"); + return change; + } + if (change.kind === "delete") { + validateRepoRelativePath(change.path); + if (change.checksumBefore !== undefined) + validateNonEmptyString(change.checksumBefore, "Repo-relative change checksumBefore"); + return change; + } + if (change.kind === "rename") { + validateRepoRelativePath(change.path); + validateRepoRelativePath(change.toPath); + if (change.checksumBefore !== undefined) + validateNonEmptyString(change.checksumBefore, "Repo-relative change checksumBefore"); + return change; + } + throw new Error(`Unknown repo-relative change kind: ${String((change as { kind?: unknown }).kind)}`); +} + +export { validateRepoRelativeChange }; + +function validateEditAfterState(afterState: Readonly>): void { + if (!afterState || typeof afterState !== "object" || Array.isArray(afterState)) { + throw new Error("Edit command result afterState must be an object"); + } + for (const [path, content] of Object.entries(afterState)) { + validateRepoRelativePath(path); + if (typeof content !== "string" && content !== null) { + throw new Error(`Edit command result afterState for ${path} must be string or null`); + } + } +} + +export { validateEditAfterState }; diff --git a/packages/contracts/src/edit/vocabulary.ts b/packages/contracts/src/edit/vocabulary.ts new file mode 100644 index 0000000..f5aff93 --- /dev/null +++ b/packages/contracts/src/edit/vocabulary.ts @@ -0,0 +1,17 @@ +const editRefusalCategories = [ + "absolute_path", + "parent_directory", + "ambiguous_repo_identity", + "validation_failed", + "provider_required_missing", + "schema_mismatch", + "unsafe_edit", + "conflict", + "unsupported_change", +] as const; + +export { editRefusalCategories }; + +type EditRefusalCategory = (typeof editRefusalCategories)[number]; + +export type { EditRefusalCategory }; diff --git a/packages/contracts/src/graph/daemon-validators-01.ts b/packages/contracts/src/graph/daemon-validators-01.ts new file mode 100644 index 0000000..f0ddeb7 --- /dev/null +++ b/packages/contracts/src/graph/daemon-validators-01.ts @@ -0,0 +1,193 @@ +import { + validateBoolean, + validateExactValue, + validateOptional, + validateRequiredObject, +} from "../shared/validators-02.js"; +import { validateRepoIdentity, validateRepoRelativePath } from "../shared/path-validators.js"; +import { validateNonEmptyString, validateStringArray } from "../shared/validators-01.js"; +import { validateGraphDaemonOperation } from "./helper-validators.js"; +import { + validateGraphWalCheckpointSummary, + validateGraphWatchLifecycle, +} from "./protocol-validators.js"; +import type { + GraphDaemonRequest, + GraphDaemonResponse, + GraphPipelinePhaseTiming, + GraphPipelineResult, + GraphPipelineSummary, +} from "./pipeline-contracts.js"; +import { validateProviderStatus } from "./provider-validators.js"; +import { + validateGraphDetectChangesRequest, + validateGraphDetectChangesResult, + validateGraphFactQueryRequest, + validateGraphFactQueryResult, + validateGraphImpactRequest, + validateGraphImpactResult, + validateGraphNamedQueryRequest, + validateGraphNamedQueryResult, + validateGraphReviewContextRequest, + validateGraphReviewContextResult, +} from "./query-validators.js"; +import { validateGraphSearchRequest, validateGraphSearchResult } from "./search-validators.js"; +import { GRAPH_SCHEMA_VERSION } from "./vocabulary-01.js"; + +function validateGraphDaemonRequest(request: GraphDaemonRequest): GraphDaemonRequest { + validateRequiredObject(request, "Graph daemon request is required"); + validateExactValue( + request.protocol, + "opcore.graph.daemon", + "Graph daemon request protocol must be opcore.graph.daemon", + ); + validateNonEmptyString(request.requestId, "Graph daemon request requestId"); + validateExactValue( + request.schemaVersion, + GRAPH_SCHEMA_VERSION, + `Graph daemon request schemaVersion must be ${GRAPH_SCHEMA_VERSION}`, + ); + validateGraphDaemonOperation(request.operation); + validateRepoIdentity(request.repo); + validateGraphDaemonQueryRequest(request); + validateGraphDaemonRequestOptions(request); + return request; +} + +export { validateGraphDaemonRequest }; + +function validateGraphDaemonQueryRequest(request: GraphDaemonRequest): void { + validateOptional(request.query, validateGraphFactQueryRequest); + validateOptional(request.namedQuery, validateGraphNamedQueryRequest); + validateOptional(request.impact, validateGraphImpactRequest); + validateOptional(request.reviewContext, validateGraphReviewContextRequest); + validateOptional(request.changes, validateGraphDetectChangesRequest); + validateOptional(request.search, validateGraphSearchRequest); + if (request.operation !== "query" || request.query !== undefined) return; + const envelopes = [request.namedQuery, request.impact, request.reviewContext, request.changes, request.search]; + if (!envelopes.some((value) => value !== undefined)) { + throw new Error("Graph daemon query request must include query"); + } +} + +function validateGraphDaemonRequestOptions(request: GraphDaemonRequest): void { + validateOptional(request.baseRef, (value) => validateNonEmptyString(value, "Graph daemon request baseRef")); + validateOptional(request.paths, (value) => validateGraphDaemonPaths(value, "paths")); + validateOptional(request.watchPaths, (value) => validateGraphDaemonPaths(value, "watchPaths")); + validateOptional(request.pollIntervalMs, (value) => + validateFiniteNumberAtLeast(value, 1, "Graph daemon request pollIntervalMs must be positive"), + ); + validateOptional(request.idleTimeoutMs, (value) => + validateFiniteNumberAtLeast( + value, + 0, + "Graph daemon request idleTimeoutMs must be a non-negative number", + ), + ); + validateOptional(request.once, (value) => validateBoolean(value, "Graph daemon request once")); + validateOptional(request.maxWalBytes, (value) => + validateFiniteNumberAtLeast(value, 1, "Graph daemon request maxWalBytes must be positive"), + ); +} + +function validateGraphDaemonPaths(paths: readonly string[], field: "paths" | "watchPaths"): void { + validateStringArray(paths, `Graph daemon request ${field}`, { allowEmpty: true }); + for (const path of paths) validateRepoRelativePath(path); +} + +function validateFiniteNumberAtLeast(value: number, minimum: number, message: string): void { + if (!Number.isFinite(value) || value < minimum) throw new Error(message); +} + +function validateGraphDaemonResponse(response: GraphDaemonResponse): GraphDaemonResponse { + validateRequiredObject(response, "Graph daemon response is required"); + validateExactValue( + response.protocol, + "opcore.graph.daemon", + "Graph daemon response protocol must be opcore.graph.daemon", + ); + validateNonEmptyString(response.requestId, "Graph daemon response requestId"); + validateExactValue( + response.schemaVersion, + GRAPH_SCHEMA_VERSION, + `Graph daemon response schemaVersion must be ${GRAPH_SCHEMA_VERSION}`, + ); + validateProviderStatus(response.status); + validateOptional(response.result, validateGraphFactQueryResult); + validateOptional(response.namedQuery, validateGraphNamedQueryResult); + validateOptional(response.impact, validateGraphImpactResult); + validateOptional(response.reviewContext, validateGraphReviewContextResult); + validateOptional(response.changes, validateGraphDetectChangesResult); + validateOptional(response.search, validateGraphSearchResult); + validateOptional(response.pipeline, validateGraphPipelineResult); + validateOptional(response.lifecycle, validateGraphWatchLifecycle); + return response; +} + +export { validateGraphDaemonResponse }; + +function validateGraphPipelineResult(result: GraphPipelineResult): GraphPipelineResult { + validateRequiredObject(result, "Graph pipeline result is required"); + validateGraphPipelineSummary(result.summary); + validateProviderStatus(result.status); + if (result.lifecycle !== undefined) validateGraphWatchLifecycle(result.lifecycle); + return result; +} + +export { validateGraphPipelineResult }; + +function validateGraphPipelineSummary(summary: GraphPipelineSummary): GraphPipelineSummary { + validateRequiredObject(summary, "Graph pipeline summary is required"); + if (!["build", "update", "watch"].includes(summary.operation)) { + throw new Error(`Unknown graph pipeline operation: ${String(summary.operation)}`); + } + validateRepoIdentity(summary.repo); + validateOptional(summary.storePath, (value) => validateNonEmptyString(value, "Graph pipeline summary storePath")); + validateNonEmptyString(summary.startedAt, "Graph pipeline summary startedAt"); + validateNonEmptyString(summary.completedAt, "Graph pipeline summary completedAt"); + validateGraphPipelineCounts(summary); + validateStringArray(summary.changedFiles, "Graph pipeline summary changedFiles", { allowEmpty: true }); + validateStringArray(summary.deletedFiles, "Graph pipeline summary deletedFiles", { allowEmpty: true }); + for (const path of summary.changedFiles) validateRepoRelativePath(path); + for (const path of summary.deletedFiles) validateRepoRelativePath(path); + validateBoolean(summary.fullRebuildRequired, "Graph pipeline summary fullRebuildRequired"); + if (!Array.isArray(summary.phaseTimings) || summary.phaseTimings.length === 0) { + throw new Error("Graph pipeline summary phaseTimings must be non-empty"); + } + for (const timing of summary.phaseTimings) validateGraphPipelinePhaseTiming(timing); + validateOptional(summary.baseRef, (value) => validateNonEmptyString(value, "Graph pipeline summary baseRef")); + validateOptional(summary.watchPaths, validateGraphPipelineWatchPaths); + validateOptional(summary.walCheckpoint, validateGraphWalCheckpointSummary); + return summary; +} + +export { validateGraphPipelineSummary }; + +function validateGraphPipelineCounts(summary: GraphPipelineSummary): void { + for (const key of ["durationMs", "discoveredFiles", "parsedFiles", "unchangedFiles", "diagnosticsCount"] as const) { + if (typeof summary[key] !== "number" || summary[key] < 0) { + throw new Error(`Graph pipeline summary ${key} must be a non-negative number`); + } + } +} + +function validateGraphPipelineWatchPaths(paths: readonly string[]): void { + validateStringArray(paths, "Graph pipeline summary watchPaths", { allowEmpty: true }); + for (const path of paths) validateRepoRelativePath(path); +} + +function validateGraphPipelinePhaseTiming(timing: GraphPipelinePhaseTiming): GraphPipelinePhaseTiming { + validateRequiredObject(timing, "Graph pipeline phase timing is required"); + validateNonEmptyString(timing.phase, "Graph pipeline phase timing phase"); + validateNonEmptyString(timing.startedAt, "Graph pipeline phase timing startedAt"); + validateNonEmptyString(timing.completedAt, "Graph pipeline phase timing completedAt"); + if (typeof timing.durationMs !== "number" || timing.durationMs < 0) { + throw new Error("Graph pipeline phase timing durationMs must be non-negative"); + } + if (timing.fileCount !== undefined && (typeof timing.fileCount !== "number" || timing.fileCount < 0)) { + throw new Error("Graph pipeline phase timing fileCount must be non-negative"); + } + return timing; +} + +export { validateGraphPipelinePhaseTiming }; diff --git a/packages/contracts/src/graph/daemon-validators-02.ts b/packages/contracts/src/graph/daemon-validators-02.ts new file mode 100644 index 0000000..a3852cc --- /dev/null +++ b/packages/contracts/src/graph/daemon-validators-02.ts @@ -0,0 +1,47 @@ +import { + validateExactValue, + validateOptional, + validateRequiredObject, +} from "../shared/validators-02.js"; +import { includesString } from "../shared/primitives.js"; +import { validateRepoIdentity } from "../shared/path-validators.js"; +import { validateNonEmptyString } from "../shared/validators-01.js"; +import type { GraphServeTransportStatus } from "./pipeline-contracts.js"; +import { + validateGraphProviderArtifactMetadata, + validateProviderFailure, +} from "./protocol-validators.js"; + +function validateGraphServeTransportStatus(status: GraphServeTransportStatus): GraphServeTransportStatus { + validateRequiredObject(status, "Graph serve transport status is required"); + validateExactValue(status.schemaVersion, 1, "Graph serve transport status schemaVersion must be 1"); + validateExactValue( + status.protocol, + "opcore.graph.daemon", + "Graph serve transport status protocol must be opcore.graph.daemon", + ); + validateExactValue(status.transport, "stdio", "Graph serve transport status transport must be stdio"); + if (!includesString(["ready", "error", "stopped"] as const, status.state)) { + throw new Error(`Unknown graph serve transport state: ${String(status.state)}`); + } + validateRepoIdentity(status.repo); + validateNonEmptyString(status.provider, "Graph serve transport status provider"); + validateOptional(status.pid, validateGraphServePid); + validateOptional(status.artifact, validateGraphProviderArtifactMetadata); + validateOptional(status.failure, validateProviderFailure); + if (status.state === "error" && status.failure === undefined) { + throw new Error("Graph serve transport error status must include failure"); + } + validateOptional(status.message, (value) => + validateNonEmptyString(value, "Graph serve transport status message"), + ); + return status; +} + +export { validateGraphServeTransportStatus }; + +function validateGraphServePid(pid: number): void { + if (!Number.isInteger(pid) || pid < 1) { + throw new Error("Graph serve transport status pid must be positive"); + } +} diff --git a/packages/contracts/src/graph/helper-validators.ts b/packages/contracts/src/graph/helper-validators.ts new file mode 100644 index 0000000..5e5cb0a --- /dev/null +++ b/packages/contracts/src/graph/helper-validators.ts @@ -0,0 +1,156 @@ +import { includesString } from "../shared/primitives.js"; +import { + validateRepoIdentity, + validateRepoRelativePath, + validateRepoRelativePaths, +} from "../shared/path-validators.js"; +import { validateNonEmptyString, validateStringArray } from "../shared/validators-01.js"; +import type { GraphDaemonOperation} from "./pipeline-contracts.js"; +import { graphDaemonOperations } from "./pipeline-contracts.js"; +import type { RepoIdentity } from "./provider-contracts-01.js"; +import type { + GraphFactQuerySelector, + GraphNamedQueryKind, + GraphProviderQueryKind, + GraphTraversalMetadata} from "./query-contracts-01.js"; +import { + graphFactQueryKinds, + graphNamedQueryKinds, +} from "./query-contracts-01.js"; +import type { GraphSearchMode, GraphSearchResultEntry, GraphSearchSummary } from "./search-contracts.js"; +import type { GraphProviderMode} from "./vocabulary-01.js"; +import { GRAPH_SCHEMA_VERSION, graphProviderModes } from "./vocabulary-01.js"; + +function validateGraphDaemonOperation(operation: unknown): GraphDaemonOperation { + if (!includesString(graphDaemonOperations, operation)) { + throw new Error(`Unknown graph daemon operation: ${String(operation)}`); + } + return operation; +} + +export { validateGraphDaemonOperation }; + +function validateGraphFactQueryKind(kind: unknown): GraphFactQuerySelector["kind"] { + if (!includesString(graphFactQueryKinds, kind)) { + throw new Error(`Unknown graph fact query kind: ${String(kind)}`); + } + return kind; +} + +export { validateGraphFactQueryKind }; + +function validateGraphNamedQueryKind(kind: unknown): GraphNamedQueryKind { + if (!includesString(graphNamedQueryKinds, kind)) { + throw new Error(`Unknown graph named query kind: ${String(kind)}`); + } + return kind; +} + +export { validateGraphNamedQueryKind }; + +function validateGraphProviderQueryKind(kind: unknown): GraphProviderQueryKind { + if ( + !includesString(graphFactQueryKinds, kind) && + !includesString(graphNamedQueryKinds, kind) && + kind !== "review_context" && + kind !== "detect_changes" && + kind !== "search" + ) { + throw new Error(`Unknown graph provider query kind: ${String(kind)}`); + } + return kind; +} + +export { validateGraphProviderQueryKind }; + +function validateGraphQueryRequestBase( + request: { + requestId?: string; + repo: RepoIdentity; + schemaVersion: number; + mode: GraphProviderMode; + }, + label: string, +): void { + if (!request || typeof request !== "object") throw new Error(`${label} is required`); + if (request.requestId !== undefined) validateNonEmptyString(request.requestId, `${label} requestId`); + validateRepoIdentity(request.repo); + if (request.schemaVersion !== GRAPH_SCHEMA_VERSION) + throw new Error(`${label} schemaVersion must be ${GRAPH_SCHEMA_VERSION}`); + if (!includesString(graphProviderModes, request.mode)) + throw new Error(`Unknown ${label} mode: ${String(request.mode)}`); +} + +export { validateGraphQueryRequestBase }; + +function validateTraversalOptions(maxDepth: number | undefined, limit: number | undefined, label: string): void { + if (maxDepth !== undefined && (!Number.isFinite(maxDepth) || maxDepth < 0)) { + throw new Error(`${label} maxDepth must be a non-negative number`); + } + if (limit !== undefined && (!Number.isFinite(limit) || limit < 1)) { + throw new Error(`${label} limit must be a positive number`); + } +} + +export { validateTraversalOptions }; + +function validateGraphTraversalMetadata(metadata: GraphTraversalMetadata): GraphTraversalMetadata { + if (!metadata || typeof metadata !== "object") throw new Error("Graph traversal metadata is required"); + if (typeof metadata.maxDepth !== "number" || metadata.maxDepth < 0) { + throw new Error("Graph traversal metadata maxDepth must be non-negative"); + } + if (typeof metadata.truncated !== "boolean") throw new Error("Graph traversal metadata truncated must be boolean"); + if (typeof metadata.total !== "number" || metadata.total < 0) + throw new Error("Graph traversal metadata total must be non-negative"); + if (typeof metadata.empty !== "boolean") throw new Error("Graph traversal metadata empty must be boolean"); + return metadata; +} + +export { validateGraphTraversalMetadata }; + +function validateGraphSearchMode(mode: GraphSearchMode): GraphSearchMode { + if (!mode || typeof mode !== "object") throw new Error("Graph search mode is required"); + validateNonEmptyString(mode.engine, "Graph search mode engine"); + validateNonEmptyString(mode.querySyntax, "Graph search mode querySyntax"); + if (!Number.isFinite(mode.limit) || mode.limit < 1) + throw new Error("Graph search mode limit must be a positive number"); + validateRepoRelativePaths(mode.contextFiles, "Graph search mode contextFiles"); + return mode; +} + +export { validateGraphSearchMode }; + +function validateGraphSearchSummary(summary: GraphSearchSummary): GraphSearchSummary { + if (!summary || typeof summary !== "object") throw new Error("Graph search summary is required"); + validateNonEmptyString(summary.query, "Graph search summary query"); + for (const key of ["total", "returned", "limit"] as const) { + if (!Number.isFinite(summary[key]) || summary[key] < (key === "limit" ? 1 : 0)) { + throw new Error(`Graph search summary ${key} must be a non-negative number`); + } + } + validateStringArray(summary.indexedNodeKinds, "Graph search summary indexedNodeKinds", { allowEmpty: true }); + validateRepoRelativePaths(summary.contextFiles, "Graph search summary contextFiles"); + return summary; +} + +export { validateGraphSearchSummary }; + +function validateGraphSearchResultEntry(entry: GraphSearchResultEntry): GraphSearchResultEntry { + if (!entry || typeof entry !== "object") throw new Error("Graph search result entry is required"); + validateNonEmptyString(entry.nodeId, "Graph search result entry nodeId"); + validateNonEmptyString(entry.kind, "Graph search result entry kind"); + if (entry.path !== undefined) validateRepoRelativePath(entry.path); + if (entry.name !== undefined) validateNonEmptyString(entry.name, "Graph search result entry name"); + validateNonEmptyString(entry.qualifiedName, "Graph search result entry qualifiedName"); + if (entry.filePath !== undefined) validateRepoRelativePath(entry.filePath); + validateNonEmptyString(entry.signature, "Graph search result entry signature"); + if (!Number.isFinite(entry.score)) throw new Error("Graph search result entry score must be numeric"); + if (!Number.isFinite(entry.rank) || entry.rank < 1) + throw new Error("Graph search result entry rank must be a positive number"); + validateStringArray(entry.matches, "Graph search result entry matches", { + allowEmpty: true, + }); + return entry; +} + +export { validateGraphSearchResultEntry }; diff --git a/packages/contracts/src/graph/payload-validators.ts b/packages/contracts/src/graph/payload-validators.ts new file mode 100644 index 0000000..0745792 --- /dev/null +++ b/packages/contracts/src/graph/payload-validators.ts @@ -0,0 +1,75 @@ +import { validateRequiredObject } from "../shared/validators-02.js"; +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { validateNonEmptyString } from "../shared/validators-01.js"; +import type { GraphExtractionDiagnostic } from "./provider-contracts-01.js"; +import type { GraphFactEdge, GraphFactNode } from "./provider-contracts-02.js"; +import { validateGraphExtractionDiagnostics } from "./protocol-validators.js"; +import { validateProviderStatus } from "./provider-validators.js"; +import type { GraphFactQueryResult, GraphNamedQueryResult } from "./query-contracts-01.js"; +import type { + GraphDetectChangesResult, + GraphImpactResult, + GraphRenamedFile, + GraphReviewContextResult, +} from "./query-contracts-02.js"; + +function validateGraphPayloadResult( + result: GraphNamedQueryResult | GraphImpactResult | GraphDetectChangesResult | GraphReviewContextResult, + label: string, + validatePayload: (payload: Record) => void, +): void { + if (!result || typeof result !== "object") throw new Error(`${label} is required`); + if (result.requestId !== undefined) validateNonEmptyString(result.requestId, `${label} requestId`); + const status = validateProviderStatus(result.status); + const payload: Record = { ...result }; + const payloadKeys = Object.keys(payload).filter((key) => key !== "requestId" && key !== "status"); + if (status.state !== "available") { + if (payloadKeys.length > 0) throw new Error(`${label} ${status.state} result must not include graph data`); + return; + } + validatePayload(payload); + if (payload.diagnostics !== undefined) { + validateGraphExtractionDiagnostics(payload.diagnostics as readonly GraphExtractionDiagnostic[]); + } +} + +export { validateGraphPayloadResult }; + + +function validateRenamedFiles(renamedFiles: readonly GraphRenamedFile[]): void { + if (!Array.isArray(renamedFiles)) throw new Error("Graph renamedFiles must be an array"); + for (const renamed of renamedFiles) { + validateRepoRelativePath(renamed.fromPath); + validateRepoRelativePath(renamed.toPath); + } +} + +export { validateRenamedFiles }; + +function isNamedQueryResult(result: GraphFactQueryResult | GraphNamedQueryResult): result is GraphNamedQueryResult { + return Object.hasOwn(result, "queryKind") || Object.hasOwn(result, "traversal"); +} + +export { isNamedQueryResult }; + +function validateGraphFactNode(node: GraphFactNode): GraphFactNode { + validateRequiredObject(node, "Graph fact node is required"); + validateNonEmptyString(node.id, "Graph fact node id"); + validateNonEmptyString(node.kind, "Graph fact node kind"); + if (node.path !== undefined) validateRepoRelativePath(node.path); + if (node.name !== undefined) validateNonEmptyString(node.name, "Graph fact node name"); + return node; +} + +export { validateGraphFactNode }; + +function validateGraphFactEdge(edge: GraphFactEdge): GraphFactEdge { + validateRequiredObject(edge, "Graph fact edge is required"); + if (edge.id !== undefined) validateNonEmptyString(edge.id, "Graph fact edge id"); + validateNonEmptyString(edge.kind, "Graph fact edge kind"); + validateNonEmptyString(edge.from, "Graph fact edge from"); + validateNonEmptyString(edge.to, "Graph fact edge to"); + return edge; +} + +export { validateGraphFactEdge }; diff --git a/packages/contracts/src/graph/pipeline-contracts.ts b/packages/contracts/src/graph/pipeline-contracts.ts new file mode 100644 index 0000000..ec6c7b6 --- /dev/null +++ b/packages/contracts/src/graph/pipeline-contracts.ts @@ -0,0 +1,150 @@ +import type { GraphProviderArtifactMetadata, ProviderFailure, RepoIdentity } from "./provider-contracts-01.js"; +import type { GraphProviderStatus } from "./provider-contracts-02.js"; +import type { + GraphFactQueryRequest, + GraphFactQueryResult, + GraphImpactRequest, + GraphNamedQueryRequest, + GraphNamedQueryResult, +} from "./query-contracts-01.js"; +import type { + GraphDetectChangesRequest, + GraphDetectChangesResult, + GraphImpactResult, + GraphReviewContextRequest, + GraphReviewContextResult, +} from "./query-contracts-02.js"; +import type { GraphSearchRequest, GraphSearchResult } from "./search-contracts.js"; + +type GraphPipelineOperation = "build" | "update" | "watch"; + +export type { GraphPipelineOperation }; + +interface GraphPipelinePhaseTiming { + phase: "discovery" | "extraction" | "store" | "watch" | "status" | (string & {}); + startedAt: string; + completedAt: string; + durationMs: number; + fileCount?: number; +} + +export type { GraphPipelinePhaseTiming }; + +interface GraphWalCheckpointSummary { + walPath: string; + bytesBefore: number; + bytesAfter: number; + budgetBytes: number; + checkpointed: boolean; +} + +export type { GraphWalCheckpointSummary }; + +interface GraphPipelineSummary { + operation: GraphPipelineOperation; + repo: RepoIdentity; + storePath?: string; + startedAt: string; + completedAt: string; + durationMs: number; + discoveredFiles: number; + parsedFiles: number; + changedFiles: readonly string[]; + deletedFiles: readonly string[]; + unchangedFiles: number; + fullRebuildRequired: boolean; + diagnosticsCount: number; + phaseTimings: readonly GraphPipelinePhaseTiming[]; + baseRef?: string; + watchPaths?: readonly string[]; + walCheckpoint?: GraphWalCheckpointSummary; +} + +export type { GraphPipelineSummary }; + +interface GraphWatchLifecycle { + state: "warming" | "available" | "error" | "stopped"; + pid?: number; + startedAt: string; + updatedAt: string; + pidPath: string; + statePath: string; + logPath: string; + pollIntervalMs: number; + idleTimeoutMs: number; + watchPaths?: readonly string[]; + message?: string; +} + +export type { GraphWatchLifecycle }; + +interface GraphServeTransportStatus { + schemaVersion: 1; + protocol: "opcore.graph.daemon"; + transport: "stdio"; + state: "ready" | "error" | "stopped"; + repo: RepoIdentity; + provider: "opcore-graph" | (string & {}); + pid?: number; + artifact?: GraphProviderArtifactMetadata; + failure?: ProviderFailure; + message?: string; +} + +export type { GraphServeTransportStatus }; + +interface GraphPipelineResult { + summary: GraphPipelineSummary; + status: GraphProviderStatus; + lifecycle?: GraphWatchLifecycle; +} + +export type { GraphPipelineResult }; + +type GraphDaemonOperation = "build" | "update" | "watch" | "status" | "query" | "ping" | "health" | "shutdown"; + +export type { GraphDaemonOperation }; + +const graphDaemonOperations = ["build", "update", "watch", "status", "query", "ping", "health", "shutdown"] as const; + +export { graphDaemonOperations }; + +interface GraphDaemonRequest { + protocol: "opcore.graph.daemon"; + requestId: string; + schemaVersion: number; + operation: GraphDaemonOperation; + repo: RepoIdentity; + query?: GraphFactQueryRequest; + namedQuery?: GraphNamedQueryRequest; + impact?: GraphImpactRequest; + reviewContext?: GraphReviewContextRequest; + changes?: GraphDetectChangesRequest; + search?: GraphSearchRequest; + baseRef?: string; + paths?: readonly string[]; + watchPaths?: readonly string[]; + pollIntervalMs?: number; + idleTimeoutMs?: number; + once?: boolean; + maxWalBytes?: number; +} + +export type { GraphDaemonRequest }; + +interface GraphDaemonResponse { + protocol: "opcore.graph.daemon"; + requestId: string; + schemaVersion: number; + status: GraphProviderStatus; + result?: GraphFactQueryResult; + namedQuery?: GraphNamedQueryResult; + impact?: GraphImpactResult; + reviewContext?: GraphReviewContextResult; + changes?: GraphDetectChangesResult; + search?: GraphSearchResult; + pipeline?: GraphPipelineResult; + lifecycle?: GraphWatchLifecycle; +} + +export type { GraphDaemonResponse }; diff --git a/packages/contracts/src/graph/protocol-validators.ts b/packages/contracts/src/graph/protocol-validators.ts new file mode 100644 index 0000000..5923cfa --- /dev/null +++ b/packages/contracts/src/graph/protocol-validators.ts @@ -0,0 +1,132 @@ +import { + includesString, + validateOptional, + validateRequiredObject, +} from "../shared/primitives.js"; +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { validateNonEmptyString, validateStringArray } from "../shared/validators-01.js"; +import type { + GraphExtractionDiagnostic, + GraphProviderArtifactMetadata, + ProviderFailure, +} from "./provider-contracts-01.js"; +import type { GraphWalCheckpointSummary, GraphWatchLifecycle } from "./pipeline-contracts.js"; +import { providerFailureCategories } from "./vocabulary-01.js"; +import { graphExtractionDiagnosticCategories } from "./vocabulary-02.js"; + +function validateGraphWatchLifecycle(lifecycle: GraphWatchLifecycle): GraphWatchLifecycle { + validateRequiredObject(lifecycle, "Graph watch lifecycle is required"); + if (!["warming", "available", "error", "stopped"].includes(lifecycle.state)) { + throw new Error(`Unknown graph watch lifecycle state: ${String(lifecycle.state)}`); + } + if (lifecycle.pid !== undefined && (!Number.isInteger(lifecycle.pid) || lifecycle.pid < 1)) { + throw new Error("Graph watch lifecycle pid must be positive"); + } + validateNonEmptyString(lifecycle.startedAt, "Graph watch lifecycle startedAt"); + validateNonEmptyString(lifecycle.updatedAt, "Graph watch lifecycle updatedAt"); + validateNonEmptyString(lifecycle.pidPath, "Graph watch lifecycle pidPath"); + validateNonEmptyString(lifecycle.statePath, "Graph watch lifecycle statePath"); + validateNonEmptyString(lifecycle.logPath, "Graph watch lifecycle logPath"); + if (typeof lifecycle.pollIntervalMs !== "number" || lifecycle.pollIntervalMs < 1) { + throw new Error("Graph watch lifecycle pollIntervalMs must be positive"); + } + if ( + typeof lifecycle.idleTimeoutMs !== "number" || + !Number.isFinite(lifecycle.idleTimeoutMs) || + lifecycle.idleTimeoutMs < 0 + ) { + throw new Error("Graph watch lifecycle idleTimeoutMs must be a non-negative number"); + } + validateOptional(lifecycle.watchPaths, (paths) => { + validateStringArray(paths, "Graph watch lifecycle watchPaths", { allowEmpty: true }); + for (const path of paths) validateRepoRelativePath(path); + }); + validateOptional(lifecycle.message, (value) => validateNonEmptyString(value, "Graph watch lifecycle message")); + return lifecycle; +} + +export { validateGraphWatchLifecycle }; + +function validateGraphWalCheckpointSummary(summary: GraphWalCheckpointSummary): GraphWalCheckpointSummary { + validateNonEmptyString(summary.walPath, "Graph WAL checkpoint walPath"); + for (const key of ["bytesBefore", "bytesAfter", "budgetBytes"] as const) { + if (typeof summary[key] !== "number" || summary[key] < 0) { + throw new Error(`Graph WAL checkpoint ${key} must be non-negative`); + } + } + if (typeof summary.checkpointed !== "boolean") { + throw new Error("Graph WAL checkpoint checkpointed must be boolean"); + } + return summary; +} + +export { validateGraphWalCheckpointSummary }; + +function validateGraphProviderArtifactMetadata(metadata: GraphProviderArtifactMetadata): GraphProviderArtifactMetadata { + validateRequiredObject(metadata, "Graph provider artifact metadata is required"); + for (const key of [ + "artifactName", + "artifactVersion", + "targetPlatform", + "binaryPath", + "checksumPath", + "checksumSha256", + "buildProfile", + ] as const) { + validateNonEmptyString(metadata[key], `Graph provider artifact metadata ${key}`); + } + for (const key of ["binaryPath", "checksumPath"] as const) { + validateRepoRelativePath(metadata[key]); + if (metadata[key].startsWith("packages/") || metadata[key].startsWith("../")) { + throw new Error(`Graph provider artifact metadata ${key} must be package-relative`); + } + } + return metadata; +} + +export { validateGraphProviderArtifactMetadata }; + +function validateGraphExtractionDiagnostics( + diagnostics: readonly GraphExtractionDiagnostic[], +): readonly GraphExtractionDiagnostic[] { + if (!Array.isArray(diagnostics)) { + throw new Error("Graph extraction diagnostics must be an array"); + } + for (const diagnostic of diagnostics) validateGraphExtractionDiagnostic(diagnostic); + return diagnostics; +} + +export { validateGraphExtractionDiagnostics }; + +function validateGraphExtractionDiagnostic(diagnostic: GraphExtractionDiagnostic): GraphExtractionDiagnostic { + validateRequiredObject(diagnostic, "Graph extraction diagnostic is required"); + if (!includesString(graphExtractionDiagnosticCategories, diagnostic.category)) { + throw new Error(`Unknown graph extraction diagnostic category: ${String(diagnostic.category)}`); + } + if (!includesString(["info", "warning", "error"] as const, diagnostic.severity)) { + throw new Error(`Unknown graph extraction diagnostic severity: ${String(diagnostic.severity)}`); + } + validateNonEmptyString(diagnostic.message, "Graph extraction diagnostic message"); + if (diagnostic.path !== undefined) validateRepoRelativePath(diagnostic.path); + if (diagnostic.language !== undefined) { + validateNonEmptyString(diagnostic.language, "Graph extraction diagnostic language"); + } + return diagnostic; +} + +export { validateGraphExtractionDiagnostic }; + +function validateProviderFailure(failure: ProviderFailure): ProviderFailure { + validateRequiredObject(failure, "Provider failure is required"); + if (!includesString(providerFailureCategories, failure.category)) { + throw new Error(`Unknown provider failure category: ${String(failure.category)}`); + } + validateNonEmptyString(failure.message, "Provider failure message"); + if (failure.retryable !== undefined && typeof failure.retryable !== "boolean") { + throw new Error("Provider failure retryable must be boolean"); + } + if (failure.cause !== undefined) validateNonEmptyString(failure.cause, "Provider failure cause"); + return failure; +} + +export { validateProviderFailure }; diff --git a/packages/contracts/src/graph/provider-contracts-01.ts b/packages/contracts/src/graph/provider-contracts-01.ts new file mode 100644 index 0000000..8be8f20 --- /dev/null +++ b/packages/contracts/src/graph/provider-contracts-01.ts @@ -0,0 +1,162 @@ +import type { GraphDaemonOperation, GraphWalCheckpointSummary, GraphWatchLifecycle } from "./pipeline-contracts.js"; +import type { GraphProviderQueryKind } from "./query-contracts-01.js"; +import type { + GraphEdgeKind, + GraphNodeKind, + GraphProviderErrorFailureCategory, + GraphProviderMode, + GraphProviderStatusState, + ProviderFailureCategory, +} from "./vocabulary-01.js"; +import type { GraphExtractionDiagnosticCategory } from "./vocabulary-02.js"; + +interface RepoIdentity { + repoId?: string; + repoRoot?: string; + remoteUrl?: string; + commitSha?: string; +} + +export type { RepoIdentity }; + +interface GraphFreshness { + generatedAt: string; + ageMs: number; + maxAgeMs?: number; + stale: boolean; + reason?: string; +} + +export type { GraphFreshness }; + +interface GraphProviderArtifactMetadata { + artifactName: "opcore-graph-core" | (string & {}); + artifactVersion: string; + targetPlatform: string; + binaryPath: string; + checksumPath: string; + checksumSha256: string; + buildProfile: string; +} + +export type { GraphProviderArtifactMetadata }; + +interface GraphProviderCapabilityHandshake { + provider: "opcore-graph" | (string & {}); + graphSchemaVersion: number; + artifactName: "opcore-graph-core" | (string & {}); + artifactVersion: string; + targetPlatform: string; + supportedOperations: readonly GraphDaemonOperation[]; + nodeKinds: readonly GraphNodeKind[]; + edgeKinds: readonly GraphEdgeKind[]; + queryKinds: readonly GraphProviderQueryKind[]; + artifact: GraphProviderArtifactMetadata; +} + +export type { GraphProviderCapabilityHandshake }; + +interface ProviderFailure { + category: ProviderFailureCategory; + message: string; + retryable?: boolean; + cause?: string; +} + +export type { ProviderFailure }; + +type ProviderFailureWithCategory = ProviderFailure & { category: Category }; + +export type { ProviderFailureWithCategory }; + +interface GraphExtractionDiagnostic { + category: GraphExtractionDiagnosticCategory; + severity: "info" | "warning" | "error"; + message: string; + path?: string; + language?: string; +} + +export type { GraphExtractionDiagnostic }; + +interface GraphProviderStatusBase { + state: GraphProviderStatusState; + mode: GraphProviderMode; + provider: string; + schemaVersion: number; + message?: string; +} + +export type { GraphProviderStatusBase }; + +interface GraphProviderAvailableStatus extends GraphProviderStatusBase { + state: "available"; + repo: RepoIdentity; + freshness: GraphFreshness; + dbPath?: string; + nodes_by_kind: Readonly>; + edges_by_kind: Readonly>; + capabilities?: readonly string[]; + handshake?: GraphProviderCapabilityHandshake; + walCheckpoint?: GraphWalCheckpointSummary; +} + +export type { GraphProviderAvailableStatus }; + +interface GraphProviderWarmingStatus extends GraphProviderStatusBase { + state: "warming"; + repo: RepoIdentity; + freshness: GraphFreshness; + lifecycle?: GraphWatchLifecycle; +} + +export type { GraphProviderWarmingStatus }; + +interface GraphProviderSkippedStatus extends GraphProviderStatusBase { + state: "skipped"; + mode: "optional"; + failure: ProviderFailureWithCategory<"provider_missing">; +} + +export type { GraphProviderSkippedStatus }; + +interface GraphProviderRequiredMissingStatus extends GraphProviderStatusBase { + state: "required_missing"; + mode: "required"; + failure: ProviderFailureWithCategory<"provider_missing">; +} + +export type { GraphProviderRequiredMissingStatus }; + +interface GraphProviderStaleStatus extends GraphProviderStatusBase { + state: "stale"; + repo: RepoIdentity; + freshness: GraphFreshness; + failure: ProviderFailureWithCategory<"stale_snapshot">; +} + +export type { GraphProviderStaleStatus }; + +interface GraphProviderSchemaMismatchStatus extends GraphProviderStatusBase { + state: "schema_mismatch"; + expectedSchemaVersion: number; + actualSchemaVersion: number; + failure: ProviderFailureWithCategory<"schema_mismatch">; +} + +export type { GraphProviderSchemaMismatchStatus }; + +interface GraphProviderDaemonUnavailableStatus extends GraphProviderStatusBase { + state: "daemon_unavailable"; + failure: ProviderFailureWithCategory<"daemon_unavailable">; +} + +export type { GraphProviderDaemonUnavailableStatus }; + +interface GraphProviderErrorStatus extends GraphProviderStatusBase { + state: "error"; + failure: ProviderFailureWithCategory; + diagnostics?: readonly GraphExtractionDiagnostic[]; +} + +export type { GraphProviderErrorStatus }; diff --git a/packages/contracts/src/graph/provider-contracts-02.ts b/packages/contracts/src/graph/provider-contracts-02.ts new file mode 100644 index 0000000..ef681cf --- /dev/null +++ b/packages/contracts/src/graph/provider-contracts-02.ts @@ -0,0 +1,69 @@ +import type { JsonValue } from "../shared/json.js"; +import type { + GraphFreshness, + GraphProviderAvailableStatus, + GraphProviderDaemonUnavailableStatus, + GraphProviderErrorStatus, + GraphProviderRequiredMissingStatus, + GraphProviderSchemaMismatchStatus, + GraphProviderSkippedStatus, + GraphProviderStaleStatus, + GraphProviderWarmingStatus, + RepoIdentity, +} from "./provider-contracts-01.js"; +import type { GraphEdgeKind, GraphNodeKind } from "./vocabulary-01.js"; + +type GraphProviderStatus = + | GraphProviderAvailableStatus + | GraphProviderWarmingStatus + | GraphProviderSkippedStatus + | GraphProviderRequiredMissingStatus + | GraphProviderStaleStatus + | GraphProviderSchemaMismatchStatus + | GraphProviderDaemonUnavailableStatus + | GraphProviderErrorStatus; + +export type { GraphProviderStatus }; + +type GraphProviderFailureStatus = Exclude< + GraphProviderStatus, + GraphProviderAvailableStatus | GraphProviderWarmingStatus +>; + +export type { GraphProviderFailureStatus }; + +type GraphProviderNonAvailableStatus = Exclude; + +export type { GraphProviderNonAvailableStatus }; + +interface GraphFactNode { + id: string; + kind: GraphNodeKind; + path?: string; + name?: string; + attributes?: Record; +} + +export type { GraphFactNode }; + +interface GraphFactEdge { + id?: string; + kind: GraphEdgeKind; + from: string; + to: string; + attributes?: Record; +} + +export type { GraphFactEdge }; + +interface GraphSnapshotMetadata { + schemaVersion: number; + provider: string; + repo: RepoIdentity; + generatedAt: string; + freshness: GraphFreshness; + nodeKinds: readonly GraphNodeKind[]; + edgeKinds: readonly GraphEdgeKind[]; +} + +export type { GraphSnapshotMetadata }; diff --git a/packages/contracts/src/graph/provider-validators.ts b/packages/contracts/src/graph/provider-validators.ts new file mode 100644 index 0000000..88d45f8 --- /dev/null +++ b/packages/contracts/src/graph/provider-validators.ts @@ -0,0 +1,159 @@ +import { validateOptional, validateRequiredObject } from "../shared/validators-02.js"; +import { includesString } from "../shared/primitives.js"; +import { validateRepoIdentity } from "../shared/path-validators.js"; +import { validateGraphFreshness, validateNonEmptyString, validateStringArray } from "../shared/validators-01.js"; +import { validateGraphDaemonOperation, validateGraphProviderQueryKind } from "./helper-validators.js"; +import { + validateGraphExtractionDiagnostics, + validateGraphProviderArtifactMetadata, + validateGraphWalCheckpointSummary, + validateGraphWatchLifecycle, +} from "./protocol-validators.js"; +import type { GraphProviderCapabilityHandshake } from "./provider-contracts-01.js"; +import type { GraphProviderFailureStatus, GraphProviderStatus } from "./provider-contracts-02.js"; +import { + graphProviderFailureCategoriesByState, + graphProviderModes, + graphProviderStatusStates, + providerFailureCategories, +} from "./vocabulary-01.js"; + +function validateProviderStatus(status: GraphProviderStatus): GraphProviderStatus { + validateRequiredObject(status, "Graph provider status is required"); + validateProviderStatusHeader(status); + if (status.state === "available") return validateAvailableProviderStatus(status); + if (status.state === "warming") return validateWarmingProviderStatus(status); + validateProviderFailureStatus(status); + return status; +} + +export { validateProviderStatus }; + +function validateProviderStatusHeader(status: GraphProviderStatus): void { + if (!includesString(graphProviderStatusStates, status.state)) { + throw new Error(`Unknown graph provider status state: ${String(status.state)}`); + } + if (!includesString(graphProviderModes, status.mode)) { + throw new Error(`Unknown graph provider mode: ${String(status.mode)}`); + } + if (typeof status.provider !== "string" || status.provider.length === 0) { + throw new Error("Graph provider status must include provider"); + } + if (typeof status.schemaVersion !== "number") { + throw new Error("Graph provider status must include numeric schemaVersion"); + } + if (status.state === "skipped" && status.mode !== "optional") { + throw new Error("Skipped graph provider status must use optional mode"); + } + if (status.state === "required_missing" && status.mode !== "required") { + throw new Error("Required-missing graph provider status must use required mode"); + } +} + +function validateAvailableProviderStatus( + status: Extract, +): GraphProviderStatus { + validateRepoIdentity(status.repo); + validateGraphFreshness(status.freshness, "Available"); + validateGraphKindCounts(status.nodes_by_kind, "nodes_by_kind"); + validateGraphKindCounts(status.edges_by_kind, "edges_by_kind"); + validateOptional(status.handshake, validateGraphProviderCapabilityHandshake); + validateOptional(status.walCheckpoint, validateGraphWalCheckpointSummary); + return status; +} + +function validateWarmingProviderStatus( + status: Extract, +): GraphProviderStatus { + validateRepoIdentity(status.repo); + validateGraphFreshness(status.freshness, "Warming"); + validateOptional(status.lifecycle, validateGraphWatchLifecycle); + return status; +} + +function validateGraphKindCounts(counts: Readonly>, label: string): void { + if (!counts || typeof counts !== "object" || Array.isArray(counts)) { + throw new Error(`Graph provider status ${label} must be an object`); + } + for (const [kind, count] of Object.entries(counts)) { + if (kind.length === 0) throw new Error(`Graph provider status ${label} kind must not be empty`); + if (!Number.isInteger(count) || count < 0) { + throw new Error(`Graph provider status ${label}.${kind} must be a non-negative integer`); + } + } +} + +export { validateGraphKindCounts }; + +function validateProviderFailureStatus(status: GraphProviderFailureStatus): void { + if (!status.failure?.category) { + throw new Error(`Graph provider ${status.state} status must include failure.category`); + } + if (!includesString(providerFailureCategories, status.failure.category)) { + throw new Error(`Unknown graph provider failure category: ${status.failure.category}`); + } + const allowedCategories = graphProviderFailureCategoriesByState[status.state]; + if (!includesString(allowedCategories, status.failure.category)) { + throw new Error( + `Graph provider ${status.state} failure category must be one of ${allowedCategories.join(", ")}; ` + + `got ${status.failure.category}`, + ); + } + validateStaleProviderStatus(status); + validateSchemaMismatchProviderStatus(status); + validateErrorProviderStatus(status); +} + +export { validateProviderFailureStatus }; + +function validateStaleProviderStatus(status: GraphProviderFailureStatus): void { + if (status.state !== "stale") return; + if (!status.repo) throw new Error("Stale graph provider status must include repo"); + validateRepoIdentity(status.repo); + validateGraphFreshness(status.freshness, "Stale"); +} + +function validateSchemaMismatchProviderStatus(status: GraphProviderFailureStatus): void { + if (status.state !== "schema_mismatch") return; + if (typeof status.expectedSchemaVersion !== "number") { + throw new Error("Schema-mismatch graph provider status must include expectedSchemaVersion"); + } + if (typeof status.actualSchemaVersion !== "number") { + throw new Error("Schema-mismatch graph provider status must include actualSchemaVersion"); + } +} + +function validateErrorProviderStatus(status: GraphProviderFailureStatus): void { + if (status.state === "error") validateOptional(status.diagnostics, validateGraphExtractionDiagnostics); +} + +function validateGraphProviderCapabilityHandshake( + handshake: GraphProviderCapabilityHandshake, +): GraphProviderCapabilityHandshake { + validateRequiredObject(handshake, "Graph provider capability handshake is required"); + validateNonEmptyString(handshake.provider, "Graph provider capability handshake provider"); + if (typeof handshake.graphSchemaVersion !== "number") { + throw new Error("Graph provider capability handshake graphSchemaVersion must be numeric"); + } + validateNonEmptyString(handshake.artifactName, "Graph provider capability handshake artifactName"); + validateNonEmptyString(handshake.artifactVersion, "Graph provider capability handshake artifactVersion"); + validateNonEmptyString(handshake.targetPlatform, "Graph provider capability handshake targetPlatform"); + validateStringArray(handshake.supportedOperations, "Graph provider capability handshake supportedOperations", { + allowEmpty: false, + }); + for (const operation of handshake.supportedOperations) validateGraphDaemonOperation(operation); + validateStringArray(handshake.nodeKinds, "Graph provider capability handshake nodeKinds", { allowEmpty: false }); + validateStringArray(handshake.edgeKinds, "Graph provider capability handshake edgeKinds", { allowEmpty: false }); + validateStringArray(handshake.queryKinds, "Graph provider capability handshake queryKinds", { allowEmpty: false }); + for (const queryKind of handshake.queryKinds) validateGraphProviderQueryKind(queryKind); + validateGraphProviderArtifactMetadata(handshake.artifact); + if (handshake.artifact.artifactName !== handshake.artifactName) { + throw new Error("Graph provider capability handshake artifactName must match artifact metadata"); + } + if (handshake.artifact.targetPlatform !== handshake.targetPlatform) { + throw new Error("Graph provider capability handshake targetPlatform must match artifact metadata"); + } + return handshake; +} + +export { validateGraphProviderCapabilityHandshake }; diff --git a/packages/contracts/src/graph/query-contracts-01.ts b/packages/contracts/src/graph/query-contracts-01.ts new file mode 100644 index 0000000..f26f3ad --- /dev/null +++ b/packages/contracts/src/graph/query-contracts-01.ts @@ -0,0 +1,157 @@ +import type { GraphExtractionDiagnostic, GraphProviderAvailableStatus, RepoIdentity } from "./provider-contracts-01.js"; +import type { + GraphFactEdge, + GraphFactNode, + GraphProviderFailureStatus, + GraphSnapshotMetadata, +} from "./provider-contracts-02.js"; +import type { GraphEdgeKind, GraphNodeKind, GraphProviderMode } from "./vocabulary-01.js"; + +interface GraphFactQuerySelector { + kind: "nodes" | "edges" | "neighbors" | "symbols" | "impact"; + nodeKinds?: readonly GraphNodeKind[]; + edgeKinds?: readonly GraphEdgeKind[]; + ids?: readonly string[]; + text?: string; + limit?: number; +} + +export type { GraphFactQuerySelector }; + +const graphFactQueryKinds = ["nodes", "edges", "neighbors", "symbols", "impact"] as const; + +export { graphFactQueryKinds }; + +const graphNamedQueryKinds = [ + "callers_of", + "callees_of", + "importers_of", + "imports_of", + "tests_for", + "inheritors_of", + "children_of", + "file_summary", +] as const; + +export { graphNamedQueryKinds }; + +type GraphNamedQueryKind = (typeof graphNamedQueryKinds)[number]; + +export type { GraphNamedQueryKind }; + +type GraphProviderQueryKind = + | GraphFactQuerySelector["kind"] + | GraphNamedQueryKind + | "review_context" + | "detect_changes" + | "search"; + +export type { GraphProviderQueryKind }; + +interface GraphFactQueryRequest { + requestId?: string; + repo: RepoIdentity; + schemaVersion: number; + mode: GraphProviderMode; + selector: GraphFactQuerySelector; +} + +export type { GraphFactQueryRequest }; + +interface GraphFactQueryAvailableResult { + requestId?: string; + status: GraphProviderAvailableStatus; + metadata: GraphSnapshotMetadata; + nodes: readonly GraphFactNode[]; + edges: readonly GraphFactEdge[]; + diagnostics?: readonly GraphExtractionDiagnostic[]; +} + +export type { GraphFactQueryAvailableResult }; + +interface GraphFactQueryFailureResult { + requestId?: string; + status: GraphProviderFailureStatus; +} + +export type { GraphFactQueryFailureResult }; + +type GraphFactQueryResult = GraphFactQueryAvailableResult | GraphFactQueryFailureResult; + +export type { GraphFactQueryResult }; + +interface GraphTraversalMetadata { + maxDepth: number; + truncated: boolean; + total: number; + empty: boolean; +} + +export type { GraphTraversalMetadata }; + +interface GraphNamedQueryRequest { + requestId?: string; + repo: RepoIdentity; + schemaVersion: number; + mode: GraphProviderMode; + queryKind: GraphNamedQueryKind; + target: string; + maxDepth?: number; + limit?: number; +} + +export type { GraphNamedQueryRequest }; + +interface GraphNamedQueryAvailableResult { + requestId?: string; + status: GraphProviderAvailableStatus; + metadata: GraphSnapshotMetadata; + queryKind: GraphNamedQueryKind; + target: string; + nodes: readonly GraphFactNode[]; + edges: readonly GraphFactEdge[]; + traversal: GraphTraversalMetadata; + diagnostics?: readonly GraphExtractionDiagnostic[]; +} + +export type { GraphNamedQueryAvailableResult }; + +interface GraphNamedQueryFailureResult { + requestId?: string; + status: GraphProviderFailureStatus; +} + +export type { GraphNamedQueryFailureResult }; + +type GraphNamedQueryResult = GraphNamedQueryAvailableResult | GraphNamedQueryFailureResult; + +export type { GraphNamedQueryResult }; + +interface GraphImpactRequest { + requestId?: string; + repo: RepoIdentity; + schemaVersion: number; + mode: GraphProviderMode; + files: readonly string[]; + baseRef?: string; + maxDepth?: number; + limit?: number; +} + +export type { GraphImpactRequest }; + +interface GraphImpactAvailableResult { + requestId?: string; + status: GraphProviderAvailableStatus; + metadata: GraphSnapshotMetadata; + changedFiles: readonly string[]; + impactedFiles: readonly string[]; + impactedSymbols: readonly string[]; + tests: readonly string[]; + nodes: readonly GraphFactNode[]; + edges: readonly GraphFactEdge[]; + traversal: GraphTraversalMetadata; + diagnostics?: readonly GraphExtractionDiagnostic[]; +} + +export type { GraphImpactAvailableResult }; diff --git a/packages/contracts/src/graph/query-contracts-02.ts b/packages/contracts/src/graph/query-contracts-02.ts new file mode 100644 index 0000000..98fc90b --- /dev/null +++ b/packages/contracts/src/graph/query-contracts-02.ts @@ -0,0 +1,105 @@ +import type { GraphExtractionDiagnostic, GraphProviderAvailableStatus, RepoIdentity } from "./provider-contracts-01.js"; +import type { + GraphFactEdge, + GraphFactNode, + GraphProviderFailureStatus, + GraphSnapshotMetadata, +} from "./provider-contracts-02.js"; +import type { GraphImpactAvailableResult, GraphTraversalMetadata } from "./query-contracts-01.js"; +import type { GraphProviderMode } from "./vocabulary-01.js"; + +interface GraphImpactFailureResult { + requestId?: string; + status: GraphProviderFailureStatus; +} + +export type { GraphImpactFailureResult }; + +type GraphImpactResult = GraphImpactAvailableResult | GraphImpactFailureResult; + +export type { GraphImpactResult }; + +interface GraphRenamedFile { + fromPath: string; + toPath: string; + checksumBefore?: string; + checksumAfter?: string; +} + +export type { GraphRenamedFile }; + +interface GraphDetectChangesRequest { + requestId?: string; + repo: RepoIdentity; + schemaVersion: number; + mode: GraphProviderMode; + files?: readonly string[]; + baseRef?: string; +} + +export type { GraphDetectChangesRequest }; + +interface GraphDetectChangesAvailableResult { + requestId?: string; + status: GraphProviderAvailableStatus; + metadata: GraphSnapshotMetadata; + changedFiles: readonly string[]; + deletedFiles: readonly string[]; + renamedFiles: readonly GraphRenamedFile[]; + diagnostics?: readonly GraphExtractionDiagnostic[]; +} + +export type { GraphDetectChangesAvailableResult }; + +interface GraphDetectChangesFailureResult { + requestId?: string; + status: GraphProviderFailureStatus; +} + +export type { GraphDetectChangesFailureResult }; + +type GraphDetectChangesResult = GraphDetectChangesAvailableResult | GraphDetectChangesFailureResult; + +export type { GraphDetectChangesResult }; + +interface GraphReviewContextRequest { + requestId?: string; + repo: RepoIdentity; + schemaVersion: number; + mode: GraphProviderMode; + files?: readonly string[]; + baseRef?: string; + maxDepth?: number; + limit?: number; +} + +export type { GraphReviewContextRequest }; + +interface GraphReviewContextAvailableResult { + requestId?: string; + status: GraphProviderAvailableStatus; + metadata: GraphSnapshotMetadata; + changedFiles: readonly string[]; + deletedFiles: readonly string[]; + renamedFiles: readonly GraphRenamedFile[]; + impactedFiles: readonly string[]; + impactedSymbols: readonly string[]; + tests: readonly string[]; + nodes: readonly GraphFactNode[]; + edges: readonly GraphFactEdge[]; + traversal: GraphTraversalMetadata; + diagnostics?: readonly GraphExtractionDiagnostic[]; +} + +export type { GraphReviewContextAvailableResult }; + +interface GraphReviewContextFailureResult { + requestId?: string; + status: GraphProviderFailureStatus; +} + +export type { GraphReviewContextFailureResult }; + +type GraphReviewContextResult = GraphReviewContextAvailableResult | GraphReviewContextFailureResult; + +export type { GraphReviewContextResult }; diff --git a/packages/contracts/src/graph/query-validators.ts b/packages/contracts/src/graph/query-validators.ts new file mode 100644 index 0000000..f22ec37 --- /dev/null +++ b/packages/contracts/src/graph/query-validators.ts @@ -0,0 +1,231 @@ +import { validateOptional, validateRequiredObject } from "../shared/validators-02.js"; +import { includesString } from "../shared/primitives.js"; +import { + validateRepoIdentity, + validateRepoRelativePath, + validateRepoRelativePaths, +} from "../shared/path-validators.js"; +import { validateNonEmptyString, validateStringArray } from "../shared/validators-01.js"; +import { validateGraphSnapshotMetadata } from "../shared/validators-02.js"; +import { + validateGraphFactQueryKind, + validateGraphNamedQueryKind, + validateGraphQueryRequestBase, + validateGraphTraversalMetadata, + validateTraversalOptions, +} from "./helper-validators.js"; +import { + validateGraphFactEdge, + validateGraphFactNode, + validateGraphPayloadResult, + validateRenamedFiles, +} from "./payload-validators.js"; +import { validateGraphExtractionDiagnostics } from "./protocol-validators.js"; +import type { GraphExtractionDiagnostic } from "./provider-contracts-01.js"; +import type { GraphFactEdge, GraphFactNode, GraphSnapshotMetadata } from "./provider-contracts-02.js"; +import { validateProviderStatus } from "./provider-validators.js"; +import type { + GraphFactQueryRequest, + GraphFactQueryResult, + GraphImpactRequest, + GraphNamedQueryRequest, + GraphNamedQueryResult, + GraphTraversalMetadata, +} from "./query-contracts-01.js"; +import type { + GraphDetectChangesRequest, + GraphDetectChangesResult, + GraphImpactResult, + GraphRenamedFile, + GraphReviewContextRequest, + GraphReviewContextResult, +} from "./query-contracts-02.js"; +import { GRAPH_SCHEMA_VERSION, graphProviderModes } from "./vocabulary-01.js"; + +function validateGraphFactQueryRequest(request: GraphFactQueryRequest): GraphFactQueryRequest { + validateRequiredObject(request, "Graph fact query request is required"); + if (request.requestId !== undefined) validateNonEmptyString(request.requestId, "Graph fact query request requestId"); + validateRepoIdentity(request.repo); + if (request.schemaVersion !== GRAPH_SCHEMA_VERSION) { + throw new Error(`Graph fact query request schemaVersion must be ${GRAPH_SCHEMA_VERSION}`); + } + if (!includesString(graphProviderModes, request.mode)) { + throw new Error(`Unknown graph fact query request mode: ${String(request.mode)}`); + } + validateRequiredObject(request.selector, "Graph fact query request selector is required"); + validateGraphFactQueryKind(request.selector.kind); + if (request.selector.nodeKinds !== undefined) { + validateStringArray(request.selector.nodeKinds, "Graph fact query selector nodeKinds", { allowEmpty: true }); + } + if (request.selector.edgeKinds !== undefined) { + validateStringArray(request.selector.edgeKinds, "Graph fact query selector edgeKinds", { allowEmpty: true }); + } + if (request.selector.ids !== undefined) { + validateStringArray(request.selector.ids, "Graph fact query selector ids", { + allowEmpty: true, + }); + } + if ( + request.selector.limit !== undefined && + (typeof request.selector.limit !== "number" || request.selector.limit < 1) + ) { + throw new Error("Graph fact query selector limit must be a positive number"); + } + return request; +} + +export { validateGraphFactQueryRequest }; + +function validateGraphFactQueryResult(result: GraphFactQueryResult): GraphFactQueryResult { + validateRequiredObject(result, "Graph fact query result is required"); + validateOptional(result.requestId, (value) => validateNonEmptyString(value, "Graph fact query result requestId")); + const status = validateProviderStatus(result.status); + const payload = result as { + metadata?: unknown; + nodes?: unknown; + edges?: unknown; + diagnostics?: unknown; + }; + if (status.state !== "available") { + validateUnavailableGraphFactQueryPayload(payload, status.state); + return result; + } + validateAvailableGraphFactQueryPayload(payload); + return result; +} + +export { validateGraphFactQueryResult }; + +interface GraphFactQueryPayload { + metadata?: unknown; + nodes?: unknown; + edges?: unknown; + diagnostics?: unknown; +} + +function validateUnavailableGraphFactQueryPayload( + payload: GraphFactQueryPayload, + state: string, +): void { + const hasGraphData = + Object.hasOwn(payload, "metadata") || Object.hasOwn(payload, "nodes") || Object.hasOwn(payload, "edges"); + if (hasGraphData) throw new Error(`Graph query ${state} result must not include graph data`); +} + +function validateAvailableGraphFactQueryPayload(payload: GraphFactQueryPayload): void { + if (!payload.metadata || !Array.isArray(payload.nodes) || !Array.isArray(payload.edges)) { + throw new Error("Available graph query result must include metadata, nodes, and edges"); + } + validateGraphSnapshotMetadata(payload.metadata as GraphSnapshotMetadata); + for (const node of payload.nodes) validateGraphFactNode(node as GraphFactNode); + for (const edge of payload.edges) validateGraphFactEdge(edge as GraphFactEdge); + validateOptional(payload.diagnostics, (value) => + validateGraphExtractionDiagnostics(value as readonly GraphExtractionDiagnostic[]), + ); +} + +function validateGraphNamedQueryRequest(request: GraphNamedQueryRequest): GraphNamedQueryRequest { + validateGraphQueryRequestBase(request, "Graph named query request"); + validateGraphNamedQueryKind(request.queryKind); + validateNonEmptyString(request.target, "Graph named query request target"); + validateTraversalOptions(request.maxDepth, request.limit, "Graph named query request"); + return request; +} + +export { validateGraphNamedQueryRequest }; + +function validateGraphNamedQueryResult(result: GraphNamedQueryResult): GraphNamedQueryResult { + validateGraphPayloadResult(result, "Graph named query result", (payload) => { + validateGraphSnapshotMetadata(payload.metadata as GraphSnapshotMetadata); + validateGraphNamedQueryKind(payload.queryKind); + validateNonEmptyString(payload.target, "Graph named query result target"); + for (const node of payload.nodes as readonly GraphFactNode[]) validateGraphFactNode(node); + for (const edge of payload.edges as readonly GraphFactEdge[]) validateGraphFactEdge(edge); + validateGraphTraversalMetadata(payload.traversal as GraphTraversalMetadata); + }); + return result; +} + +export { validateGraphNamedQueryResult }; + +function validateGraphImpactRequest(request: GraphImpactRequest): GraphImpactRequest { + validateGraphQueryRequestBase(request, "Graph impact request"); + validateStringArray(request.files, "Graph impact request files", { + allowEmpty: false, + }); + for (const file of request.files) validateRepoRelativePath(file); + if (request.baseRef !== undefined) validateNonEmptyString(request.baseRef, "Graph impact request baseRef"); + validateTraversalOptions(request.maxDepth, request.limit, "Graph impact request"); + return request; +} + +export { validateGraphImpactRequest }; + +function validateGraphImpactResult(result: GraphImpactResult): GraphImpactResult { + validateGraphPayloadResult(result, "Graph impact result", (payload) => { + validateGraphSnapshotMetadata(payload.metadata as GraphSnapshotMetadata); + validateRepoRelativePaths(payload.changedFiles, "Graph impact result changedFiles"); + validateRepoRelativePaths(payload.impactedFiles, "Graph impact result impactedFiles"); + validateStringArray(payload.impactedSymbols as readonly string[], "Graph impact result impactedSymbols", { + allowEmpty: true, + }); + validateRepoRelativePaths(payload.tests, "Graph impact result tests"); + for (const node of payload.nodes as readonly GraphFactNode[]) validateGraphFactNode(node); + for (const edge of payload.edges as readonly GraphFactEdge[]) validateGraphFactEdge(edge); + validateGraphTraversalMetadata(payload.traversal as GraphTraversalMetadata); + }); + return result; +} + +export { validateGraphImpactResult }; + +function validateGraphDetectChangesRequest(request: GraphDetectChangesRequest): GraphDetectChangesRequest { + validateGraphQueryRequestBase(request, "Graph detect-changes request"); + if (request.files !== undefined) validateRepoRelativePaths(request.files, "Graph detect-changes request files"); + if (request.baseRef !== undefined) validateNonEmptyString(request.baseRef, "Graph detect-changes request baseRef"); + return request; +} + +export { validateGraphDetectChangesRequest }; + +function validateGraphDetectChangesResult(result: GraphDetectChangesResult): GraphDetectChangesResult { + validateGraphPayloadResult(result, "Graph detect-changes result", (payload) => { + validateGraphSnapshotMetadata(payload.metadata as GraphSnapshotMetadata); + validateRepoRelativePaths(payload.changedFiles, "Graph detect-changes result changedFiles"); + validateRepoRelativePaths(payload.deletedFiles, "Graph detect-changes result deletedFiles"); + validateRenamedFiles(payload.renamedFiles as readonly GraphRenamedFile[]); + }); + return result; +} + +export { validateGraphDetectChangesResult }; + +function validateGraphReviewContextRequest(request: GraphReviewContextRequest): GraphReviewContextRequest { + validateGraphQueryRequestBase(request, "Graph review-context request"); + if (request.files !== undefined) validateRepoRelativePaths(request.files, "Graph review-context request files"); + if (request.baseRef !== undefined) validateNonEmptyString(request.baseRef, "Graph review-context request baseRef"); + validateTraversalOptions(request.maxDepth, request.limit, "Graph review-context request"); + return request; +} + +export { validateGraphReviewContextRequest }; + +function validateGraphReviewContextResult(result: GraphReviewContextResult): GraphReviewContextResult { + validateGraphPayloadResult(result, "Graph review-context result", (payload) => { + validateGraphSnapshotMetadata(payload.metadata as GraphSnapshotMetadata); + validateRepoRelativePaths(payload.changedFiles, "Graph review-context result changedFiles"); + validateRepoRelativePaths(payload.deletedFiles, "Graph review-context result deletedFiles"); + validateRenamedFiles(payload.renamedFiles as readonly GraphRenamedFile[]); + validateRepoRelativePaths(payload.impactedFiles, "Graph review-context result impactedFiles"); + validateStringArray(payload.impactedSymbols as readonly string[], "Graph review-context result impactedSymbols", { + allowEmpty: true, + }); + validateRepoRelativePaths(payload.tests, "Graph review-context result tests"); + for (const node of payload.nodes as readonly GraphFactNode[]) validateGraphFactNode(node); + for (const edge of payload.edges as readonly GraphFactEdge[]) validateGraphFactEdge(edge); + validateGraphTraversalMetadata(payload.traversal as GraphTraversalMetadata); + }); + return result; +} + +export { validateGraphReviewContextResult }; diff --git a/packages/contracts/src/graph/search-contracts.ts b/packages/contracts/src/graph/search-contracts.ts new file mode 100644 index 0000000..17c5fb0 --- /dev/null +++ b/packages/contracts/src/graph/search-contracts.ts @@ -0,0 +1,77 @@ +import type { GraphExtractionDiagnostic, GraphProviderAvailableStatus, RepoIdentity } from "./provider-contracts-01.js"; +import type { GraphProviderNonAvailableStatus, GraphSnapshotMetadata } from "./provider-contracts-02.js"; +import type { GraphNodeKind, GraphProviderMode } from "./vocabulary-01.js"; + +interface GraphSearchRequest { + requestId?: string; + repo: RepoIdentity; + schemaVersion: number; + mode: GraphProviderMode; + query: string; + limit?: number; + files?: readonly string[]; +} + +export type { GraphSearchRequest }; + +interface GraphSearchMode { + engine: "fts5" | (string & {}); + querySyntax: "fts5" | (string & {}); + limit: number; + contextFiles: readonly string[]; +} + +export type { GraphSearchMode }; + +interface GraphSearchResultEntry { + nodeId: string; + kind: GraphNodeKind; + path?: string; + name?: string; + qualifiedName: string; + filePath?: string; + signature: string; + score: number; + rank: number; + matches: readonly string[]; +} + +export type { GraphSearchResultEntry }; + +interface GraphSearchSummary { + query: string; + total: number; + returned: number; + limit: number; + indexedNodeKinds: readonly GraphNodeKind[]; + contextFiles: readonly string[]; +} + +export type { GraphSearchSummary }; + +interface GraphSearchAvailableResult { + requestId?: string; + status: GraphProviderAvailableStatus; + metadata: GraphSnapshotMetadata; + query: string; + searchMode: GraphSearchMode; + summary: GraphSearchSummary; + results: readonly GraphSearchResultEntry[]; + hints: readonly string[]; + diagnostics?: readonly GraphExtractionDiagnostic[]; +} + +export type { GraphSearchAvailableResult }; + +interface GraphSearchFailureResult { + requestId?: string; + status: GraphProviderNonAvailableStatus; + hints?: readonly string[]; + diagnostics?: readonly GraphExtractionDiagnostic[]; +} + +export type { GraphSearchFailureResult }; + +type GraphSearchResult = GraphSearchAvailableResult | GraphSearchFailureResult; + +export type { GraphSearchResult }; diff --git a/packages/contracts/src/graph/search-validators.ts b/packages/contracts/src/graph/search-validators.ts new file mode 100644 index 0000000..ff30742 --- /dev/null +++ b/packages/contracts/src/graph/search-validators.ts @@ -0,0 +1,100 @@ +import { validateNonEmptyString, validateStringArray } from "../shared/validators-01.js"; +import { validateRepoRelativePaths } from "../shared/path-validators.js"; +import { + validateGraphSnapshotMetadata, + validateOptional, + validateRequiredObject, +} from "../shared/validators-02.js"; +import { + validateGraphQueryRequestBase, + validateGraphSearchMode, + validateGraphSearchResultEntry, + validateGraphSearchSummary, +} from "./helper-validators.js"; +import { validateGraphExtractionDiagnostics } from "./protocol-validators.js"; +import type { GraphExtractionDiagnostic } from "./provider-contracts-01.js"; +import type { GraphSnapshotMetadata } from "./provider-contracts-02.js"; +import { validateProviderStatus } from "./provider-validators.js"; +import type { + GraphSearchMode, + GraphSearchRequest, + GraphSearchResult, + GraphSearchResultEntry, + GraphSearchSummary, +} from "./search-contracts.js"; + +function validateGraphSearchRequest(request: GraphSearchRequest): GraphSearchRequest { + validateGraphQueryRequestBase(request, "Graph search request"); + validateNonEmptyString(request.query, "Graph search request query"); + if (request.query.trim().length === 0) throw new Error("Graph search request query must not be empty"); + if (request.limit !== undefined && (!Number.isFinite(request.limit) || request.limit < 1)) { + throw new Error("Graph search request limit must be a positive number"); + } + if (request.files !== undefined) validateRepoRelativePaths(request.files, "Graph search request files"); + return request; +} + +export { validateGraphSearchRequest }; + +function validateGraphSearchResult(result: GraphSearchResult): GraphSearchResult { + validateRequiredObject(result, "Graph search result is required"); + validateOptional(result.requestId, (value) => validateNonEmptyString(value, "Graph search result requestId")); + const status = validateProviderStatus(result.status); + const payload = result as GraphSearchPayload; + if (status.state !== "available") { + validateUnavailableGraphSearchPayload(payload, status.state); + return result; + } + validateAvailableGraphSearchPayload(payload); + return result; +} + +export { validateGraphSearchResult }; + +interface GraphSearchPayload { + metadata?: unknown; + query?: unknown; + searchMode?: unknown; + summary?: unknown; + results?: unknown; + hints?: unknown; + diagnostics?: unknown; +} + +function validateUnavailableGraphSearchPayload( + payload: GraphSearchPayload, + state: GraphSearchResult["status"]["state"], +): void { + const dataFields = ["metadata", "query", "searchMode", "summary", "results"] as const; + if (dataFields.some((field) => Object.hasOwn(payload, field))) { + throw new Error(`Graph search ${state} result must not include search data`); + } + validateGraphSearchOptionalEvidence(payload); +} + +function validateAvailableGraphSearchPayload(payload: GraphSearchPayload): void { + if ( + !payload.metadata || + typeof payload.query !== "string" || + !payload.searchMode || + !payload.summary || + !Array.isArray(payload.results) + ) { + throw new Error("Available graph search result must include metadata, query, searchMode, summary, and results"); + } + validateGraphSnapshotMetadata(payload.metadata as GraphSnapshotMetadata); + validateNonEmptyString(payload.query, "Graph search result query"); + validateGraphSearchMode(payload.searchMode as GraphSearchMode); + validateGraphSearchSummary(payload.summary as GraphSearchSummary); + for (const entry of payload.results as readonly GraphSearchResultEntry[]) validateGraphSearchResultEntry(entry); + validateGraphSearchOptionalEvidence(payload); +} + +function validateGraphSearchOptionalEvidence(payload: GraphSearchPayload): void { + validateOptional(payload.hints, (value) => + validateStringArray(value as readonly string[], "Graph search result hints", { allowEmpty: true }), + ); + validateOptional(payload.diagnostics, (value) => + validateGraphExtractionDiagnostics(value as readonly GraphExtractionDiagnostic[]), + ); +} diff --git a/packages/contracts/src/graph/vocabulary-01.ts b/packages/contracts/src/graph/vocabulary-01.ts new file mode 100644 index 0000000..1d871ee --- /dev/null +++ b/packages/contracts/src/graph/vocabulary-01.ts @@ -0,0 +1,139 @@ +const GRAPH_SCHEMA_VERSION = 1 as const; + +export { GRAPH_SCHEMA_VERSION }; + +const CLONE_PROTOCOL = "opcore.clone.v1" as const; + +export { CLONE_PROTOCOL }; + +const graphProviderModes = ["optional", "required"] as const; + +export { graphProviderModes }; + +type GraphProviderMode = (typeof graphProviderModes)[number]; + +export type { GraphProviderMode }; + +const graphProviderStatusStates = [ + "available", + "warming", + "skipped", + "required_missing", + "stale", + "schema_mismatch", + "daemon_unavailable", + "error", +] as const; + +export { graphProviderStatusStates }; + +type GraphProviderStatusState = (typeof graphProviderStatusStates)[number]; + +export type { GraphProviderStatusState }; + +const requiredGraphNodeKinds = [ + "repo", + "package", + "file", + "symbol", + "test", + "File", + "Module", + "Class", + "Function", + "Variable", + "Type", + "Test", + "Struct", + "Enum", + "Trait", + "Impl", + "Method", + "TypeAlias", + "Const", + "Static", + "Macro", +] as const; + +export { requiredGraphNodeKinds }; + +type GraphNodeKind = (typeof requiredGraphNodeKinds)[number] | (string & {}); + +export type { GraphNodeKind }; + +const requiredGraphEdgeKinds = [ + "CONTAINS", + "DECLARES", + "IMPORTS_FROM", + "CALLS", + "TESTED_BY", + "INHERITS", + "IMPLEMENTS", + "DEPENDS_ON", +] as const; + +export { requiredGraphEdgeKinds }; + +type GraphEdgeKind = (typeof requiredGraphEdgeKinds)[number] | (string & {}); + +export type { GraphEdgeKind }; + +const graphSnapshotMetadataKeys = [ + "schemaVersion", + "provider", + "repo", + "generatedAt", + "freshness", + "nodeKinds", + "edgeKinds", +] as const; + +export { graphSnapshotMetadataKeys }; + +type GraphSnapshotMetadataKey = (typeof graphSnapshotMetadataKeys)[number]; + +export type { GraphSnapshotMetadataKey }; + +const providerFailureCategories = [ + "provider_missing", + "daemon_unavailable", + "schema_mismatch", + "stale_snapshot", + "query_failed", + "incompatible_provider", + "provider_error", + "permission_denied", + "unsupported_mode", + "unknown", +] as const; + +export { providerFailureCategories }; + +type ProviderFailureCategory = (typeof providerFailureCategories)[number]; + +export type { ProviderFailureCategory }; + +const graphProviderFailureCategoriesByState = { + skipped: ["provider_missing"], + required_missing: ["provider_missing"], + stale: ["stale_snapshot"], + schema_mismatch: ["schema_mismatch"], + daemon_unavailable: ["daemon_unavailable"], + error: [ + "query_failed", + "incompatible_provider", + "provider_error", + "permission_denied", + "unsupported_mode", + "unknown", + ], +} as const satisfies Record< + Exclude, + readonly ProviderFailureCategory[] +>; + +export { graphProviderFailureCategoriesByState }; + +type GraphProviderErrorFailureCategory = (typeof graphProviderFailureCategoriesByState.error)[number]; + +export type { GraphProviderErrorFailureCategory }; diff --git a/packages/contracts/src/graph/vocabulary-02.ts b/packages/contracts/src/graph/vocabulary-02.ts new file mode 100644 index 0000000..0eba8f2 --- /dev/null +++ b/packages/contracts/src/graph/vocabulary-02.ts @@ -0,0 +1,18 @@ +const graphExtractionDiagnosticCategories = [ + "missing_tsconfig", + "malformed_tsconfig", + "unsupported_language", + "parse_error", + "missing_parser", + "unresolved_import", + "max_files_exceeded", + "max_depth_exceeded", + "path_traversal", + "io_error", +] as const; + +export { graphExtractionDiagnosticCategories }; + +type GraphExtractionDiagnosticCategory = (typeof graphExtractionDiagnosticCategories)[number]; + +export type { GraphExtractionDiagnosticCategory }; diff --git a/packages/contracts/src/index.ts b/packages/contracts/src/index.ts index 79503d3..d3c95d8 100644 --- a/packages/contracts/src/index.ts +++ b/packages/contracts/src/index.ts @@ -1,10190 +1,272 @@ -export const GRAPH_SCHEMA_VERSION = 1 as const; -export const CLONE_PROTOCOL = "opcore.clone.v1" as const; - -export const graphProviderModes = ["optional", "required"] as const; -export type GraphProviderMode = (typeof graphProviderModes)[number]; - -export const graphProviderStatusStates = [ - "available", - "warming", - "skipped", - "required_missing", - "stale", - "schema_mismatch", - "daemon_unavailable", - "error" -] as const; -export type GraphProviderStatusState = (typeof graphProviderStatusStates)[number]; - -export const requiredGraphNodeKinds = [ - "repo", - "package", - "file", - "symbol", - "test", - "File", - "Module", - "Class", - "Function", - "Variable", - "Type", - "Test", - "Struct", - "Enum", - "Trait", - "Impl", - "Method", - "TypeAlias", - "Const", - "Static", - "Macro" -] as const; -export type GraphNodeKind = (typeof requiredGraphNodeKinds)[number] | (string & {}); - -export const requiredGraphEdgeKinds = [ - "CONTAINS", - "DECLARES", - "IMPORTS_FROM", - "CALLS", - "TESTED_BY", - "INHERITS", - "IMPLEMENTS", - "DEPENDS_ON" -] as const; -export type GraphEdgeKind = (typeof requiredGraphEdgeKinds)[number] | (string & {}); - -export const graphSnapshotMetadataKeys = [ - "schemaVersion", - "provider", - "repo", - "generatedAt", - "freshness", - "nodeKinds", - "edgeKinds" -] as const; -export type GraphSnapshotMetadataKey = (typeof graphSnapshotMetadataKeys)[number]; - -export const providerFailureCategories = [ - "provider_missing", - "daemon_unavailable", - "schema_mismatch", - "stale_snapshot", - "query_failed", - "incompatible_provider", - "provider_error", - "permission_denied", - "unsupported_mode", - "unknown" -] as const; -export type ProviderFailureCategory = (typeof providerFailureCategories)[number]; - -export const graphProviderFailureCategoriesByState = { - skipped: ["provider_missing"], - required_missing: ["provider_missing"], - stale: ["stale_snapshot"], - schema_mismatch: ["schema_mismatch"], - daemon_unavailable: ["daemon_unavailable"], - error: ["query_failed", "incompatible_provider", "provider_error", "permission_denied", "unsupported_mode", "unknown"] -} as const satisfies Record, readonly ProviderFailureCategory[]>; -export type GraphProviderErrorFailureCategory = (typeof graphProviderFailureCategoriesByState.error)[number]; - -export const graphExtractionDiagnosticCategories = [ - "missing_tsconfig", - "malformed_tsconfig", - "unsupported_language", - "parse_error", - "missing_parser", - "unresolved_import", - "max_files_exceeded", - "max_depth_exceeded", - "path_traversal", - "io_error" -] as const; -export type GraphExtractionDiagnosticCategory = (typeof graphExtractionDiagnosticCategories)[number]; - -export const editRefusalCategories = [ - "absolute_path", - "parent_directory", - "ambiguous_repo_identity", - "validation_failed", - "provider_required_missing", - "schema_mismatch", - "unsafe_edit", - "conflict", - "unsupported_change" -] as const; -export type EditRefusalCategory = (typeof editRefusalCategories)[number]; - -export const validationDiagnosticCategories = [ - "syntax", - "types", - "lint", - "test", - "graph", - "policy", - "provider", - "infrastructure", - "edit_safety" -] as const; -export type ValidationDiagnosticCategory = (typeof validationDiagnosticCategories)[number]; - -export const validationResultStatuses = [ - "passed", - "policy_failure", - "infrastructure_failure", - "provider_failure", - "unsupported_request", - "invalid_payload", - "skipped", - "refused" -] as const; -export type ValidationResultStatus = (typeof validationResultStatuses)[number]; - -export const validationFailureCategories = [ - "policy_failure", - "infrastructure_failure", - "provider_failure", - "unsupported_request", - "invalid_payload", - "skipped" -] as const; -export type ValidationFailureCategory = (typeof validationFailureCategories)[number]; - -export const validationReportModes = ["all", "introduced"] as const; -export type ValidationReportMode = (typeof validationReportModes)[number]; - -export const validationCheckRunStatuses = [ - "passed", - "policy_failure", - "infrastructure_failure", - "provider_failure", - "unsupported_request", - "skipped" -] as const; -export type ValidationCheckRunStatus = (typeof validationCheckRunStatuses)[number]; - -export const validationCheckOutcomes = [ - "passed", - "findings", - "tool_unavailable", - "invalid_config", - "timeout", - "unsupported_target", - "tool_failure" -] as const; -export type ValidationCheckOutcome = (typeof validationCheckOutcomes)[number]; - -export const pythonValidationCapabilityRunStatuses = [...validationCheckOutcomes] as const; -export type PythonValidationCapabilityRunStatus = (typeof pythonValidationCapabilityRunStatuses)[number]; - -export const pythonValidationAuthorities = ["mypy", "pyright"] as const; -export type PythonValidationAuthority = (typeof pythonValidationAuthorities)[number]; - -export const pythonValidationAuthoritySources = ["explicit", "project_config"] as const; -export type PythonValidationAuthoritySource = (typeof pythonValidationAuthoritySources)[number]; - -export const pythonValidationCapabilityTerminationKinds = ["exited", "timeout", "signal", "spawn_error"] as const; -export type PythonValidationCapabilityTerminationKind = (typeof pythonValidationCapabilityTerminationKinds)[number]; - -export const pythonValidationCapabilities = ["types", "ruff_lint", "ruff_format", "pytest"] as const; -export type PythonValidationCapability = (typeof pythonValidationCapabilities)[number]; - -export const pythonValidationCapabilityStates = [ - ...validationCheckOutcomes, - "not_applicable", - "disabled" -] as const; -export type PythonValidationCapabilityState = (typeof pythonValidationCapabilityStates)[number]; - -export const pythonValidationCapabilityTerminations = [ - "exited", - "timeout", - "signal", - "spawn_error", - "overflow" -] as const; -export type PythonValidationCapabilityTermination = (typeof pythonValidationCapabilityTerminations)[number]; - -export const validationSkippedCheckReasons = [ - "graph_unavailable", - "unsupported_scope", - "not_requested", - "no_files", - "provider_failure" -] as const; -export type ValidationSkippedCheckReason = (typeof validationSkippedCheckReasons)[number]; - -export const validationCheckIdPattern = "^[a-z][a-z0-9]*(?:[._:-][a-z0-9]+)*$" as const; -const validationCheckIdRegex = new RegExp(validationCheckIdPattern); -const latencyStableIdRegex = /^[a-z][a-z0-9_-]*$/; -const latencyTelemetryCommandTokenRegex = /^(?=.*[A-Za-z0-9])[-@A-Za-z0-9._,:=]+$/; -const latencyTelemetrySourceFileExtensionRegex = - /\.(?:[cm]?[tj]sx?|mjs|cjs|jsonl?|rs|pyi?|mdx?|toml|lock|ya?ml|txt|inc|css|s[ac]ss|html?|vue|svelte|go|java|rb|php|swift|kts?|scala|lua|cs|c|cc|cpp|h|hpp)(?:$|[,=:])/i; - -export type JsonPrimitive = string | number | boolean | null; -export type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue }; - -export interface RepoIdentity { - repoId?: string; - repoRoot?: string; - remoteUrl?: string; - commitSha?: string; -} - -export interface GraphFreshness { - generatedAt: string; - ageMs: number; - maxAgeMs?: number; - stale: boolean; - reason?: string; -} - -export interface GraphProviderArtifactMetadata { - artifactName: "opcore-graph-core" | (string & {}); - artifactVersion: string; - targetPlatform: string; - binaryPath: string; - checksumPath: string; - checksumSha256: string; - buildProfile: string; -} - -export interface GraphProviderCapabilityHandshake { - provider: "opcore-graph" | (string & {}); - graphSchemaVersion: number; - artifactName: "opcore-graph-core" | (string & {}); - artifactVersion: string; - targetPlatform: string; - supportedOperations: readonly GraphDaemonOperation[]; - nodeKinds: readonly GraphNodeKind[]; - edgeKinds: readonly GraphEdgeKind[]; - queryKinds: readonly GraphProviderQueryKind[]; - artifact: GraphProviderArtifactMetadata; -} - -export interface ProviderFailure { - category: ProviderFailureCategory; - message: string; - retryable?: boolean; - cause?: string; -} -export type ProviderFailureWithCategory = ProviderFailure & { category: Category }; - -export interface GraphExtractionDiagnostic { - category: GraphExtractionDiagnosticCategory; - severity: "info" | "warning" | "error"; - message: string; - path?: string; - language?: string; -} - -export interface GraphProviderStatusBase { - state: GraphProviderStatusState; - mode: GraphProviderMode; - provider: string; - schemaVersion: number; - message?: string; -} - -export interface GraphProviderAvailableStatus extends GraphProviderStatusBase { - state: "available"; - repo: RepoIdentity; - freshness: GraphFreshness; - dbPath?: string; - nodes_by_kind: Readonly>; - edges_by_kind: Readonly>; - capabilities?: readonly string[]; - handshake?: GraphProviderCapabilityHandshake; - walCheckpoint?: GraphWalCheckpointSummary; -} - -export interface GraphProviderWarmingStatus extends GraphProviderStatusBase { - state: "warming"; - repo: RepoIdentity; - freshness: GraphFreshness; - lifecycle?: GraphWatchLifecycle; -} - -export interface GraphProviderSkippedStatus extends GraphProviderStatusBase { - state: "skipped"; - mode: "optional"; - failure: ProviderFailureWithCategory<"provider_missing">; -} - -export interface GraphProviderRequiredMissingStatus extends GraphProviderStatusBase { - state: "required_missing"; - mode: "required"; - failure: ProviderFailureWithCategory<"provider_missing">; -} - -export interface GraphProviderStaleStatus extends GraphProviderStatusBase { - state: "stale"; - repo: RepoIdentity; - freshness: GraphFreshness; - failure: ProviderFailureWithCategory<"stale_snapshot">; -} - -export interface GraphProviderSchemaMismatchStatus extends GraphProviderStatusBase { - state: "schema_mismatch"; - expectedSchemaVersion: number; - actualSchemaVersion: number; - failure: ProviderFailureWithCategory<"schema_mismatch">; -} - -export interface GraphProviderDaemonUnavailableStatus extends GraphProviderStatusBase { - state: "daemon_unavailable"; - failure: ProviderFailureWithCategory<"daemon_unavailable">; -} - -export interface GraphProviderErrorStatus extends GraphProviderStatusBase { - state: "error"; - failure: ProviderFailureWithCategory; - diagnostics?: readonly GraphExtractionDiagnostic[]; -} - -export type GraphProviderStatus = - | GraphProviderAvailableStatus - | GraphProviderWarmingStatus - | GraphProviderSkippedStatus - | GraphProviderRequiredMissingStatus - | GraphProviderStaleStatus - | GraphProviderSchemaMismatchStatus - | GraphProviderDaemonUnavailableStatus - | GraphProviderErrorStatus; - -export type GraphProviderFailureStatus = Exclude; -export type GraphProviderNonAvailableStatus = Exclude; - -export interface GraphFactNode { - id: string; - kind: GraphNodeKind; - path?: string; - name?: string; - attributes?: Record; -} - -export interface GraphFactEdge { - id?: string; - kind: GraphEdgeKind; - from: string; - to: string; - attributes?: Record; -} - -export interface GraphSnapshotMetadata { - schemaVersion: number; - provider: string; - repo: RepoIdentity; - generatedAt: string; - freshness: GraphFreshness; - nodeKinds: readonly GraphNodeKind[]; - edgeKinds: readonly GraphEdgeKind[]; -} - -export interface GraphFactQuerySelector { - kind: "nodes" | "edges" | "neighbors" | "symbols" | "impact"; - nodeKinds?: readonly GraphNodeKind[]; - edgeKinds?: readonly GraphEdgeKind[]; - ids?: readonly string[]; - text?: string; - limit?: number; -} - -export const graphFactQueryKinds = ["nodes", "edges", "neighbors", "symbols", "impact"] as const; - -export const graphNamedQueryKinds = [ - "callers_of", - "callees_of", - "importers_of", - "imports_of", - "tests_for", - "inheritors_of", - "children_of", - "file_summary" -] as const; -export type GraphNamedQueryKind = (typeof graphNamedQueryKinds)[number]; -export type GraphProviderQueryKind = GraphFactQuerySelector["kind"] | GraphNamedQueryKind | "review_context" | "detect_changes" | "search"; - -export interface GraphFactQueryRequest { - requestId?: string; - repo: RepoIdentity; - schemaVersion: number; - mode: GraphProviderMode; - selector: GraphFactQuerySelector; -} - -export interface GraphFactQueryAvailableResult { - requestId?: string; - status: GraphProviderAvailableStatus; - metadata: GraphSnapshotMetadata; - nodes: readonly GraphFactNode[]; - edges: readonly GraphFactEdge[]; - diagnostics?: readonly GraphExtractionDiagnostic[]; -} - -export interface GraphFactQueryFailureResult { - requestId?: string; - status: GraphProviderFailureStatus; -} - -export type GraphFactQueryResult = GraphFactQueryAvailableResult | GraphFactQueryFailureResult; - -export interface GraphTraversalMetadata { - maxDepth: number; - truncated: boolean; - total: number; - empty: boolean; -} - -export interface GraphNamedQueryRequest { - requestId?: string; - repo: RepoIdentity; - schemaVersion: number; - mode: GraphProviderMode; - queryKind: GraphNamedQueryKind; - target: string; - maxDepth?: number; - limit?: number; -} - -export interface GraphNamedQueryAvailableResult { - requestId?: string; - status: GraphProviderAvailableStatus; - metadata: GraphSnapshotMetadata; - queryKind: GraphNamedQueryKind; - target: string; - nodes: readonly GraphFactNode[]; - edges: readonly GraphFactEdge[]; - traversal: GraphTraversalMetadata; - diagnostics?: readonly GraphExtractionDiagnostic[]; -} - -export interface GraphNamedQueryFailureResult { - requestId?: string; - status: GraphProviderFailureStatus; -} - -export type GraphNamedQueryResult = GraphNamedQueryAvailableResult | GraphNamedQueryFailureResult; - -export interface GraphImpactRequest { - requestId?: string; - repo: RepoIdentity; - schemaVersion: number; - mode: GraphProviderMode; - files: readonly string[]; - baseRef?: string; - maxDepth?: number; - limit?: number; -} - -export interface GraphImpactAvailableResult { - requestId?: string; - status: GraphProviderAvailableStatus; - metadata: GraphSnapshotMetadata; - changedFiles: readonly string[]; - impactedFiles: readonly string[]; - impactedSymbols: readonly string[]; - tests: readonly string[]; - nodes: readonly GraphFactNode[]; - edges: readonly GraphFactEdge[]; - traversal: GraphTraversalMetadata; - diagnostics?: readonly GraphExtractionDiagnostic[]; -} - -export interface GraphImpactFailureResult { - requestId?: string; - status: GraphProviderFailureStatus; -} - -export type GraphImpactResult = GraphImpactAvailableResult | GraphImpactFailureResult; - -export interface GraphRenamedFile { - fromPath: string; - toPath: string; - checksumBefore?: string; - checksumAfter?: string; -} - -export interface GraphDetectChangesRequest { - requestId?: string; - repo: RepoIdentity; - schemaVersion: number; - mode: GraphProviderMode; - files?: readonly string[]; - baseRef?: string; -} - -export interface GraphDetectChangesAvailableResult { - requestId?: string; - status: GraphProviderAvailableStatus; - metadata: GraphSnapshotMetadata; - changedFiles: readonly string[]; - deletedFiles: readonly string[]; - renamedFiles: readonly GraphRenamedFile[]; - diagnostics?: readonly GraphExtractionDiagnostic[]; -} - -export interface GraphDetectChangesFailureResult { - requestId?: string; - status: GraphProviderFailureStatus; -} - -export type GraphDetectChangesResult = GraphDetectChangesAvailableResult | GraphDetectChangesFailureResult; - -export interface GraphReviewContextRequest { - requestId?: string; - repo: RepoIdentity; - schemaVersion: number; - mode: GraphProviderMode; - files?: readonly string[]; - baseRef?: string; - maxDepth?: number; - limit?: number; -} - -export interface GraphReviewContextAvailableResult { - requestId?: string; - status: GraphProviderAvailableStatus; - metadata: GraphSnapshotMetadata; - changedFiles: readonly string[]; - deletedFiles: readonly string[]; - renamedFiles: readonly GraphRenamedFile[]; - impactedFiles: readonly string[]; - impactedSymbols: readonly string[]; - tests: readonly string[]; - nodes: readonly GraphFactNode[]; - edges: readonly GraphFactEdge[]; - traversal: GraphTraversalMetadata; - diagnostics?: readonly GraphExtractionDiagnostic[]; -} - -export interface GraphReviewContextFailureResult { - requestId?: string; - status: GraphProviderFailureStatus; -} - -export type GraphReviewContextResult = GraphReviewContextAvailableResult | GraphReviewContextFailureResult; - -export interface InspectSymbolTarget { - kind: "node" | "file_symbol"; - nodeId?: string; - path?: string; - symbolName?: string; - line?: number; - column?: number; -} - -export type InspectReferenceTarget = InspectSymbolTarget; - -export interface InspectTextSpan { - startLine: number; - startColumn: number; - endLine: number; - endColumn: number; - startOffset?: number; - endOffset?: number; -} - -export type InspectReferenceSpan = InspectTextSpan; - -export interface InspectSymbolSummary { - id: string; - name: string; - kind?: GraphNodeKind; -} - -export interface InspectSymbolEvidence { - graphNodeIds: readonly string[]; - resolver: "graph" | "language_service"; -} - -export interface InspectReferenceEntry { - file: string; - line: number; - column: number; - text: string; - span: InspectTextSpan; - symbol: InspectSymbolSummary; - isDefinition: boolean; - isDeclaration?: boolean; - evidence: InspectSymbolEvidence; -} - -export const inspectSignatureKinds = [ - "function", - "method", - "constructor", - "interface", - "type_alias", - "class", - "variable_function" -] as const; -export type InspectSignatureKind = (typeof inspectSignatureKinds)[number]; - -export interface InspectSignatureParameter { - name: string; - type: string; - optional: boolean; - rest?: boolean; - defaultValue?: string; -} - -export interface InspectSignatureTypeParameter { - name: string; - constraint?: string; - default?: string; -} - -export interface InspectSignatureEntry { - file: string; - line: number; - column: number; - text: string; - signature: string; - kind: InspectSignatureKind; - parameters: readonly InspectSignatureParameter[]; - typeParameters: readonly InspectSignatureTypeParameter[]; - exported: boolean; - async: boolean; - returnType?: string; - span: InspectTextSpan; - symbol: InspectSymbolSummary; - overloadIndex?: number; - evidence: InspectSymbolEvidence; -} - -export const inspectImplementationKinds = ["implements", "inherited_implements", "extends", "interface_extends"] as const; -export type InspectImplementationKind = (typeof inspectImplementationKinds)[number]; - -export interface InspectImplementationEntry { - file: string; - line: number; - column: number; - text: string; - span: InspectTextSpan; - kind: InspectImplementationKind; - symbol: InspectSymbolSummary; - target: InspectSymbolSummary; - isDeclaration?: boolean; - evidence: InspectSymbolEvidence; -} - -export const inspectFailureCategories = [ - "graph_unavailable", - "target_ambiguous", - "target_not_found", - "unsupported_language", - "malformed_target", - "language_service_error", - "unsupported_route" -] as const; -export type InspectFailureCategory = (typeof inspectFailureCategories)[number]; - -export interface InspectRouteFailure { - category: InspectFailureCategory; - message: string; - candidates?: readonly InspectSymbolTarget[]; -} - -export interface InspectReferenceResult { - route: "references"; - status: "ok" | "degraded"; - target: InspectSymbolTarget; - providerStatus: GraphProviderStatus; - failure?: InspectRouteFailure; - references: readonly InspectReferenceEntry[]; -} - -export interface InspectSignatureResult { - route: "signature"; - status: "ok" | "degraded"; - target: InspectSymbolTarget; - providerStatus: GraphProviderStatus; - failure?: InspectRouteFailure; - signatures: readonly InspectSignatureEntry[]; -} - -export interface InspectImplementationResult { - route: "implementations"; - status: "ok" | "degraded"; - target: InspectSymbolTarget; - providerStatus: GraphProviderStatus; - failure?: InspectRouteFailure; - implementations: readonly InspectImplementationEntry[]; -} - -export interface InspectRouteErrorResult { - route: "references" | "signature" | "implementations"; - status: "error" | "degraded"; - target?: InspectSymbolTarget; - providerStatus?: GraphProviderStatus; - failure: InspectRouteFailure; -} - -export type InspectRouteResult = - | InspectReferenceResult - | InspectSignatureResult - | InspectImplementationResult - | InspectRouteErrorResult; - -export const aspWarmMethodNames = ["inspect/references", "edit/rename", "check/evaluate", "session/shutdown"] as const; -export type AspWarmMethodName = (typeof aspWarmMethodNames)[number]; - -export interface AspWarmProviderSummary { - id: "opcore"; - capabilityFamily: "inspect" | "edit" | "session"; -} - -export interface AspWarmInspectReferencesParams { - path: string; - symbolName: string; - line?: number; - column?: number; - limit?: number; -} - -export interface AspWarmInspectReferencesOkResult { - route: "references"; - status: "ok"; - target: InspectReferenceTarget; - references: readonly InspectReferenceEntry[]; -} - -export interface AspWarmInspectReferencesErrorResult { - route: "references"; - status: "error"; - target?: InspectReferenceTarget; - failure: InspectRouteFailure; -} - -export interface AspWarmInspectReferencesResponse { - provider: AspWarmProviderSummary; - inspectResult: AspWarmInspectReferencesOkResult | AspWarmInspectReferencesErrorResult; - timing: CommandTiming; -} - -export interface SymbolEditTarget { - path: string; - name: string; - line?: number; - column?: number; - nodeId?: string; -} - -export interface AspWarmEditRenameParams { - target: SymbolEditTarget; - newName: string; -} - -export interface AspWarmAffectedChecksum { - path: string; - checksumBefore?: string; - checksumAfter?: string; -} - -export interface AspWarmEditRenamePreviewResult { - route: "rename"; - status: "preview"; - changes: readonly RepoRelativeChange[]; - affectedChecksums: readonly AspWarmAffectedChecksum[]; -} - -export interface AspWarmEditRenameRefusedResult { - route: "rename"; - status: "refused"; - refusal: EditRefusal; -} - -export interface AspWarmEditRenameResponse { - provider: AspWarmProviderSummary; - editResult: AspWarmEditRenamePreviewResult | AspWarmEditRenameRefusedResult; - timing: CommandTiming; -} - -export interface AspWarmSessionShutdownResponse { - provider: AspWarmProviderSummary; - session: { - state: "shutdown"; - }; - timing: CommandTiming; -} - -export interface GraphSearchRequest { - requestId?: string; - repo: RepoIdentity; - schemaVersion: number; - mode: GraphProviderMode; - query: string; - limit?: number; - files?: readonly string[]; -} - -export interface GraphSearchMode { - engine: "fts5" | (string & {}); - querySyntax: "fts5" | (string & {}); - limit: number; - contextFiles: readonly string[]; -} - -export interface GraphSearchResultEntry { - nodeId: string; - kind: GraphNodeKind; - path?: string; - name?: string; - qualifiedName: string; - filePath?: string; - signature: string; - score: number; - rank: number; - matches: readonly string[]; -} - -export interface GraphSearchSummary { - query: string; - total: number; - returned: number; - limit: number; - indexedNodeKinds: readonly GraphNodeKind[]; - contextFiles: readonly string[]; -} - -export interface GraphSearchAvailableResult { - requestId?: string; - status: GraphProviderAvailableStatus; - metadata: GraphSnapshotMetadata; - query: string; - searchMode: GraphSearchMode; - summary: GraphSearchSummary; - results: readonly GraphSearchResultEntry[]; - hints: readonly string[]; - diagnostics?: readonly GraphExtractionDiagnostic[]; -} - -export interface GraphSearchFailureResult { - requestId?: string; - status: GraphProviderNonAvailableStatus; - hints?: readonly string[]; - diagnostics?: readonly GraphExtractionDiagnostic[]; -} - -export type GraphSearchResult = GraphSearchAvailableResult | GraphSearchFailureResult; - -export type GraphPipelineOperation = "build" | "update" | "watch"; - -export interface GraphPipelinePhaseTiming { - phase: "discovery" | "extraction" | "store" | "watch" | "status" | (string & {}); - startedAt: string; - completedAt: string; - durationMs: number; - fileCount?: number; -} - -export interface GraphWalCheckpointSummary { - walPath: string; - bytesBefore: number; - bytesAfter: number; - budgetBytes: number; - checkpointed: boolean; -} - -export interface GraphPipelineSummary { - operation: GraphPipelineOperation; - repo: RepoIdentity; - storePath?: string; - startedAt: string; - completedAt: string; - durationMs: number; - discoveredFiles: number; - parsedFiles: number; - changedFiles: readonly string[]; - deletedFiles: readonly string[]; - unchangedFiles: number; - fullRebuildRequired: boolean; - diagnosticsCount: number; - phaseTimings: readonly GraphPipelinePhaseTiming[]; - baseRef?: string; - watchPaths?: readonly string[]; - walCheckpoint?: GraphWalCheckpointSummary; -} - -export interface GraphWatchLifecycle { - state: "warming" | "available" | "error" | "stopped"; - pid?: number; - startedAt: string; - updatedAt: string; - pidPath: string; - statePath: string; - logPath: string; - pollIntervalMs: number; - idleTimeoutMs: number; - watchPaths?: readonly string[]; - message?: string; -} - -export interface GraphServeTransportStatus { - schemaVersion: 1; - protocol: "opcore.graph.daemon"; - transport: "stdio"; - state: "ready" | "error" | "stopped"; - repo: RepoIdentity; - provider: "opcore-graph" | (string & {}); - pid?: number; - artifact?: GraphProviderArtifactMetadata; - failure?: ProviderFailure; - message?: string; -} - -export interface GraphPipelineResult { - summary: GraphPipelineSummary; - status: GraphProviderStatus; - lifecycle?: GraphWatchLifecycle; -} - -export type GraphDaemonOperation = "build" | "update" | "watch" | "status" | "query" | "ping" | "health" | "shutdown"; -export const graphDaemonOperations = ["build", "update", "watch", "status", "query", "ping", "health", "shutdown"] as const; - -export interface GraphDaemonRequest { - protocol: "opcore.graph.daemon"; - requestId: string; - schemaVersion: number; - operation: GraphDaemonOperation; - repo: RepoIdentity; - query?: GraphFactQueryRequest; - namedQuery?: GraphNamedQueryRequest; - impact?: GraphImpactRequest; - reviewContext?: GraphReviewContextRequest; - changes?: GraphDetectChangesRequest; - search?: GraphSearchRequest; - baseRef?: string; - paths?: readonly string[]; - watchPaths?: readonly string[]; - pollIntervalMs?: number; - idleTimeoutMs?: number; - once?: boolean; - maxWalBytes?: number; -} - -export interface GraphDaemonResponse { - protocol: "opcore.graph.daemon"; - requestId: string; - schemaVersion: number; - status: GraphProviderStatus; - result?: GraphFactQueryResult; - namedQuery?: GraphNamedQueryResult; - impact?: GraphImpactResult; - reviewContext?: GraphReviewContextResult; - changes?: GraphDetectChangesResult; - search?: GraphSearchResult; - pipeline?: GraphPipelineResult; - lifecycle?: GraphWatchLifecycle; -} - -export interface RepoRelativeChangeBase { - path: string; - checksumBefore?: string; - checksumAfter?: string; -} - -export type RepoRelativeChange = - | (RepoRelativeChangeBase & { - kind: "create" | "replace"; - content: string; - }) - | (RepoRelativeChangeBase & { - kind: "delete"; - }) - | { - kind: "rename"; - path: string; - toPath: string; - checksumBefore?: string; - }; - -export interface AtomicApplyMetadata { - strategy: "all_or_nothing"; - planHash?: string; - expectedBaseSha?: string; -} - -export interface EditPlanValidationRequirement { - required: boolean; - request: ValidationRequest; -} - -export interface EditPlan { - planId: string; - repo: RepoIdentity; - changes: readonly RepoRelativeChange[]; - atomic: AtomicApplyMetadata; - validation: EditPlanValidationRequirement; -} - -export interface EditRefusal { - category: EditRefusalCategory; - message: string; - path?: string; -} - -export interface EditPlanResult { - planId: string; - ok: boolean; - applied: boolean; - appliedAt?: string; - refusal?: EditRefusal; - validation?: ValidationResult; -} - -export interface EditPlanRollbackState { - completed: boolean; - restoredPaths: readonly string[]; - failedPaths: readonly string[]; - cleanupFailedPaths: readonly string[]; -} - -export interface EditCommandResult { - ok: boolean; - applied: boolean; - planId?: string; - planHash?: string; - appliedAt?: string; - matchCount?: number; - afterState?: Readonly>; - validationRequest?: ValidationRequest; - validation?: ValidationResult; - refusal?: EditRefusal; - rollback?: EditPlanRollbackState; -} - -export const validationScopeKinds = ["files", "changed", "staged", "tree", "all", "repo", "package"] as const; -export type ValidationScopeKind = (typeof validationScopeKinds)[number]; - -export type ValidationScope = - | { - kind: "files"; - files: readonly string[]; - } - | { - kind: "changed"; - baseRef: string; - } - | { - kind: "staged"; - } - | { - kind: "tree"; - treeRef: string; - changedFrom: string; - } - | { - kind: "all"; - } - | { - kind: "repo"; - } - | { - kind: "package"; - packageName: string; - packageRoot: string; - }; - -export type HypotheticalOverlay = - | { - path: string; - action: "write"; - content: string; - checksumBefore?: string; - } - | { - path: string; - action: "delete"; - checksumBefore?: string; - }; - -export const cloneReportModes = ["all", "introduced"] as const; -export type CloneReportMode = (typeof cloneReportModes)[number]; - -export const cloneSourceReadModes = ["disk", "gitIndex", "gitTree"] as const; -export type CloneSourceReadMode = (typeof cloneSourceReadModes)[number]; - -export interface CloneAnalysisRequest { - protocol: typeof CLONE_PROTOCOL; - requestId?: string; - schemaVersion: 1; - repo: RepoIdentity; - reportMode: CloneReportMode; - paths?: readonly string[]; - sourcePaths?: readonly string[]; - sourceReadMode?: CloneSourceReadMode; - sourceTreeRef?: string; - overlays: readonly HypotheticalOverlay[]; - windowSize?: number; - minLines?: number; - minTokens?: number; - threshold?: number; - partitions?: readonly (readonly string[])[]; - exclude?: readonly string[]; - modes?: readonly string[]; -} - -export interface CloneFinding { - cloneClassId: string; - contentHash: string; - path: string; - peerPath: string; - paths: readonly string[]; - lineCount: number; - tokenCount: number; - introduced: boolean; -} - -export interface CloneAnalysisSummary { - analyzedFiles: number; - cloneClassCount: number; - findingCount: number; - overlayCount: number; -} - -export interface CloneAnalysisResult { - protocol: typeof CLONE_PROTOCOL; - requestId?: string; - schemaVersion: 1; - repo: RepoIdentity; - reportMode: CloneReportMode; - status: "passed"; - persisted: boolean; - dbPath?: string; - findings: readonly CloneFinding[]; - summary: CloneAnalysisSummary; -} - -export interface ValidationFailure { - category: ValidationFailureCategory; - message: string; - retryable?: boolean; - cause?: string; -} - -export interface ValidationGraphConfig { - mode: GraphProviderMode; - provider?: string; - maxAgeMs?: number; - status?: GraphProviderStatus; -} - -export interface ValidationRequest { - requestId?: string; - repo: RepoIdentity; - scope: ValidationScope; - graph: ValidationGraphConfig; - overlays: readonly HypotheticalOverlay[]; - checks?: readonly string[]; - reportMode?: ValidationReportMode; -} - -export const PYTHON_PROJECT_CONTEXT_SCHEMA_ID = "opcore.python.project-context.v1" as const; -export const PYTHON_VALIDATION_CAPABILITY_RUN_SCHEMA_ID = "opcore.python.validation-capability-run" as const; - -export const pythonProjectContextOutcomes = ["resolved", "degraded", "unsupported", "ambiguous"] as const; -export type PythonProjectContextOutcome = (typeof pythonProjectContextOutcomes)[number]; - -export const pythonProjectContextReasonCodes = [ - "missing_config", - "invalid_config", - "conflicting_managers", - "conflicting_targets", - "interpreter_unavailable", - "tool_unavailable", - "probe_timeout", - "probe_signal", - "probe_spawn_failure", - "probe_exit_failure", - "malformed_probe_output", - "unsupported_target", - "unsupported_platform", - "path_refused", - "symlink_refused", - "incompatible_interpreter", - "ambiguous_path" -] as const; -export type PythonProjectContextReasonCode = (typeof pythonProjectContextReasonCodes)[number]; - -export const pythonProjectManagerKinds = ["pip", "uv", "poetry", "pdm", "pipenv"] as const; -export type PythonProjectManagerKind = (typeof pythonProjectManagerKinds)[number]; - -export const pythonProjectLayoutKinds = ["flat", "src", "namespace", "stub", "package"] as const; -export type PythonProjectLayoutKind = (typeof pythonProjectLayoutKinds)[number]; - -export const pythonProjectExecutableSources = [ - "explicit_override", - "active_environment", - "project_local_environment", - "manager_environment", - "path" -] as const; -export type PythonProjectExecutableSource = (typeof pythonProjectExecutableSources)[number]; - -export const pythonProjectToolKinds = ["mypy", "pyright", "ruff", "pytest", "build"] as const; -export type PythonProjectToolKind = (typeof pythonProjectToolKinds)[number]; - -export interface PythonProjectContextReason { - code: PythonProjectContextReasonCode; - message: string; - path?: string; - tool?: string; -} - -export interface PythonProjectFileEvidence { - path: string; - role: "boundary" | "config" | "lock" | "requirements" | "build" | "layout"; -} - -export interface PythonProjectManagerEvidence { - kind: PythonProjectManagerKind; - configFiles: readonly string[]; - lockFiles: readonly string[]; -} - -export interface PythonProjectExecutableProvenance { - executable: string; - argv: readonly string[]; - cwd: string; - source: PythonProjectExecutableSource; - version?: string; - configFile?: string; -} - -export interface PythonInterpreterProvenance extends PythonProjectExecutableProvenance { - version: string; - implementation: string; - platform: string; - architecture: string; - abi: string; - soabi: string; -} - -export interface PythonProjectToolProvenance extends PythonProjectExecutableProvenance { - tool: PythonProjectToolKind; - available: boolean; -} - -export interface PythonProjectTarget { - requiresPython?: string; - version?: string; - platform?: string; - implementation?: string; - conflicts: readonly string[]; -} - -export interface PythonProjectLayoutEvidence { - kinds: readonly PythonProjectLayoutKind[]; - paths: readonly string[]; -} - -export interface PythonProjectBuildSystem { - configFile: string; - backend?: string; - requires: readonly string[]; -} - -export interface PythonProjectContext { - schemaId: typeof PYTHON_PROJECT_CONTEXT_SCHEMA_ID; - schemaVersion: 1; - target: string; - repositoryRoot: string; - projectRoot: string; - projectBoundary: string; - sourceRoots: readonly string[]; - layout: PythonProjectLayoutEvidence; - evidence: readonly PythonProjectFileEvidence[]; - targetRuntime: PythonProjectTarget; - managers: readonly PythonProjectManagerEvidence[]; - buildSystem?: PythonProjectBuildSystem; - interpreter?: PythonInterpreterProvenance; - tools: readonly PythonProjectToolProvenance[]; - projectKey: string; - contextFingerprint: string; - outcome: PythonProjectContextOutcome; - reasons: readonly PythonProjectContextReason[]; -} - -export interface PythonValidationCapabilityToolProvenance { - name: PythonValidationAuthority; - /** Portable executable locator: repo:, project:, path:, or external:. */ - executable: string; - argv: readonly string[]; - cwd: string; - source: PythonProjectExecutableSource; - version?: string; - configFile?: string; -} - -export interface PythonValidationCapabilityExecution { - termination: PythonValidationCapabilityTerminationKind; - exitCode?: number; - signal?: string; - failureSummary?: string; -} - -/** Portable, source-free evidence for one attempted Python capability in one canonical project. */ -export interface PythonTypesValidationCapabilityRun { - schemaId: typeof PYTHON_VALIDATION_CAPABILITY_RUN_SCHEMA_ID; - schemaVersion: 1; - capability: "types"; - checkId: "python.types"; - projectKey: string; - contextFingerprint: string; - projectRoot: string; - targets: readonly string[]; - selectedSourcePaths: readonly string[]; - selectedConfigPaths: readonly string[]; - afterStateManifestFingerprint: string; - authority?: PythonValidationAuthority; - authoritySource?: PythonValidationAuthoritySource; - status: PythonValidationCapabilityRunStatus; - tool?: PythonValidationCapabilityToolProvenance; - execution?: PythonValidationCapabilityExecution; - durationMs: number; - diagnosticCount: number; - errorCount: number; - warningCount: number; - noteCount: number; -} - -export interface ValidationDiagnostic { - category: ValidationDiagnosticCategory; - message: string; - path?: string; - severity: "info" | "warning" | "error"; - code?: string; - line?: number; - column?: number; - endLine?: number; - endColumn?: number; - tool?: ValidationDiagnosticToolProvenance; -} - -export interface ValidationDiagnosticToolProvenance { - name: string; - command: string; - version?: string; - source?: string; - cwd?: string; -} - -export interface ValidationCheckManifestEntry { - checkId: string; - owner: string; - adapter: string; - defaultSeverity: ValidationDiagnostic["severity"]; - supportedScopes: readonly ValidationScopeKind[]; - requiresGraph: boolean; -} - -export interface PythonRuffValidationCapabilityRun { - schemaId: typeof PYTHON_VALIDATION_CAPABILITY_RUN_SCHEMA_ID; - schemaVersion: 1; - checkId: "python.ruff-lint" | "python.ruff-format"; - capability: "ruff_lint" | "ruff_format"; - state: PythonValidationCapabilityState; - projectKey?: string; - contextFingerprint?: string; - afterStateManifestFingerprint?: string; - sourcePaths?: readonly string[]; - configPaths?: readonly string[]; - executable?: string; - command?: string; - argv?: readonly string[]; - cwd?: string; - configPath?: string; - toolVersion?: string; - toolSource?: PythonProjectExecutableSource; - termination?: PythonValidationCapabilityTermination; - exitCode?: number; - signal?: string; - invocations?: readonly PythonValidationCapabilityInvocation[]; - durationMs: number; - diagnosticCount: number; - failureMessage?: string; -} - -export type PythonValidationCapabilityRun = - | PythonTypesValidationCapabilityRun - | PythonRuffValidationCapabilityRun - | PythonPytestValidationCapabilityRun; - -export interface PythonValidationCapabilityInvocation { - argv: readonly string[]; - termination: PythonValidationCapabilityTermination; - exitCode?: number; - signal?: string; - durationMs: number; -} - -export interface ValidationCheckRunSummary { - checkId: string; - status: ValidationCheckRunStatus; - outcome?: ValidationCheckOutcome; - durationMs?: number; - diagnosticCount?: number; - failureMessage?: string; - pythonCapabilityRuns?: readonly PythonValidationCapabilityRun[]; -} - -export interface ValidationSkippedCheck { - checkId: string; - reason: ValidationSkippedCheckReason; - message: string; -} - -export interface ValidationResultManifest { - schemaVersion: number; - checks: readonly string[]; - generatedAt: string; - entries?: readonly ValidationCheckManifestEntry[]; - durationMs?: number; - runs?: readonly ValidationCheckRunSummary[]; - skippedChecks?: readonly ValidationSkippedCheck[]; -} - -export const pythonCapabilityActivations = ["enabled", "disabled", "not_applicable"] as const; -export type PythonCapabilityActivation = (typeof pythonCapabilityActivations)[number]; - -export const pythonPytestSelectionModes = ["none", "direct_argv", "manifest"] as const; -export type PythonPytestSelectionMode = (typeof pythonPytestSelectionModes)[number]; - -export const pythonCapabilityProcessTerminations = ["exited", "timeout", "signal", "spawn_error", "overflow"] as const; -export type PythonCapabilityProcessTermination = (typeof pythonCapabilityProcessTerminations)[number]; - -export interface PythonCapabilityCounts { - candidateCount: number; - collectedCount: number; - executedCount: number; - passedCount: number; - failedCount: number; - skippedCount: number; - xfailedCount: number; - xpassedCount: number; - errorCount: number; -} - -export interface PythonCapabilityCleanupEvidence { - attempted: boolean; - ok: boolean; - failureMessage?: string; -} - -export interface PythonCapabilityInvocation { - stage: "collection" | "execution"; - command: string; - argsDigest: string; - argCount: number; - selectionMode: PythonPytestSelectionMode; - selectionDigest?: string; - durationMs: number; - termination: PythonCapabilityProcessTermination; - exitCode?: number; - signal?: string; - outputBytes: number; - stdoutDigest?: string; - stderrDigest?: string; -} - -export interface PythonPytestValidationCapabilityRun { - capability: "pytest"; - checkId: string; - activation: PythonCapabilityActivation; - outcome: string; - message: string; - projectKey?: string; - projectRoot?: string; - configFile?: string; - targetCount?: number; - candidatePaths?: readonly string[]; - collectedNodeIds?: readonly string[]; - afterStateFingerprint?: string; - selectionMode?: PythonPytestSelectionMode; - selectionDigest?: string; - counts?: PythonCapabilityCounts; - collection?: PythonCapabilityInvocation; - execution?: PythonCapabilityInvocation; - cleanup?: PythonCapabilityCleanupEvidence; -} - -export interface ValidationResult { - ok: boolean; - status: ValidationResultStatus; - diagnostics: readonly ValidationDiagnostic[]; - graphStatus?: GraphProviderStatus; - failure?: ValidationFailure; - refusal?: EditRefusal; - manifest?: ValidationResultManifest; - pythonProjectContexts?: readonly PythonProjectContext[]; - pythonCapabilityRuns?: readonly PythonValidationCapabilityRun[]; -} - -export interface RequiredContextDocPolicy { - filenames: readonly string[]; - requiredPaths: readonly string[]; - requireRoot?: boolean; - minimumContentLength: number; - maxLines?: number; - maxSectionLines?: number; -} - -export const requiredContextDocPolicy = { - filenames: ["AGENTS.md", "CLAUDE.md"], - requiredPaths: ["."], - requireRoot: true, - minimumContentLength: 120 -} as const satisfies RequiredContextDocPolicy; - -export interface PreWriteValidationOverlaySummary { - count: number; - writeCount: number; - deleteCount: number; - paths: readonly string[]; -} - -export interface PreWriteValidationFailureSummary { - category: ValidationResultStatus; - message: string; - cause?: string; - retryable?: boolean; -} - -export interface PreWriteValidationReceipt { - schemaVersion: 1; - kind: "pre_write_validation"; - route: "validate.pre-write"; - canonicalCommand: readonly string[]; - generatedAt: string; - durationMs: number; - timeoutMs: number; - ok: boolean; - requestId?: string; - repo?: RepoIdentity; - scope?: ValidationScope; - checks?: readonly string[]; - graph?: { - mode: GraphProviderMode; - provider?: string; - status?: GraphProviderStatus; - }; - overlays?: PreWriteValidationOverlaySummary; - validationStatus: ValidationResultStatus; - diagnosticCount: number; - failureSummary?: PreWriteValidationFailureSummary; -} - -export const validationDaemonReadinessStates = ["not_configured", "ready", "unavailable", "error"] as const; -export type ValidationDaemonReadinessState = (typeof validationDaemonReadinessStates)[number]; - -export const validationAdapterRuntimeStates = ["available", "degraded", "unavailable"] as const; -export type ValidationAdapterRuntimeState = (typeof validationAdapterRuntimeStates)[number]; - -export interface ValidationAdapterToolchainStatus { - tool: string; - available: boolean; - command?: string; - version?: string; - failureMessage?: string; - cwd?: string; - configFile?: string; - source?: string; -} - -export interface ValidationAdapterDegradedCheckStatus { - checkId: string; - status: ValidationCheckRunStatus; - reason: string; - message: string; - requiredTool?: string; - retainedCompatibility?: boolean; - followUpIssue?: string; - currentUsage?: { - opcore: boolean; - orchestra: boolean; - covibes: boolean; - gateway: boolean; - }; -} - -export interface ValidationAdapterRuntimeStatus { - adapter: string; - status: ValidationAdapterRuntimeState; - checkIds: readonly string[]; - toolchain?: readonly ValidationAdapterToolchainStatus[]; - degradedChecks?: readonly ValidationAdapterDegradedCheckStatus[]; - tempWorkspaceRequired?: boolean; -} - -export interface ValidationStatusPayload { - schemaVersion: 1; - ready: boolean; - generatedAt: string; - adapterRegistry: { - checkRoutes: readonly string[]; - validateRoutes: readonly string[]; - checkIds: readonly string[]; - entries: readonly ValidationCheckManifestEntry[]; - adapters?: readonly ValidationAdapterRuntimeStatus[]; - }; - graph: { - mode: GraphProviderMode; - status: GraphProviderStatus; - }; - daemon?: { - state: ValidationDaemonReadinessState; - message?: string; - }; -} - -export const managedToolDescriptorCommandGroups = ["graph", "inspect", "edit", "check", "validate", "status", "doctor"] as const; -export type ManagedToolDescriptorCommandGroupName = (typeof managedToolDescriptorCommandGroups)[number]; - -const managedToolDescriptorCommandGroupPackageNames: Record = { - graph: "opcore", - inspect: "opcore", - edit: "opcore", - check: "opcore", - validate: "opcore", - status: "opcore", - doctor: "opcore" -}; - -export const managedToolDescriptorArtifactTypes = [ - "entrypoint", - "descriptor", - "schema", - "manifest", - "native_binary", - "checksum", - "receipt" -] as const; -export type ManagedToolDescriptorArtifactType = (typeof managedToolDescriptorArtifactTypes)[number]; - -export interface ManagedToolDescriptor { - schemaVersion: 1; - descriptorKind: "aggregate_opcore"; - aggregateIdentity: { - name: "opcore"; - releaseLine: "opcore"; - packageName: "opcore"; - version?: string; - }; - packageIdentity: { - packageName: "opcore"; - artifactName: "opcore"; - version?: string; - }; - entrypoints: readonly ManagedToolDescriptorEntrypoint[]; - commandGroups: readonly ManagedToolDescriptorCommandGroup[]; - healthProbes: readonly ManagedToolDescriptorHealthProbe[]; - capabilities: ManagedToolDescriptorCapabilities; - artifacts: readonly ManagedToolDescriptorArtifactReference[]; - checksums: readonly ManagedToolDescriptorChecksumReference[]; - provenanceHooks: readonly ManagedToolDescriptorProvenanceHook[]; - optionalSurfaces: readonly GraphReleaseOptionalSurfaceReceipt[]; -} - -export interface ManagedToolDescriptorEntrypoint { - bin: "opcore"; - packageName: "opcore"; - path: string; - command: readonly string[]; -} - -export interface ManagedToolDescriptorCommandGroup { - name: ManagedToolDescriptorCommandGroupName; - canonicalCommand: readonly string[]; - commands: readonly string[]; - packageName: string; -} - -export interface ManagedToolDescriptorHealthProbe { - id: string; - command: readonly string[]; - expectedExitCode: 0; - output: "json"; -} - -export interface ManagedToolDescriptorCapabilities { - graph: { - provider: "opcore-graph"; - schemaVersion: 1; - commands: readonly string[]; - queryKinds: readonly string[]; - daemonOperations: readonly string[]; - nativeArtifacts: readonly ManagedToolDescriptorNativeArtifact[]; - }; - edit: { - commands: readonly string[]; - safeEditModes: readonly string[]; - symbolEditModes: readonly string[]; - validationRequiredForApply: true; - dryRun: true; - }; - validation: { - checkRoutes: readonly string[]; - validateRoutes: readonly string[]; - scopeModes: readonly ValidationScopeKind[]; - graphModes: readonly GraphProviderMode[]; - hypothetical: true; - statusSurfaces: readonly ("status" | "doctor")[]; - pythonProjectContext: { - schemaId: typeof PYTHON_PROJECT_CONTEXT_SCHEMA_ID; - outcomes: readonly PythonProjectContextOutcome[]; - readOnly: true; - installs: false; - }; - writeGate: { - initScopes: readonly OpcoreInitScope[]; - harnesses: readonly ("claude-code" | "codex")[]; - adapterPath: string; - validationCommand: readonly string[]; - adapterErrorPolicy: "fail_open"; - validationErrorPolicy: "fail_closed"; - codexBoundary: "pretooluse_guardrail"; - }; - checkIds: readonly string[]; - }; -} - -export interface ManagedToolDescriptorNativeArtifact { - targetPlatform: GraphCoreNativeSupportedTarget; - packageName: "opcore"; - bundledPackageName: GraphCoreNativePackageName; - binaryPath: string; - metadataPath: string; - checksumPath: string; - artifactIds: { - binaryArtifactId: string; - metadataArtifactId: string; - checksumId: string; - checksumArtifactId: string; - }; -} - -export interface ManagedToolDescriptorArtifactReference { - id: string; - packageName: string; - path: string; - type: ManagedToolDescriptorArtifactType; - required: boolean; - checksumRef?: string; -} - -export interface ManagedToolDescriptorChecksumReference { - id: string; - packageName: string; - path: string; - algorithm: "sha256"; - artifactRef: string; - required: boolean; - value?: string; -} - -export interface ManagedToolDescriptorProvenanceHook { - id: string; - command: readonly string[]; - expectedExitCode: 0; -} - -export const commandOwners = ["graph", "inspect", "edit", "validation", "runtime"] as const; -export type CommandOwner = (typeof commandOwners)[number]; - -export const commandRouteStatuses = ["ok", "error", "not_implemented", "unsupported"] as const; -export type CommandRouteStatus = (typeof commandRouteStatuses)[number]; - -export const commandTimingProcessStates = ["cold", "warm"] as const; -export type CommandTimingProcessState = (typeof commandTimingProcessStates)[number]; - -export const commandTimingDegradationReasons = ["no_source", "no_paths"] as const; -export type CommandTimingDegradationReason = (typeof commandTimingDegradationReasons)[number]; - -export const latencyBudgetResultStatuses = ["pass", "over"] as const; -export type LatencyBudgetResultStatus = (typeof latencyBudgetResultStatuses)[number]; - -export const commandLatencyTelemetryBins = ["opcore", "opcore-asp-provider"] as const; -export type CommandLatencyTelemetryBin = (typeof commandLatencyTelemetryBins)[number]; - -export const commandLatencyTelemetryArtifactPolicy = { - path: ".opcore/telemetry.jsonl", - maxRecords: 500, - maxBytes: 1024 * 1024, - rotation: "ring_buffer" -} as const; - -export const graphReleaseSurfaceClassifications = ["required", "supporting", "optional", "deferred"] as const; -export type GraphReleaseSurfaceClassification = (typeof graphReleaseSurfaceClassifications)[number]; - -export const graphReleaseCoreCommandIds = [ - "opcore-graph-build", - "opcore-graph-update", - "opcore-graph-watch", - "opcore-graph-status", - "opcore-graph-query", - "opcore-graph-impact", - "opcore-graph-search", - "opcore-graph-serve" -] as const; -export type GraphReleaseCoreCommandId = (typeof graphReleaseCoreCommandIds)[number]; - -export const graphReleaseRustCommandIds = [ - "opcore-graph-rust-build", - "opcore-graph-rust-update", - "opcore-graph-rust-watch", - "opcore-graph-rust-status", - "opcore-graph-rust-query", - "opcore-graph-rust-impact", - "opcore-graph-rust-search", - "opcore-graph-rust-serve" -] as const; -export type GraphReleaseRustCommandId = (typeof graphReleaseRustCommandIds)[number]; - -export const graphReleaseBenchmarkMetrics = [ - "install_setup_ms", - "cold_build_ms", - "incremental_update_ms", - "impact_cold_ms", - "impact_hot_ms", - "search_ms", - "daemon_startup_ms", - "daemon_query_ms", - "db_size_bytes", - "wal_size_bytes" -] as const; -export type GraphReleaseBenchmarkMetric = (typeof graphReleaseBenchmarkMetrics)[number]; - -export const graphReleaseRequiredChildren = ["#35", "#8", "#9", "#10", "#11", "#12", "#19", "#47"] as const; -export type GraphReleaseRequiredChild = (typeof graphReleaseRequiredChildren)[number]; - -export const graphReleaseDeferredChildren = ["#13", "#14", "#15", "#16"] as const; -export type GraphReleaseDeferredChild = (typeof graphReleaseDeferredChildren)[number]; - -export const graphReleaseOptionalAnalysisSurfaces = [ - { - issue: "#13", - id: "coverage", - classification: "deferred", - status: "deferred" - }, - { - issue: "#14", - id: "flows", - classification: "optional", - status: "deferred" - }, - { - issue: "#15", - id: "communities", - classification: "optional", - status: "deferred" - }, - { - issue: "#16", - id: "read_only_suggestions", - classification: "supporting", - status: "deferred" - } -] as const; -export type GraphReleaseOptionalAnalysisSurface = (typeof graphReleaseOptionalAnalysisSurfaces)[number]; - -export const graphReleaseHandoffIssues = ["#7", "#28", "#29"] as const; -export type GraphReleaseHandoffIssue = (typeof graphReleaseHandoffIssues)[number]; - -export const graphReleaseDirectSqliteQueryIds = [ - "status-counts", - "status-edge-counts", - "impact-edges-from-file", - "search-by-name", - "freshness-metadata" -] as const; -export type GraphReleaseDirectSqliteQueryId = (typeof graphReleaseDirectSqliteQueryIds)[number]; - -export const graphReleaseServeTransportIds = [ - "serve-jsonl-ping", - "serve-jsonl-status", - "serve-jsonl-query", - "serve-jsonl-search", - "serve-jsonl-shutdown" -] as const; -export type GraphReleaseServeTransportId = (typeof graphReleaseServeTransportIds)[number]; - -export const graphReleaseReportReceiptIds = ["conformance", "pack", "license", "provenance"] as const; -export type GraphReleaseReportReceiptId = (typeof graphReleaseReportReceiptIds)[number]; - -export const graphCoreNativeSupportedTargets = ["darwin-arm64", "darwin-x64", "linux-x64"] as const; -export type GraphCoreNativeSupportedTarget = (typeof graphCoreNativeSupportedTargets)[number]; - -export const graphCoreNativePackageNames = [ - "@the-open-engine/opcore-graph-core-darwin-arm64", - "@the-open-engine/opcore-graph-core-darwin-x64", - "@the-open-engine/opcore-graph-core-linux-x64" -] as const; -export type GraphCoreNativePackageName = (typeof graphCoreNativePackageNames)[number]; - -export const graphCoreNativePackageNamesByTarget = { - "darwin-arm64": "@the-open-engine/opcore-graph-core-darwin-arm64", - "darwin-x64": "@the-open-engine/opcore-graph-core-darwin-x64", - "linux-x64": "@the-open-engine/opcore-graph-core-linux-x64" -} as const satisfies Record; - -export function graphCoreNativePackageNameForTarget(target: GraphCoreNativeSupportedTarget): GraphCoreNativePackageName { - return graphCoreNativePackageNamesByTarget[target]; -} - -export const releaseReceiptPackageNames = ["opcore"] as const; -export type ReleaseReceiptPackageName = (typeof releaseReceiptPackageNames)[number]; - -export const releaseReceiptBundledPackageNames = [ - "@the-open-engine/opcore-asp-provider", - "@the-open-engine/opcore-contracts", - "@the-open-engine/opcore-edit", - "@the-open-engine/opcore-graph", - "@the-open-engine/opcore-validation", - "@the-open-engine/opcore-validation-clone", - "@the-open-engine/opcore-validation-docs", - "@the-open-engine/opcore-validation-python", - "@the-open-engine/opcore-validation-rust", - "@the-open-engine/opcore-validation-typescript", - ...graphCoreNativePackageNames -] as const; - -export const releaseReceiptCommandGroups = ["graph", "inspect", "edit", "check", "validate", "status", "doctor"] as const; -export type ReleaseReceiptCommandGroupName = (typeof releaseReceiptCommandGroups)[number]; - -export const releaseReceiptReportIds = [ - "package-inspection", - "license", - "provenance", - "release-hygiene", - "graph-release", - "secret-history" -] as const; -export type ReleaseReceiptReportId = (typeof releaseReceiptReportIds)[number]; - -export const releaseReceiptSecretFindingScopes = ["current-tree", "git-history"] as const; -export type ReleaseReceiptSecretFindingScope = (typeof releaseReceiptSecretFindingScopes)[number]; - -export const releaseCutoverRequiredCommandIds = [ - "opcore-scan", - "opcore-status", - "opcore-check-changed", - "opcore-measure", - "opcore-try", - "status", - "doctor", - "graph-build", - "graph-status", - "graph-query", - "graph-impact", - "graph-review-context", - "graph-detect-changes", - "graph-search", - "graph-serve", - "inspect-symbols", - "inspect-definition", - "inspect-references", - "inspect-signature", - "inspect-implementations", - "inspect-search", - "edit-preview", - "edit-apply", - "edit-refused", - "check-files", - "validate-request", - "validate-pre-write-pass", - "validate-pre-write-fail" -] as const; -export type ReleaseCutoverCommandId = (typeof releaseCutoverRequiredCommandIds)[number]; - -export const releaseCutoverRustCommandIds = [ - "graph-rust-build", - "graph-rust-status", - "graph-rust-query", - "graph-rust-impact", - "graph-rust-review-context", - "graph-rust-detect-changes", - "graph-rust-search" -] as const; -export type ReleaseCutoverRustCommandId = (typeof releaseCutoverRustCommandIds)[number]; - -export const releaseCutoverPythonCommandIds = [ - "opcore-python-scan", - "opcore-python-status", - "opcore-python-check-changed", - "opcore-python-measure", - "graph-python-build", - "graph-python-status", - "graph-python-query", - "graph-python-search" -] as const; -export type ReleaseCutoverPythonCommandId = (typeof releaseCutoverPythonCommandIds)[number]; - -export const releaseCutoverNegativeCheckIds = [ - "missing-required-graph-check", - "missing-required-graph-validate", - "python-types-degraded-no-tools", - "python-source-hygiene-no-ruff", - "python-relevant-tests-no-pytest", - "python-toolchain-degraded-no-tools" -] as const; -export type ReleaseCutoverNegativeCheckId = (typeof releaseCutoverNegativeCheckIds)[number]; - -export const releaseCutoverInputIssues = ["#17", "#29", "#58"] as const; -export type ReleaseCutoverInputIssue = (typeof releaseCutoverInputIssues)[number]; - -export const aspDogfoodUnsupportedSurfaceIds = ["inspect", "edit"] as const; -export type AspDogfoodUnsupportedSurfaceId = (typeof aspDogfoodUnsupportedSurfaceIds)[number]; - -export const aspDogfoodForbiddenProviderMarkers = ["opcore asp serve", "opcore asp"] as const; -export type AspDogfoodForbiddenProviderMarker = (typeof aspDogfoodForbiddenProviderMarkers)[number]; - -const releaseCutoverRequestFilePlaceholder = ""; -const releaseCutoverMissingGraphRepoPlaceholder = ""; -const releaseCutoverRequiredGraphRequestPlaceholder = ""; - -type ReleaseCutoverExpectedCommandStatus = CommandRouteStatus; - -interface ReleaseCutoverCommandExpectation { - readonly canonicalCommand: readonly string[]; - readonly requestFileBasename?: string; - readonly owner: CommandOwner; - readonly status: ReleaseCutoverExpectedCommandStatus; - readonly exitCode: 0 | 1 | 2 | 64; - readonly bin: "opcore"; -} - -const releaseCutoverCommandExpectations = { - "opcore-scan": { canonicalCommand: ["opcore", "scan"], owner: "runtime", status: "ok", exitCode: 0, bin: "opcore" }, - "opcore-status": { canonicalCommand: ["opcore", "status"], owner: "runtime", status: "ok", exitCode: 0, bin: "opcore" }, - "opcore-check-changed": { - canonicalCommand: ["opcore", "check", "changed", "--report-mode", "introduced", "--base", "HEAD", "--checks", "typescript.syntax"], - owner: "validation", - status: "ok", - exitCode: 0, - bin: "opcore" - }, - "opcore-measure": { canonicalCommand: ["opcore", "measure"], owner: "runtime", status: "ok", exitCode: 0, bin: "opcore" }, - "opcore-try": { canonicalCommand: ["opcore", "try"], owner: "runtime", status: "ok", exitCode: 0, bin: "opcore" }, - status: { canonicalCommand: ["opcore", "status"], owner: "runtime", status: "ok", exitCode: 0, bin: "opcore" }, - doctor: { canonicalCommand: ["opcore", "doctor"], owner: "runtime", status: "ok", exitCode: 0, bin: "opcore" }, - "graph-build": { canonicalCommand: ["opcore", "graph", "build"], owner: "graph", status: "ok", exitCode: 0, bin: "opcore" }, - "graph-status": { canonicalCommand: ["opcore", "graph", "status"], owner: "graph", status: "ok", exitCode: 0, bin: "opcore" }, - "graph-query": { canonicalCommand: ["opcore", "graph", "query"], owner: "graph", status: "ok", exitCode: 0, bin: "opcore" }, - "graph-impact": { - canonicalCommand: ["opcore", "graph", "impact", "--files", "src/components/GreetingCard.tsx"], - owner: "graph", - status: "ok", - exitCode: 0, - bin: "opcore" - }, - "graph-review-context": { - canonicalCommand: ["opcore", "graph", "review-context", "--files", "src/components/GreetingCard.tsx"], - owner: "graph", - status: "ok", - exitCode: 0, - bin: "opcore" - }, - "graph-detect-changes": { - canonicalCommand: ["opcore", "graph", "detect-changes", "--files", "src/components/GreetingCard.tsx"], - owner: "graph", - status: "ok", - exitCode: 0, - bin: "opcore" - }, - "graph-search": { - canonicalCommand: ["opcore", "graph", "search", "Greeting", "--limit", "5"], - owner: "graph", - status: "ok", - exitCode: 0, - bin: "opcore" - }, - "graph-serve": { canonicalCommand: ["opcore", "graph", "serve"], owner: "graph", status: "ok", exitCode: 0, bin: "opcore" }, - "inspect-symbols": { - canonicalCommand: ["opcore", "inspect", "symbols", "Greeting", "--limit", "5"], - owner: "inspect", - status: "ok", - exitCode: 0, - bin: "opcore" - }, - "inspect-definition": { - canonicalCommand: ["opcore", "inspect", "definition", "GreetingCard"], - owner: "inspect", - status: "ok", - exitCode: 0, - bin: "opcore" - }, - "inspect-references": { - canonicalCommand: ["opcore", "inspect", "references", "function:src/components/GreetingCard.tsx#GreetingCard", "--limit", "5"], - owner: "inspect", - status: "ok", - exitCode: 0, - bin: "opcore" - }, - "inspect-signature": { - canonicalCommand: ["opcore", "inspect", "signature", "function:src/components/GreetingCard.tsx#GreetingCard"], - owner: "inspect", - status: "ok", - exitCode: 0, - bin: "opcore" - }, - "inspect-implementations": { - canonicalCommand: ["opcore", "inspect", "implementations", "class:src/models.ts#GreetingModel"], - owner: "inspect", - status: "ok", - exitCode: 0, - bin: "opcore" - }, - "inspect-search": { - canonicalCommand: ["opcore", "inspect", "search", "Greeting", "--limit", "5"], - owner: "inspect", - status: "ok", - exitCode: 0, - bin: "opcore" - }, - "edit-preview": { - canonicalCommand: [ - "opcore", - "edit", - "exact", - "--path", - "src/cutover.ts", - "--expected", - "export const cutoverValue: number = 1;", - "--replacement", - "export const cutoverValue: number = 2;" - ], - owner: "edit", - status: "ok", - exitCode: 0, - bin: "opcore" - }, - "edit-apply": { - canonicalCommand: [ - "opcore", - "edit", - "exact", - "--path", - "src/cutover.ts", - "--expected", - "export const cutoverValue: number = 1;", - "--replacement", - "export const cutoverValue: number = 2;", - "--apply" - ], - owner: "edit", - status: "ok", - exitCode: 0, - bin: "opcore" - }, - "edit-refused": { - canonicalCommand: [ - "opcore", - "edit", - "exact", - "--path", - "src/cutover.ts", - "--expected", - "export const cutoverValue: number = 2;", - "--replacement", - "export const cutoverValue: number = missingCutoverSymbol;", - "--apply" - ], - owner: "edit", - status: "error", - exitCode: 1, - bin: "opcore" - }, - "check-files": { - canonicalCommand: ["opcore", "check", "files", "src/cutover.ts", "--checks", "typescript.syntax,typescript.types"], - owner: "validation", - status: "ok", - exitCode: 0, - bin: "opcore" - }, - "validate-request": { - canonicalCommand: ["opcore", "validate", "request", "--request-file", releaseCutoverRequestFilePlaceholder], - requestFileBasename: "validate-request.json", - owner: "validation", - status: "ok", - exitCode: 0, - bin: "opcore" - }, - "validate-pre-write-pass": { - canonicalCommand: [ - "opcore", - "validate", - "pre-write", - "--request-file", - releaseCutoverRequestFilePlaceholder, - "--timeout-ms", - "30000" - ], - requestFileBasename: "pre-write-pass.json", - owner: "validation", - status: "ok", - exitCode: 0, - bin: "opcore" - }, - "validate-pre-write-fail": { - canonicalCommand: [ - "opcore", - "validate", - "pre-write", - "--request-file", - releaseCutoverRequestFilePlaceholder, - "--timeout-ms", - "30000" - ], - requestFileBasename: "pre-write-fail.json", - owner: "validation", - status: "error", - exitCode: 1, - bin: "opcore" - } -} as const satisfies Record; - -const releaseCutoverRustCommandExpectations = { - "graph-rust-build": { canonicalCommand: ["opcore", "graph", "build"], owner: "graph", status: "ok", exitCode: 0, bin: "opcore" }, - "graph-rust-status": { canonicalCommand: ["opcore", "graph", "status"], owner: "graph", status: "ok", exitCode: 0, bin: "opcore" }, - "graph-rust-query": { canonicalCommand: ["opcore", "graph", "query"], owner: "graph", status: "ok", exitCode: 0, bin: "opcore" }, - "graph-rust-impact": { - canonicalCommand: ["opcore", "graph", "impact", "--files", "src/helpers.rs"], - owner: "graph", - status: "ok", - exitCode: 0, - bin: "opcore" - }, - "graph-rust-review-context": { - canonicalCommand: ["opcore", "graph", "review-context", "--files", "src/helpers.rs"], - owner: "graph", - status: "ok", - exitCode: 0, - bin: "opcore" - }, - "graph-rust-detect-changes": { - canonicalCommand: ["opcore", "graph", "detect-changes", "--files", "src/helpers.rs"], - owner: "graph", - status: "ok", - exitCode: 0, - bin: "opcore" - }, - "graph-rust-search": { - canonicalCommand: ["opcore", "graph", "search", "Widget", "--limit", "5"], - owner: "graph", - status: "ok", - exitCode: 0, - bin: "opcore" - } -} as const satisfies Record; - -const releaseCutoverPythonCommandExpectations = { - "opcore-python-scan": { canonicalCommand: ["opcore", "scan"], owner: "runtime", status: "ok", exitCode: 0, bin: "opcore" }, - "opcore-python-status": { canonicalCommand: ["opcore", "status"], owner: "runtime", status: "ok", exitCode: 0, bin: "opcore" }, - "opcore-python-check-changed": { - canonicalCommand: [ - "opcore", - "check", - "changed", - "--report-mode", - "introduced", - "--base", - "HEAD", - "--checks", - "python.syntax,python.source-hygiene" - ], - owner: "validation", - status: "ok", - exitCode: 0, - bin: "opcore" - }, - "opcore-python-measure": { canonicalCommand: ["opcore", "measure"], owner: "runtime", status: "ok", exitCode: 0, bin: "opcore" }, - "graph-python-build": { canonicalCommand: ["opcore", "graph", "build"], owner: "graph", status: "ok", exitCode: 0, bin: "opcore" }, - "graph-python-status": { canonicalCommand: ["opcore", "graph", "status"], owner: "graph", status: "ok", exitCode: 0, bin: "opcore" }, - "graph-python-query": { canonicalCommand: ["opcore", "graph", "query"], owner: "graph", status: "ok", exitCode: 0, bin: "opcore" }, - "graph-python-search": { - canonicalCommand: ["opcore", "graph", "search", "Greeter", "--limit", "5"], - owner: "graph", - status: "ok", - exitCode: 0, - bin: "opcore" - } -} as const satisfies Record; - -const releaseCutoverPythonEvidenceExpectations = { - "opcore-python-scan": ["python-coverage", "python-validation", "python-types-degraded"], - "opcore-python-status": ["python-coverage", "python-validation"], - "opcore-python-check-changed": ["python-syntax", "python-source-hygiene"], - "opcore-python-measure": ["python-measure-delta"], - "graph-python-build": ["python-graph-provider"], - "graph-python-status": ["python-graph-provider"], - "graph-python-query": ["src/acme/app.py", "Greeter", "build_name"], - "graph-python-search": ["src/acme/app.py", "Greeter"] -} as const satisfies Record; - -const releaseCutoverNegativeCheckExpectations = { - "missing-required-graph-check": [ - "opcore", - "check", - "files", - "src/index.ts", - "--repo", - releaseCutoverMissingGraphRepoPlaceholder, - "--graph-mode", - "required", - "--checks", - "typescript.import-graph" - ], - "missing-required-graph-validate": [ - "opcore", - "validate", - "request", - "--request-file", - releaseCutoverRequiredGraphRequestPlaceholder - ], - "python-types-degraded-no-tools": ["opcore", "check", "files", "src/acme/app.py", "--checks", "python.types"], - "python-source-hygiene-no-ruff": ["opcore", "check", "files", "src/acme/app.py", "--checks", "python.source-hygiene"], - "python-relevant-tests-no-pytest": ["opcore", "check", "files", "src/acme/app.py", "--checks", "python.relevant-tests"], - "python-toolchain-degraded-no-tools": ["opcore", "status"] -} as const satisfies Record; - -export interface CommandExitSemantics { - ok: 0; - error: 1; - notImplemented: 2; - unsupported: 64; - jsonStable: boolean; -} - -export interface CommandGroupContract { - name: string; - owner: CommandOwner; - canonicalCommand: readonly string[]; - commands: readonly string[]; - summary: string; -} - -export interface CommandRouterManifest { - schemaVersion: 1; - packageName: "opcore" | (string & {}); - bins: readonly string[]; - exitSemantics: CommandExitSemantics; - ownershipBoundaries: readonly { - owner: CommandOwner; - summary: string; - }[]; - commandGroups: readonly CommandGroupContract[]; -} - -export interface OpcoreRepoStatePayload { - schemaVersion: 1; - repo: { - root: string; - requestedPath: string; - git: { - available: boolean; - branch?: string; - changed?: number; - staged?: number; - unstaged?: number; - untracked?: number; - conflicted?: number; - clean?: boolean; - }; - }; - coverage: { - totalFiles: number; - languages: readonly { - language: string; - files: number; - graphSupported: boolean; - validationSupported: boolean; - }[]; - graph: { - supportedFiles: number; - extensions: readonly { - extension: string; - count: number; - }[]; - }; - validation: { - supportedFiles: number; - retainedFiles: number; - extensions: readonly { - extension: string; - count: number; - }[]; - }; - unsupported: { - totalFiles: number; - stacks: readonly { - extension: string; - language: string; - count: number; - examples: readonly string[]; - }[]; - }; - }; - graph: { - state: GraphProviderStatusState; - mode: GraphProviderMode; - provider: string; - action: string; - message?: string; - status: GraphProviderStatus; - }; - validation: { - ready: boolean; - checkCount: number; - policy: OpcoreValidationPolicySummary; - adapters: readonly { - adapter: string; - status: ValidationAdapterRuntimeState; - checkCount: number; - degradedChecks: readonly string[]; - missingTools: readonly string[]; - }[]; - degradedToolchains: readonly { - adapter: string; - tool: string; - failureMessage?: string; - }[]; - pythonProjectContexts?: readonly PythonProjectContext[]; - }; - activation: { - ready: boolean; - level: "ready" | "degraded" | "blocked"; - summary: string; - asp: { - state: "enrolled" | "not_enrolled"; - paths: readonly string[]; - }; - }; - warnings: readonly string[]; - blockers: readonly string[]; - nextActions: readonly string[]; -} - -export interface OpcoreValidationPolicySummary { - path: ".opcore/config"; - state: "missing" | "loaded"; - adapters: readonly string[]; - packs: readonly string[]; - disabledChecks: readonly string[]; - defaultChecks: readonly string[]; - configuredChecks: readonly string[]; -} - -export const opcoreRuntimeArtifactSources = ["source_checkout", "installed_package", "unknown"] as const; -export type OpcoreRuntimeArtifactSource = (typeof opcoreRuntimeArtifactSources)[number]; - -export interface OpcoreRuntimeInfoPayload { - schemaVersion: 1; - packageName: "opcore"; - version: string; - bin: "opcore"; - artifactSource: OpcoreRuntimeArtifactSource; - packageRoot: string; - entrypoint: string; -} - -export interface OpcoreDoctorPayload { - schemaVersion: 1; - runtime: OpcoreRuntimeInfoPayload; - repo: { - root: string; - requestedPath: string; - }; - config: { - path: ".opcore/config"; - state: "found" | "missing" | "unreadable"; - message?: string; - }; - checks: { - count: number; - ids: readonly string[]; - }; - policy: OpcoreValidationPolicySummary; - graph: GraphProviderStatus; - generatedState: { - ignored: readonly string[]; - guidance: string; - }; - nextActions: readonly string[]; -} - -export const opcoreInitScopes = ["repo", "global"] as const; -export type OpcoreInitScope = (typeof opcoreInitScopes)[number]; - -export interface OpcoreInitAction { - kind: "write" | "upsert_block" | "create_hook" | "wire_harness" | "restore" | "remove"; - path: string; - targetScope: OpcoreInitScope; - summary: string; - requiresApproval: boolean; - outsideOpcore: boolean; -} - -export interface OpcoreInitScanSummary { - totalFiles: number; - graphSupportedFiles: number; - validationSupportedFiles: number; - validationRetainedFiles: number; - unsupportedFiles: number; - languages: readonly { - language: string; - files: number; - graphSupported: boolean; - validationSupported: boolean; - }[]; - unsupportedStacks: readonly { - extension: string; - language: string; - count: number; - examples: readonly string[]; - }[]; - degradedRustTools: readonly { - adapter: string; - tool: string; - failureMessage?: string; - }[]; - diagnosticCount: number; - validationStatus: ValidationResultStatus; - failedChecks: readonly string[]; - graphState: GraphProviderStatusState; - activationLevel: "ready" | "degraded" | "blocked"; -} - -export interface OpcoreInitLanguageSetting { - language: string; - files: number; - state: "supported" | "retained" | "unsupported" | "degraded"; - graph: "supported" | "unsupported"; - validation: "supported" | "retained" | "unsupported" | "degraded"; - checks: readonly string[]; - notes: readonly string[]; -} - -export interface OpcoreInitPythonEnvironment { - dependencyManagers: readonly { - kind: "pyproject" | "requirements" | "pipfile" | "poetry" | "uv"; - path: string; - }[]; - virtualEnvironments: readonly { - kind: "venv"; - path: string; - }[]; - notes: readonly string[]; - contexts?: readonly PythonProjectContext[]; -} - -export interface OpcoreInitSettings { - languages: readonly OpcoreInitLanguageSetting[]; - python?: OpcoreInitPythonEnvironment; -} - -export interface OpcoreInitInteraction { - tty: boolean; - promptState: "not_requested" | "requested" | "approved" | "declined"; -} - -export interface OpcoreInitTiming { - scanMs: number; - planMs: number; - promptMs: number; - applyMs: number; - totalMs: number; - firstOutputMs: number; -} - -export interface OpcoreInitPlanPayload { - schemaVersion: 1; - mode: "plan" | "apply" | "undo"; - approved: boolean; - repo: { - root: string; - requestedPath: string; - }; - options: { - scope: OpcoreInitScope; - failClosedHook: boolean; - dryRun: boolean; - }; - agentFiles: readonly string[]; - actions: readonly OpcoreInitAction[]; - warnings: readonly string[]; - nextActions: readonly string[]; - undoAvailable: boolean; - scan: OpcoreInitScanSummary; - settings: OpcoreInitSettings; - interaction: OpcoreInitInteraction; - timings: OpcoreInitTiming; -} - -export interface OpcoreMetricEvidence { - source: string; - path: string; - message: string; - checkId?: string; - code?: string; - line?: number; - column?: number; -} - -export interface OpcoreMetricSignal { - id: string; - title: string; - category: "coverage" | "typescript" | "rust" | "graph" | (string & {}); - severity: "info" | "warning" | "error"; - count: number; - evidence: readonly OpcoreMetricEvidence[]; -} - -export interface OpcoreMetricDegradation { - id: string; - title: string; - source: string; - severity: "info" | "warning" | "error"; - message: string; - checkId?: string; - requiredTool?: string; -} - -export interface OpcoreMetricReport { - schemaVersion: 1; - kind: "opcore_metric_report"; - generatedAt: string; - repo: { - root: string; - requestedPath: string; - git: OpcoreRepoStatePayload["repo"]["git"]; - }; - coverage: OpcoreRepoStatePayload["coverage"]; - graph: { - state: GraphProviderStatusState; - mode: GraphProviderMode; - provider: string; - }; - validation: { - status?: ValidationResultStatus; - diagnosticCount: number; - checkCount: number; - policy?: OpcoreValidationPolicySummary; - pythonProjectContexts?: readonly PythonProjectContext[]; - }; - signals: readonly OpcoreMetricSignal[]; - degradations: readonly OpcoreMetricDegradation[]; - warnings: readonly string[]; - nextActions: readonly string[]; -} - -export interface OpcoreMetricHistoryEntry { - schemaVersion: 1; - kind: "opcore_metric_history_entry"; - recordedAt: string; - report: OpcoreMetricReport; -} - -export interface OpcoreMeasureSignalCount { - id: string; - title: string; - count: number; -} - -export interface OpcoreMeasureSignalDelta { - id: string; - title: string; - currentCount: number; - comparisonCount: number; - delta: number; -} - -export const opcoreMeasureLatencyStatuses = ["ok", "slower", "over_budget"] as const; -export type OpcoreMeasureLatencyStatus = (typeof opcoreMeasureLatencyStatuses)[number]; -export const opcoreMeasureLatencyFindingStatuses = ["slower", "over_budget"] as const; -export type OpcoreMeasureLatencyFindingStatus = (typeof opcoreMeasureLatencyFindingStatuses)[number]; - -export interface OpcoreMeasureLatencyPhase { - phase: string; - durationMs: number; -} - -export interface OpcoreMeasureLatencyFinding { - canonicalCommand: readonly string[]; - repoShapeBucket: string; - processState: CommandTimingProcessState; - status: OpcoreMeasureLatencyFindingStatus; - currentDurationMs: number; - dominantPhase?: OpcoreMeasureLatencyPhase; - baselineDurationMs?: number; - previousDurationMs?: number; - baselineDeltaMs?: number; - previousDeltaMs?: number; - budgetMs?: number; - overBudgetMs?: number; -} - -export interface OpcoreMeasureLatencyReport { - kind: "opcore_latency_report"; - recordCount: number; - budgetCount: number; - findings: readonly OpcoreMeasureLatencyFinding[]; -} - -export interface OpcoreMeasureComparison { - recordedAt: string; - generatedAt: string; - coverage: OpcoreMetricReport["coverage"]; - signals: readonly OpcoreMeasureSignalCount[]; - deltas: readonly OpcoreMeasureSignalDelta[]; -} - -export interface OpcoreMeasureDelta { - schemaVersion: 1; - kind: "opcore_measure_delta"; - generatedAt: string; - current: { - generatedAt: string; - coverage: OpcoreMetricReport["coverage"]; - signals: readonly OpcoreMeasureSignalCount[]; - }; - latency?: OpcoreMeasureLatencyReport; - baseline?: OpcoreMeasureComparison; - previous?: OpcoreMeasureComparison; - warnings: readonly string[]; - degradations: readonly OpcoreMetricDegradation[]; - nextActions: readonly string[]; -} - -export interface OpcoreTrySignalSummary { - id: string; - title: string; - count: number; - delta: number; -} - -export interface OpcoreTryScenario { - id: string; - repoRoot: string; - title: string; - commands: readonly string[]; - coverage: { - totalFiles: number; - validationSupportedFiles: number; - unsupportedFiles: number; - }; - signals: readonly OpcoreTrySignalSummary[]; -} - -export interface OpcoreTryCommandSummary { - scenarioId: string; - command: readonly string[]; - canonicalCommand: readonly string[]; - owner: CommandOwner; - status: CommandRouteStatus; - exitCode: number; -} - -export interface OpcoreTryPayload { - schemaVersion: 1; - sampleRoot: string; - published: false; - scenarios: readonly OpcoreTryScenario[]; - commands: readonly OpcoreTryCommandSummary[]; -} - -export type CommandTimingPhase = Pick; - -export interface CommandTiming { - durationMs: number; - phases: readonly CommandTimingPhase[]; - processState: CommandTimingProcessState; - degradations?: readonly CommandTimingDegradationReason[]; -} - -export interface RepoShapeFingerprint { - totalFiles: number; - languages: readonly { - language: string; - files: number; - }[]; - graph: { - supportedFiles: number; - unsupportedFiles: number; - }; - git: { - available: boolean; - clean?: boolean; - }; -} - -export interface CommandLatencyRecord { - schemaVersion: 1; - recordedAt: string; - bin: string; - canonicalCommand: readonly string[]; - owner: CommandOwner; - status: CommandRouteStatus; - exitCode: number; - repo: RepoShapeFingerprint; - timing: CommandTiming; - opcoreVersion: string; -} - -export interface LatencyPhaseBudget { - phase: string; - budgetMs: number; -} - -export interface LatencyBudget { - schemaVersion: 1; - canonicalCommand: readonly string[]; - scope: string; - repoShapeBucket: string; - budgetMs: number; - phaseBudgets?: readonly LatencyPhaseBudget[]; -} - -export interface LatencyBudgetResult { - schemaVersion: 1; - status: LatencyBudgetResultStatus; - budget: LatencyBudget; - observed: { - canonicalCommand: readonly string[]; - phase: string; - durationMs: number; - }; - evidence: { - canonicalCommand: readonly string[]; - phase: string; - repoShapeBucket: string; - observedMs: number; - budgetMs: number; - overByMs: number; - }; -} - -export interface CommandRouterResult { - schemaVersion: 1; - bin: string; - argv: readonly string[]; - canonicalCommand: readonly string[]; - owner: CommandOwner; - status: CommandRouteStatus; - exitCode: number; - message: string; - json: boolean; - providerStatus?: GraphProviderStatus; - graphPipeline?: GraphPipelineResult; - graphQuery?: GraphFactQueryResult | GraphNamedQueryResult; - graphSearch?: GraphSearchResult; - inspectResult?: InspectRouteResult; - graphImpact?: GraphImpactResult; - graphReviewContext?: GraphReviewContextResult; - graphChanges?: GraphDetectChangesResult; - graphServe?: GraphServeTransportStatus; - validationResult?: ValidationResult; - validationStatus?: ValidationStatusPayload; - receipt?: PreWriteValidationReceipt; - editPlan?: EditPlan; - editResult?: EditCommandResult; - repoState?: OpcoreRepoStatePayload; - runtimeInfo?: OpcoreRuntimeInfoPayload; - opcoreDoctor?: OpcoreDoctorPayload; - opcoreInit?: OpcoreInitPlanPayload; - opcoreMeasure?: OpcoreMeasureDelta; - opcoreTry?: OpcoreTryPayload; - timing?: CommandTiming; -} - -export interface ParsedCommandArgv { - args: readonly string[]; - json: boolean; -} - -export interface CommandRouterResultInput { - bin: string; - argv: readonly string[]; - canonicalCommand: readonly string[]; - owner: CommandOwner; - status: CommandRouteStatus; - json: boolean; - message: string; - providerStatus?: GraphProviderStatus; - graphPipeline?: GraphPipelineResult; - graphQuery?: GraphFactQueryResult | GraphNamedQueryResult; - graphSearch?: GraphSearchResult; - inspectResult?: InspectRouteResult; - graphImpact?: GraphImpactResult; - graphReviewContext?: GraphReviewContextResult; - graphChanges?: GraphDetectChangesResult; - graphServe?: GraphServeTransportStatus; - validationResult?: ValidationResult; - validationStatus?: ValidationStatusPayload; - receipt?: PreWriteValidationReceipt; - editPlan?: EditPlan; - editResult?: EditCommandResult; - repoState?: OpcoreRepoStatePayload; - runtimeInfo?: OpcoreRuntimeInfoPayload; - opcoreDoctor?: OpcoreDoctorPayload; - opcoreInit?: OpcoreInitPlanPayload; - opcoreMeasure?: OpcoreMeasureDelta; - opcoreTry?: OpcoreTryPayload; - timing?: CommandTiming; -} - -export interface CommandAdapterRequest { - schemaVersion: 1; - bin: string; - argv: readonly string[]; - args: readonly string[]; - json: boolean; - group: CommandGroupContract; - canonicalCommand: readonly string[]; -} - -export type CommandAdapter = (request: CommandAdapterRequest) => CommandRouterResult | Promise; - -export type CommandRouterWriter = (text: string) => void; - -export interface RouteCommandAdapterOptions { - bin: string; - argv: readonly string[]; - groupName: string; - adapter: CommandAdapter; - args?: readonly string[]; - json?: boolean; - showHelpOnEmpty?: boolean; - validateFirstRouteArg?: boolean; -} - -export interface RunCommandAdapterCliOptions extends Omit { - argv?: readonly string[]; - stdout?: CommandRouterWriter; - stderr?: CommandRouterWriter; -} - -export const commandExitSemantics: CommandExitSemantics = { - ok: 0, - error: 1, - notImplemented: 2, - unsupported: 64, - jsonStable: true -}; - -export const commandRouterManifest: CommandRouterManifest = { - schemaVersion: 1, - packageName: "opcore", - bins: ["opcore"], - exitSemantics: commandExitSemantics, - ownershipBoundaries: [ - { - owner: "graph", - summary: "Graph provider owns extraction, persistent facts, freshness, query, search, and impact contracts." - }, - { - owner: "inspect", - summary: "Inspect owns read-only code intelligence over graph facts and language-service surfaces." - }, - { - owner: "edit", - summary: "Edit planner owns symbol-aware rename, move, signature, patch, and tree edit orchestration." - }, - { - owner: "validation", - summary: "Validation owns checks, hypothetical validation, manifests, failure policy, and check status." - }, - { - owner: "runtime", - summary: "Runtime owns shared router health, help, and doctor surfaces." - } - ], - commandGroups: [ - { - name: "graph", - owner: "graph", - canonicalCommand: ["opcore", "graph"], - commands: [ - "build", - "update", - "watch", - "status", - "query", - "serve", - "impact", - "review-context", - "detect-changes", - "search" - ], - summary: - "GraphProvider build, update, watch, status, query, impact, review context, change detection, daemon lifecycle, and freshness behavior." - }, - { - name: "inspect", - owner: "inspect", - canonicalCommand: ["opcore", "inspect"], - commands: ["symbols", "definition", "references", "signature", "implementations", "search"], - summary: "Read-only code intelligence over graph and inspect-owned language services." - }, - { - name: "edit", - owner: "edit", - canonicalCommand: ["opcore", "edit"], - commands: ["exact", "multi", "search-replace", "check", "apply", "patch", "tree", "rename", "move", "signature"], - summary: "Exact edit, multi-edit, search-replace, patch/tree, graph-backed symbol rename/move/signature, preview/check, and apply routes." - }, - { - name: "check", - owner: "validation", - canonicalCommand: ["opcore", "check"], - commands: ["files", "staged", "changed", "tree", "all", "manifest"], - summary: "Mechanical check execution and check manifest behavior." - }, - { - name: "validate", - owner: "validation", - canonicalCommand: ["opcore", "validate"], - commands: ["request", "hypothetical", "pre-write", "manifest"], - summary: "Hypothetical, pre-write, and validation request behavior." - }, - { - name: "status", - owner: "runtime", - canonicalCommand: ["opcore", "status"], - commands: ["status"], - summary: "Shared router and runtime health status." - }, - { - name: "doctor", - owner: "runtime", - canonicalCommand: ["opcore", "doctor"], - commands: ["doctor"], - summary: "Shared runtime diagnostic summary." - } - ] -}; - -export interface GraphReleaseCommandCoverage { - id: GraphReleaseCoreCommandId; - bin: "opcore"; - command: readonly string[]; - canonicalCommand: readonly string[]; - status: "passed"; - exitCode: 0; - fixture: string; - durationMs: number; -} - -export interface GraphReleaseRustCommandCoverage { - id: GraphReleaseRustCommandId; - bin: "opcore"; - command: readonly string[]; - canonicalCommand: readonly string[]; - status: "passed"; - exitCode: 0; - fixture: string; - durationMs: number; -} - -export interface GraphReleaseDirectSqliteQueryReceipt { - id: GraphReleaseDirectSqliteQueryId; - query: string; - status: "passed"; - rowCount: number; - fixture: string; -} - -export interface GraphReleaseServeTransportReceipt { - id: GraphReleaseServeTransportId; - protocol: "opcore.graph.daemon" | "jsonrpc-2.0" | (string & {}); - operation: "ping" | "status" | "query" | "search" | "shutdown" | (string & {}); - status: "passed"; - exitCode: 0; -} - -export interface GraphReleaseBenchmarkReceipt { - metric: GraphReleaseBenchmarkMetric; - value: number; - unit: "ms" | "bytes"; - baselineIssue: "#19"; - baselineReceipt: string; - comparison: "recorded" | "within_baseline" | "above_baseline" | "below_baseline"; -} - -export interface GraphReleasePackageInspection { - packageName: "@the-open-engine/opcore-graph"; - tarballName: string; - fileCount: number; - files: readonly string[]; - forbiddenMarkersAbsent: true; - generatedBuildMetadataAbsent: true; - privatePathsAbsent: true; - sourceProvenanceAbsent: true; - packageMetadataAbsent: true; - gitHistoryAbsent: true; - foreignImplementationNamesAbsent: true; - inspections: readonly string[]; -} - -export interface GraphReleaseNativeArtifactEvidence { - packageName: GraphCoreNativePackageName; - targetPlatform: GraphCoreNativeSupportedTarget; - metadata: GraphProviderArtifactMetadata; - binaryPath: "opcore-graph-core"; - checksumPath: "opcore-graph-core.sha256"; - metadataPath: "metadata.json"; - binarySha256: string; - checksumFileSha256: string; - metadataSha256: string; - packageFiles: readonly string[]; -} - -export interface GraphReleaseReportReceipt { - id: GraphReleaseReportReceiptId; - command: readonly string[]; - status: "passed"; - exitCode: 0; - path: string; - checksumSha256?: string; -} - -export interface GraphReleaseOptionalSurfaceReceipt { - issue: GraphReleaseDeferredChild; - id: GraphReleaseOptionalAnalysisSurface["id"] | (string & {}); - classification: GraphReleaseSurfaceClassification; - status: "unsupported" | "deferred"; -} - -export interface GraphReleaseHandoffReceipt { - issue: GraphReleaseHandoffIssue; - receiptPath: string; - checksumSha256: string; - rollbackNote: string; -} - -export interface GraphReleasePackageVersion { - packageName: string; - version: string; -} - -export interface GraphReleaseReceipt { - schemaVersion: 1; - issue: "#17"; - origin: "covibes-authored-synthetic"; - generatedAt: string; - commitSha: string; - graphPackageVersions: readonly GraphReleasePackageVersion[]; - graphProviderSchemaVersion: 1; - requiredChildren: readonly string[]; - deferredChildren: readonly string[]; - commandCoverage: readonly GraphReleaseCommandCoverage[]; - rustCommandCoverage: readonly GraphReleaseRustCommandCoverage[]; - directSqliteQueries: readonly GraphReleaseDirectSqliteQueryReceipt[]; - serveTransport: readonly GraphReleaseServeTransportReceipt[]; - benchmarks: readonly GraphReleaseBenchmarkReceipt[]; - packageInspection: GraphReleasePackageInspection; - supportedNativeTargets: readonly GraphCoreNativeSupportedTarget[]; - nativeArtifacts: readonly GraphReleaseNativeArtifactEvidence[]; - reportReceipts: readonly GraphReleaseReportReceipt[]; - graphArtifact: GraphProviderArtifactMetadata; - optionalSurfaces: readonly GraphReleaseOptionalSurfaceReceipt[]; - handoff: readonly GraphReleaseHandoffReceipt[]; -} - -export interface ReleaseReceiptTarballEvidence { - filename: string; - path: string; - sha256: string; - integrity?: string; - shasum?: string; -} - -export interface ReleaseReceiptPackageManifestMetadata { - name: ReleaseReceiptPackageName; - version: string; - license: string; - main?: string; - types?: string; - files: readonly string[]; - bins: Readonly>; - dependencies: Readonly>; - optionalDependencies?: Readonly>; - bundledDependencies: readonly string[]; - os?: readonly string[]; - cpu?: readonly string[]; -} - -export interface ReleaseReceiptNativeArtifactEvidence { - packageName: "opcore"; - bundledPackageName: GraphCoreNativePackageName; - targetPlatform: GraphCoreNativeSupportedTarget; - metadata: GraphProviderArtifactMetadata; - binaryPath: string; - checksumPath: string; - metadataPath: string; - binarySha256: string; - checksumFileSha256: string; - metadataSha256: string; - descriptorArtifactId: string; - descriptorChecksumId: string; -} - -export interface ReleaseReceiptPackageEvidence { - packageName: ReleaseReceiptPackageName; - packageRoot: string; - version: string; - manifest: ReleaseReceiptPackageManifestMetadata; - tarball: ReleaseReceiptTarballEvidence; - files: readonly string[]; - fileCount: number; - expectedFiles: readonly string[]; - expectedFileCount: number; - bins: Readonly>; - descriptorReferences: readonly ManagedToolDescriptorArtifactReference[]; - nativeArtifacts: readonly ReleaseReceiptNativeArtifactEvidence[]; -} - -export interface ReleaseReceiptDescriptorCommandGroupEvidence { - name: ReleaseReceiptCommandGroupName; - canonicalCommand: readonly string[]; - packageName: string; -} - -export interface ReleaseReceiptResolvedArtifactEvidence { - id: string; - packageName: ReleaseReceiptPackageName; - path: string; - type: ManagedToolDescriptorArtifactType; - required: boolean; - packageFile: true; - checksumRef?: string; -} - -export interface ReleaseReceiptResolvedChecksumEvidence { - id: string; - packageName: ReleaseReceiptPackageName; - path: string; - algorithm: "sha256"; - artifactRef: string; - required: boolean; - packageFile: true; - value: string; -} - -export interface ReleaseReceiptDescriptorEvidence { - path: string; - packageName: "opcore"; - checksumSha256: string; - descriptor: ManagedToolDescriptor; - commandGroups: readonly ReleaseReceiptDescriptorCommandGroupEvidence[]; - resolvedArtifacts: readonly ReleaseReceiptResolvedArtifactEvidence[]; - resolvedChecksums: readonly ReleaseReceiptResolvedChecksumEvidence[]; -} - -export interface ReleaseReceiptLicensePackageEvidence { - name: string; - version: string; - license: string; - source: string; - bundled: boolean; -} - -export interface ReleaseReceiptLicenseEvidence { - reportPath: string; - reportSha256: string; - productionDependencyCount: number; - bundledDependencyCount: number; - workspacePackageCount: number; - unresolvedLicenseCount: 0; - packages: readonly ReleaseReceiptLicensePackageEvidence[]; -} - -export interface ReleaseReceiptProvenanceFinding { - scope: "current-tree" | "git-history"; - marker: string; - path?: string; - commit?: string; - line?: number; -} - -export interface ReleaseReceiptProvenanceEvidence { - reportPath: string; - reportSha256: string; - scannedFileCount: number; - historyCommitCount: number; - findingCount: 0; - findings: readonly ReleaseReceiptProvenanceFinding[]; -} - -export interface ReleaseReceiptSecretFinding { - scope: ReleaseReceiptSecretFindingScope; - kind: string; - path?: string; - commit?: string; - line?: number; - fingerprint: string; - allowlisted: boolean; -} - -export interface ReleaseReceiptSecretHistoryEvidence { - allowlistPath: string; - allowlistSha256: string; - currentTreeScannedFileCount: number; - gitHistoryScannedCommitCount: number; - findingCount: 0; - findings: readonly ReleaseReceiptSecretFinding[]; -} - -export interface ReleaseReceiptReport { - id: ReleaseReceiptReportId; - command: readonly string[]; - status: "passed"; - exitCode: 0; - path?: string; - checksumSha256?: string; - summary: string; -} - -export interface ReleaseReceiptGraphReleaseEvidence { - path: string; - issue: "#17"; - checksumSha256: string; -} - -export interface ReleaseReceipt { - schemaVersion: 1; - issue: "#29"; - origin: "covibes-authored-release-proof"; - generatedAt: string; - commitSha: string; - privateRepo: true; - packageNames: readonly ReleaseReceiptPackageName[]; - commandGroups: readonly ReleaseReceiptCommandGroupName[]; - packages: readonly ReleaseReceiptPackageEvidence[]; - descriptor: ReleaseReceiptDescriptorEvidence; - nativeArtifacts: readonly ReleaseReceiptNativeArtifactEvidence[]; - license: ReleaseReceiptLicenseEvidence; - provenance: ReleaseReceiptProvenanceEvidence; - secretHistory: ReleaseReceiptSecretHistoryEvidence; - reports: readonly ReleaseReceiptReport[]; - graphReleaseReceipt: ReleaseReceiptGraphReleaseEvidence; -} - -export interface ReleaseCutoverTarballEvidence { - filename: string; - sha256: string; -} - -export interface ReleaseCutoverInstalledManifestEvidence { - path: string; - sha256: string; - bins: Readonly>; -} - -export interface ReleaseCutoverInstalledFileEvidence { - path: string; - sha256: string; -} - -export interface ReleaseCutoverInstalledPackageEvidence { - packageName: ReleaseReceiptPackageName; - version: string; - tarball: ReleaseCutoverTarballEvidence; - installedManifest: ReleaseCutoverInstalledManifestEvidence; - installedFiles: readonly ReleaseCutoverInstalledFileEvidence[]; -} - -export interface ReleaseCutoverDescriptorEvidence { - path: string; - packageName: "opcore"; - checksumSha256: string; - descriptor: ManagedToolDescriptor; - resolvedArtifacts: readonly ReleaseReceiptResolvedArtifactEvidence[]; - resolvedChecksums: readonly ReleaseReceiptResolvedChecksumEvidence[]; -} - -export interface ReleaseCutoverEnvironmentIsolationEvidence { - pathSanitized: true; - siblingRepositoriesExcluded: true; - opcoreBinsVerified: true; -} - -export interface ReleaseCutoverCommandReceipt { - id: ReleaseCutoverCommandId; - command: readonly string[]; - canonicalCommand: readonly string[]; - owner: CommandOwner; - status: CommandRouteStatus; - exitCode: number; - binPath: string; - stdoutSha256: string; - stderrSha256: string; - assertion: string; -} - -export interface ReleaseCutoverRustCommandReceipt { - id: ReleaseCutoverRustCommandId; - command: readonly string[]; - canonicalCommand: readonly string[]; - owner: "graph"; - status: "ok"; - exitCode: 0; - binPath: string; - stdoutSha256: string; - stderrSha256: string; - assertion: string; -} - -export interface ReleaseCutoverPythonCommandReceipt { - id: ReleaseCutoverPythonCommandId; - command: readonly string[]; - canonicalCommand: readonly string[]; - evidence: readonly string[]; - owner: CommandOwner; - status: "ok"; - exitCode: 0; - binPath: string; - stdoutSha256: string; - stderrSha256: string; - assertion: string; -} - -export interface ReleaseCutoverNegativeCheck { - id: ReleaseCutoverNegativeCheckId; - command: readonly string[]; - status: "passed"; - exitCode: 0; - assertion: string; -} - -export interface OpcoreSelfValidationReceipt { - id: "opcore-self-check"; - command: readonly ["npm", "run", "opcore:self-check"]; - status: "passed"; - exitCode: 0; - stdoutSha256: string; - stderrSha256: string; - assertion: string; -} - -export interface ReleaseCutoverForbiddenMarkerScan { - scannedTextCount: number; - findingCount: 0; - markersBlocked: readonly string[]; -} - -export interface ReleaseCutoverInputEvidence { - issue: ReleaseCutoverInputIssue; - path: string; - checksumSha256: string; -} - -export interface ReleaseCutoverReceipt { - schemaVersion: 1; - issue: "#30"; - origin: "covibes-authored-cutover-proof"; - generatedAt: string; - commitSha: string; - privateRepo: true; - packageNames: readonly ReleaseReceiptPackageName[]; - installedPackages: readonly ReleaseCutoverInstalledPackageEvidence[]; - descriptor: ReleaseCutoverDescriptorEvidence; - environmentIsolation: ReleaseCutoverEnvironmentIsolationEvidence; - commandReceipts: readonly ReleaseCutoverCommandReceipt[]; - rustCommandReceipts: readonly ReleaseCutoverRustCommandReceipt[]; - pythonCommandReceipts: readonly ReleaseCutoverPythonCommandReceipt[]; - negativeChecks: readonly ReleaseCutoverNegativeCheck[]; - selfValidation: OpcoreSelfValidationReceipt; - forbiddenMarkerScan: ReleaseCutoverForbiddenMarkerScan; - inputEvidence: readonly ReleaseCutoverInputEvidence[]; -} - -export interface AspDogfoodManagerEvidence { - bootstrapSource: "local-sibling"; - aspRepoPath: string; - aspBinPath: string; - cliPath: string; - commitSha: string; -} - -export interface AspDogfoodAspHomeEvidence { - path: string; - temp: true; - isolated: true; - sharedStateMutated: false; - pathSanitized: true; -} - -export interface AspDogfoodHostFixtureEvidence { - repo: string; - temp: true; - sourceRepoMutated: false; - baselineCommitted: true; - changedPaths: readonly string[]; -} - -export interface AspDogfoodCommandRunReceipt { - id: string; - command: readonly string[]; - status: "passed" | "failed" | "retained-not-run"; - exitCode: number | null; - stdoutSha256: string; - stderrSha256: string; - output?: unknown; - assertion: string; -} - -export interface AspDogfoodProviderManifestEvidence { - manifestPath: string; - manifestSha256: string; - manifest: unknown; -} - -export interface AspDogfoodProviderEvidence { - providerId: "opcore"; - packageName: "opcore"; - binPath: string; - indexPath: string; - indexSha256: string; - command: readonly ["opcore-asp-provider", "--stdio"]; - entrypoint: { - transport: "stdio"; - bin: string; - args: readonly ["--stdio"]; - }; - manifest: AspDogfoodProviderManifestEvidence; -} - -export interface AspDogfoodRepoEnrollmentEvidence { - repo: string; - mode: "advisory" | "shadow"; - repoAdd: AspDogfoodCommandRunReceipt; - repoEnable: AspDogfoodCommandRunReceipt; - repoStatus: AspDogfoodCommandRunReceipt; -} - -export interface AspDogfoodManagerStateEvidence { - status: AspDogfoodCommandRunReceipt; - serverAdd: AspDogfoodCommandRunReceipt; - serverStatus: AspDogfoodCommandRunReceipt; -} - -export interface AspDogfoodHostCheckEvidence extends AspDogfoodCommandRunReceipt { - hostDecision: unknown; - receipt: unknown; - assurance: { - mode: string; - transactionGuarantee: string; - }; -} - -export interface AspDogfoodHostEvaluationEvidence { - check: AspDogfoodHostCheckEvidence; - ciVerify?: AspDogfoodCommandRunReceipt; -} - -export interface AspDogfoodProviderProbeEvidence extends AspDogfoodCommandRunReceipt { - assessment: unknown; - validAsOf: unknown; - coverage: unknown; - diagnosticsCount: number; - hostOwnedFieldLeak: false; -} - -export interface AspDogfoodUnsupportedSurfaceEvidence { - surface: AspDogfoodUnsupportedSurfaceId; - status: "degraded" | "parity-blocker"; - cleanCoverage: false; - blocker: string; -} - -export interface AspDogfoodParityBlocker { - source: string; - detail: string; -} - -export interface AspDogfoodAuthorityEvidence { - hostOwnsDecisions: true; - providerOutputIsHostDecision: false; - localAuthorityOverride: { - present: false; - sharedAuthorityWeakened: false; - }; -} - -export interface AspDogfoodForbiddenMarkerScan { - scannedTextCount: number; - findingCount: 0; - markersBlocked: readonly AspDogfoodForbiddenProviderMarker[]; -} - -export interface AspDogfoodReceipt { - schemaVersion: 1; - issue: "#120"; - origin: "covibes-authored-asp-dogfood-proof"; - generatedAt: string; - commitSha: string; - privateRepo: true; - bootstrapSource: "local-sibling"; - packageNames: readonly ReleaseReceiptPackageName[]; - installedPackages: readonly ReleaseCutoverInstalledPackageEvidence[]; - manager: AspDogfoodManagerEvidence; - aspHome: AspDogfoodAspHomeEvidence; - hostFixture: AspDogfoodHostFixtureEvidence; - provider: AspDogfoodProviderEvidence; - managerState: AspDogfoodManagerStateEvidence; - repoEnrollment: AspDogfoodRepoEnrollmentEvidence; - hostEvaluation: AspDogfoodHostEvaluationEvidence; - providerProbe: AspDogfoodProviderProbeEvidence; - selfValidation: OpcoreSelfValidationReceipt; - unsupportedSurfaces: readonly AspDogfoodUnsupportedSurfaceEvidence[]; - parityBlockers: readonly AspDogfoodParityBlocker[]; - authority: AspDogfoodAuthorityEvidence; - publicReleaseActions: readonly []; - forbiddenMarkerScan: AspDogfoodForbiddenMarkerScan; -} - -declare const process: { - argv: string[]; - stdout: { - write(text: string): void; - }; - stderr: { - write(text: string): void; - }; -}; - -const commandHelpArgs = new Set(["--help", "-h", "help"]); - -export function parseCommandArgv(argv: readonly string[]): ParsedCommandArgv { - return { - args: argv.filter((arg) => arg !== "--json"), - json: argv.includes("--json") - }; -} - -export function normalizeCommandBin(bin: string): string { - const normalized = bin.replaceAll("\\", "/").split("/").at(-1) ?? bin; - return normalized.endsWith(".js") ? "opcore" : normalized; -} - -export function commandExitCodeForStatus(status: CommandRouteStatus): number { - if (status === "ok") return commandRouterManifest.exitSemantics.ok; - if (status === "error") return commandRouterManifest.exitSemantics.error; - if (status === "not_implemented") return commandRouterManifest.exitSemantics.notImplemented; - return commandRouterManifest.exitSemantics.unsupported; -} - -export function createCommandRouterResult(input: CommandRouterResultInput): CommandRouterResult { - return validateCommandRouterResult(withoutUndefinedProperties({ - schemaVersion: 1, - bin: input.bin, - argv: input.argv, - canonicalCommand: input.canonicalCommand, - owner: input.owner, - status: input.status, - exitCode: commandExitCodeForStatus(input.status), - message: input.message, - json: input.json, - providerStatus: input.providerStatus, - graphPipeline: input.graphPipeline, - graphQuery: input.graphQuery, - graphSearch: input.graphSearch, - inspectResult: input.inspectResult, - graphImpact: input.graphImpact, - graphReviewContext: input.graphReviewContext, - graphChanges: input.graphChanges, - graphServe: input.graphServe, - validationResult: input.validationResult, - validationStatus: input.validationStatus, - receipt: input.receipt, - editPlan: input.editPlan, - editResult: input.editResult, - repoState: input.repoState, - runtimeInfo: input.runtimeInfo, - opcoreDoctor: input.opcoreDoctor, - opcoreInit: input.opcoreInit, - opcoreMeasure: input.opcoreMeasure, - opcoreTry: input.opcoreTry, - timing: input.timing - }) as CommandRouterResult); -} - -export function commandGroupByName(groupName: string): CommandGroupContract | undefined { - return commandRouterManifest.commandGroups.find((group) => group.name === groupName); -} - -export async function routeCommandAdapter(options: RouteCommandAdapterOptions): Promise { - const parsedArgv = parseCommandArgv(options.argv); - const parsed = { - args: options.args ?? parsedArgv.args, - json: options.json ?? parsedArgv.json - }; - const bin = normalizeCommandBin(options.bin); - const group = commandGroupByName(options.groupName); - if (!group) { - return createCommandRouterResult({ - bin, - argv: options.argv, - canonicalCommand: ["opcore", options.groupName], - owner: "runtime", - status: "unsupported", - json: parsed.json, - message: `Unsupported opcore command group: ${options.groupName}` - }); - } - - const showHelpOnEmpty = options.showHelpOnEmpty ?? group.name !== "check"; - if (parsed.args.some((arg) => commandHelpArgs.has(arg)) || (parsed.args.length === 0 && showHelpOnEmpty)) { - const routeName = parsed.args.find((arg) => !commandHelpArgs.has(arg) && !arg.startsWith("-")); - return commandHelpResult(bin, options.argv, parsed.json, group.name, routeName); - } - - const canonicalCommand = [...group.canonicalCommand, ...parsed.args.map(canonicalCommandArg)]; - const firstRouteArg = parsed.args.find((arg) => !arg.startsWith("-")); - const validateFirstRouteArg = options.validateFirstRouteArg ?? true; - if (validateFirstRouteArg && firstRouteArg && !group.commands.includes(firstRouteArg)) { - return createCommandRouterResult({ - bin, - argv: options.argv, - canonicalCommand, - owner: group.owner, - status: "unsupported", - json: parsed.json, - message: `${canonicalCommand.join(" ")} is not a supported ${group.name} route.` - }); - } - - const adapterRequest = validateCommandAdapterRequest({ - schemaVersion: 1, - bin, - argv: options.argv, - args: parsed.args, - json: parsed.json, - group, - canonicalCommand - }); - try { - return validateCommandRouterResult(await options.adapter(adapterRequest)); - } catch (error) { - return createCommandRouterResult({ - bin, - argv: options.argv, - canonicalCommand, - owner: group.owner, - status: "error", - json: parsed.json, - message: `${canonicalCommand.join(" ")} failed: ${errorMessage(error)}` - }); - } -} - -export async function runCommandAdapterCli(options: RunCommandAdapterCliOptions): Promise { - const stdout = options.stdout ?? ((text: string) => process.stdout.write(text)); - const stderr = options.stderr ?? ((text: string) => process.stderr.write(text)); - const argv = options.argv ?? process.argv.slice(2); - const routed = await routeCommandAdapter({ - ...options, - argv - }); - const text = routed.json ? JSON.stringify(routed) : routed.message; - const write = routed.json || routed.status === "ok" ? stdout : stderr; - write(`${text}\n`); - return routed.exitCode; -} - -function commandHelpResult( - bin: string, - argv: readonly string[], - json: boolean, - groupName?: string, - routeName?: string -): CommandRouterResult { - const group = groupName ? commandGroupByName(groupName) : undefined; - const canonicalCommand = group && routeName ? [...group.canonicalCommand, routeName, "help"] : group ? [...group.canonicalCommand, "help"] : ["opcore", "help"]; - return createCommandRouterResult({ - bin, - argv, - canonicalCommand, - owner: group?.owner ?? "runtime", - status: "ok", - json, - message: commandHelpMessage(groupName, routeName) - }); -} - -function canonicalCommandArg(arg: string): string { - return arg.length === 0 ? "" : arg; -} - -function errorMessage(error: unknown): string { - return error instanceof Error ? error.message : String(error); -} - -function commandHelpMessage(groupName?: string, routeName?: string): string { - if (!groupName) { - return [ - "Opcore - local code intelligence and edit safety for coding agents.", - "Groups: graph, inspect, edit, check, validate, status, doctor" - ].join("\n"); - } - const group = commandGroupByName(groupName); - if (!group) return `Unknown opcore command group: ${groupName}`; - const routeHelp = routeName ? commandRouteHelpMessage(groupName, routeName) : undefined; - if (routeHelp !== undefined) return routeHelp; - return [ - `${group.canonicalCommand.join(" ")} - ${group.summary}`, - `Commands: ${group.commands.join(", ")}`, - ...(groupName === "graph" ? [`Syntax: ${contractHelpSyntax(group)}`] : []), - `Example: ${contractHelpExample(groupName)}` - ].join("\n"); -} - -function commandRouteHelpMessage(groupName: string, routeName: string): string | undefined { - if (groupName === "graph" && routeName === "update") { - return [ - "Usage: opcore graph update [--repo ] [--base ] [--paths ] [--json]", - "Flags:", - " --repo Repository root to update.", - " --base Optional base ref for changed-file metadata.", - " --paths Optional repo-relative paths to refresh.", - " --json Emit structured JSON.", - "Defaults:", - " --repo defaults to the current working directory; JSON output is summary-oriented.", - "Examples:", - " opcore graph update --repo . --base HEAD --json", - " opcore graph update --repo . --paths src tests --json", - "Exit codes: 0 updated, 1 update failed, 64 unsupported." - ].join("\n"); - } - if (groupName === "graph" && routeName === "build") { - return [ - "Usage: opcore graph build [--repo ] [--paths ] [--json]", - "Flags:", - " --repo Repository root to build.", - " --paths Optional repo-relative paths to index.", - " --json Emit structured JSON.", - "Defaults:", - " --repo defaults to the current working directory; JSON output is summary-oriented.", - "Examples:", - " opcore graph build --repo . --json", - "Exit codes: 0 built, 1 build failed, 64 unsupported." - ].join("\n"); - } - if (groupName === "graph" && routeName === "status") { - return [ - "Usage: opcore graph status [--repo ] [--json]", - "Flags:", - " --repo Repository root to inspect.", - " --json Emit structured JSON.", - "Defaults:", - " --repo defaults to the current working directory.", - "Examples:", - " opcore graph status --repo . --json", - "Exit codes: 0 available or stale status read, 1 status failed, 64 unsupported." - ].join("\n"); - } - if (groupName === "validate" && routeName === "pre-write") { - return [ - "Usage: opcore validate pre-write --request-file [--timeout-ms ] [--json]", - "Flags:", - " --request-file ValidationRequest JSON payload.", - " --timeout-ms Pre-write timeout in milliseconds.", - " --json Emit structured JSON.", - "Defaults:", - " --timeout-ms defaults to 30000.", - "Examples:", - " opcore validate pre-write --request-file ./validation-request.json --timeout-ms 30000 --json", - "Exit codes: 0 passed, 1 findings or errors, 64 unsupported." - ].join("\n"); - } - return undefined; -} - -function contractHelpSyntax(group: CommandGroupContract): string { - return `${group.canonicalCommand.join(" ")} <${group.commands.join("|")}> --repo . [--json]`; -} - -function contractHelpExample(groupName: string): string { - if (groupName === "graph") return 'opcore graph search "GreetingCard" --repo . --limit 5'; - if (groupName === "inspect") return "opcore inspect definition GreetingCard --repo ."; - if (groupName === "edit") return 'opcore edit exact --path src/a.ts --expected "old" --replacement "new" --json'; - if (groupName === "check") return "opcore check files --files src/index.ts --json"; - if (groupName === "validate") return "opcore validate pre-write --request-file ./validation-request.json --timeout-ms 30000 --json"; - if (groupName === "status") return "opcore status"; - if (groupName === "doctor") return "opcore doctor --json"; - return `opcore ${groupName} --help`; -} - -export function validateCommandRouterManifest(manifest: CommandRouterManifest): CommandRouterManifest { - validateCommandRouterManifestHeader(manifest); - validateManifestBins(manifest.bins); - validateCommandExitSemantics(manifest.exitSemantics); - - validateManifestGroups(manifest.commandGroups); - validateManifestOwnershipBoundaries(manifest.ownershipBoundaries); - - return manifest; -} - -export function validateManagedToolDescriptor(descriptor: ManagedToolDescriptor): ManagedToolDescriptor { - if (!descriptor || typeof descriptor !== "object") { - throw new Error("Managed tool descriptor is required"); - } - if (descriptor.schemaVersion !== 1) throw new Error("Managed tool descriptor schemaVersion must be 1"); - if (descriptor.descriptorKind !== "aggregate_opcore") { - throw new Error("Managed tool descriptor descriptorKind must be aggregate_opcore"); - } - validateManagedToolIdentity(descriptor); - validateManagedToolEntrypoints(descriptor.entrypoints); - validateManagedToolCommandGroups(descriptor.commandGroups); - validateManagedToolHealthProbes(descriptor.healthProbes); - validateManagedToolCapabilities(descriptor.capabilities); - const artifactReferences = validateManagedToolArtifacts(descriptor.artifacts); - validateManagedToolChecksums(descriptor.checksums, artifactReferences); - validateManagedToolProvenanceHooks(descriptor.provenanceHooks); - validateGraphReleaseOptionalSurfaces(descriptor.optionalSurfaces); - validateManagedToolDescriptorForbiddenStrings(descriptor); - return descriptor; -} - -function validateManagedToolIdentity(descriptor: ManagedToolDescriptor): void { - const aggregate = descriptor.aggregateIdentity; - if (!aggregate || typeof aggregate !== "object") throw new Error("Managed tool descriptor aggregateIdentity is required"); - if (aggregate.name !== "opcore") throw new Error("Managed tool descriptor aggregateIdentity.name must be opcore"); - if (aggregate.releaseLine !== "opcore") throw new Error("Managed tool descriptor aggregateIdentity.releaseLine must be opcore"); - if (aggregate.packageName !== "opcore") { - throw new Error("Managed tool descriptor aggregateIdentity.packageName must be opcore"); - } - if (aggregate.version !== undefined) validateNonEmptyString(aggregate.version, "Managed tool descriptor aggregateIdentity.version"); - - const packageIdentity = descriptor.packageIdentity; - if (!packageIdentity || typeof packageIdentity !== "object") { - throw new Error("Managed tool descriptor packageIdentity is required"); - } - if (packageIdentity.packageName !== "opcore") { - throw new Error("Managed tool descriptor packageIdentity.packageName must be opcore"); - } - if (packageIdentity.artifactName !== "opcore") { - throw new Error("Managed tool descriptor packageIdentity.artifactName must be opcore"); - } - if (packageIdentity.version !== undefined) validateNonEmptyString(packageIdentity.version, "Managed tool descriptor packageIdentity.version"); -} - -function validateManagedToolEntrypoints(entrypoints: readonly ManagedToolDescriptorEntrypoint[]): void { - validateNonEmptyArray(entrypoints, "Managed tool descriptor entrypoints"); - validateExactStringSet( - entrypoints.map((entrypoint) => entrypoint.bin), - ["opcore"], - "Managed tool descriptor entrypoint bins" - ); - for (const entrypoint of entrypoints) { - if (!entrypoint || typeof entrypoint !== "object") throw new Error("Managed tool descriptor entrypoint is required"); - if (entrypoint.bin !== "opcore") throw new Error("Managed tool descriptor must expose the opcore entrypoint"); - if (entrypoint.packageName !== "opcore") { - throw new Error("Managed tool descriptor entrypoint packageName must be opcore"); - } - validateManagedToolPackagePath(entrypoint.path, "Managed tool descriptor entrypoint path"); - if (entrypoint.path !== "dist/index.js") { - throw new Error("Managed tool descriptor entrypoint path must be dist/index.js"); - } - validateExactStringSequence(entrypoint.command, ["opcore"], "Managed tool descriptor entrypoint command"); - } -} - -function validateManagedToolCommandGroups(commandGroups: readonly ManagedToolDescriptorCommandGroup[]): void { - validateNonEmptyArray(commandGroups, "Managed tool descriptor command groups"); - validateExactStringSet( - commandGroups.map((group) => group.name), - managedToolDescriptorCommandGroups, - "Managed tool descriptor command groups" - ); - for (const group of commandGroups) { - if (!group || typeof group !== "object") throw new Error("Managed tool descriptor command group is required"); - if (!includesString(managedToolDescriptorCommandGroups, group.name)) { - throw new Error(`Unknown managed tool descriptor command group: ${String(group.name)}`); - } - const expectedCanonical = ["opcore", group.name]; - validateExactStringSequence(group.canonicalCommand, expectedCanonical, `Managed tool descriptor ${group.name} canonicalCommand`); - validateStringArray(group.commands, `Managed tool descriptor ${group.name} commands`, { allowEmpty: false }); - const expectedPackageName = managedToolDescriptorCommandGroupPackageNames[group.name]; - if (group.packageName !== expectedPackageName) { - throw new Error(`Managed tool descriptor ${group.name} packageName must be ${expectedPackageName}`); - } - validateManagedToolCommandTokens(group.commands, `Managed tool descriptor ${group.name} commands`); - } -} - -function validateManagedToolHealthProbes(healthProbes: readonly ManagedToolDescriptorHealthProbe[]): void { - validateNonEmptyArray(healthProbes, "Managed tool descriptor health probes"); - let hasStatus = false; - let hasDoctor = false; - for (const probe of healthProbes) { - if (!probe || typeof probe !== "object") throw new Error("Managed tool descriptor health probe is required"); - validateNonEmptyString(probe.id, "Managed tool descriptor health probe id"); - validateStringArray(probe.command, "Managed tool descriptor health probe command", { allowEmpty: false }); - validateManagedToolCommandTokens(probe.command, "Managed tool descriptor health probe command"); - if (probe.command[0] !== "opcore") throw new Error("Managed tool descriptor health probes must use opcore commands"); - if (probe.expectedExitCode !== 0) throw new Error("Managed tool descriptor health probe expectedExitCode must be 0"); - if (probe.output !== "json") throw new Error("Managed tool descriptor health probe output must be json"); - if (sameStringArray(probe.command, ["opcore", "status", "--json"])) hasStatus = true; - if (sameStringArray(probe.command, ["opcore", "doctor", "--json"])) hasDoctor = true; - } - if (!hasStatus) throw new Error("Managed tool descriptor health probes must include status"); - if (!hasDoctor) throw new Error("Managed tool descriptor health probes must include doctor"); -} - -function validateManagedToolCapabilities(capabilities: ManagedToolDescriptorCapabilities): void { - if (!capabilities || typeof capabilities !== "object") throw new Error("Managed tool descriptor capabilities are required"); - validateManagedToolGraphCapabilities(capabilities.graph); - validateManagedToolEditCapabilities(capabilities.edit); - validateManagedToolValidationCapabilities(capabilities.validation); -} - -function validateManagedToolGraphCapabilities(graph: ManagedToolDescriptorCapabilities["graph"]): void { - if (!graph || typeof graph !== "object") throw new Error("Managed tool descriptor graph capabilities are required"); - if (graph.provider !== "opcore-graph") throw new Error("Managed tool descriptor graph provider must be opcore-graph"); - if (graph.schemaVersion !== 1) throw new Error("Managed tool descriptor graph schemaVersion must be 1"); - validateExactStringSet( - graph.commands, - ["build", "update", "watch", "status", "query", "impact", "review-context", "detect-changes", "search", "serve"], - "Managed tool descriptor graph commands" - ); - validateStringArray(graph.queryKinds, "Managed tool descriptor graph queryKinds", { allowEmpty: false }); - validateExactStringSet( - graph.daemonOperations, - ["ping", "status", "query", "search", "shutdown"], - "Managed tool descriptor graph daemonOperations" - ); - if (!Array.isArray(graph.nativeArtifacts)) { - throw new Error("Managed tool descriptor graph native artifacts are required"); - } - validateExactStringSet( - graph.nativeArtifacts.map((artifact) => artifact?.targetPlatform), - graphCoreNativeSupportedTargets, - "Managed tool descriptor graph native targets" - ); - for (const artifact of graph.nativeArtifacts) { - if (!artifact || typeof artifact !== "object") throw new Error("Managed tool descriptor graph native artifact is required"); - const expectedBundledPackageName = graphCoreNativePackageNameForTarget(artifact.targetPlatform); - if (artifact.packageName !== "opcore") { - throw new Error(`Managed tool descriptor graph native packageName for ${artifact.targetPlatform} must be opcore`); - } - if (artifact.bundledPackageName !== expectedBundledPackageName) { - throw new Error(`Managed tool descriptor graph native bundledPackageName for ${artifact.targetPlatform} must be ${expectedBundledPackageName}`); - } - if (artifact.binaryPath !== bundledGraphCoreNativePath(expectedBundledPackageName, "opcore-graph-core")) { - throw new Error("Managed tool descriptor graph native binaryPath must point at bundled opcore-graph-core"); - } - if (artifact.metadataPath !== bundledGraphCoreNativePath(expectedBundledPackageName, "metadata.json")) { - throw new Error("Managed tool descriptor graph native metadataPath must point at bundled metadata.json"); - } - if (artifact.checksumPath !== bundledGraphCoreNativePath(expectedBundledPackageName, "opcore-graph-core.sha256")) { - throw new Error("Managed tool descriptor graph native checksumPath must point at bundled opcore-graph-core.sha256"); - } - if (!artifact.artifactIds || typeof artifact.artifactIds !== "object") { - throw new Error("Managed tool descriptor graph native artifact ids are required"); - } - const suffix = artifact.targetPlatform; - if (artifact.artifactIds.binaryArtifactId !== `graph-core-binary-${suffix}`) { - throw new Error(`Managed tool descriptor graph native binary artifact id must be graph-core-binary-${suffix}`); - } - if (artifact.artifactIds.metadataArtifactId !== `graph-core-metadata-${suffix}`) { - throw new Error(`Managed tool descriptor graph native metadata artifact id must be graph-core-metadata-${suffix}`); - } - if (artifact.artifactIds.checksumArtifactId !== `graph-core-checksum-${suffix}`) { - throw new Error(`Managed tool descriptor graph native checksum artifact id must be graph-core-checksum-${suffix}`); - } - if (artifact.artifactIds.checksumId !== `graph-core-binary-sha256-${suffix}`) { - throw new Error(`Managed tool descriptor graph native checksum id must be graph-core-binary-sha256-${suffix}`); - } - } -} - -function validateManagedToolEditCapabilities(edit: ManagedToolDescriptorCapabilities["edit"]): void { - if (!edit || typeof edit !== "object") throw new Error("Managed tool descriptor edit capabilities are required"); - validateExactStringSet( - edit.commands, - ["exact", "multi", "search-replace", "patch", "tree", "rename", "move", "signature", "check", "apply"], - "Managed tool descriptor edit commands" - ); - validateExactStringSet(edit.safeEditModes, ["exact", "multi", "search-replace", "patch", "tree"], "Managed tool descriptor safe edit modes"); - validateExactStringSet(edit.symbolEditModes, ["rename", "move", "signature"], "Managed tool descriptor symbol edit modes"); - if (edit.validationRequiredForApply !== true) { - throw new Error("Managed tool descriptor edit validationRequiredForApply must be true"); - } - if (edit.dryRun !== true) throw new Error("Managed tool descriptor edit dryRun must be true"); -} - -function validateManagedToolValidationCapabilities(validation: ManagedToolDescriptorCapabilities["validation"]): void { - if (!validation || typeof validation !== "object") throw new Error("Managed tool descriptor validation capabilities are required"); - validateExactStringSet( - validation.checkRoutes, - ["files", "staged", "changed", "tree", "all", "manifest"], - "Managed tool descriptor check routes" - ); - validateExactStringSet(validation.validateRoutes, ["request", "hypothetical", "pre-write", "manifest"], "Managed tool descriptor validate routes"); - validateExactStringSet(validation.scopeModes, validationScopeKinds, "Managed tool descriptor validation scope modes"); - validateExactStringSet(validation.graphModes, graphProviderModes, "Managed tool descriptor validation graph modes"); - if (validation.hypothetical !== true) throw new Error("Managed tool descriptor validation hypothetical must be true"); - validateExactStringSet(validation.statusSurfaces, ["status", "doctor"], "Managed tool descriptor validation status surfaces"); - if (!validation.pythonProjectContext || typeof validation.pythonProjectContext !== "object") { - throw new Error("Managed tool descriptor Python project context capability is required"); - } - if (validation.pythonProjectContext.schemaId !== PYTHON_PROJECT_CONTEXT_SCHEMA_ID) { - throw new Error(`Managed tool descriptor Python project context schemaId must be ${PYTHON_PROJECT_CONTEXT_SCHEMA_ID}`); - } - validateExactStringSet( - validation.pythonProjectContext.outcomes, - pythonProjectContextOutcomes, - "Managed tool descriptor Python project context outcomes" - ); - if (validation.pythonProjectContext.readOnly !== true || validation.pythonProjectContext.installs !== false) { - throw new Error("Managed tool descriptor Python project context must be read-only and no-install"); - } - validateManagedToolValidationWriteGate(validation.writeGate); - validateValidationChecks(validation.checkIds, "Managed tool descriptor validation checkIds"); -} - -function validateManagedToolValidationWriteGate( - writeGate: ManagedToolDescriptorCapabilities["validation"]["writeGate"] -): void { - if (!writeGate || typeof writeGate !== "object") { - throw new Error("Managed tool descriptor validation writeGate is required"); - } - validateExactStringSet(writeGate.initScopes, opcoreInitScopes, "Managed tool descriptor writeGate initScopes"); - validateExactStringSet(writeGate.harnesses, ["claude-code", "codex"], "Managed tool descriptor writeGate harnesses"); - validateManagedToolPackagePath(writeGate.adapterPath, "Managed tool descriptor writeGate adapterPath"); - if (writeGate.adapterPath !== "dist/agent-gate.js") { - throw new Error("Managed tool descriptor writeGate adapterPath must be dist/agent-gate.js"); - } - validateExactStringSequence( - writeGate.validationCommand, - ["opcore", "validate", "pre-write", "--request-file", "", "--timeout-ms", "30000", "--json"], - "Managed tool descriptor writeGate validationCommand" - ); - if (writeGate.adapterErrorPolicy !== "fail_open") { - throw new Error("Managed tool descriptor writeGate adapterErrorPolicy must be fail_open"); - } - if (writeGate.validationErrorPolicy !== "fail_closed") { - throw new Error("Managed tool descriptor writeGate validationErrorPolicy must be fail_closed"); - } - if (writeGate.codexBoundary !== "pretooluse_guardrail") { - throw new Error("Managed tool descriptor writeGate codexBoundary must be pretooluse_guardrail"); - } -} - -interface ManagedToolDescriptorArtifactValidationState { - artifactIds: Set; - artifactChecksumRefs: readonly { artifactId: string; checksumRef: string }[]; -} - -function validateManagedToolArtifacts(artifacts: readonly ManagedToolDescriptorArtifactReference[]): ManagedToolDescriptorArtifactValidationState { - validateNonEmptyArray(artifacts, "Managed tool descriptor artifacts"); - const artifactIds = new Set(); - const artifactChecksumRefs: { artifactId: string; checksumRef: string }[] = []; - for (const artifact of artifacts) { - if (!artifact || typeof artifact !== "object") throw new Error("Managed tool descriptor artifact is required"); - validateNonEmptyString(artifact.id, "Managed tool descriptor artifact id"); - if (artifactIds.has(artifact.id)) throw new Error(`Managed tool descriptor artifact id must be unique: ${artifact.id}`); - artifactIds.add(artifact.id); - validateNonEmptyString(artifact.packageName, "Managed tool descriptor artifact packageName"); - validateManagedToolPackagePath(artifact.path, "Managed tool descriptor artifact path"); - if (!includesString(managedToolDescriptorArtifactTypes, artifact.type)) { - throw new Error(`Unknown managed tool descriptor artifact type: ${String(artifact.type)}`); - } - if (typeof artifact.required !== "boolean") throw new Error("Managed tool descriptor artifact required must be boolean"); - if (artifact.checksumRef !== undefined) { - validateNonEmptyString(artifact.checksumRef, "Managed tool descriptor artifact checksumRef"); - artifactChecksumRefs.push({ artifactId: artifact.id, checksumRef: artifact.checksumRef }); - } - } - - for (const target of graphCoreNativeSupportedTargets) { - const bundledPackageName = graphCoreNativePackageNameForTarget(target); - const binaryPath = bundledGraphCoreNativePath(bundledPackageName, "opcore-graph-core"); - const metadataPath = bundledGraphCoreNativePath(bundledPackageName, "metadata.json"); - const checksumPath = bundledGraphCoreNativePath(bundledPackageName, "opcore-graph-core.sha256"); - const binary = artifacts.find((artifact) => artifact.id === `graph-core-binary-${target}`); - const metadata = artifacts.find((artifact) => artifact.id === `graph-core-metadata-${target}`); - const checksum = artifacts.find((artifact) => artifact.id === `graph-core-checksum-${target}`); - if ( - !binary || - binary.packageName !== "opcore" || - binary.type !== "native_binary" || - !binary.required || - binary.path !== binaryPath || - binary.checksumRef !== `graph-core-binary-sha256-${target}` - ) { - throw new Error(`Managed tool descriptor must include graph native binary artifact for ${target}`); - } - if (!metadata || metadata.packageName !== "opcore" || metadata.type !== "manifest" || !metadata.required || metadata.path !== metadataPath) { - throw new Error(`Managed tool descriptor must include graph native metadata artifact for ${target}`); - } - if ( - !checksum || - checksum.packageName !== "opcore" || - checksum.type !== "checksum" || - !checksum.required || - checksum.path !== checksumPath - ) { - throw new Error(`Managed tool descriptor must include graph native checksum artifact for ${target}`); - } - } - if ( - !artifacts.some( - (artifact) => - artifact.id === "descriptor" && - artifact.packageName === "opcore" && - artifact.path === "dist/descriptors/opcore.managed-tool.json" && - artifact.type === "descriptor" && - artifact.required - ) - ) { - throw new Error("Managed tool descriptor must include packaged descriptor artifact"); - } - return { artifactIds, artifactChecksumRefs }; -} - -function validateManagedToolChecksums( - checksums: readonly ManagedToolDescriptorChecksumReference[], - artifactReferences: ManagedToolDescriptorArtifactValidationState -): void { - validateNonEmptyArray(checksums, "Managed tool descriptor checksums"); - const checksumIds = new Set(); - for (const checksum of checksums) { - if (!checksum || typeof checksum !== "object") throw new Error("Managed tool descriptor checksum is required"); - validateNonEmptyString(checksum.id, "Managed tool descriptor checksum id"); - if (checksumIds.has(checksum.id)) throw new Error(`Managed tool descriptor checksum id must be unique: ${checksum.id}`); - checksumIds.add(checksum.id); - validateNonEmptyString(checksum.packageName, "Managed tool descriptor checksum packageName"); - validateManagedToolPackagePath(checksum.path, "Managed tool descriptor checksum path"); - if (checksum.algorithm !== "sha256") throw new Error("Managed tool descriptor checksum algorithm must be sha256"); - validateNonEmptyString(checksum.artifactRef, "Managed tool descriptor checksum artifactRef"); - if (!artifactReferences.artifactIds.has(checksum.artifactRef)) { - throw new Error(`Managed tool descriptor checksum artifactRef must reference an artifact: ${checksum.artifactRef}`); - } - if (typeof checksum.required !== "boolean") throw new Error("Managed tool descriptor checksum required must be boolean"); - if (checksum.value !== undefined && !/^[a-f0-9]{64}$/i.test(checksum.value)) { - throw new Error("Managed tool descriptor checksum value must be a sha256 hex digest"); - } - } - for (const target of graphCoreNativeSupportedTargets) { - if (!checksums.some((checksum) => checksum.id === `graph-core-binary-sha256-${target}` && checksum.artifactRef === `graph-core-binary-${target}`)) { - throw new Error(`Managed tool descriptor must include graph native checksum reference for ${target}`); - } - } - for (const artifactChecksumRef of artifactReferences.artifactChecksumRefs) { - if (!checksumIds.has(artifactChecksumRef.checksumRef)) { - throw new Error( - `Managed tool descriptor artifact checksumRef must reference a checksum: ${artifactChecksumRef.artifactId} -> ${artifactChecksumRef.checksumRef}` - ); - } - } -} - -function validateManagedToolProvenanceHooks(provenanceHooks: readonly ManagedToolDescriptorProvenanceHook[]): void { - validateNonEmptyArray(provenanceHooks, "Managed tool descriptor provenance hooks"); - for (const hook of provenanceHooks) { - if (!hook || typeof hook !== "object") throw new Error("Managed tool descriptor provenance hook is required"); - validateNonEmptyString(hook.id, "Managed tool descriptor provenance hook id"); - validateStringArray(hook.command, "Managed tool descriptor provenance hook command", { allowEmpty: false }); - validateManagedToolCommandTokens(hook.command, "Managed tool descriptor provenance hook command"); - if (hook.expectedExitCode !== 0) throw new Error("Managed tool descriptor provenance hook expectedExitCode must be 0"); - } -} - -const managedToolPrivateRuntimePathPattern = /(?:^|\/)(?:\.agents|\.claude|\.codex|\.gemini|\.opencode)(?:\/|$)/; - -function normalizeManagedToolDescriptorString(value: string): string { - return value.replaceAll("\\", "/"); -} - -function validateManagedToolPackagePath(path: string, label: string): string { - const normalized = validateRepoRelativePath(path); - if (normalized === "~" || normalized.startsWith("~/")) { - throw new Error(`${label} must not reference private home paths`); - } - if (managedToolPrivateRuntimePathPattern.test(normalized)) { - throw new Error(`${label} must not reference private runtime paths`); - } - return normalized; -} - -function validateManagedToolCommandTokens(tokens: readonly string[], label: string): void { - for (const token of tokens) { - validateNonEmptyString(token, label); - const normalizedToken = normalizeManagedToolDescriptorString(token); - if (managedToolPrivateRuntimePathPattern.test(normalizedToken)) { - throw new Error("Managed tool descriptor must not reference private runtime paths"); - } - } -} - -function validateManagedToolDescriptorForbiddenStrings(value: unknown): void { - for (const text of collectStrings(value)) { - const normalizedText = normalizeManagedToolDescriptorString(text); - if (managedToolPrivateRuntimePathPattern.test(normalizedText)) { - throw new Error("Managed tool descriptor must not reference private runtime paths"); - } - if (normalizedText.includes("/Users/tom")) { - throw new Error("Managed tool descriptor must not reference private paths"); - } - } -} - -function sameStringArray(actual: readonly string[], expected: readonly string[]): boolean { - return actual.length === expected.length && actual.every((value, index) => value === expected[index]); -} - -function withoutUndefinedProperties>(value: T): Partial { - return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined)) as Partial; -} - -function validateCommandRouterManifestHeader(manifest: CommandRouterManifest): void { - if (!manifest || typeof manifest !== "object") { - throw new Error("Command router manifest is required"); - } - if (manifest.schemaVersion !== 1) { - throw new Error("Command router manifest schemaVersion must be 1"); - } - if (typeof manifest.packageName !== "string" || manifest.packageName.length === 0) { - throw new Error("Command router manifest packageName must be a non-empty string"); - } -} - -function validateManifestBins(bins: readonly string[]): void { - if (!Array.isArray(bins) || bins.length === 0) { - throw new Error("Command router manifest must include bins"); - } - for (const bin of bins) { - if (typeof bin !== "string" || bin.length === 0) { - throw new Error("Command router manifest bins must be non-empty strings"); - } - } -} - -function validateManifestGroups(commandGroups: readonly CommandGroupContract[]): Set { - const groupNames = new Set(); - for (const group of commandGroups) { - validateCommandOwner(group.owner); - validateNonEmptyString(group.name, "Command group name"); - validateStringArray(group.canonicalCommand, "Command group canonicalCommand", { allowEmpty: false }); - validateStringArray(group.commands, "Command group commands", { allowEmpty: false }); - validateNonEmptyString(group.summary, "Command group summary"); - groupNames.add(group.name); - } - - return groupNames; -} - -function validateManifestOwnershipBoundaries(boundaries: CommandRouterManifest["ownershipBoundaries"]): void { - for (const boundary of boundaries) { - validateCommandOwner(boundary.owner); - validateNonEmptyString(boundary.summary, "Command ownership boundary summary"); - } -} - -export function validateCommandRouterResult(result: CommandRouterResult): CommandRouterResult { - if (!result || typeof result !== "object") { - throw new Error("Command router result is required"); - } - if (result.schemaVersion !== 1) { - throw new Error("Command router result schemaVersion must be 1"); - } - validateNonEmptyString(result.bin, "Command router result bin"); - validateStringArray(result.argv, "Command router result argv", { allowEmpty: true, allowEmptyValues: true }); - validateStringArray(result.canonicalCommand, "Command router result canonicalCommand", { allowEmpty: false }); - validateCommandOwner(result.owner); - validateCommandRouteStatus(result.status); - validateExitCodeForStatus(result.exitCode, result.status); - validateNonEmptyString(result.message, "Command router result message"); - if (typeof result.json !== "boolean") { - throw new Error("Command router result json must be boolean"); - } - if (result.providerStatus !== undefined) validateProviderStatus(result.providerStatus); - if (result.graphPipeline !== undefined) validateGraphPipelineResult(result.graphPipeline); - if (result.graphQuery !== undefined) { - if (isNamedQueryResult(result.graphQuery)) validateGraphNamedQueryResult(result.graphQuery); - else validateGraphFactQueryResult(result.graphQuery); - } - if (result.graphSearch !== undefined) validateGraphSearchResult(result.graphSearch); - if (result.inspectResult !== undefined) validateInspectRouteResult(result.inspectResult); - if (result.graphImpact !== undefined) validateGraphImpactResult(result.graphImpact); - if (result.graphReviewContext !== undefined) validateGraphReviewContextResult(result.graphReviewContext); - if (result.graphChanges !== undefined) validateGraphDetectChangesResult(result.graphChanges); - if (result.graphServe !== undefined) validateGraphServeTransportStatus(result.graphServe); - if (result.validationResult !== undefined) validateValidationResultPayload(result.validationResult); - if (result.validationStatus !== undefined) validateValidationStatusPayload(result.validationStatus); - if (result.receipt !== undefined) validatePreWriteValidationReceipt(result.receipt); - if (result.editPlan !== undefined) validateEditPlanPayload(result.editPlan); - if (result.editResult !== undefined) validateEditCommandResult(result.editResult); - if (result.repoState !== undefined) validateOpcoreRepoStatePayload(result.repoState); - if (result.runtimeInfo !== undefined) validateOpcoreRuntimeInfoPayload(result.runtimeInfo); - if (result.opcoreDoctor !== undefined) validateOpcoreDoctorPayload(result.opcoreDoctor); - if (result.opcoreInit !== undefined) validateOpcoreInitPlanPayload(result.opcoreInit); - if (result.opcoreMeasure !== undefined) validateOpcoreMeasureDelta(result.opcoreMeasure); - if (result.opcoreTry !== undefined) validateOpcoreTryPayload(result.opcoreTry); - if (result.timing !== undefined) validateCommandTiming(result.timing); - if (result.repoState !== undefined && result.owner !== "runtime") { - throw new Error("Opcore repoState payload requires runtime owner"); - } - if (result.runtimeInfo !== undefined && result.owner !== "runtime") { - throw new Error("Opcore runtime info payload requires runtime owner"); - } - if (result.opcoreDoctor !== undefined && result.owner !== "runtime") { - throw new Error("Opcore doctor payload requires runtime owner"); - } - if (result.opcoreInit !== undefined && result.owner !== "runtime") { - throw new Error("Opcore init payload requires runtime owner"); - } - if (result.opcoreMeasure !== undefined && result.owner !== "runtime") { - throw new Error("Opcore measure payload requires runtime owner"); - } - if (result.opcoreTry !== undefined && result.owner !== "runtime") { - throw new Error("Opcore try payload requires runtime owner"); - } - if ((result.editPlan !== undefined || result.editResult !== undefined) && result.owner !== "edit") { - throw new Error("Edit router payloads require edit owner"); - } - if (result.owner === "edit" && result.status === "ok" && result.editPlan === undefined && result.editResult === undefined) { - const hiddenPayloadPattern = /"?(editPlan|editResult|planId|changes|afterState)"?\s*[:{[]/; - if (hiddenPayloadPattern.test(result.message)) { - throw new Error("Edit router payloads must use editPlan/editResult fields, not message strings"); - } - } - return result; -} - -export function validateOpcoreRepoStatePayload(payload: OpcoreRepoStatePayload): OpcoreRepoStatePayload { - if (!payload || typeof payload !== "object") { - throw new Error("Opcore repo state payload is required"); - } - if (payload.schemaVersion !== 1) { - throw new Error("Opcore repo state payload schemaVersion must be 1"); - } - if (!payload.repo || typeof payload.repo !== "object") { - throw new Error("Opcore repo state repo is required"); - } - validateNonEmptyString(payload.repo.root, "Opcore repo state repo root"); - validateNonEmptyString(payload.repo.requestedPath, "Opcore repo state requested path"); - if (!payload.repo.git || typeof payload.repo.git !== "object") { - throw new Error("Opcore repo state git payload is required"); - } - if (typeof payload.repo.git.available !== "boolean") { - throw new Error("Opcore repo state git available must be boolean"); - } - if (payload.repo.git.branch !== undefined) validateNonEmptyString(payload.repo.git.branch, "Opcore repo state git branch"); - for (const [key, value] of Object.entries(payload.repo.git)) { - if (key === "available" || key === "branch" || key === "clean") continue; - validateNonNegativeInteger(value, `Opcore repo state git ${key}`); - } - if (payload.repo.git.clean !== undefined && typeof payload.repo.git.clean !== "boolean") { - throw new Error("Opcore repo state git clean must be boolean"); - } - - if (!payload.coverage || typeof payload.coverage !== "object") { - throw new Error("Opcore repo state coverage is required"); - } - validateNonNegativeInteger(payload.coverage.totalFiles, "Opcore repo state coverage totalFiles"); - if (!Array.isArray(payload.coverage.languages)) { - throw new Error("Opcore repo state coverage languages must be an array"); - } - for (const language of payload.coverage.languages) { - validateNonEmptyString(language.language, "Opcore repo state language"); - validateNonNegativeInteger(language.files, "Opcore repo state language files"); - if (typeof language.graphSupported !== "boolean" || typeof language.validationSupported !== "boolean") { - throw new Error("Opcore repo state language support flags must be boolean"); - } - } - validateOpcoreCoverageCounts(payload.coverage.graph, "graph"); - validateOpcoreCoverageCounts(payload.coverage.validation, "validation"); - validateNonNegativeInteger(payload.coverage.validation.retainedFiles, "Opcore repo state validation retainedFiles"); - if (!payload.coverage.unsupported || typeof payload.coverage.unsupported !== "object") { - throw new Error("Opcore repo state unsupported coverage is required"); - } - validateNonNegativeInteger(payload.coverage.unsupported.totalFiles, "Opcore repo state unsupported totalFiles"); - if (!Array.isArray(payload.coverage.unsupported.stacks)) { - throw new Error("Opcore repo state unsupported stacks must be an array"); - } - for (const stack of payload.coverage.unsupported.stacks) { - validateNonEmptyString(stack.extension, "Opcore repo state unsupported extension"); - validateNonEmptyString(stack.language, "Opcore repo state unsupported language"); - validateNonNegativeInteger(stack.count, "Opcore repo state unsupported count"); - validateStringArray(stack.examples, "Opcore repo state unsupported examples", { allowEmpty: true }); - } - - if (!payload.graph || typeof payload.graph !== "object") { - throw new Error("Opcore repo state graph is required"); - } - if (!includesString(graphProviderStatusStates, payload.graph.state)) { - throw new Error(`Unknown Opcore repo state graph state: ${String(payload.graph.state)}`); - } - if (!includesString(graphProviderModes, payload.graph.mode)) { - throw new Error(`Unknown Opcore repo state graph mode: ${String(payload.graph.mode)}`); - } - validateNonEmptyString(payload.graph.provider, "Opcore repo state graph provider"); - validateNonEmptyString(payload.graph.action, "Opcore repo state graph action"); - if (payload.graph.message !== undefined) validateNonEmptyString(payload.graph.message, "Opcore repo state graph message"); - const graphStatus = validateProviderStatus(payload.graph.status); - if (graphStatus.state !== payload.graph.state || graphStatus.mode !== payload.graph.mode || graphStatus.provider !== payload.graph.provider) { - throw new Error("Opcore repo state graph summary must match provider status"); - } - - if (!payload.validation || typeof payload.validation !== "object") { - throw new Error("Opcore repo state validation is required"); - } - if (typeof payload.validation.ready !== "boolean") { - throw new Error("Opcore repo state validation ready must be boolean"); - } - validateNonNegativeInteger(payload.validation.checkCount, "Opcore repo state validation checkCount"); - validateOpcoreValidationPolicySummary(payload.validation.policy, "Opcore repo state validation policy"); - if (!Array.isArray(payload.validation.adapters)) { - throw new Error("Opcore repo state validation adapters must be an array"); - } - for (const adapter of payload.validation.adapters) { - validateNonEmptyString(adapter.adapter, "Opcore repo state validation adapter"); - if (!includesString(validationAdapterRuntimeStates, adapter.status)) { - throw new Error(`Unknown Opcore validation adapter status: ${String(adapter.status)}`); - } - validateNonNegativeInteger(adapter.checkCount, "Opcore repo state validation adapter checkCount"); - validateStringArray(adapter.degradedChecks, "Opcore repo state validation degradedChecks", { allowEmpty: true }); - validateStringArray(adapter.missingTools, "Opcore repo state validation missingTools", { allowEmpty: true }); - } - if (!Array.isArray(payload.validation.degradedToolchains)) { - throw new Error("Opcore repo state validation degradedToolchains must be an array"); - } - for (const tool of payload.validation.degradedToolchains) { - validateNonEmptyString(tool.adapter, "Opcore repo state validation degraded adapter"); - validateNonEmptyString(tool.tool, "Opcore repo state validation degraded tool"); - if (tool.failureMessage !== undefined) { - validateNonEmptyString(tool.failureMessage, "Opcore repo state validation degraded failureMessage"); - } - } - if (payload.validation.pythonProjectContexts !== undefined) { - validatePythonProjectContexts(payload.validation.pythonProjectContexts); - } - - if (!payload.activation || typeof payload.activation !== "object") { - throw new Error("Opcore repo state activation is required"); - } - if (typeof payload.activation.ready !== "boolean") { - throw new Error("Opcore repo state activation ready must be boolean"); - } - if (!includesString(["ready", "degraded", "blocked"] as const, payload.activation.level)) { - throw new Error(`Unknown Opcore activation level: ${String(payload.activation.level)}`); - } - validateNonEmptyString(payload.activation.summary, "Opcore repo state activation summary"); - if (!payload.activation.asp || typeof payload.activation.asp !== "object") { - throw new Error("Opcore repo state ASP status is required"); - } - if (!includesString(["enrolled", "not_enrolled"] as const, payload.activation.asp.state)) { - throw new Error(`Unknown Opcore ASP state: ${String(payload.activation.asp.state)}`); - } - validateStringArray(payload.activation.asp.paths, "Opcore repo state ASP paths", { allowEmpty: true }); - validateStringArray(payload.warnings, "Opcore repo state warnings", { allowEmpty: true }); - validateStringArray(payload.blockers, "Opcore repo state blockers", { allowEmpty: true }); - validateStringArray(payload.nextActions, "Opcore repo state nextActions", { allowEmpty: false }); - return payload; -} - -function validateOpcoreValidationPolicySummary( - summary: OpcoreValidationPolicySummary, - label: string -): OpcoreValidationPolicySummary { - if (!summary || typeof summary !== "object") { - throw new Error(`${label} is required`); - } - if (summary.path !== ".opcore/config") { - throw new Error(`${label} path must be .opcore/config`); - } - if (!includesString(["missing", "loaded"] as const, summary.state)) { - throw new Error(`Unknown ${label} state: ${String(summary.state)}`); - } - validateStringArray(summary.adapters, `${label} adapters`, { allowEmpty: true }); - validateStringArray(summary.packs, `${label} packs`, { allowEmpty: true }); - validateStringArray(summary.disabledChecks, `${label} disabledChecks`, { allowEmpty: true }); - validateStringArray(summary.defaultChecks, `${label} defaultChecks`, { allowEmpty: true }); - validateStringArray(summary.configuredChecks, `${label} configuredChecks`, { allowEmpty: true }); - return summary; -} - -export function validateOpcoreRuntimeInfoPayload(payload: OpcoreRuntimeInfoPayload): OpcoreRuntimeInfoPayload { - if (!payload || typeof payload !== "object") { - throw new Error("Opcore runtime info payload is required"); - } - if (payload.schemaVersion !== 1) { - throw new Error("Opcore runtime info schemaVersion must be 1"); - } - if (payload.packageName !== "opcore") { - throw new Error("Opcore runtime info packageName must be opcore"); - } - validateNonEmptyString(payload.version, "Opcore runtime info version"); - if (payload.bin !== "opcore") { - throw new Error("Opcore runtime info bin must be opcore"); - } - if (!includesString(opcoreRuntimeArtifactSources, payload.artifactSource)) { - throw new Error(`Unknown Opcore runtime artifact source: ${String(payload.artifactSource)}`); - } - validateNonEmptyString(payload.packageRoot, "Opcore runtime info packageRoot"); - validateNonEmptyString(payload.entrypoint, "Opcore runtime info entrypoint"); - return payload; -} - -export function validateOpcoreDoctorPayload(payload: OpcoreDoctorPayload): OpcoreDoctorPayload { - if (!payload || typeof payload !== "object") { - throw new Error("Opcore doctor payload is required"); - } - if (payload.schemaVersion !== 1) { - throw new Error("Opcore doctor payload schemaVersion must be 1"); - } - validateOpcoreRuntimeInfoPayload(payload.runtime); - if (!payload.repo || typeof payload.repo !== "object") { - throw new Error("Opcore doctor repo is required"); - } - validateNonEmptyString(payload.repo.root, "Opcore doctor repo root"); - validateNonEmptyString(payload.repo.requestedPath, "Opcore doctor repo requestedPath"); - if (!payload.config || typeof payload.config !== "object") { - throw new Error("Opcore doctor config is required"); - } - if (payload.config.path !== ".opcore/config") { - throw new Error("Opcore doctor config path must be .opcore/config"); - } - if (!includesString(["found", "missing", "unreadable"] as const, payload.config.state)) { - throw new Error(`Unknown Opcore doctor config state: ${String(payload.config.state)}`); - } - if (payload.config.message !== undefined) validateNonEmptyString(payload.config.message, "Opcore doctor config message"); - if (!payload.checks || typeof payload.checks !== "object") { - throw new Error("Opcore doctor checks are required"); - } - validateNonNegativeInteger(payload.checks.count, "Opcore doctor checks count"); - validateStringArray(payload.checks.ids, "Opcore doctor checks ids", { allowEmpty: false }); - if (payload.checks.ids.length !== payload.checks.count) { - throw new Error("Opcore doctor checks count must match ids length"); - } - validateOpcoreValidationPolicySummary(payload.policy, "Opcore doctor policy"); - validateProviderStatus(payload.graph); - if (!payload.generatedState || typeof payload.generatedState !== "object") { - throw new Error("Opcore doctor generatedState is required"); - } - validateStringArray(payload.generatedState.ignored, "Opcore doctor generatedState ignored", { allowEmpty: false }); - validateNonEmptyString(payload.generatedState.guidance, "Opcore doctor generatedState guidance"); - validateStringArray(payload.nextActions, "Opcore doctor nextActions", { allowEmpty: false }); - return payload; -} - -export function validateOpcoreInitPlanPayload(payload: OpcoreInitPlanPayload): OpcoreInitPlanPayload { - if (!payload || typeof payload !== "object") { - throw new Error("Opcore init payload is required"); - } - if (payload.schemaVersion !== 1) { - throw new Error("Opcore init payload schemaVersion must be 1"); - } - if (!includesString(["plan", "apply", "undo"] as const, payload.mode)) { - throw new Error(`Unknown Opcore init mode: ${String(payload.mode)}`); - } - if (typeof payload.approved !== "boolean") { - throw new Error("Opcore init approved must be boolean"); - } - if (payload.mode === "plan" && payload.approved) { - throw new Error("Opcore init approved plan must use apply mode"); - } - if (payload.mode === "apply" && !payload.approved) { - throw new Error("Opcore init apply mode requires approval"); - } - if (!payload.repo || typeof payload.repo !== "object") { - throw new Error("Opcore init repo is required"); - } - validateNonEmptyString(payload.repo.root, "Opcore init repo root"); - validateNonEmptyString(payload.repo.requestedPath, "Opcore init requested path"); - if (!payload.options || typeof payload.options !== "object") { - throw new Error("Opcore init options are required"); - } - if (!includesString(opcoreInitScopes, payload.options.scope)) { - throw new Error(`Unknown Opcore init scope: ${String(payload.options.scope)}`); - } - if (typeof payload.options.failClosedHook !== "boolean") { - throw new Error("Opcore init failClosedHook option must be boolean"); - } - if (typeof payload.options.dryRun !== "boolean") { - throw new Error("Opcore init dryRun option must be boolean"); - } - validateStringArray(payload.agentFiles, "Opcore init agentFiles", { allowEmpty: true }); - for (const agentFile of payload.agentFiles) validateRepoRelativePath(agentFile); - validateNonEmptyArray(payload.actions, "Opcore init actions"); - for (const action of payload.actions) validateOpcoreInitAction(action); - validateStringArray(payload.warnings, "Opcore init warnings", { allowEmpty: true }); - validateStringArray(payload.nextActions, "Opcore init nextActions", { allowEmpty: false }); - if (typeof payload.undoAvailable !== "boolean") { - throw new Error("Opcore init undoAvailable must be boolean"); - } - validateOpcoreInitScanSummary(payload.scan); - validateOpcoreInitSettings(payload.settings); - validateOpcoreInitInteraction(payload.interaction); - validateOpcoreInitTiming(payload.timings); - return payload; -} - -function validateOpcoreInitScanSummary(scan: OpcoreInitScanSummary): OpcoreInitScanSummary { - if (!scan || typeof scan !== "object") { - throw new Error("Opcore init scan summary is required"); - } - validateNonNegativeInteger(scan.totalFiles, "Opcore init scan totalFiles"); - validateNonNegativeInteger(scan.graphSupportedFiles, "Opcore init scan graphSupportedFiles"); - validateNonNegativeInteger(scan.validationSupportedFiles, "Opcore init scan validationSupportedFiles"); - validateNonNegativeInteger(scan.validationRetainedFiles, "Opcore init scan validationRetainedFiles"); - validateNonNegativeInteger(scan.unsupportedFiles, "Opcore init scan unsupportedFiles"); - validateOpcoreCoverageLanguages(scan.languages, "Opcore init scan"); - validateOpcoreUnsupportedStacks(scan.unsupportedStacks, "Opcore init scan"); - if (!Array.isArray(scan.degradedRustTools)) { - throw new Error("Opcore init scan degradedRustTools must be an array"); - } - for (const tool of scan.degradedRustTools) { - if (!tool || typeof tool !== "object") { - throw new Error("Opcore init scan degraded Rust tool is required"); - } - validateNonEmptyString(tool.adapter, "Opcore init scan degraded Rust adapter"); - validateNonEmptyString(tool.tool, "Opcore init scan degraded Rust tool"); - if (tool.failureMessage !== undefined) validateNonEmptyString(tool.failureMessage, "Opcore init scan degraded Rust failureMessage"); - } - validateNonNegativeInteger(scan.diagnosticCount, "Opcore init scan diagnosticCount"); - if (!includesString(validationResultStatuses, scan.validationStatus)) { - throw new Error(`Unknown Opcore init scan validationStatus: ${String(scan.validationStatus)}`); - } - validateStringArray(scan.failedChecks, "Opcore init scan failedChecks", { allowEmpty: true }); - if (!includesString(graphProviderStatusStates, scan.graphState)) { - throw new Error(`Unknown Opcore init scan graphState: ${String(scan.graphState)}`); - } - if (!includesString(["ready", "degraded", "blocked"] as const, scan.activationLevel)) { - throw new Error(`Unknown Opcore init scan activationLevel: ${String(scan.activationLevel)}`); - } - return scan; -} - -function validateOpcoreInitSettings(settings: OpcoreInitSettings): OpcoreInitSettings { - if (!settings || typeof settings !== "object") { - throw new Error("Opcore init settings are required"); - } - if (!Array.isArray(settings.languages)) { - throw new Error("Opcore init settings languages must be an array"); - } - for (const language of settings.languages) { - validateOpcoreInitLanguageSetting(language); - } - if (settings.python !== undefined) validateOpcoreInitPythonEnvironment(settings.python); - return settings; -} - -function validateOpcoreInitLanguageSetting(setting: OpcoreInitLanguageSetting): OpcoreInitLanguageSetting { - if (!setting || typeof setting !== "object") { - throw new Error("Opcore init language setting is required"); - } - validateNonEmptyString(setting.language, "Opcore init language setting language"); - validateNonNegativeInteger(setting.files, "Opcore init language setting files"); - if (!includesString(["supported", "retained", "unsupported", "degraded"] as const, setting.state)) { - throw new Error(`Unknown Opcore init language setting state: ${String(setting.state)}`); - } - if (!includesString(["supported", "unsupported"] as const, setting.graph)) { - throw new Error(`Unknown Opcore init language setting graph: ${String(setting.graph)}`); - } - if (!includesString(["supported", "retained", "unsupported", "degraded"] as const, setting.validation)) { - throw new Error(`Unknown Opcore init language setting validation: ${String(setting.validation)}`); - } - validateValidationChecks(setting.checks, "Opcore init language setting checks"); - validateStringArray(setting.notes, "Opcore init language setting notes", { allowEmpty: true }); - return setting; -} - -function validateOpcoreInitPythonEnvironment(environment: OpcoreInitPythonEnvironment): OpcoreInitPythonEnvironment { - if (!environment || typeof environment !== "object") { - throw new Error("Opcore init Python environment is required"); - } - if (!Array.isArray(environment.dependencyManagers)) { - throw new Error("Opcore init Python dependencyManagers must be an array"); - } - for (const manager of environment.dependencyManagers) { - if (!manager || typeof manager !== "object") { - throw new Error("Opcore init Python dependency manager is required"); - } - if (!includesString(["pyproject", "requirements", "pipfile", "poetry", "uv"] as const, manager.kind)) { - throw new Error(`Unknown Opcore init Python dependency manager kind: ${String(manager.kind)}`); - } - validateRepoRelativePath(manager.path); - } - if (!Array.isArray(environment.virtualEnvironments)) { - throw new Error("Opcore init Python virtualEnvironments must be an array"); - } - for (const virtualEnvironment of environment.virtualEnvironments) { - if (!virtualEnvironment || typeof virtualEnvironment !== "object") { - throw new Error("Opcore init Python virtual environment is required"); - } - if (virtualEnvironment.kind !== "venv") { - throw new Error(`Unknown Opcore init Python virtual environment kind: ${String(virtualEnvironment.kind)}`); - } - validateRepoRelativePath(virtualEnvironment.path); - } - validateStringArray(environment.notes, "Opcore init Python environment notes", { allowEmpty: true }); - if (environment.contexts !== undefined) validatePythonProjectContexts(environment.contexts); - return environment; -} - -function validateOpcoreInitInteraction(interaction: OpcoreInitInteraction): OpcoreInitInteraction { - if (!interaction || typeof interaction !== "object") { - throw new Error("Opcore init interaction is required"); - } - if (typeof interaction.tty !== "boolean") { - throw new Error("Opcore init interaction tty must be boolean"); - } - if (!includesString(["not_requested", "requested", "approved", "declined"] as const, interaction.promptState)) { - throw new Error(`Unknown Opcore init interaction promptState: ${String(interaction.promptState)}`); - } - return interaction; -} - -function validateOpcoreInitTiming(timing: OpcoreInitTiming): OpcoreInitTiming { - if (!timing || typeof timing !== "object") { - throw new Error("Opcore init timings are required"); - } - validateNonNegativeNumber(timing.scanMs, "Opcore init timing scanMs"); - validateNonNegativeNumber(timing.planMs, "Opcore init timing planMs"); - validateNonNegativeNumber(timing.promptMs, "Opcore init timing promptMs"); - validateNonNegativeNumber(timing.applyMs, "Opcore init timing applyMs"); - validateNonNegativeNumber(timing.totalMs, "Opcore init timing totalMs"); - validateNonNegativeNumber(timing.firstOutputMs, "Opcore init timing firstOutputMs"); - return timing; -} - -export function validateCommandTiming(timing: CommandTiming): CommandTiming { - assertNoOpaqueScoreFields(timing, "Command timing"); - assertNoTelemetrySourceFields(timing, "Command timing"); - if (!timing || typeof timing !== "object") { - throw new Error("Command timing is required"); - } - validateNonNegativeNumber(timing.durationMs, "Command timing durationMs"); - if (!Array.isArray(timing.phases)) { - throw new Error("Command timing phases must be an array"); - } - for (const phase of timing.phases) validateCommandTimingPhase(phase); - if (!includesString(commandTimingProcessStates, timing.processState)) { - throw new Error(`Unknown command timing processState: ${String(timing.processState)}`); - } - if (timing.degradations !== undefined) { - validateStringArray(timing.degradations, "Command timing degradations", { allowEmpty: true }); - for (const degradation of timing.degradations) { - if (!includesString(commandTimingDegradationReasons, degradation)) { - throw new Error(`Unknown command timing degradation: ${String(degradation)}`); - } - } - } - return timing; -} - -export function validateRepoShapeFingerprint(fingerprint: RepoShapeFingerprint): RepoShapeFingerprint { - assertNoOpaqueScoreFields(fingerprint, "Repo shape fingerprint"); - assertNoTelemetrySourceFields(fingerprint, "Repo shape fingerprint"); - if (!fingerprint || typeof fingerprint !== "object") { - throw new Error("Repo shape fingerprint is required"); - } - validateNonNegativeInteger(fingerprint.totalFiles, "Repo shape fingerprint totalFiles"); - if (!Array.isArray(fingerprint.languages)) { - throw new Error("Repo shape fingerprint languages must be an array"); - } - for (const language of fingerprint.languages) { - validateNonEmptyString(language.language, "Repo shape fingerprint language"); - validateNonNegativeInteger(language.files, "Repo shape fingerprint language files"); - } - if (!fingerprint.graph || typeof fingerprint.graph !== "object") { - throw new Error("Repo shape fingerprint graph is required"); - } - validateNonNegativeInteger(fingerprint.graph.supportedFiles, "Repo shape fingerprint graph supportedFiles"); - validateNonNegativeInteger(fingerprint.graph.unsupportedFiles, "Repo shape fingerprint graph unsupportedFiles"); - if (!fingerprint.git || typeof fingerprint.git !== "object") { - throw new Error("Repo shape fingerprint git is required"); - } - if (typeof fingerprint.git.available !== "boolean") { - throw new Error("Repo shape fingerprint git available must be boolean"); - } - if (fingerprint.git.clean !== undefined && typeof fingerprint.git.clean !== "boolean") { - throw new Error("Repo shape fingerprint git clean must be boolean"); - } - return fingerprint; -} - -export function validateCommandLatencyRecord(record: CommandLatencyRecord): CommandLatencyRecord { - assertNoOpaqueScoreFields(record, "Command latency record"); - assertNoTelemetrySourceFields(record, "Command latency record"); - if (!record || typeof record !== "object") { - throw new Error("Command latency record is required"); - } - if (record.schemaVersion !== 1) { - throw new Error("Command latency record schemaVersion must be 1"); - } - validateNonEmptyString(record.recordedAt, "Command latency record recordedAt"); - validateLatencyTelemetryCommandBin(record.bin, "Command latency record bin"); - validateLatencyCanonicalCommand(record.canonicalCommand, "Command latency record canonicalCommand"); - validateCommandOwner(record.owner); - const status = validateCommandRouteStatus(record.status); - validateExitCodeForStatus(record.exitCode, status); - validateRepoShapeFingerprint(record.repo); - validateCommandTiming(record.timing); - validateNonEmptyString(record.opcoreVersion, "Command latency record opcoreVersion"); - return record; -} - -export function validateLatencyBudget(budget: LatencyBudget): LatencyBudget { - assertNoOpaqueScoreFields(budget, "Latency budget"); - assertNoTelemetrySourceFields(budget, "Latency budget"); - if (!budget || typeof budget !== "object") { - throw new Error("Latency budget is required"); - } - if (budget.schemaVersion !== 1) { - throw new Error("Latency budget schemaVersion must be 1"); - } - validateLatencyCanonicalCommand(budget.canonicalCommand, "Latency budget canonicalCommand"); - validateLatencyStableId(budget.scope, "Latency budget scope"); - validateLatencyStableId(budget.repoShapeBucket, "Latency budget repoShapeBucket"); - validateNonNegativeNumber(budget.budgetMs, "Latency budget budgetMs"); - if (budget.phaseBudgets !== undefined) { - if (!Array.isArray(budget.phaseBudgets)) { - throw new Error("Latency budget phaseBudgets must be an array"); - } - const phases = new Set(); - for (const phaseBudget of budget.phaseBudgets) { - const validatedPhaseBudget = validateLatencyPhaseBudget(phaseBudget); - if (phases.has(validatedPhaseBudget.phase)) { - throw new Error("Latency budget phaseBudgets must not include duplicate phases"); - } - phases.add(validatedPhaseBudget.phase); - } - } - return budget; -} - -export function validateLatencyBudgetResult(result: LatencyBudgetResult): LatencyBudgetResult { - assertNoOpaqueScoreFields(result, "Latency budget result"); - assertNoTelemetrySourceFields(result, "Latency budget result"); - if (!result || typeof result !== "object") { - throw new Error("Latency budget result is required"); - } - if (result.schemaVersion !== 1) { - throw new Error("Latency budget result schemaVersion must be 1"); - } - if (!includesString(latencyBudgetResultStatuses, result.status)) { - throw new Error(`Unknown latency budget result status: ${String(result.status)}`); - } - const budget = validateLatencyBudget(result.budget); - if (!result.observed || typeof result.observed !== "object") { - throw new Error("Latency budget result observed is required"); - } - validateLatencyCanonicalCommand(result.observed.canonicalCommand, "Latency budget result observed canonicalCommand"); - validateLatencyStableId(result.observed.phase, "Latency budget result observed phase"); - validateNonNegativeNumber(result.observed.durationMs, "Latency budget result observed durationMs"); - if (!result.evidence || typeof result.evidence !== "object") { - throw new Error("Latency budget result evidence is required"); - } - validateLatencyCanonicalCommand(result.evidence.canonicalCommand, "Latency budget result evidence canonicalCommand"); - validateLatencyStableId(result.evidence.phase, "Latency budget result evidence phase"); - validateLatencyStableId(result.evidence.repoShapeBucket, "Latency budget result evidence repoShapeBucket"); - validateNonNegativeNumber(result.evidence.observedMs, "Latency budget result evidence observedMs"); - validateNonNegativeNumber(result.evidence.budgetMs, "Latency budget result evidence budgetMs"); - validateNonNegativeNumber(result.evidence.overByMs, "Latency budget result evidence overByMs"); - if (!sameStringArray(result.observed.canonicalCommand, result.evidence.canonicalCommand)) { - throw new Error("Latency budget result observed and evidence commands must match"); - } - if (!sameStringArray(budget.canonicalCommand, result.evidence.canonicalCommand)) { - throw new Error("Latency budget result evidence command must match budget command"); - } - if (result.observed.phase !== result.evidence.phase) { - throw new Error("Latency budget result observed and evidence phases must match"); - } - if (budget.repoShapeBucket !== result.evidence.repoShapeBucket) { - throw new Error("Latency budget result evidence bucket must match budget bucket"); - } - if (result.observed.durationMs !== result.evidence.observedMs) { - throw new Error("Latency budget result observed duration must match evidence observedMs"); - } - const appliedBudgetMs = resolveLatencyAppliedBudgetMs(budget, result.evidence.phase); - if (result.evidence.budgetMs !== appliedBudgetMs) { - throw new Error("Latency budget result evidence budgetMs must match the applied budget"); - } - const computedOverByMs = Math.max(0, result.evidence.observedMs - appliedBudgetMs); - if (result.evidence.overByMs !== computedOverByMs) { - throw new Error("Latency budget result overByMs must equal observedMs over budgetMs"); - } - if (result.status === "pass" && result.evidence.overByMs !== 0) { - throw new Error("Latency budget pass result must not exceed budget"); - } - if (result.status === "over" && result.evidence.overByMs <= 0) { - throw new Error("Latency budget over result must exceed budget"); - } - return result; -} - -function validateOpcoreInitAction(action: OpcoreInitAction): OpcoreInitAction { - if (!action || typeof action !== "object") { - throw new Error("Opcore init action is required"); - } - if (!includesString(["write", "upsert_block", "create_hook", "wire_harness", "restore", "remove"] as const, action.kind)) { - throw new Error(`Unknown Opcore init action kind: ${String(action.kind)}`); - } - if (!includesString(opcoreInitScopes, action.targetScope)) { - throw new Error(`Unknown Opcore init action targetScope: ${String(action.targetScope)}`); - } - const rawPath = validateNonEmptyString(action.path, "Opcore init action path"); - const path = action.targetScope === "global" ? validateHomeRelativePath(rawPath) : validateRepoRelativePath(rawPath); - validateNonEmptyString(action.summary, "Opcore init action summary"); - if (typeof action.requiresApproval !== "boolean") { - throw new Error("Opcore init action requiresApproval must be boolean"); - } - if (typeof action.outsideOpcore !== "boolean") { - throw new Error("Opcore init action outsideOpcore must be boolean"); - } - const insideOpcore = action.targetScope === "global" - ? path === "~/.opcore" || path.startsWith("~/.opcore/") - : path === ".opcore" || path.startsWith(".opcore/"); - if (action.outsideOpcore === insideOpcore) { - throw new Error("Opcore init action outsideOpcore must match action path"); - } - if (action.outsideOpcore && !action.requiresApproval) { - throw new Error("Opcore init action outside .opcore requires approval"); - } - return action; -} - -export function validateOpcoreMetricReport(report: OpcoreMetricReport): OpcoreMetricReport { - assertNoOpaqueScoreFields(report, "Opcore metric report"); - if (!report || typeof report !== "object") { - throw new Error("Opcore metric report is required"); - } - if (report.schemaVersion !== 1) { - throw new Error("Opcore metric report schemaVersion must be 1"); - } - if (report.kind !== "opcore_metric_report") { - throw new Error("Opcore metric report kind must be opcore_metric_report"); - } - validateNonEmptyString(report.generatedAt, "Opcore metric report generatedAt"); - if (!report.repo || typeof report.repo !== "object") { - throw new Error("Opcore metric report repo is required"); - } - validateNonEmptyString(report.repo.root, "Opcore metric report repo root"); - validateNonEmptyString(report.repo.requestedPath, "Opcore metric report repo requestedPath"); - if (!report.repo.git || typeof report.repo.git !== "object") { - throw new Error("Opcore metric report repo git is required"); - } - if (typeof report.repo.git.available !== "boolean") { - throw new Error("Opcore metric report repo git available must be boolean"); - } - if (report.repo.git.branch !== undefined) validateNonEmptyString(report.repo.git.branch, "Opcore metric report git branch"); - for (const [key, value] of Object.entries(report.repo.git)) { - if (key === "available" || key === "branch" || key === "clean") continue; - validateNonNegativeInteger(value, `Opcore metric report git ${key}`); - } - if (report.repo.git.clean !== undefined && typeof report.repo.git.clean !== "boolean") { - throw new Error("Opcore metric report git clean must be boolean"); - } - validateOpcoreMetricCoverage(report.coverage, "Opcore metric report coverage"); - if (!report.graph || typeof report.graph !== "object") { - throw new Error("Opcore metric report graph is required"); - } - if (!includesString(graphProviderStatusStates, report.graph.state)) { - throw new Error(`Unknown Opcore metric graph state: ${String(report.graph.state)}`); - } - if (!includesString(graphProviderModes, report.graph.mode)) { - throw new Error(`Unknown Opcore metric graph mode: ${String(report.graph.mode)}`); - } - validateNonEmptyString(report.graph.provider, "Opcore metric report graph provider"); - if (!report.validation || typeof report.validation !== "object") { - throw new Error("Opcore metric report validation is required"); - } - if (report.validation.status !== undefined && !includesString(validationResultStatuses, report.validation.status)) { - throw new Error(`Unknown Opcore metric validation status: ${String(report.validation.status)}`); - } - validateNonNegativeInteger(report.validation.diagnosticCount, "Opcore metric report diagnosticCount"); - validateNonNegativeInteger(report.validation.checkCount, "Opcore metric report checkCount"); - if (report.validation.policy !== undefined) { - validateOpcoreValidationPolicySummary(report.validation.policy, "Opcore metric report validation policy"); - } - if (report.validation.pythonProjectContexts !== undefined) { - validatePythonProjectContexts(report.validation.pythonProjectContexts); - } - if (!Array.isArray(report.signals)) { - throw new Error("Opcore metric report signals must be an array"); - } - for (const signal of report.signals) validateOpcoreMetricSignal(signal); - if (!Array.isArray(report.degradations)) { - throw new Error("Opcore metric report degradations must be an array"); - } - for (const degradation of report.degradations) validateOpcoreMetricDegradation(degradation); - validateStringArray(report.warnings, "Opcore metric report warnings", { allowEmpty: true }); - validateStringArray(report.nextActions, "Opcore metric report nextActions", { allowEmpty: false }); - return report; -} - -export function validateOpcoreMetricHistoryEntry(entry: OpcoreMetricHistoryEntry): OpcoreMetricHistoryEntry { - assertNoOpaqueScoreFields(entry, "Opcore metric history entry"); - if (!entry || typeof entry !== "object") { - throw new Error("Opcore metric history entry is required"); - } - if (entry.schemaVersion !== 1) { - throw new Error("Opcore metric history entry schemaVersion must be 1"); - } - if (entry.kind !== "opcore_metric_history_entry") { - throw new Error("Opcore metric history entry kind must be opcore_metric_history_entry"); - } - validateNonEmptyString(entry.recordedAt, "Opcore metric history entry recordedAt"); - validateOpcoreMetricReport(entry.report); - return entry; -} - -export function validateOpcoreMeasureDelta(delta: OpcoreMeasureDelta): OpcoreMeasureDelta { - assertNoOpaqueScoreFields(delta, "Opcore measure delta"); - if (!delta || typeof delta !== "object") { - throw new Error("Opcore measure delta is required"); - } - if (delta.schemaVersion !== 1) { - throw new Error("Opcore measure delta schemaVersion must be 1"); - } - if (delta.kind !== "opcore_measure_delta") { - throw new Error("Opcore measure delta kind must be opcore_measure_delta"); - } - validateNonEmptyString(delta.generatedAt, "Opcore measure delta generatedAt"); - if (!delta.current || typeof delta.current !== "object") { - throw new Error("Opcore measure delta current is required"); - } - validateNonEmptyString(delta.current.generatedAt, "Opcore measure delta current generatedAt"); - validateOpcoreMetricCoverage(delta.current.coverage, "Opcore measure delta current coverage"); - validateOpcoreMeasureSignalCounts(delta.current.signals, "Opcore measure delta current signals"); - if (delta.latency !== undefined) validateOpcoreMeasureLatencyReport(delta.latency); - if (delta.baseline !== undefined) validateOpcoreMeasureComparison(delta.baseline, "baseline"); - if (delta.previous !== undefined) validateOpcoreMeasureComparison(delta.previous, "previous"); - validateStringArray(delta.warnings, "Opcore measure delta warnings", { allowEmpty: true }); - if (!Array.isArray(delta.degradations)) { - throw new Error("Opcore measure delta degradations must be an array"); - } - for (const degradation of delta.degradations) validateOpcoreMetricDegradation(degradation); - validateStringArray(delta.nextActions, "Opcore measure delta nextActions", { allowEmpty: false }); - return delta; -} - -export function validateOpcoreTryPayload(payload: OpcoreTryPayload): OpcoreTryPayload { - assertNoOpaqueScoreFields(payload, "Opcore try payload"); - if (!payload || typeof payload !== "object") { - throw new Error("Opcore try payload is required"); - } - if (payload.schemaVersion !== 1) { - throw new Error("Opcore try payload schemaVersion must be 1"); - } - validateNonEmptyString(payload.sampleRoot, "Opcore try sampleRoot"); - if (payload.published !== false) { - throw new Error("Opcore try published must be false"); - } - validateNonEmptyArray(payload.scenarios, "Opcore try scenarios"); - for (const scenario of payload.scenarios) validateOpcoreTryScenario(scenario); - validateNonEmptyArray(payload.commands, "Opcore try commands"); - for (const command of payload.commands) validateOpcoreTryCommand(command); - return payload; -} - -function validateOpcoreTryScenario(scenario: OpcoreTryScenario): OpcoreTryScenario { - if (!scenario || typeof scenario !== "object") { - throw new Error("Opcore try scenario is required"); - } - validateNonEmptyString(scenario.id, "Opcore try scenario id"); - validateNonEmptyString(scenario.repoRoot, "Opcore try scenario repoRoot"); - validateNonEmptyString(scenario.title, "Opcore try scenario title"); - validateStringArray(scenario.commands, "Opcore try scenario commands", { allowEmpty: false }); - if (!scenario.coverage || typeof scenario.coverage !== "object") { - throw new Error("Opcore try scenario coverage is required"); - } - validateNonNegativeInteger(scenario.coverage.totalFiles, "Opcore try scenario totalFiles"); - validateNonNegativeInteger(scenario.coverage.validationSupportedFiles, "Opcore try scenario validationSupportedFiles"); - validateNonNegativeInteger(scenario.coverage.unsupportedFiles, "Opcore try scenario unsupportedFiles"); - if (!Array.isArray(scenario.signals)) { - throw new Error("Opcore try scenario signals must be an array"); - } - for (const signal of scenario.signals) validateOpcoreTrySignal(signal); - return scenario; -} - -function validateOpcoreTrySignal(signal: OpcoreTrySignalSummary): OpcoreTrySignalSummary { - if (!signal || typeof signal !== "object") { - throw new Error("Opcore try signal is required"); - } - validateNonEmptyString(signal.id, "Opcore try signal id"); - validateNonEmptyString(signal.title, "Opcore try signal title"); - validateNonNegativeInteger(signal.count, "Opcore try signal count"); - if (!Number.isInteger(signal.delta)) { - throw new Error("Opcore try signal delta must be an integer"); - } - return signal; -} - -function validateOpcoreTryCommand(command: OpcoreTryCommandSummary): OpcoreTryCommandSummary { - if (!command || typeof command !== "object") { - throw new Error("Opcore try command is required"); - } - validateNonEmptyString(command.scenarioId, "Opcore try command scenarioId"); - validateStringArray(command.command, "Opcore try command", { allowEmpty: false }); - validateStringArray(command.canonicalCommand, "Opcore try command canonicalCommand", { allowEmpty: false }); - validateCommandOwner(command.owner); - validateCommandRouteStatus(command.status); - validateExitCodeForStatus(command.exitCode, command.status); - return command; -} - -function validateOpcoreMetricCoverage(coverage: OpcoreRepoStatePayload["coverage"], label: string): void { - if (!coverage || typeof coverage !== "object") { - throw new Error(`${label} is required`); - } - validateNonNegativeInteger(coverage.totalFiles, `${label} totalFiles`); - validateOpcoreCoverageLanguages(coverage.languages, label); - validateOpcoreCoverageCounts(coverage.graph, "graph"); - validateOpcoreCoverageCounts(coverage.validation, "validation"); - validateNonNegativeInteger(coverage.validation.retainedFiles, `${label} validation retainedFiles`); - validateOpcoreUnsupportedSection(coverage.unsupported, label); -} - -function validateOpcoreCoverageLanguages( - languages: OpcoreRepoStatePayload["coverage"]["languages"], - label: string -): void { - if (!Array.isArray(languages)) { - throw new Error(`${label} languages must be an array`); - } - for (const language of languages) { - validateNonEmptyString(language.language, `${label} language`); - validateNonNegativeInteger(language.files, `${label} language files`); - if (typeof language.graphSupported !== "boolean" || typeof language.validationSupported !== "boolean") { - throw new Error(`${label} language support flags must be boolean`); - } - } -} - -function validateOpcoreUnsupportedSection( - unsupported: OpcoreRepoStatePayload["coverage"]["unsupported"], - label: string -): void { - if (!unsupported || typeof unsupported !== "object") { - throw new Error(`${label} unsupported is required`); - } - validateNonNegativeInteger(unsupported.totalFiles, `${label} unsupported totalFiles`); - validateOpcoreUnsupportedStacks(unsupported.stacks, label); -} - -function validateOpcoreUnsupportedStacks( - stacks: OpcoreRepoStatePayload["coverage"]["unsupported"]["stacks"], - label: string -): void { - if (!Array.isArray(stacks)) { - throw new Error(`${label} unsupported stacks must be an array`); - } - for (const stack of stacks) { - validateNonEmptyString(stack.extension, `${label} unsupported extension`); - validateNonEmptyString(stack.language, `${label} unsupported language`); - validateNonNegativeInteger(stack.count, `${label} unsupported count`); - validateStringArray(stack.examples, `${label} unsupported examples`, { allowEmpty: true }); - } -} - -function validateOpcoreMetricSignal(signal: OpcoreMetricSignal): OpcoreMetricSignal { - if (!signal || typeof signal !== "object") { - throw new Error("Opcore metric signal is required"); - } - validateNonEmptyString(signal.id, "Opcore metric signal id"); - validateNonEmptyString(signal.title, "Opcore metric signal title"); - validateNonEmptyString(signal.category, "Opcore metric signal category"); - if (!includesString(["info", "warning", "error"] as const, signal.severity)) { - throw new Error(`Unknown Opcore metric signal severity: ${String(signal.severity)}`); - } - if (!Number.isInteger(signal.count) || signal.count <= 0) { - throw new Error("Opcore metric signal count must be a positive integer"); - } - if (!Array.isArray(signal.evidence) || signal.evidence.length === 0) { - throw new Error("Opcore metric signal evidence must be a non-empty array"); - } - for (const evidence of signal.evidence) validateOpcoreMetricEvidence(evidence); - return signal; -} - -function validateOpcoreMetricEvidence(evidence: OpcoreMetricEvidence): OpcoreMetricEvidence { - if (!evidence || typeof evidence !== "object") { - throw new Error("Opcore metric evidence is required"); - } - validateNonEmptyString(evidence.source, "Opcore metric evidence source"); - validateRepoRelativePath(validateNonEmptyString(evidence.path, "Opcore metric evidence path")); - validateNonEmptyString(evidence.message, "Opcore metric evidence message"); - if (evidence.checkId !== undefined) validateValidationCheckId(evidence.checkId, "Opcore metric evidence checkId"); - if (evidence.code !== undefined) validateNonEmptyString(evidence.code, "Opcore metric evidence code"); - if (evidence.line !== undefined && (!Number.isInteger(evidence.line) || evidence.line < 1)) { - throw new Error("Opcore metric evidence line must be a positive integer"); - } - if (evidence.column !== undefined && (!Number.isInteger(evidence.column) || evidence.column < 1)) { - throw new Error("Opcore metric evidence column must be a positive integer"); - } - return evidence; -} - -function validateOpcoreMetricDegradation(degradation: OpcoreMetricDegradation): OpcoreMetricDegradation { - if (!degradation || typeof degradation !== "object") { - throw new Error("Opcore metric degradation is required"); - } - validateNonEmptyString(degradation.id, "Opcore metric degradation id"); - validateNonEmptyString(degradation.title, "Opcore metric degradation title"); - validateNonEmptyString(degradation.source, "Opcore metric degradation source"); - if (!includesString(["info", "warning", "error"] as const, degradation.severity)) { - throw new Error(`Unknown Opcore metric degradation severity: ${String(degradation.severity)}`); - } - validateNonEmptyString(degradation.message, "Opcore metric degradation message"); - if (degradation.checkId !== undefined) validateValidationCheckId(degradation.checkId, "Opcore metric degradation checkId"); - if (degradation.requiredTool !== undefined) { - validateNonEmptyString(degradation.requiredTool, "Opcore metric degradation requiredTool"); - } - return degradation; -} - -function validateOpcoreMeasureLatencyReport(report: OpcoreMeasureLatencyReport): OpcoreMeasureLatencyReport { - assertNoOpaqueScoreFields(report, "Opcore measure latency report"); - assertNoTelemetrySourceFields(report, "Opcore measure latency report"); - if (!report || typeof report !== "object") { - throw new Error("Opcore measure latency report is required"); - } - if (report.kind !== "opcore_latency_report") { - throw new Error("Opcore measure latency report kind must be opcore_latency_report"); - } - validateNonNegativeInteger(report.recordCount, "Opcore measure latency report recordCount"); - validateNonNegativeInteger(report.budgetCount, "Opcore measure latency report budgetCount"); - if (!Array.isArray(report.findings)) { - throw new Error("Opcore measure latency report findings must be an array"); - } - for (const finding of report.findings) validateOpcoreMeasureLatencyFinding(finding); - return report; -} - -function validateOpcoreMeasureLatencyFinding(finding: OpcoreMeasureLatencyFinding): OpcoreMeasureLatencyFinding { - assertNoOpaqueScoreFields(finding, "Opcore measure latency finding"); - assertNoTelemetrySourceFields(finding, "Opcore measure latency finding"); - if (!finding || typeof finding !== "object") { - throw new Error("Opcore measure latency finding is required"); - } - validateLatencyCanonicalCommand(finding.canonicalCommand, "Opcore measure latency finding canonicalCommand"); - validateLatencyStableId(finding.repoShapeBucket, "Opcore measure latency finding repoShapeBucket"); - if (!includesString(commandTimingProcessStates, finding.processState)) { - throw new Error(`Unknown Opcore measure latency processState: ${String(finding.processState)}`); - } - if (!includesString(opcoreMeasureLatencyFindingStatuses, finding.status)) { - throw new Error(`Unknown Opcore measure latency status: ${String(finding.status)}`); - } - validateNonNegativeNumber(finding.currentDurationMs, "Opcore measure latency finding currentDurationMs"); - if (finding.dominantPhase !== undefined) validateOpcoreMeasureLatencyPhase(finding.dominantPhase); - if (finding.baselineDurationMs !== undefined) validateNonNegativeNumber(finding.baselineDurationMs, "Opcore measure latency finding baselineDurationMs"); - if (finding.previousDurationMs !== undefined) validateNonNegativeNumber(finding.previousDurationMs, "Opcore measure latency finding previousDurationMs"); - if (finding.baselineDeltaMs !== undefined && !Number.isFinite(finding.baselineDeltaMs)) { - throw new Error("Opcore measure latency finding baselineDeltaMs must be a finite number"); - } - if (finding.previousDeltaMs !== undefined && !Number.isFinite(finding.previousDeltaMs)) { - throw new Error("Opcore measure latency finding previousDeltaMs must be a finite number"); - } - if (finding.budgetMs !== undefined) validateNonNegativeNumber(finding.budgetMs, "Opcore measure latency finding budgetMs"); - if (finding.overBudgetMs !== undefined) validateNonNegativeNumber(finding.overBudgetMs, "Opcore measure latency finding overBudgetMs"); - if (finding.status === "over_budget" && (finding.overBudgetMs ?? 0) <= 0) { - throw new Error("Opcore measure latency over_budget finding must include overBudgetMs"); - } - return finding; -} - -function validateOpcoreMeasureLatencyPhase(phase: OpcoreMeasureLatencyPhase): OpcoreMeasureLatencyPhase { - if (!phase || typeof phase !== "object") { - throw new Error("Opcore measure latency phase is required"); - } - validateLatencyStableId(phase.phase, "Opcore measure latency phase"); - validateNonNegativeNumber(phase.durationMs, "Opcore measure latency phase durationMs"); - return phase; -} - -function validateOpcoreMeasureComparison(comparison: OpcoreMeasureComparison, label: string): OpcoreMeasureComparison { - if (!comparison || typeof comparison !== "object") { - throw new Error(`Opcore measure delta ${label} comparison is required`); - } - validateNonEmptyString(comparison.recordedAt, `Opcore measure delta ${label} recordedAt`); - validateNonEmptyString(comparison.generatedAt, `Opcore measure delta ${label} generatedAt`); - validateOpcoreMetricCoverage(comparison.coverage, `Opcore measure delta ${label} coverage`); - validateOpcoreMeasureSignalCounts(comparison.signals, `Opcore measure delta ${label} signals`); - if (!Array.isArray(comparison.deltas)) { - throw new Error(`Opcore measure delta ${label} deltas must be an array`); - } - for (const entry of comparison.deltas) validateOpcoreMeasureSignalDelta(entry, label); - return comparison; -} - -function validateOpcoreMeasureSignalCounts(counts: readonly OpcoreMeasureSignalCount[], label: string): void { - if (!Array.isArray(counts)) { - throw new Error(`${label} must be an array`); - } - for (const count of counts) { - if (!count || typeof count !== "object") { - throw new Error(`${label} entry is required`); - } - validateNonEmptyString(count.id, `${label} id`); - validateNonEmptyString(count.title, `${label} title`); - validateNonNegativeInteger(count.count, `${label} count`); - } -} - -function validateOpcoreMeasureSignalDelta(delta: OpcoreMeasureSignalDelta, label: string): OpcoreMeasureSignalDelta { - if (!delta || typeof delta !== "object") { - throw new Error(`Opcore measure delta ${label} entry is required`); - } - validateNonEmptyString(delta.id, `Opcore measure delta ${label} id`); - validateNonEmptyString(delta.title, `Opcore measure delta ${label} title`); - validateNonNegativeInteger(delta.currentCount, `Opcore measure delta ${label} currentCount`); - validateNonNegativeInteger(delta.comparisonCount, `Opcore measure delta ${label} comparisonCount`); - if (!Number.isInteger(delta.delta)) { - throw new Error(`Opcore measure delta ${label} delta must be an integer`); - } - return delta; -} - -function assertNoOpaqueScoreFields(value: unknown, label: string): void { - if (!value || typeof value !== "object") return; - if (Array.isArray(value)) { - for (const entry of value) assertNoOpaqueScoreFields(entry, label); - return; - } - for (const [key, entry] of Object.entries(value)) { - if (key === "score" || key === "blendedScore") { - throw new Error(`${label} must not include opaque score fields`); - } - assertNoOpaqueScoreFields(entry, label); - } -} - -function assertNoTelemetrySourceFields(value: unknown, label: string): void { - const blockedKeys = new Set([ - "root", - "requestedPath", - "path", - "paths", - "examples", - "content", - "contents", - "source", - "secret", - "secrets", - "token", - "tokens", - "apiKey", - "password" - ]); - visitTelemetryValue(value); - - function visitTelemetryValue(entry: unknown): void { - if (!entry || typeof entry !== "object") return; - if (Array.isArray(entry)) { - for (const item of entry) visitTelemetryValue(item); - return; - } - for (const [key, child] of Object.entries(entry)) { - if (blockedKeys.has(key)) { - throw new Error(`${label} must remain source-safe and must not include ${key}`); - } - visitTelemetryValue(child); - } - } -} - -function validateCommandTimingPhase(phase: CommandTimingPhase): CommandTimingPhase { - if (!phase || typeof phase !== "object") { - throw new Error("Command timing phase is required"); - } - validateLatencyStableId(phase.phase, "Command timing phase"); - validateNonNegativeNumber(phase.durationMs, "Command timing phase durationMs"); - if (phase.fileCount !== undefined) validateNonNegativeInteger(phase.fileCount, "Command timing phase fileCount"); - return phase; -} - -function validateLatencyPhaseBudget(phaseBudget: LatencyPhaseBudget): LatencyPhaseBudget { - if (!phaseBudget || typeof phaseBudget !== "object") { - throw new Error("Latency phase budget is required"); - } - validateLatencyStableId(phaseBudget.phase, "Latency phase budget phase"); - validateNonNegativeNumber(phaseBudget.budgetMs, "Latency phase budget budgetMs"); - return phaseBudget; -} - -function resolveLatencyAppliedBudgetMs(budget: LatencyBudget, phase: string): number { - if (phase === "total") return budget.budgetMs; - const phaseBudget = budget.phaseBudgets?.find((entry) => entry.phase === phase); - if (!phaseBudget) { - throw new Error("Latency budget result phase must match total or a configured phase budget"); - } - return phaseBudget.budgetMs; -} - -function validateLatencyStableId(value: unknown, label: string): string { - const stableId = validateNonEmptyString(value, label); - if (!latencyStableIdRegex.test(stableId)) { - throw new Error(`${label} must be a stable latency id`); - } - return stableId; -} - -function validateLatencyTelemetryCommandBin(value: unknown, label: string): CommandLatencyTelemetryBin { - const bin = validateNonEmptyString(value, label); - if (!includesString(commandLatencyTelemetryBins, bin)) { - throw new Error(`${label} must be a source-safe command bin`); - } - return bin; -} - -function validateLatencyCanonicalCommand(command: readonly string[], label: string): readonly string[] { - const parts = validateStringArray(command, label, { allowEmpty: false }); - for (const [index, part] of parts.entries()) { - validateLatencyCanonicalCommandToken(part, `${label} entry ${index}`); - } - return parts; -} - -function validateLatencyCanonicalCommandToken(value: string, label: string): string { - if (!latencyTelemetryCommandTokenRegex.test(value)) { - throw new Error(`${label} must be a source-safe canonicalCommand token`); - } - if ( - value.includes("/") || - value.includes("\\") || - value === "." || - value === ".." || - value.startsWith("~") || - /^[A-Za-z]:/.test(value) || - /^file:/i.test(value) || - latencyTelemetrySourceFileExtensionRegex.test(value) - ) { - throw new Error(`${label} must be a source-safe canonicalCommand token`); - } - return value; -} - -function validateOpcoreCoverageCounts( - section: - | OpcoreRepoStatePayload["coverage"]["graph"] - | OpcoreRepoStatePayload["coverage"]["validation"], - label: string -): void { - if (!section || typeof section !== "object") { - throw new Error(`Opcore repo state ${label} coverage is required`); - } - validateNonNegativeInteger(section.supportedFiles, `Opcore repo state ${label} supportedFiles`); - if (!Array.isArray(section.extensions)) { - throw new Error(`Opcore repo state ${label} extensions must be an array`); - } - for (const entry of section.extensions) { - validateNonEmptyString(entry.extension, `Opcore repo state ${label} extension`); - validateNonNegativeInteger(entry.count, `Opcore repo state ${label} count`); - } -} - -export function validateCommandAdapterRequest(request: CommandAdapterRequest): CommandAdapterRequest { - if (!request || typeof request !== "object") { - throw new Error("Command adapter request is required"); - } - if (request.schemaVersion !== 1) { - throw new Error("Command adapter request schemaVersion must be 1"); - } - validateNonEmptyString(request.bin, "Command adapter request bin"); - validateStringArray(request.argv, "Command adapter request argv", { allowEmpty: true, allowEmptyValues: true }); - validateStringArray(request.args, "Command adapter request args", { allowEmpty: true, allowEmptyValues: true }); - if (typeof request.json !== "boolean") { - throw new Error("Command adapter request json must be boolean"); - } - if (!request.group || typeof request.group !== "object") { - throw new Error("Command adapter request group is required"); - } - validateManifestGroups([request.group]); - validateStringArray(request.canonicalCommand, "Command adapter request canonicalCommand", { allowEmpty: false }); - if (!request.group.canonicalCommand.every((part, index) => request.canonicalCommand[index] === part)) { - throw new Error("Command adapter request canonicalCommand must start with the group canonicalCommand"); - } - return request; -} - -export function validateGraphReleaseReceipt(receipt: GraphReleaseReceipt): GraphReleaseReceipt { - if (!receipt || typeof receipt !== "object") { - throw new Error("Graph release receipt is required"); - } - if (receipt.schemaVersion !== 1) { - throw new Error("Graph release receipt schemaVersion must be 1"); - } - if (receipt.issue !== "#17") { - throw new Error("Graph release receipt issue must be #17"); - } - if (receipt.origin !== "covibes-authored-synthetic") { - throw new Error("Graph release receipt origin must be covibes-authored-synthetic"); - } - - validateNonEmptyString(receipt.generatedAt, "Graph release receipt generatedAt"); - validateNonEmptyString(receipt.commitSha, "Graph release receipt commitSha"); - if (receipt.graphProviderSchemaVersion !== 1) { - throw new Error("Graph release receipt graphProviderSchemaVersion must be 1"); - } - validateGraphReleasePackageVersions(receipt.graphPackageVersions); - validateExactStringSet(receipt.requiredChildren, graphReleaseRequiredChildren, "Graph release required children"); - validateExactStringSet(receipt.deferredChildren, graphReleaseDeferredChildren, "Graph release deferred children"); - validateGraphReleaseCommandCoverage(receipt.commandCoverage); - validateGraphReleaseRustCommandCoverage(receipt.rustCommandCoverage); - validateGraphReleaseDirectSqliteQueries(receipt.directSqliteQueries); - validateGraphReleaseServeTransport(receipt.serveTransport); - validateGraphReleaseBenchmarks(receipt.benchmarks); - validateGraphReleasePackageInspection(receipt.packageInspection); - validateExactStringSet(receipt.supportedNativeTargets, graphCoreNativeSupportedTargets, "Graph release supported native targets"); - validateGraphReleaseNativeArtifacts(receipt.nativeArtifacts); - validateGraphReleaseReportReceipts(receipt.reportReceipts); - validateGraphProviderArtifactMetadata(receipt.graphArtifact); - validateGraphReleaseOptionalSurfaces(receipt.optionalSurfaces); - validateGraphReleaseHandoff(receipt.handoff); - validateGraphReleaseSourceFreeStrings(receipt); - - return receipt; -} - -export function validateReleaseReceipt(receipt: ReleaseReceipt): ReleaseReceipt { - if (!receipt || typeof receipt !== "object") throw new Error("Release receipt is required"); - if (receipt.schemaVersion !== 1) throw new Error("Release receipt schemaVersion must be 1"); - if (receipt.issue !== "#29") throw new Error("Release receipt issue must be #29"); - if (receipt.origin !== "covibes-authored-release-proof") { - throw new Error("Release receipt origin must be covibes-authored-release-proof"); - } - validateNonEmptyString(receipt.generatedAt, "Release receipt generatedAt"); - validateNonEmptyString(receipt.commitSha, "Release receipt commitSha"); - if (receipt.privateRepo !== true) throw new Error("Release receipt maintainer evidence marker must be true"); - validateExactStringSet(receipt.packageNames, releaseReceiptPackageNames, "Release receipt package names"); - validateExactStringSet(receipt.commandGroups, releaseReceiptCommandGroups, "Release receipt command groups"); - validateReleaseReceiptPackages(receipt.packages); - validateReleaseReceiptDescriptor(receipt.descriptor, receipt.packages); - validateReleaseReceiptNativeArtifacts(receipt.nativeArtifacts, receipt.packages, receipt.descriptor); - validateReleaseReceiptLicense(receipt.license); - validateReleaseReceiptProvenance(receipt.provenance); - validateReleaseReceiptSecretHistory(receipt.secretHistory); - validateReleaseReceiptReports(receipt.reports); - validateReleaseReceiptGraphReleaseEvidence(receipt.graphReleaseReceipt); - return receipt; -} - -const validateOpcoreSelfValidation = (receipt: OpcoreSelfValidationReceipt, label: string): void => { - if (!receipt || typeof receipt !== "object") throw new Error(`${label} receipt is required`); - if (receipt.id !== "opcore-self-check") throw new Error(`${label} id must be opcore-self-check`); - validateExactStringSequence(receipt.command, ["npm", "run", "opcore:self-check"], `${label} command`); - if (receipt.status !== "passed") throw new Error(`${label} status must be passed`); - if (receipt.exitCode !== 0) throw new Error(`${label} exitCode must be 0`); - validateSha256(receipt.stdoutSha256, `${label} stdoutSha256`); - validateSha256(receipt.stderrSha256, `${label} stderrSha256`); - validateNonEmptyString(receipt.assertion, `${label} assertion`); -}; - -export function validateReleaseCutoverReceipt(receipt: ReleaseCutoverReceipt): ReleaseCutoverReceipt { - if (!receipt || typeof receipt !== "object") throw new Error("Release cutover receipt is required"); - if (receipt.schemaVersion !== 1) throw new Error("Release cutover receipt schemaVersion must be 1"); - if (receipt.issue !== "#30") throw new Error("Release cutover receipt issue must be #30"); - if (receipt.origin !== "covibes-authored-cutover-proof") { - throw new Error("Release cutover receipt origin must be covibes-authored-cutover-proof"); - } - validateNonEmptyString(receipt.generatedAt, "Release cutover receipt generatedAt"); - validateNonEmptyString(receipt.commitSha, "Release cutover receipt commitSha"); - if (receipt.privateRepo !== true) throw new Error("Release cutover receipt maintainer evidence marker must be true"); - validateExactStringSet(receipt.packageNames, releaseReceiptPackageNames, "Release cutover receipt package names"); - validateReleaseCutoverInstalledPackages(receipt.installedPackages); - validateReleaseCutoverDescriptor(receipt.descriptor); - validateReleaseCutoverEnvironmentIsolation(receipt.environmentIsolation); - validateReleaseCutoverCommandReceipts(receipt.commandReceipts); - validateReleaseCutoverRustCommandReceipts(receipt.rustCommandReceipts); - validateReleaseCutoverPythonCommandReceipts(receipt.pythonCommandReceipts); - validateReleaseCutoverNegativeChecks(receipt.negativeChecks); - validateOpcoreSelfValidation(receipt.selfValidation, "Release cutover self-validation"); - validateReleaseCutoverForbiddenMarkerScan(receipt.forbiddenMarkerScan); - validateReleaseCutoverInputEvidence(receipt.inputEvidence); - return receipt; -} - -export function validateAspDogfoodReceipt(receipt: AspDogfoodReceipt): AspDogfoodReceipt { - if (!receipt || typeof receipt !== "object") throw new Error("ASP dogfood receipt is required"); - if (receipt.schemaVersion !== 1) throw new Error("ASP dogfood receipt schemaVersion must be 1"); - if (receipt.issue !== "#120") throw new Error("ASP dogfood receipt issue must be #120"); - if (receipt.origin !== "covibes-authored-asp-dogfood-proof") { - throw new Error("ASP dogfood receipt origin must be covibes-authored-asp-dogfood-proof"); - } - validateNonEmptyString(receipt.generatedAt, "ASP dogfood receipt generatedAt"); - validateNonEmptyString(receipt.commitSha, "ASP dogfood receipt commitSha"); - if (receipt.privateRepo !== true) throw new Error("ASP dogfood receipt privateRepo must be true"); - if (receipt.bootstrapSource !== "local-sibling") throw new Error("ASP dogfood bootstrapSource must be local-sibling"); - validateExactStringSet(receipt.packageNames, releaseReceiptPackageNames, "ASP dogfood receipt package names"); - validateReleaseCutoverInstalledPackages(receipt.installedPackages); - validateAspDogfoodManager(receipt.manager); - validateAspDogfoodAspHome(receipt.aspHome); - validateAspDogfoodHostFixture(receipt.hostFixture); - validateAspDogfoodProvider(receipt.provider); - validateAspDogfoodManagerState(receipt.managerState); - validateAspDogfoodRepoEnrollment(receipt.repoEnrollment); - validateAspDogfoodHostEvaluation(receipt.hostEvaluation); - validateAspDogfoodProviderProbe(receipt.providerProbe); - validateOpcoreSelfValidation(receipt.selfValidation, "ASP dogfood self-validation"); - validateAspDogfoodUnsupportedSurfaces(receipt.unsupportedSurfaces); - validateAspDogfoodParityBlockers(receipt.parityBlockers); - validateAspDogfoodAuthority(receipt.authority); - if (!Array.isArray(receipt.publicReleaseActions) || receipt.publicReleaseActions.length !== 0) { - throw new Error("ASP dogfood receipt must not record public publish, registry, or standard-readiness actions"); - } - validateAspDogfoodForbiddenMarkerScan(receipt.forbiddenMarkerScan); - validateAspDogfoodForbiddenProviderEntrypoint(receipt); - return receipt; -} - -export function validateRepoRelativePath(path: string): string { - if (typeof path !== "string" || path.length === 0) { - throw new Error("Repo-relative path must be a non-empty string"); - } - if (path.includes("\0")) { - throw new Error(`Repo-relative path contains a null byte: ${path}`); - } - if (/^[\\/]/.test(path) || /^[A-Za-z]:[\\/]/.test(path)) { - throw new Error(`Repo-relative path must not be absolute: ${path}`); - } - const normalized = path.replaceAll("\\", "/"); - if ( - normalized === "." || - normalized === ".." || - normalized.startsWith("../") || - normalized.includes("/../") || - normalized.endsWith("/..") - ) { - throw new Error(`Repo-relative path must not escape the repository: ${path}`); - } - return normalized; -} - -export function validateHomeRelativePath(path: string): string { - if (typeof path !== "string" || path.length === 0) { - throw new Error("Home-relative path must be a non-empty string"); - } - if (path.includes("\0")) { - throw new Error(`Home-relative path contains a null byte: ${path}`); - } - const normalized = path.replaceAll("\\", "/"); - if (!normalized.startsWith("~/")) { - throw new Error(`Home-relative path must start with ~/: ${path}`); - } - if ( - normalized === "~/" || - normalized === "~/." || - normalized.includes("/../") || - normalized.endsWith("/..") || - normalized.includes("//") - ) { - throw new Error(`Home-relative path must not escape the home directory: ${path}`); - } - return normalized; -} - -export function validateRepoIdentity(repo: RepoIdentity): RepoIdentity { - if (!repo || typeof repo !== "object") { - throw new Error("Repo identity is required"); - } - if (repo.repoId && repo.repoRoot) { - throw new Error("Repo identity is ambiguous: use repoId or repoRoot, not both"); - } - if (!repo.repoId && !repo.repoRoot && !repo.remoteUrl) { - throw new Error("Repo identity must include repoId, repoRoot, or remoteUrl"); - } - return repo; -} - -export function validateProviderStatus(status: GraphProviderStatus): GraphProviderStatus { - if (!status || typeof status !== "object") { - throw new Error("Graph provider status is required"); - } - if (!includesString(graphProviderStatusStates, status.state)) { - throw new Error(`Unknown graph provider status state: ${String(status.state)}`); - } - if (!includesString(graphProviderModes, status.mode)) { - throw new Error(`Unknown graph provider mode: ${String(status.mode)}`); - } - if (typeof status.provider !== "string" || status.provider.length === 0) { - throw new Error("Graph provider status must include provider"); - } - if (typeof status.schemaVersion !== "number") { - throw new Error("Graph provider status must include numeric schemaVersion"); - } - if (status.state === "skipped" && status.mode !== "optional") { - throw new Error("Skipped graph provider status must use optional mode"); - } - if (status.state === "required_missing" && status.mode !== "required") { - throw new Error("Required-missing graph provider status must use required mode"); - } - if (status.state === "available") { - validateRepoIdentity(status.repo); - validateGraphFreshness(status.freshness, "Available"); - validateGraphKindCounts(status.nodes_by_kind, "nodes_by_kind"); - validateGraphKindCounts(status.edges_by_kind, "edges_by_kind"); - if (status.handshake !== undefined) validateGraphProviderCapabilityHandshake(status.handshake); - if (status.walCheckpoint !== undefined) validateGraphWalCheckpointSummary(status.walCheckpoint); - return status; - } - if (status.state === "warming") { - validateRepoIdentity(status.repo); - validateGraphFreshness(status.freshness, "Warming"); - if (status.lifecycle !== undefined) validateGraphWatchLifecycle(status.lifecycle); - return status; - } - validateProviderFailureStatus(status); - return status; -} - -function validateGraphKindCounts(counts: Readonly>, label: string): void { - if (!counts || typeof counts !== "object" || Array.isArray(counts)) { - throw new Error(`Graph provider status ${label} must be an object`); - } - for (const [kind, count] of Object.entries(counts)) { - if (kind.length === 0) throw new Error(`Graph provider status ${label} kind must not be empty`); - if (!Number.isInteger(count) || count < 0) { - throw new Error(`Graph provider status ${label}.${kind} must be a non-negative integer`); - } - } -} - -function validateProviderFailureStatus(status: GraphProviderFailureStatus): void { - if (!status.failure?.category) { - throw new Error(`Graph provider ${status.state} status must include failure.category`); - } - if (!includesString(providerFailureCategories, status.failure.category)) { - throw new Error(`Unknown graph provider failure category: ${status.failure.category}`); - } - const allowedCategories = graphProviderFailureCategoriesByState[status.state]; - if (!includesString(allowedCategories, status.failure.category)) { - throw new Error( - `Graph provider ${status.state} failure category must be one of ${allowedCategories.join(", ")}; got ${status.failure.category}` - ); - } - if (status.state === "stale") { - if (!status.repo) { - throw new Error("Stale graph provider status must include repo"); - } - validateRepoIdentity(status.repo); - validateGraphFreshness(status.freshness, "Stale"); - } - if (status.state === "schema_mismatch") { - if (typeof status.expectedSchemaVersion !== "number") { - throw new Error("Schema-mismatch graph provider status must include expectedSchemaVersion"); - } - if (typeof status.actualSchemaVersion !== "number") { - throw new Error("Schema-mismatch graph provider status must include actualSchemaVersion"); - } - } - if (status.state === "error" && status.diagnostics !== undefined) { - validateGraphExtractionDiagnostics(status.diagnostics); - } -} - -export function validateGraphProviderArtifactMetadata( - metadata: GraphProviderArtifactMetadata -): GraphProviderArtifactMetadata { - if (!metadata || typeof metadata !== "object") { - throw new Error("Graph provider artifact metadata is required"); - } - for (const key of [ - "artifactName", - "artifactVersion", - "targetPlatform", - "binaryPath", - "checksumPath", - "checksumSha256", - "buildProfile" - ] as const) { - validateNonEmptyString(metadata[key], `Graph provider artifact metadata ${key}`); - } - for (const key of ["binaryPath", "checksumPath"] as const) { - validateRepoRelativePath(metadata[key]); - if (metadata[key].startsWith("packages/") || metadata[key].startsWith("../")) { - throw new Error(`Graph provider artifact metadata ${key} must be package-relative`); - } - } - return metadata; -} - -export function validateGraphProviderCapabilityHandshake( - handshake: GraphProviderCapabilityHandshake -): GraphProviderCapabilityHandshake { - if (!handshake || typeof handshake !== "object") { - throw new Error("Graph provider capability handshake is required"); - } - validateNonEmptyString(handshake.provider, "Graph provider capability handshake provider"); - if (typeof handshake.graphSchemaVersion !== "number") { - throw new Error("Graph provider capability handshake graphSchemaVersion must be numeric"); - } - validateNonEmptyString(handshake.artifactName, "Graph provider capability handshake artifactName"); - validateNonEmptyString(handshake.artifactVersion, "Graph provider capability handshake artifactVersion"); - validateNonEmptyString(handshake.targetPlatform, "Graph provider capability handshake targetPlatform"); - validateStringArray(handshake.supportedOperations, "Graph provider capability handshake supportedOperations", { - allowEmpty: false - }); - for (const operation of handshake.supportedOperations) validateGraphDaemonOperation(operation); - validateStringArray(handshake.nodeKinds, "Graph provider capability handshake nodeKinds", { allowEmpty: false }); - validateStringArray(handshake.edgeKinds, "Graph provider capability handshake edgeKinds", { allowEmpty: false }); - validateStringArray(handshake.queryKinds, "Graph provider capability handshake queryKinds", { allowEmpty: false }); - for (const queryKind of handshake.queryKinds) validateGraphProviderQueryKind(queryKind); - validateGraphProviderArtifactMetadata(handshake.artifact); - if (handshake.artifact.artifactName !== handshake.artifactName) { - throw new Error("Graph provider capability handshake artifactName must match artifact metadata"); - } - if (handshake.artifact.targetPlatform !== handshake.targetPlatform) { - throw new Error("Graph provider capability handshake targetPlatform must match artifact metadata"); - } - return handshake; -} - -export function validateGraphFactQueryRequest(request: GraphFactQueryRequest): GraphFactQueryRequest { - if (!request || typeof request !== "object") { - throw new Error("Graph fact query request is required"); - } - if (request.requestId !== undefined) validateNonEmptyString(request.requestId, "Graph fact query request requestId"); - validateRepoIdentity(request.repo); - if (request.schemaVersion !== GRAPH_SCHEMA_VERSION) { - throw new Error(`Graph fact query request schemaVersion must be ${GRAPH_SCHEMA_VERSION}`); - } - if (!includesString(graphProviderModes, request.mode)) { - throw new Error(`Unknown graph fact query request mode: ${String(request.mode)}`); - } - if (!request.selector || typeof request.selector !== "object") { - throw new Error("Graph fact query request selector is required"); - } - validateGraphFactQueryKind(request.selector.kind); - if (request.selector.nodeKinds !== undefined) { - validateStringArray(request.selector.nodeKinds, "Graph fact query selector nodeKinds", { allowEmpty: true }); - } - if (request.selector.edgeKinds !== undefined) { - validateStringArray(request.selector.edgeKinds, "Graph fact query selector edgeKinds", { allowEmpty: true }); - } - if (request.selector.ids !== undefined) { - validateStringArray(request.selector.ids, "Graph fact query selector ids", { allowEmpty: true }); - } - if (request.selector.limit !== undefined && (typeof request.selector.limit !== "number" || request.selector.limit < 1)) { - throw new Error("Graph fact query selector limit must be a positive number"); - } - return request; -} - -export function validateGraphFactQueryResult(result: GraphFactQueryResult): GraphFactQueryResult { - if (!result || typeof result !== "object") { - throw new Error("Graph fact query result is required"); - } - if (result.requestId !== undefined) validateNonEmptyString(result.requestId, "Graph fact query result requestId"); - const status = validateProviderStatus(result.status); - const payload = result as { - metadata?: unknown; - nodes?: unknown; - edges?: unknown; - diagnostics?: unknown; - }; - const hasGraphData = - Object.hasOwn(payload, "metadata") || Object.hasOwn(payload, "nodes") || Object.hasOwn(payload, "edges"); - if (status.state !== "available") { - if (hasGraphData) { - throw new Error(`Graph query ${status.state} result must not include graph data`); - } - return result; - } - if (!payload.metadata || !Array.isArray(payload.nodes) || !Array.isArray(payload.edges)) { - throw new Error("Available graph query result must include metadata, nodes, and edges"); - } - validateGraphSnapshotMetadata(payload.metadata as GraphSnapshotMetadata); - for (const node of payload.nodes) validateGraphFactNode(node as GraphFactNode); - for (const edge of payload.edges) validateGraphFactEdge(edge as GraphFactEdge); - if (payload.diagnostics !== undefined) { - validateGraphExtractionDiagnostics(payload.diagnostics as readonly GraphExtractionDiagnostic[]); - } - return result; -} - -export function validateGraphNamedQueryRequest(request: GraphNamedQueryRequest): GraphNamedQueryRequest { - validateGraphQueryRequestBase(request, "Graph named query request"); - validateGraphNamedQueryKind(request.queryKind); - validateNonEmptyString(request.target, "Graph named query request target"); - validateTraversalOptions(request.maxDepth, request.limit, "Graph named query request"); - return request; -} - -export function validateGraphNamedQueryResult(result: GraphNamedQueryResult): GraphNamedQueryResult { - validateGraphPayloadResult(result, "Graph named query result", (payload) => { - validateGraphSnapshotMetadata(payload.metadata as GraphSnapshotMetadata); - validateGraphNamedQueryKind(payload.queryKind); - validateNonEmptyString(payload.target, "Graph named query result target"); - for (const node of payload.nodes as readonly GraphFactNode[]) validateGraphFactNode(node); - for (const edge of payload.edges as readonly GraphFactEdge[]) validateGraphFactEdge(edge); - validateGraphTraversalMetadata(payload.traversal as GraphTraversalMetadata); - }); - return result; -} - -export function validateGraphImpactRequest(request: GraphImpactRequest): GraphImpactRequest { - validateGraphQueryRequestBase(request, "Graph impact request"); - validateStringArray(request.files, "Graph impact request files", { allowEmpty: false }); - for (const file of request.files) validateRepoRelativePath(file); - if (request.baseRef !== undefined) validateNonEmptyString(request.baseRef, "Graph impact request baseRef"); - validateTraversalOptions(request.maxDepth, request.limit, "Graph impact request"); - return request; -} - -export function validateGraphImpactResult(result: GraphImpactResult): GraphImpactResult { - validateGraphPayloadResult(result, "Graph impact result", (payload) => { - validateGraphSnapshotMetadata(payload.metadata as GraphSnapshotMetadata); - validateRepoRelativePaths(payload.changedFiles, "Graph impact result changedFiles"); - validateRepoRelativePaths(payload.impactedFiles, "Graph impact result impactedFiles"); - validateStringArray(payload.impactedSymbols as readonly string[], "Graph impact result impactedSymbols", { allowEmpty: true }); - validateRepoRelativePaths(payload.tests, "Graph impact result tests"); - for (const node of payload.nodes as readonly GraphFactNode[]) validateGraphFactNode(node); - for (const edge of payload.edges as readonly GraphFactEdge[]) validateGraphFactEdge(edge); - validateGraphTraversalMetadata(payload.traversal as GraphTraversalMetadata); - }); - return result; -} - -export function validateGraphDetectChangesRequest(request: GraphDetectChangesRequest): GraphDetectChangesRequest { - validateGraphQueryRequestBase(request, "Graph detect-changes request"); - if (request.files !== undefined) validateRepoRelativePaths(request.files, "Graph detect-changes request files"); - if (request.baseRef !== undefined) validateNonEmptyString(request.baseRef, "Graph detect-changes request baseRef"); - return request; -} - -export function validateGraphDetectChangesResult(result: GraphDetectChangesResult): GraphDetectChangesResult { - validateGraphPayloadResult(result, "Graph detect-changes result", (payload) => { - validateGraphSnapshotMetadata(payload.metadata as GraphSnapshotMetadata); - validateRepoRelativePaths(payload.changedFiles, "Graph detect-changes result changedFiles"); - validateRepoRelativePaths(payload.deletedFiles, "Graph detect-changes result deletedFiles"); - validateRenamedFiles(payload.renamedFiles as readonly GraphRenamedFile[]); - }); - return result; -} - -export function validateGraphReviewContextRequest(request: GraphReviewContextRequest): GraphReviewContextRequest { - validateGraphQueryRequestBase(request, "Graph review-context request"); - if (request.files !== undefined) validateRepoRelativePaths(request.files, "Graph review-context request files"); - if (request.baseRef !== undefined) validateNonEmptyString(request.baseRef, "Graph review-context request baseRef"); - validateTraversalOptions(request.maxDepth, request.limit, "Graph review-context request"); - return request; -} - -export function validateGraphReviewContextResult(result: GraphReviewContextResult): GraphReviewContextResult { - validateGraphPayloadResult(result, "Graph review-context result", (payload) => { - validateGraphSnapshotMetadata(payload.metadata as GraphSnapshotMetadata); - validateRepoRelativePaths(payload.changedFiles, "Graph review-context result changedFiles"); - validateRepoRelativePaths(payload.deletedFiles, "Graph review-context result deletedFiles"); - validateRenamedFiles(payload.renamedFiles as readonly GraphRenamedFile[]); - validateRepoRelativePaths(payload.impactedFiles, "Graph review-context result impactedFiles"); - validateStringArray(payload.impactedSymbols as readonly string[], "Graph review-context result impactedSymbols", { allowEmpty: true }); - validateRepoRelativePaths(payload.tests, "Graph review-context result tests"); - for (const node of payload.nodes as readonly GraphFactNode[]) validateGraphFactNode(node); - for (const edge of payload.edges as readonly GraphFactEdge[]) validateGraphFactEdge(edge); - validateGraphTraversalMetadata(payload.traversal as GraphTraversalMetadata); - }); - return result; -} - -export function validateGraphSearchRequest(request: GraphSearchRequest): GraphSearchRequest { - validateGraphQueryRequestBase(request, "Graph search request"); - validateNonEmptyString(request.query, "Graph search request query"); - if (request.query.trim().length === 0) throw new Error("Graph search request query must not be empty"); - if (request.limit !== undefined && (!Number.isFinite(request.limit) || request.limit < 1)) { - throw new Error("Graph search request limit must be a positive number"); - } - if (request.files !== undefined) validateRepoRelativePaths(request.files, "Graph search request files"); - return request; -} - -export function validateGraphSearchResult(result: GraphSearchResult): GraphSearchResult { - if (!result || typeof result !== "object") throw new Error("Graph search result is required"); - if (result.requestId !== undefined) validateNonEmptyString(result.requestId, "Graph search result requestId"); - const status = validateProviderStatus(result.status); - const payload = result as { - metadata?: unknown; - query?: unknown; - searchMode?: unknown; - summary?: unknown; - results?: unknown; - hints?: unknown; - diagnostics?: unknown; - }; - const hasSearchData = - Object.hasOwn(payload, "metadata") || - Object.hasOwn(payload, "query") || - Object.hasOwn(payload, "searchMode") || - Object.hasOwn(payload, "summary") || - Object.hasOwn(payload, "results"); - if (status.state !== "available") { - if (hasSearchData) throw new Error(`Graph search ${status.state} result must not include search data`); - if (payload.hints !== undefined) validateStringArray(payload.hints as readonly string[], "Graph search result hints", { allowEmpty: true }); - if (payload.diagnostics !== undefined) validateGraphExtractionDiagnostics(payload.diagnostics as readonly GraphExtractionDiagnostic[]); - return result; - } - if (!payload.metadata || typeof payload.query !== "string" || !payload.searchMode || !payload.summary || !Array.isArray(payload.results)) { - throw new Error("Available graph search result must include metadata, query, searchMode, summary, and results"); - } - validateGraphSnapshotMetadata(payload.metadata as GraphSnapshotMetadata); - validateNonEmptyString(payload.query, "Graph search result query"); - validateGraphSearchMode(payload.searchMode as GraphSearchMode); - validateGraphSearchSummary(payload.summary as GraphSearchSummary); - for (const entry of payload.results as readonly GraphSearchResultEntry[]) validateGraphSearchResultEntry(entry); - if (payload.hints !== undefined) validateStringArray(payload.hints as readonly string[], "Graph search result hints", { allowEmpty: true }); - if (payload.diagnostics !== undefined) validateGraphExtractionDiagnostics(payload.diagnostics as readonly GraphExtractionDiagnostic[]); - return result; -} - -export function validateInspectRouteResult(result: InspectRouteResult): InspectRouteResult { - if (!result || typeof result !== "object") throw new Error("Inspect route result is required"); - const route = validateInspectRouteName((result as { route?: unknown }).route); - if (!includesString(["ok", "error", "degraded"] as const, result.status)) { - throw new Error(`Unknown inspect route result status: ${String((result as { status?: unknown }).status)}`); - } - if (result.providerStatus !== undefined) validateProviderStatus(result.providerStatus); - if (result.status === "ok") { - validateInspectSymbolTarget(result.target, `Inspect ${route} target`); - if (result.providerStatus === undefined || result.providerStatus.state !== "available") { - throw new Error(`Successful inspect ${route} result requires available providerStatus`); - } - validateInspectRoutePayload(result, route); - if (Object.hasOwn(result, "failure")) throw new Error(`Successful inspect ${route} result must not include failure`); - } else if (result.status === "degraded" && inspectResultHasPayload(result, route)) { - const target = result.target; - if (target === undefined) throw new Error(`Degraded inspect ${route} result requires target`); - validateInspectSymbolTarget(target, `Inspect ${route} target`); - if (result.providerStatus === undefined) throw new Error(`Degraded inspect ${route} result requires providerStatus`); - validateInspectRoutePayload(result, route); - const failure = result.failure; - if (failure === undefined) throw new Error(`Degraded inspect ${route} result requires failure`); - validateInspectRouteFailure(failure); - } else { - if (result.target !== undefined) validateInspectSymbolTarget(result.target, `Inspect ${route} target`); - const failure = result.failure; - if (failure === undefined) throw new Error(`Failed inspect ${route} result requires failure`); - validateInspectRouteFailure(failure); - for (const field of ["references", "signatures", "implementations"] as const) { - if (Object.hasOwn(result, field)) throw new Error(`Failed inspect ${route} result must not include ${field}`); - } - } - return result; -} - -function inspectResultHasPayload(result: InspectRouteResult, route: InspectRouteResult["route"]): boolean { - return ( - (route === "references" && Object.hasOwn(result, "references")) || - (route === "signature" && Object.hasOwn(result, "signatures")) || - (route === "implementations" && Object.hasOwn(result, "implementations")) - ); -} - -export function validateGraphDaemonRequest(request: GraphDaemonRequest): GraphDaemonRequest { - if (!request || typeof request !== "object") { - throw new Error("Graph daemon request is required"); - } - if (request.protocol !== "opcore.graph.daemon") { - throw new Error("Graph daemon request protocol must be opcore.graph.daemon"); - } - validateNonEmptyString(request.requestId, "Graph daemon request requestId"); - if (request.schemaVersion !== GRAPH_SCHEMA_VERSION) { - throw new Error(`Graph daemon request schemaVersion must be ${GRAPH_SCHEMA_VERSION}`); - } - validateGraphDaemonOperation(request.operation); - validateRepoIdentity(request.repo); - if (request.query !== undefined) validateGraphFactQueryRequest(request.query); - if (request.namedQuery !== undefined) validateGraphNamedQueryRequest(request.namedQuery); - if (request.impact !== undefined) validateGraphImpactRequest(request.impact); - if (request.reviewContext !== undefined) validateGraphReviewContextRequest(request.reviewContext); - if (request.changes !== undefined) validateGraphDetectChangesRequest(request.changes); - if (request.search !== undefined) validateGraphSearchRequest(request.search); - if (request.operation === "query" && request.query === undefined) { - const hasQueryEnvelope = - request.namedQuery !== undefined || - request.impact !== undefined || - request.reviewContext !== undefined || - request.changes !== undefined || - request.search !== undefined; - if (!hasQueryEnvelope) throw new Error("Graph daemon query request must include query"); - } - if (request.baseRef !== undefined) validateNonEmptyString(request.baseRef, "Graph daemon request baseRef"); - if (request.paths !== undefined) { - validateStringArray(request.paths, "Graph daemon request paths", { allowEmpty: true }); - for (const path of request.paths) validateRepoRelativePath(path); - } - if (request.watchPaths !== undefined) { - validateStringArray(request.watchPaths, "Graph daemon request watchPaths", { allowEmpty: true }); - for (const path of request.watchPaths) validateRepoRelativePath(path); - } - if ( - request.pollIntervalMs !== undefined && - (!Number.isFinite(request.pollIntervalMs) || request.pollIntervalMs < 1) - ) { - throw new Error("Graph daemon request pollIntervalMs must be positive"); - } - if ( - request.idleTimeoutMs !== undefined && - (!Number.isFinite(request.idleTimeoutMs) || request.idleTimeoutMs < 0) - ) { - throw new Error("Graph daemon request idleTimeoutMs must be a non-negative number"); - } - if (request.once !== undefined && typeof request.once !== "boolean") { - throw new Error("Graph daemon request once must be boolean"); - } - if (request.maxWalBytes !== undefined && (!Number.isFinite(request.maxWalBytes) || request.maxWalBytes < 1)) { - throw new Error("Graph daemon request maxWalBytes must be positive"); - } - return request; -} - -export function validateGraphDaemonResponse(response: GraphDaemonResponse): GraphDaemonResponse { - if (!response || typeof response !== "object") { - throw new Error("Graph daemon response is required"); - } - if (response.protocol !== "opcore.graph.daemon") { - throw new Error("Graph daemon response protocol must be opcore.graph.daemon"); - } - validateNonEmptyString(response.requestId, "Graph daemon response requestId"); - if (response.schemaVersion !== GRAPH_SCHEMA_VERSION) { - throw new Error(`Graph daemon response schemaVersion must be ${GRAPH_SCHEMA_VERSION}`); - } - validateProviderStatus(response.status); - if (response.result !== undefined) validateGraphFactQueryResult(response.result); - if (response.namedQuery !== undefined) validateGraphNamedQueryResult(response.namedQuery); - if (response.impact !== undefined) validateGraphImpactResult(response.impact); - if (response.reviewContext !== undefined) validateGraphReviewContextResult(response.reviewContext); - if (response.changes !== undefined) validateGraphDetectChangesResult(response.changes); - if (response.search !== undefined) validateGraphSearchResult(response.search); - if (response.pipeline !== undefined) validateGraphPipelineResult(response.pipeline); - if (response.lifecycle !== undefined) validateGraphWatchLifecycle(response.lifecycle); - return response; -} - -export function validateGraphPipelineResult(result: GraphPipelineResult): GraphPipelineResult { - if (!result || typeof result !== "object") { - throw new Error("Graph pipeline result is required"); - } - validateGraphPipelineSummary(result.summary); - validateProviderStatus(result.status); - if (result.lifecycle !== undefined) validateGraphWatchLifecycle(result.lifecycle); - return result; -} - -export function validateGraphPipelineSummary(summary: GraphPipelineSummary): GraphPipelineSummary { - if (!summary || typeof summary !== "object") { - throw new Error("Graph pipeline summary is required"); - } - if (!["build", "update", "watch"].includes(summary.operation)) { - throw new Error(`Unknown graph pipeline operation: ${String(summary.operation)}`); - } - validateRepoIdentity(summary.repo); - if (summary.storePath !== undefined) validateNonEmptyString(summary.storePath, "Graph pipeline summary storePath"); - validateNonEmptyString(summary.startedAt, "Graph pipeline summary startedAt"); - validateNonEmptyString(summary.completedAt, "Graph pipeline summary completedAt"); - for (const key of ["durationMs", "discoveredFiles", "parsedFiles", "unchangedFiles", "diagnosticsCount"] as const) { - if (typeof summary[key] !== "number" || summary[key] < 0) { - throw new Error(`Graph pipeline summary ${key} must be a non-negative number`); - } - } - validateStringArray(summary.changedFiles, "Graph pipeline summary changedFiles", { allowEmpty: true }); - validateStringArray(summary.deletedFiles, "Graph pipeline summary deletedFiles", { allowEmpty: true }); - for (const path of summary.changedFiles) validateRepoRelativePath(path); - for (const path of summary.deletedFiles) validateRepoRelativePath(path); - if (typeof summary.fullRebuildRequired !== "boolean") { - throw new Error("Graph pipeline summary fullRebuildRequired must be boolean"); - } - if (!Array.isArray(summary.phaseTimings) || summary.phaseTimings.length === 0) { - throw new Error("Graph pipeline summary phaseTimings must be non-empty"); - } - for (const timing of summary.phaseTimings) validateGraphPipelinePhaseTiming(timing); - if (summary.baseRef !== undefined) validateNonEmptyString(summary.baseRef, "Graph pipeline summary baseRef"); - if (summary.watchPaths !== undefined) { - validateStringArray(summary.watchPaths, "Graph pipeline summary watchPaths", { allowEmpty: true }); - for (const path of summary.watchPaths) validateRepoRelativePath(path); - } - if (summary.walCheckpoint !== undefined) validateGraphWalCheckpointSummary(summary.walCheckpoint); - return summary; -} - -function validateGraphPipelinePhaseTiming(timing: GraphPipelinePhaseTiming): GraphPipelinePhaseTiming { - if (!timing || typeof timing !== "object") { - throw new Error("Graph pipeline phase timing is required"); - } - validateNonEmptyString(timing.phase, "Graph pipeline phase timing phase"); - validateNonEmptyString(timing.startedAt, "Graph pipeline phase timing startedAt"); - validateNonEmptyString(timing.completedAt, "Graph pipeline phase timing completedAt"); - if (typeof timing.durationMs !== "number" || timing.durationMs < 0) { - throw new Error("Graph pipeline phase timing durationMs must be non-negative"); - } - if (timing.fileCount !== undefined && (typeof timing.fileCount !== "number" || timing.fileCount < 0)) { - throw new Error("Graph pipeline phase timing fileCount must be non-negative"); - } - return timing; -} - -function validateGraphWalCheckpointSummary(summary: GraphWalCheckpointSummary): GraphWalCheckpointSummary { - validateNonEmptyString(summary.walPath, "Graph WAL checkpoint walPath"); - for (const key of ["bytesBefore", "bytesAfter", "budgetBytes"] as const) { - if (typeof summary[key] !== "number" || summary[key] < 0) { - throw new Error(`Graph WAL checkpoint ${key} must be non-negative`); - } - } - if (typeof summary.checkpointed !== "boolean") { - throw new Error("Graph WAL checkpoint checkpointed must be boolean"); - } - return summary; -} - -export function validateGraphWatchLifecycle(lifecycle: GraphWatchLifecycle): GraphWatchLifecycle { - if (!lifecycle || typeof lifecycle !== "object") { - throw new Error("Graph watch lifecycle is required"); - } - if (!["warming", "available", "error", "stopped"].includes(lifecycle.state)) { - throw new Error(`Unknown graph watch lifecycle state: ${String(lifecycle.state)}`); - } - if (lifecycle.pid !== undefined && (!Number.isInteger(lifecycle.pid) || lifecycle.pid < 1)) { - throw new Error("Graph watch lifecycle pid must be positive"); - } - validateNonEmptyString(lifecycle.startedAt, "Graph watch lifecycle startedAt"); - validateNonEmptyString(lifecycle.updatedAt, "Graph watch lifecycle updatedAt"); - validateNonEmptyString(lifecycle.pidPath, "Graph watch lifecycle pidPath"); - validateNonEmptyString(lifecycle.statePath, "Graph watch lifecycle statePath"); - validateNonEmptyString(lifecycle.logPath, "Graph watch lifecycle logPath"); - if (typeof lifecycle.pollIntervalMs !== "number" || lifecycle.pollIntervalMs < 1) { - throw new Error("Graph watch lifecycle pollIntervalMs must be positive"); - } - if ( - typeof lifecycle.idleTimeoutMs !== "number" || - !Number.isFinite(lifecycle.idleTimeoutMs) || - lifecycle.idleTimeoutMs < 0 - ) { - throw new Error("Graph watch lifecycle idleTimeoutMs must be a non-negative number"); - } - if (lifecycle.watchPaths !== undefined) { - validateStringArray(lifecycle.watchPaths, "Graph watch lifecycle watchPaths", { allowEmpty: true }); - for (const path of lifecycle.watchPaths) validateRepoRelativePath(path); - } - if (lifecycle.message !== undefined) validateNonEmptyString(lifecycle.message, "Graph watch lifecycle message"); - return lifecycle; -} - -export function validateGraphServeTransportStatus(status: GraphServeTransportStatus): GraphServeTransportStatus { - if (!status || typeof status !== "object") { - throw new Error("Graph serve transport status is required"); - } - if (status.schemaVersion !== 1) { - throw new Error("Graph serve transport status schemaVersion must be 1"); - } - if (status.protocol !== "opcore.graph.daemon") { - throw new Error("Graph serve transport status protocol must be opcore.graph.daemon"); - } - if (status.transport !== "stdio") { - throw new Error("Graph serve transport status transport must be stdio"); - } - if (!includesString(["ready", "error", "stopped"] as const, status.state)) { - throw new Error(`Unknown graph serve transport state: ${String(status.state)}`); - } - validateRepoIdentity(status.repo); - validateNonEmptyString(status.provider, "Graph serve transport status provider"); - if (status.pid !== undefined && (!Number.isInteger(status.pid) || status.pid < 1)) { - throw new Error("Graph serve transport status pid must be positive"); - } - if (status.artifact !== undefined) validateGraphProviderArtifactMetadata(status.artifact); - if (status.failure !== undefined) validateProviderFailure(status.failure); - if (status.state === "error" && status.failure === undefined) { - throw new Error("Graph serve transport error status must include failure"); - } - if (status.message !== undefined) validateNonEmptyString(status.message, "Graph serve transport status message"); - return status; -} - -export function validateCloneAnalysisRequest(request: CloneAnalysisRequest): CloneAnalysisRequest { - if (!request || typeof request !== "object") { - throw new Error("Clone analysis request is required"); - } - if (request.protocol !== CLONE_PROTOCOL) { - throw new Error(`Clone analysis request protocol must be ${CLONE_PROTOCOL}`); - } - if (request.requestId !== undefined) validateNonEmptyString(request.requestId, "Clone analysis request requestId"); - if (request.schemaVersion !== 1) { - throw new Error("Clone analysis request schemaVersion must be 1"); - } - validateRepoIdentity(request.repo); - validateCloneReportMode(request.reportMode, "Clone analysis request reportMode"); - if (request.paths !== undefined) validateRepoRelativePaths(request.paths, "Clone analysis request paths"); - if (request.sourcePaths !== undefined) validateRepoRelativePaths(request.sourcePaths, "Clone analysis request sourcePaths"); - if (request.sourceReadMode !== undefined) validateCloneSourceReadMode(request.sourceReadMode, "Clone analysis request sourceReadMode"); - if (request.sourceTreeRef !== undefined) validateNonEmptyString(request.sourceTreeRef, "Clone analysis request sourceTreeRef"); - validateHypotheticalOverlays(request.overlays); - if (request.windowSize !== undefined) validatePositiveInteger(request.windowSize, "Clone analysis request windowSize"); - if (request.minLines !== undefined) validatePositiveInteger(request.minLines, "Clone analysis request minLines"); - if (request.minTokens !== undefined) validatePositiveInteger(request.minTokens, "Clone analysis request minTokens"); - if (request.threshold !== undefined) validatePositiveInteger(request.threshold, "Clone analysis request threshold"); - if (request.partitions !== undefined) validateCloneAnalysisPartitions(request.partitions); - if (request.exclude !== undefined) validateStringArray(request.exclude, "Clone analysis request exclude", { allowEmpty: true }); - if (request.modes !== undefined) validateStringArray(request.modes, "Clone analysis request modes", { allowEmpty: true }); - return request; -} - -function validateCloneSourceReadMode(value: unknown, label: string): asserts value is CloneSourceReadMode { - if (!cloneSourceReadModes.includes(value as CloneSourceReadMode)) { - throw new Error(`${label} must be one of ${cloneSourceReadModes.join(", ")}`); - } -} - -function validateCloneAnalysisPartitions(partitions: readonly (readonly string[])[]): void { - if (!Array.isArray(partitions)) throw new Error("Clone analysis request partitions must be an array"); - for (const [index, partition] of partitions.entries()) { - validateStringArray(partition, `Clone analysis request partitions[${index}]`, { allowEmpty: false }); - } -} - -export function validateCloneAnalysisResult(result: CloneAnalysisResult): CloneAnalysisResult { - if (!result || typeof result !== "object") { - throw new Error("Clone analysis result is required"); - } - if (result.protocol !== CLONE_PROTOCOL) { - throw new Error(`Clone analysis result protocol must be ${CLONE_PROTOCOL}`); - } - if (result.requestId !== undefined) validateNonEmptyString(result.requestId, "Clone analysis result requestId"); - if (result.schemaVersion !== 1) { - throw new Error("Clone analysis result schemaVersion must be 1"); - } - validateRepoIdentity(result.repo); - validateCloneReportMode(result.reportMode, "Clone analysis result reportMode"); - if (result.status !== "passed") { - throw new Error("Clone analysis result status must be passed"); - } - if (typeof result.persisted !== "boolean") { - throw new Error("Clone analysis result persisted must be boolean"); - } - if (result.dbPath !== undefined) validateRepoRelativePath(result.dbPath); - if (!Array.isArray(result.findings)) { - throw new Error("Clone analysis result findings must be an array"); - } - for (const finding of result.findings) validateCloneFinding(finding); - validateCloneAnalysisSummary(result.summary, result.findings.length); - return result; -} - -function validateCloneReportMode(mode: unknown, label: string): CloneReportMode { - if (!includesString(cloneReportModes, mode)) { - throw new Error(`Unknown ${label}: ${String(mode)}`); - } - return mode; -} - -function validateCloneFinding(finding: CloneFinding): CloneFinding { - if (!finding || typeof finding !== "object") { - throw new Error("Clone finding is required"); - } - for (const forbidden of ["line", "column", "startLine", "endLine", "startColumn", "endColumn"]) { - if (Object.hasOwn(finding, forbidden)) { - throw new Error("Clone finding identity must stay line-free"); - } - } - validateCloneClassId(finding.cloneClassId, "Clone finding cloneClassId"); - validateSha256Hex(finding.contentHash, "Clone finding contentHash"); - const path = validateRepoRelativePath(finding.path); - const peerPath = validateRepoRelativePath(finding.peerPath); - if (path === peerPath) { - throw new Error("Clone finding path and peerPath must be distinct"); - } - validateRepoRelativePaths(finding.paths, "Clone finding paths"); - if (!finding.paths.includes(path) || !finding.paths.includes(peerPath)) { - throw new Error("Clone finding paths must include path and peerPath"); - } - validatePositiveInteger(finding.lineCount, "Clone finding lineCount"); - validatePositiveInteger(finding.tokenCount, "Clone finding tokenCount"); - if (typeof finding.introduced !== "boolean") { - throw new Error("Clone finding introduced must be boolean"); - } - return finding; -} - -function validateCloneAnalysisSummary(summary: CloneAnalysisSummary, findingsLength: number): CloneAnalysisSummary { - if (!summary || typeof summary !== "object") { - throw new Error("Clone analysis summary is required"); - } - validateNonNegativeInteger(summary.analyzedFiles, "Clone analysis summary analyzedFiles"); - validateNonNegativeInteger(summary.cloneClassCount, "Clone analysis summary cloneClassCount"); - validateNonNegativeInteger(summary.findingCount, "Clone analysis summary findingCount"); - validateNonNegativeInteger(summary.overlayCount, "Clone analysis summary overlayCount"); - if (summary.findingCount !== findingsLength) { - throw new Error("Clone analysis summary findingCount must equal findings length"); - } - return summary; -} - -function validateCloneClassId(value: unknown, label: string): string { - const id = validateNonEmptyString(value, label); - if (!/^clone-[a-f0-9]{16}$/u.test(id)) { - throw new Error(`${label} must be a stable clone id`); - } - return id; -} - -function validateSha256Hex(value: unknown, label: string): string { - const sha = validateNonEmptyString(value, label); - if (!/^[a-f0-9]{64}$/u.test(sha)) { - throw new Error(`${label} must be a SHA-256 hex digest`); - } - return sha; -} - -export function validateValidationRequestPayload(request: ValidationRequest): ValidationRequest { - if (!request || typeof request !== "object") { - throw new Error("Validation request is required"); - } - if (request.requestId !== undefined) validateNonEmptyString(request.requestId, "Validation request requestId"); - validateRepoIdentity(request.repo); - validateValidationScope(request.scope); - validateValidationGraphConfig(request.graph); - validateHypotheticalOverlays(request.overlays); - if (request.checks !== undefined) validateValidationChecks(request.checks, "Validation request checks"); - if (request.reportMode !== undefined && !includesString(validationReportModes, request.reportMode)) { - throw new Error(`Unknown validation request reportMode: ${String(request.reportMode)}`); - } - return request; -} - -export function validateValidationResultPayload(result: ValidationResult): ValidationResult { - if (!result || typeof result !== "object") { - throw new Error("Validation result is required"); - } - if (typeof result.ok !== "boolean") { - throw new Error("Validation result ok must be boolean"); - } - if (!includesString(validationResultStatuses, result.status)) { - throw new Error(`Unknown validation result status: ${String(result.status)}`); - } - if (result.status === "passed" && !result.ok) { - throw new Error("Validation passed result must use ok=true"); - } - if (result.ok && result.status !== "passed") { - throw new Error("Validation result ok=true must use passed status"); - } - validateValidationDiagnostics(result.diagnostics); - if (result.graphStatus !== undefined) validateProviderStatus(result.graphStatus); - if (result.failure !== undefined) validateValidationFailure(result.failure); - if (result.refusal !== undefined) validateEditRefusal(result.refusal); - if (result.status === "refused" && result.refusal === undefined) { - throw new Error("Validation refused result must include refusal"); - } - if (result.status === "refused" && result.failure !== undefined) { - throw new Error("Validation refused result must not include failure"); - } - if (includesString(validationFailureCategories, result.status) && result.failure === undefined) { - throw new Error(`Validation ${result.status} result must include failure`); - } - if (includesString(validationFailureCategories, result.status)) { - if (result.failure?.category !== result.status) { - throw new Error("Validation failure category must match result status"); - } - if (result.refusal !== undefined) { - throw new Error("Validation failure result must not include refusal"); - } - } - if (result.status === "passed" && (result.failure !== undefined || result.refusal !== undefined)) { - throw new Error("Validation passed result must not include failure or refusal"); - } - if (result.manifest !== undefined) { - validateValidationResultManifest(result.manifest); - } - if (result.pythonProjectContexts !== undefined) validatePythonProjectContexts(result.pythonProjectContexts); - if (result.pythonCapabilityRuns !== undefined) validatePythonValidationCapabilityRuns(result.pythonCapabilityRuns); - return result; -} - -export function validatePythonValidationCapabilityRun( - run: PythonValidationCapabilityRun -): PythonValidationCapabilityRun { - if (run.capability === "types") return validatePythonTypesValidationCapabilityRun(run); - if (run.capability === "pytest") return validatePythonPytestValidationCapabilityRun(run); - return validatePythonRuffValidationCapabilityRun(run); -} - -function validatePythonTypesValidationCapabilityRun( - run: PythonTypesValidationCapabilityRun -): PythonTypesValidationCapabilityRun { - if (!run || typeof run !== "object") throw new Error("Python validation capability run is required"); - validatePythonCapabilityRunShape(run); - validatePythonCapabilityRunIdentity(run); - validatePythonCapabilityRunCounts(run); - if (run.tool !== undefined) validatePythonValidationCapabilityTool(run.tool, run); - if (run.execution !== undefined) validatePythonValidationCapabilityExecution(run.execution); - validatePythonCapabilityRunStatus(run); - return run; -} - -function validatePythonCapabilityRunShape(run: PythonTypesValidationCapabilityRun): void { - validateExactObjectKeys(run, [ - "schemaId", "schemaVersion", "capability", "checkId", "projectKey", "contextFingerprint", "projectRoot", - "targets", "selectedSourcePaths", "selectedConfigPaths", "afterStateManifestFingerprint", "authority", - "authoritySource", "status", "tool", "execution", "durationMs", "diagnosticCount", "errorCount", - "warningCount", "noteCount" - ], "Python validation capability run"); - if (run.schemaId !== PYTHON_VALIDATION_CAPABILITY_RUN_SCHEMA_ID) { - throw new Error(`Python validation capability run schemaId must be ${PYTHON_VALIDATION_CAPABILITY_RUN_SCHEMA_ID}`); - } - if (run.schemaVersion !== 1) throw new Error("Python validation capability run schemaVersion must be 1"); - if (run.capability !== "types" || run.checkId !== "python.types") { - throw new Error("Python validation capability run must describe python.types"); - } -} - -function validatePythonCapabilityRunIdentity(run: PythonTypesValidationCapabilityRun): void { - validateSha256Identity(run.projectKey, "Python validation capability run projectKey"); - validateSha256Identity(run.contextFingerprint, "Python validation capability run contextFingerprint"); - validatePythonProjectRoot(run.projectRoot, "Python validation capability run projectRoot"); - validateSortedUniqueRepoPaths(run.targets, "Python validation capability run targets", false); - validateSortedUniqueRepoPaths(run.selectedSourcePaths, "Python validation capability run selectedSourcePaths", false); - validateSortedUniqueRepoPaths(run.selectedConfigPaths, "Python validation capability run selectedConfigPaths", true); - for (const target of run.targets) { - if (!run.selectedSourcePaths.includes(target)) { - throw new Error("Python validation capability run targets must be selected source paths"); - } - } - validateSha256Identity(run.afterStateManifestFingerprint, "Python validation capability run afterStateManifestFingerprint"); - if (run.authority !== undefined && !includesString(pythonValidationAuthorities, run.authority)) { - throw new Error(`Unknown Python validation authority: ${String(run.authority)}`); - } - if (run.authoritySource !== undefined && !includesString(pythonValidationAuthoritySources, run.authoritySource)) { - throw new Error(`Unknown Python validation authority source: ${String(run.authoritySource)}`); - } - if ((run.authority === undefined) !== (run.authoritySource === undefined)) { - throw new Error("Python validation capability authority and authoritySource must be present together"); - } - if (run.authority === undefined && run.status !== "invalid_config" && run.status !== "unsupported_target") { - throw new Error(`Python validation capability run ${run.status} requires selected authority evidence`); - } - if (!includesString(pythonValidationCapabilityRunStatuses, run.status)) { - throw new Error(`Unknown Python validation capability run status: ${String(run.status)}`); - } -} - -function validatePythonCapabilityRunCounts(run: PythonTypesValidationCapabilityRun): void { - for (const [key, value] of [ - ["durationMs", run.durationMs], ["diagnosticCount", run.diagnosticCount], ["errorCount", run.errorCount], - ["warningCount", run.warningCount], ["noteCount", run.noteCount] - ] as const) validateNonNegativeInteger(value, `Python validation capability run ${key}`); - if (run.diagnosticCount !== run.errorCount + run.warningCount + run.noteCount) { - throw new Error("Python validation capability run diagnosticCount must equal severity counts"); - } -} - -function validatePythonCapabilityRunStatus(run: PythonTypesValidationCapabilityRun): void { - if (run.status === "passed") validatePassedPythonCapability(run); - if (run.status === "findings") validateFindingsPythonCapability(run); - if (run.status === "timeout") validateTimeoutPythonCapability(run); - if (run.status === "invalid_config") validateInvalidPythonCapability(run); - if (run.status === "unsupported_target") validateUnexecutedPythonCapability(run); - if (run.status === "tool_unavailable") validateUnavailablePythonCapability(run); - if (run.status === "tool_failure") validateFailedPythonCapability(run); -} - -function validatePassedPythonCapability(run: PythonTypesValidationCapabilityRun): void { - requireExitedPythonCapability(run); - if (run.execution?.exitCode !== 0 || run.errorCount !== 0) { - throw new Error("Passed Python validation capability run requires exit 0 and zero errors"); - } -} - -function validateFindingsPythonCapability(run: PythonTypesValidationCapabilityRun): void { - requireExitedPythonCapability(run); - if (run.execution?.exitCode !== 1 || run.diagnosticCount === 0) { - throw new Error("Findings Python validation capability run requires exit 1 and diagnostics"); - } - if (run.errorCount + run.warningCount === 0) { - throw new Error("Findings Python validation capability run requires an error or warning"); - } -} - -function requireExitedPythonCapability(run: PythonTypesValidationCapabilityRun): void { - if (run.tool === undefined || run.execution?.termination !== "exited") { - throw new Error(`Python validation capability run ${run.status} requires exited tool evidence`); - } -} - -function validateTimeoutPythonCapability(run: PythonTypesValidationCapabilityRun): void { - if (run.tool === undefined || run.execution?.termination !== "timeout" || run.execution.failureSummary === undefined) { - throw new Error("Timeout Python validation capability run requires tool and timeout failure evidence"); - } -} - -function validateInvalidPythonCapability(run: PythonTypesValidationCapabilityRun): void { - if (run.authority === undefined && (run.tool !== undefined || run.execution !== undefined)) { - throw new Error("Unselected invalid-config Python validation capability run must not include tool or execution evidence"); - } - if (run.execution === undefined) return; - if (run.tool === undefined || run.execution.termination !== "exited" || run.execution.failureSummary === undefined) { - throw new Error("Executed invalid-config Python validation capability run requires exited tool failure evidence"); - } -} - -function validateUnexecutedPythonCapability(run: PythonTypesValidationCapabilityRun): void { - if (run.execution !== undefined) throw new Error(`${run.status} Python validation capability run must not include execution evidence`); -} - -function validateUnavailablePythonCapability(run: PythonTypesValidationCapabilityRun): void { - if (run.tool === undefined || run.execution !== undefined) { - throw new Error("Tool-unavailable Python validation capability run requires tool provenance without execution"); - } -} - -function validateFailedPythonCapability(run: PythonTypesValidationCapabilityRun): void { - if (run.tool === undefined || run.execution === undefined || run.execution.termination === "timeout" || run.execution.failureSummary === undefined) { - throw new Error("Tool-failure Python validation capability run requires non-timeout tool failure evidence"); - } -} - -export function validatePythonValidationCapabilityRuns( - runs: readonly PythonValidationCapabilityRun[] -): readonly PythonValidationCapabilityRun[] { - if (!Array.isArray(runs)) throw new Error("Python validation capability runs must be an array"); - for (const run of runs) validatePythonValidationCapabilityRun(run); - return runs; -} - -function validatePythonPytestValidationCapabilityRun( - run: PythonPytestValidationCapabilityRun -): PythonPytestValidationCapabilityRun { - if (!run || typeof run !== "object") throw new Error("Python pytest capability run is required"); - validateExactObjectKeys(run, [ - "capability", - "checkId", - "activation", - "outcome", - "message", - "projectKey", - "projectRoot", - "configFile", - "targetCount", - "candidatePaths", - "collectedNodeIds", - "afterStateFingerprint", - "selectionMode", - "selectionDigest", - "counts", - "collection", - "execution", - "cleanup" - ], "Python pytest capability run"); - if (run.capability !== "pytest" || run.checkId !== "python.pytest") { - throw new Error("Python pytest capability run must describe python.pytest"); - } - if (!includesString(pythonCapabilityActivations, run.activation)) { - throw new Error(`Unknown Python pytest capability activation: ${String(run.activation)}`); - } - validateNonEmptyString(run.outcome, "Python pytest capability run outcome"); - validateNonEmptyString(run.message, "Python pytest capability run message"); - if (run.projectKey !== undefined) validateSha256Identity(run.projectKey, "Python pytest capability run projectKey"); - if (run.projectRoot !== undefined) validatePythonProjectRoot(run.projectRoot, "Python pytest capability run projectRoot"); - if (run.configFile !== undefined) validateRepoRelativePath(run.configFile); - if (run.targetCount !== undefined) validateNonNegativeInteger(run.targetCount, "Python pytest capability run targetCount"); - if (run.candidatePaths !== undefined) validateRepoPathArray(run.candidatePaths, "Python pytest capability run candidatePaths"); - if (run.collectedNodeIds !== undefined) { - validateStringArray(run.collectedNodeIds, "Python pytest capability run collectedNodeIds", { allowEmpty: true }); - } - if (run.afterStateFingerprint !== undefined) { - validateSha256Identity(run.afterStateFingerprint, "Python pytest capability run afterStateFingerprint"); - } - if (run.selectionMode !== undefined && !includesString(pythonPytestSelectionModes, run.selectionMode)) { - throw new Error(`Unknown Python pytest capability selection mode: ${String(run.selectionMode)}`); - } - if (run.selectionDigest !== undefined) { - validateSha256Identity(run.selectionDigest, "Python pytest capability run selectionDigest"); - } - if (run.counts !== undefined) validatePythonPytestCapabilityCounts(run.counts); - if (run.collection !== undefined) validatePythonPytestCapabilityInvocation(run.collection, "Python pytest capability collection"); - if (run.execution !== undefined) validatePythonPytestCapabilityInvocation(run.execution, "Python pytest capability execution"); - if (run.cleanup !== undefined) validatePythonPytestCapabilityCleanupEvidence(run.cleanup); - return run; -} - -function validatePythonPytestCapabilityCounts(counts: PythonCapabilityCounts): void { - if (!counts || typeof counts !== "object") throw new Error("Python pytest capability counts are required"); - validateExactObjectKeys(counts, [ - "candidateCount", - "collectedCount", - "executedCount", - "passedCount", - "failedCount", - "skippedCount", - "xfailedCount", - "xpassedCount", - "errorCount" - ], "Python pytest capability counts"); - for (const key of Object.keys(counts) as (keyof PythonCapabilityCounts)[]) { - validateNonNegativeInteger(counts[key], `Python pytest capability counts ${key}`); - } -} - -function validatePythonPytestCapabilityInvocation(invocation: PythonCapabilityInvocation, label: string): void { - if (!invocation || typeof invocation !== "object") throw new Error(`${label} is required`); - validateExactObjectKeys(invocation, [ - "stage", - "command", - "argsDigest", - "argCount", - "selectionMode", - "selectionDigest", - "durationMs", - "termination", - "exitCode", - "signal", - "outputBytes", - "stdoutDigest", - "stderrDigest" - ], label); - if (!includesString(["collection", "execution"] as const, invocation.stage)) { - throw new Error(`${label} stage must be collection or execution`); - } - validateNonEmptyString(invocation.command, `${label} command`); - validateSha256Identity(invocation.argsDigest, `${label} argsDigest`); - validateNonNegativeInteger(invocation.argCount, `${label} argCount`); - if (!includesString(pythonPytestSelectionModes, invocation.selectionMode)) { - throw new Error(`Unknown ${label} selectionMode: ${String(invocation.selectionMode)}`); - } - if (invocation.selectionDigest !== undefined) { - validateSha256Identity(invocation.selectionDigest, `${label} selectionDigest`); - } - validateNonNegativeNumber(invocation.durationMs, `${label} durationMs`); - if (!includesString(pythonCapabilityProcessTerminations, invocation.termination)) { - throw new Error(`Unknown ${label} termination: ${String(invocation.termination)}`); - } - if (invocation.exitCode !== undefined) validateNonNegativeInteger(invocation.exitCode, `${label} exitCode`); - if (invocation.signal !== undefined) validateNonEmptyString(invocation.signal, `${label} signal`); - validateNonNegativeInteger(invocation.outputBytes, `${label} outputBytes`); - if (invocation.stdoutDigest !== undefined) validateSha256Identity(invocation.stdoutDigest, `${label} stdoutDigest`); - if (invocation.stderrDigest !== undefined) validateSha256Identity(invocation.stderrDigest, `${label} stderrDigest`); -} - -function validatePythonPytestCapabilityCleanupEvidence(cleanup: PythonCapabilityCleanupEvidence): void { - if (!cleanup || typeof cleanup !== "object") throw new Error("Python pytest capability cleanup evidence is required"); - validateExactObjectKeys(cleanup, ["attempted", "ok", "failureMessage"], "Python pytest capability cleanup evidence"); - if (typeof cleanup.attempted !== "boolean") throw new Error("Python pytest capability cleanup attempted must be boolean"); - if (typeof cleanup.ok !== "boolean") throw new Error("Python pytest capability cleanup ok must be boolean"); - if (cleanup.failureMessage !== undefined) { - validateNonEmptyString(cleanup.failureMessage, "Python pytest capability cleanup failureMessage"); - } -} - -function validatePythonValidationCapabilityTool( - tool: PythonValidationCapabilityToolProvenance, - run: PythonTypesValidationCapabilityRun -): void { - validateExactObjectKeys(tool, ["name", "executable", "argv", "cwd", "source", "version", "configFile"], "Python validation capability tool"); - if (run.authority === undefined || tool.name !== run.authority) throw new Error("Python validation capability tool must match authority"); - validatePortablePythonCapabilityExecutable(tool.executable); - validateStringArray(tool.argv, "Python validation capability tool argv", { allowEmpty: false }); - if (tool.argv[0] !== tool.executable) throw new Error("Python validation capability tool argv must start with executable"); - for (const argument of tool.argv) { - if (containsHostAbsolutePath(argument)) { - throw new Error("Python validation capability tool requires portable argv without host-absolute paths"); - } - } - validatePythonProjectRoot(tool.cwd, "Python validation capability tool cwd"); - if (tool.cwd !== run.projectRoot) throw new Error("Python validation capability tool cwd must equal projectRoot"); - if (!includesString(pythonProjectExecutableSources, tool.source)) { - throw new Error(`Unknown Python validation capability tool source: ${String(tool.source)}`); - } - if (tool.version !== undefined) { - validateNonEmptyString(tool.version, "Python validation capability tool version"); - if (!/^[0-9]+\.[0-9][-+._A-Za-z0-9]*$/u.test(tool.version)) { - throw new Error("Python validation capability tool version must be exact version provenance"); - } - } - if (tool.configFile !== undefined) { - validateRepoRelativePath(tool.configFile); - if (!run.selectedConfigPaths.includes(tool.configFile)) { - throw new Error("Python validation capability tool configFile must be a selected config path"); - } - } -} - -function validatePortablePythonCapabilityExecutable(executable: string): void { - validateNonEmptyString(executable, "Python validation capability tool executable"); - const match = /^(repo|project|path|external):(.+)$/u.exec(executable); - if (match === null) throw new Error("Python validation capability tool requires a portable executable locator"); - const [, kind, value] = match; - if (kind === "repo" || kind === "project") { - try { - validateRepoRelativePath(value); - } catch { - throw new Error("Python validation capability tool requires a portable executable locator"); - } - return; - } - if (!/^[A-Za-z0-9_.+-]+$/u.test(value)) { - throw new Error("Python validation capability tool requires a portable executable locator"); - } -} - -function containsHostAbsolutePath(value: string): boolean { - return /^(?:\/|\\\\|[A-Za-z]:[\\/])/u.test(value) || - /[\s("'=](?:\/|\\\\|[A-Za-z]:[\\/])/u.test(value) || - /file:\/\//iu.test(value); -} - -function validatePythonValidationCapabilityExecution(execution: PythonValidationCapabilityExecution): void { - validateExactObjectKeys(execution, ["termination", "exitCode", "signal", "failureSummary"], "Python validation capability execution"); - if (!includesString(pythonValidationCapabilityTerminationKinds, execution.termination)) { - throw new Error(`Unknown Python validation capability termination: ${String(execution.termination)}`); - } - validatePythonCapabilityExit(execution); - validatePythonCapabilitySignal(execution); - validatePythonCapabilityFailureSummary(execution); -} - -function validatePythonCapabilityExit(execution: PythonValidationCapabilityExecution): void { - if (execution.exitCode !== undefined) validateNonNegativeInteger(execution.exitCode, "Python validation capability execution exitCode"); - if (execution.termination === "exited" && execution.exitCode === undefined) { - throw new Error("Exited Python validation capability execution requires exitCode"); - } - if (execution.termination !== "exited" && execution.exitCode !== undefined) { - throw new Error("Non-exited Python validation capability execution must not include exitCode"); - } -} - -function validatePythonCapabilitySignal(execution: PythonValidationCapabilityExecution): void { - if (execution.termination === "signal" && execution.signal === undefined) { - throw new Error("Signaled Python validation capability execution requires signal"); - } - if (execution.termination !== "signal" && execution.signal !== undefined) { - throw new Error("Non-signaled Python validation capability execution must not include signal"); - } - if (execution.signal !== undefined) validateNonEmptyString(execution.signal, "Python validation capability execution signal"); -} - -function validatePythonCapabilityFailureSummary(execution: PythonValidationCapabilityExecution): void { - if (execution.failureSummary !== undefined) { - validateNonEmptyString(execution.failureSummary, "Python validation capability execution failureSummary"); - if (execution.failureSummary.length > 1024) throw new Error("Python validation capability execution failureSummary is too long"); - if (containsHostAbsolutePath(execution.failureSummary)) { - throw new Error("Python validation capability execution failureSummary must not contain host-absolute paths"); - } - } - if (execution.termination !== "exited" && execution.failureSummary === undefined) { - throw new Error("Non-exited Python validation capability execution requires failureSummary"); - } -} - -function validateSortedUniqueRepoPaths(values: readonly string[], label: string, allowEmpty: boolean): void { - if (!Array.isArray(values) || (!allowEmpty && values.length === 0)) { - throw new Error(`${label} must be ${allowEmpty ? "an" : "a non-empty"} array`); - } - for (const value of values) validateRepoRelativePath(value); - const sorted = [...new Set(values)].sort(); - if (sorted.length !== values.length || sorted.some((value, index) => value !== values[index])) { - throw new Error(`${label} must be sorted and unique`); - } -} - -export function validatePythonProjectContext(context: PythonProjectContext): PythonProjectContext { - if (!context || typeof context !== "object") throw new Error("Python project context is required"); - validateExactObjectKeys(context, [ - "schemaId", "schemaVersion", "target", "repositoryRoot", "projectRoot", "projectBoundary", "sourceRoots", - "layout", "evidence", "targetRuntime", "managers", "buildSystem", "interpreter", "tools", "projectKey", - "contextFingerprint", "outcome", "reasons" - ], "Python project context"); - if (context.schemaId !== PYTHON_PROJECT_CONTEXT_SCHEMA_ID) { - throw new Error(`Python project context schemaId must be ${PYTHON_PROJECT_CONTEXT_SCHEMA_ID}`); - } - if (context.schemaVersion !== 1) throw new Error("Python project context schemaVersion must be 1"); - validateRepoRelativePath(context.target); - if (!/\.pyi?$/u.test(context.target)) throw new Error("Python project context target must be a .py or .pyi path"); - validateNonEmptyString(context.repositoryRoot, "Python project context repositoryRoot"); - validatePythonProjectRoot(context.projectRoot, "Python project context projectRoot"); - validatePythonProjectRoot(context.projectBoundary, "Python project context projectBoundary"); - validatePythonProjectRoots(context.sourceRoots, "Python project context sourceRoots"); - if (!context.layout || typeof context.layout !== "object") throw new Error("Python project context layout is required"); - validateExactObjectKeys(context.layout, ["kinds", "paths"], "Python project context layout"); - validateExactEnumArray(context.layout.kinds, pythonProjectLayoutKinds, "Python project context layout kinds", false); - validatePythonProjectRoots(context.layout.paths, "Python project context layout paths"); - if (!Array.isArray(context.evidence)) throw new Error("Python project context evidence must be an array"); - for (const entry of context.evidence) { - validateExactObjectKeys(entry, ["path", "role"], "Python project context evidence"); - validateRepoRelativePath(entry.path); - if (!includesString(["boundary", "config", "lock", "requirements", "build", "layout"] as const, entry.role)) { - throw new Error(`Unknown Python project evidence role: ${String(entry.role)}`); - } - } - validatePythonProjectTarget(context.targetRuntime); - if (!Array.isArray(context.managers)) throw new Error("Python project context managers must be an array"); - for (const manager of context.managers) { - validateExactObjectKeys(manager, ["kind", "configFiles", "lockFiles"], "Python project manager evidence"); - if (!includesString(pythonProjectManagerKinds, manager.kind)) { - throw new Error(`Unknown Python project manager kind: ${String(manager.kind)}`); - } - validateRepoPathArray(manager.configFiles, "Python project manager configFiles"); - validateRepoPathArray(manager.lockFiles, "Python project manager lockFiles"); - } - if (context.buildSystem !== undefined) { - validateExactObjectKeys(context.buildSystem, ["configFile", "backend", "requires"], "Python project buildSystem"); - validateRepoRelativePath(context.buildSystem.configFile); - if (context.buildSystem.backend !== undefined) { - validateNonEmptyString(context.buildSystem.backend, "Python project buildSystem backend"); - } - validateStringArray(context.buildSystem.requires, "Python project buildSystem requires", { allowEmpty: true }); - } - if (context.interpreter !== undefined) validatePythonInterpreterProvenance(context.interpreter); - if (!Array.isArray(context.tools)) throw new Error("Python project context tools must be an array"); - for (const tool of context.tools) { - validateExactObjectKeys( - tool, - ["tool", "available", "executable", "argv", "cwd", "source", "version", "configFile"], - "Python project tool provenance" - ); - if (!includesString(pythonProjectToolKinds, tool.tool)) throw new Error(`Unknown Python project tool: ${String(tool.tool)}`); - if (typeof tool.available !== "boolean") throw new Error("Python project tool available must be boolean"); - validatePythonExecutableProvenance(tool, `Python project tool ${tool.tool}`); - if (tool.available && tool.version === undefined) { - throw new Error(`Available Python project tool ${tool.tool} must include version provenance`); - } - } - validateSha256Identity(context.projectKey, "Python project context projectKey"); - validateSha256Identity(context.contextFingerprint, "Python project context contextFingerprint"); - if (!includesString(pythonProjectContextOutcomes, context.outcome)) { - throw new Error(`Unknown Python project context outcome: ${String(context.outcome)}`); - } - if (!Array.isArray(context.reasons)) throw new Error("Python project context reasons must be an array"); - for (const reason of context.reasons) { - validateExactObjectKeys(reason, ["code", "message", "path", "tool"], "Python project context reason"); - if (!includesString(pythonProjectContextReasonCodes, reason.code)) { - throw new Error(`Unknown Python project context reason: ${String(reason.code)}`); - } - validateNonEmptyString(reason.message, "Python project context reason message"); - if (reason.path !== undefined) validateRepoRelativePath(reason.path); - if (reason.tool !== undefined) validateNonEmptyString(reason.tool, "Python project context reason tool"); - } - if (context.outcome === "resolved" && context.reasons.length > 0) { - throw new Error("Resolved Python project context must not include reasons"); - } - if (context.outcome !== "resolved" && context.reasons.length === 0) { - throw new Error("Non-resolved Python project context must include reasons"); - } - return context; -} - -export function validatePythonProjectContexts(contexts: readonly PythonProjectContext[]): readonly PythonProjectContext[] { - if (!Array.isArray(contexts)) throw new Error("Python project contexts must be an array"); - const targets = new Set(); - for (const context of contexts) { - validatePythonProjectContext(context); - if (targets.has(context.target)) throw new Error(`Duplicate Python project context target: ${context.target}`); - targets.add(context.target); - } - return contexts; -} - -function validatePythonCapabilityCounts(counts: PythonCapabilityCounts): void { - if (!counts || typeof counts !== "object") throw new Error("Python capability counts are required"); - validateExactObjectKeys(counts, [ - "candidateCount", - "collectedCount", - "executedCount", - "passedCount", - "failedCount", - "skippedCount", - "xfailedCount", - "xpassedCount", - "errorCount" - ], "Python capability counts"); - for (const key of Object.keys(counts) as (keyof PythonCapabilityCounts)[]) { - validateNonNegativeInteger(counts[key], `Python capability counts ${key}`); - } -} - -function validatePythonCapabilityCleanupEvidence(cleanup: PythonCapabilityCleanupEvidence): void { - if (!cleanup || typeof cleanup !== "object") throw new Error("Python capability cleanup evidence is required"); - validateExactObjectKeys(cleanup, ["attempted", "ok", "failureMessage"], "Python capability cleanup evidence"); - if (typeof cleanup.attempted !== "boolean") throw new Error("Python capability cleanup attempted must be boolean"); - if (typeof cleanup.ok !== "boolean") throw new Error("Python capability cleanup ok must be boolean"); - if (cleanup.failureMessage !== undefined) { - validateNonEmptyString(cleanup.failureMessage, "Python capability cleanup failureMessage"); - } -} - -function validatePythonCapabilityInvocation(invocation: PythonCapabilityInvocation, label: string): void { - if (!invocation || typeof invocation !== "object") throw new Error(`${label} is required`); - validateExactObjectKeys(invocation, [ - "stage", - "command", - "argsDigest", - "argCount", - "selectionMode", - "selectionDigest", - "durationMs", - "termination", - "exitCode", - "signal", - "outputBytes", - "stdoutDigest", - "stderrDigest" - ], label); - if (!includesString(["collection", "execution"] as const, invocation.stage)) { - throw new Error(`${label} stage must be collection or execution`); - } - validateNonEmptyString(invocation.command, `${label} command`); - validateSha256Identity(invocation.argsDigest, `${label} argsDigest`); - validateNonNegativeInteger(invocation.argCount, `${label} argCount`); - if (!includesString(pythonPytestSelectionModes, invocation.selectionMode)) { - throw new Error(`Unknown ${label} selectionMode: ${String(invocation.selectionMode)}`); - } - if (invocation.selectionDigest !== undefined) validateSha256Identity(invocation.selectionDigest, `${label} selectionDigest`); - validateNonNegativeInteger(invocation.durationMs, `${label} durationMs`); - if (!includesString(pythonCapabilityProcessTerminations, invocation.termination)) { - throw new Error(`Unknown ${label} termination: ${String(invocation.termination)}`); - } - if (invocation.exitCode !== undefined) validateNonNegativeInteger(invocation.exitCode, `${label} exitCode`); - if (invocation.signal !== undefined) validateNonEmptyString(invocation.signal, `${label} signal`); - validateNonNegativeInteger(invocation.outputBytes, `${label} outputBytes`); - if (invocation.stdoutDigest !== undefined) validateSha256Identity(invocation.stdoutDigest, `${label} stdoutDigest`); - if (invocation.stderrDigest !== undefined) validateSha256Identity(invocation.stderrDigest, `${label} stderrDigest`); -} - -function validatePythonProjectTarget(target: PythonProjectTarget): void { - if (!target || typeof target !== "object") throw new Error("Python project targetRuntime is required"); - validateExactObjectKeys( - target, - ["requiresPython", "version", "platform", "implementation", "conflicts"], - "Python project targetRuntime" - ); - for (const [key, value] of Object.entries(target)) { - if (key === "conflicts") continue; - if (value !== undefined) validateNonEmptyString(value, `Python project targetRuntime ${key}`); - } - validateStringArray(target.conflicts, "Python project targetRuntime conflicts", { allowEmpty: true }); -} - -function validatePythonExecutableProvenance(value: PythonProjectExecutableProvenance, label: string): void { - if (!value || typeof value !== "object") throw new Error(`${label} provenance is required`); - validateNonEmptyString(value.executable, `${label} executable`); - validateStringArray(value.argv, `${label} argv`, { allowEmpty: false }); - if (value.argv[0] !== value.executable) throw new Error(`${label} argv must start with executable`); - validateNonEmptyString(value.cwd, `${label} cwd`); - if (!includesString(pythonProjectExecutableSources, value.source)) throw new Error(`Unknown ${label} source: ${String(value.source)}`); - if (value.version !== undefined) { - validateNonEmptyString(value.version, `${label} version`); - if (!/^[0-9]+\.[0-9][-+._A-Za-z0-9]*$/u.test(value.version)) { - throw new Error(`${label} version must be exact version provenance`); - } - } - if (value.configFile !== undefined) validateRepoRelativePath(value.configFile); -} - -function validatePythonInterpreterProvenance(value: PythonInterpreterProvenance): void { - validateExactObjectKeys(value, [ - "executable", "argv", "cwd", "source", "version", "configFile", "implementation", "platform", "architecture", - "abi", "soabi" - ], "Python interpreter provenance"); - validatePythonExecutableProvenance(value, "Python interpreter"); - if (!/^\d+\.\d+\.\d+(?:(?:a|b|rc)\d+)?(?:\+[A-Za-z0-9]+(?:[._-][A-Za-z0-9]+)*)?$/u.test(value.version)) { - throw new Error("Python interpreter version must be an exact Python version"); - } - for (const [key, field] of [ - ["implementation", value.implementation], - ["platform", value.platform], - ["architecture", value.architecture], - ["abi", value.abi], - ["soabi", value.soabi] - ] as const) { - validateNonEmptyString(field, `Python interpreter ${key}`); - } -} - -function validatePythonProjectRoot(value: string, label: string): void { - if (value === ".") return; - validateRepoRelativePath(value); -} - -function validatePythonProjectRoots(values: readonly string[], label: string): void { - if (!Array.isArray(values) || values.length === 0) throw new Error(`${label} must be a non-empty array`); - for (const value of values) validatePythonProjectRoot(value, label); -} - -function validateRepoPathArray(values: readonly string[], label: string): void { - if (!Array.isArray(values)) throw new Error(`${label} must be an array`); - for (const value of values) validateRepoRelativePath(value); -} - -function validateExactEnumArray( - values: readonly T[], allowed: readonly T[], label: string, requireAll: boolean -): void { - if (!Array.isArray(values) || values.length === 0) throw new Error(`${label} must be a non-empty array`); - const seen = new Set(); - for (const value of values) { - if (!includesString(allowed, value)) throw new Error(`Unknown ${label} value: ${String(value)}`); - if (seen.has(value)) throw new Error(`${label} must not contain duplicates`); - seen.add(value); - } - if (requireAll && seen.size !== allowed.length) throw new Error(`${label} must contain every supported value`); -} - -function validateSha256Identity(value: string, label: string): void { - if (!/^sha256:[a-f0-9]{64}$/u.test(value)) throw new Error(`${label} must be a sha256 identity`); -} - -function validateExactObjectKeys(value: object, allowedKeys: readonly string[], label: string): void { - const allowed = new Set(allowedKeys); - const unexpected = Object.keys(value).filter((key) => !allowed.has(key)); - if (unexpected.length > 0) { - throw new Error(`${label} has unexpected properties: ${unexpected.sort().join(", ")}`); - } -} - -export function validateRequiredContextDocPolicy(policy: RequiredContextDocPolicy): RequiredContextDocPolicy { - if (!policy || typeof policy !== "object") { - throw new Error("Required context doc policy is required"); - } - validateStringArray(policy.filenames, "Required context doc policy filenames", { allowEmpty: false }); - for (const filename of policy.filenames) validateContextDocFilename(filename); - validateStringArray(policy.requiredPaths, "Required context doc policy requiredPaths", { allowEmpty: false }); - for (const path of policy.requiredPaths) validateContextDocRequiredPath(path); - if (policy.requireRoot !== undefined && typeof policy.requireRoot !== "boolean") { - throw new Error("Required context doc policy requireRoot must be boolean"); - } - if (!Number.isInteger(policy.minimumContentLength) || policy.minimumContentLength < 1) { - throw new Error("Required context doc policy minimumContentLength must be a positive integer"); - } - if (policy.maxLines !== undefined) validatePositiveInteger(policy.maxLines, "Required context doc policy maxLines"); - if (policy.maxSectionLines !== undefined) { - validatePositiveInteger(policy.maxSectionLines, "Required context doc policy maxSectionLines"); - } - return policy; -} - -export function validatePreWriteValidationReceipt(receipt: PreWriteValidationReceipt): PreWriteValidationReceipt { - if (!receipt || typeof receipt !== "object") { - throw new Error("Pre-write validation receipt is required"); - } - if (receipt.schemaVersion !== 1) { - throw new Error("Pre-write validation receipt schemaVersion must be 1"); - } - if (receipt.kind !== "pre_write_validation") { - throw new Error("Pre-write validation receipt kind must be pre_write_validation"); - } - if (receipt.route !== "validate.pre-write") { - throw new Error("Pre-write validation receipt route must be validate.pre-write"); - } - validateStringArray(receipt.canonicalCommand, "Pre-write validation receipt canonicalCommand", { allowEmpty: false }); - validateNonEmptyString(receipt.generatedAt, "Pre-write validation receipt generatedAt"); - validateNonNegativeNumber(receipt.durationMs, "Pre-write validation receipt durationMs"); - if (!Number.isInteger(receipt.timeoutMs) || receipt.timeoutMs < 1) { - throw new Error("Pre-write validation receipt timeoutMs must be a positive integer"); - } - if (typeof receipt.ok !== "boolean") { - throw new Error("Pre-write validation receipt ok must be boolean"); - } - if (receipt.requestId !== undefined) validateNonEmptyString(receipt.requestId, "Pre-write validation receipt requestId"); - if (receipt.repo !== undefined) validateRepoIdentity(receipt.repo); - if (receipt.scope !== undefined) validateValidationScope(receipt.scope); - if (receipt.checks !== undefined) validateValidationChecks(receipt.checks, "Pre-write validation receipt checks"); - if (receipt.graph !== undefined) validatePreWriteValidationGraph(receipt.graph); - if (receipt.overlays !== undefined) validatePreWriteValidationOverlaySummary(receipt.overlays); - if (!includesString(validationResultStatuses, receipt.validationStatus)) { - throw new Error(`Unknown pre-write validation receipt status: ${String(receipt.validationStatus)}`); - } - if (!Number.isInteger(receipt.diagnosticCount) || receipt.diagnosticCount < 0) { - throw new Error("Pre-write validation receipt diagnosticCount must be a non-negative integer"); - } - if (receipt.failureSummary !== undefined) validatePreWriteValidationFailureSummary(receipt.failureSummary); - if (receipt.ok) { - if (receipt.validationStatus !== "passed") { - throw new Error("Pre-write validation pass receipt must use passed validationStatus"); - } - if ( - receipt.repo === undefined || - receipt.scope === undefined || - receipt.checks === undefined || - receipt.graph === undefined || - receipt.overlays === undefined - ) { - throw new Error("Pre-write validation pass receipt must include repo, scope, checks, graph, and overlays"); - } - if (receipt.failureSummary !== undefined) { - throw new Error("Pre-write validation pass receipt must not include failureSummary"); - } - } else { - if (receipt.validationStatus === "passed") { - throw new Error("Pre-write validation failure receipt must not use passed validationStatus"); - } - if (receipt.failureSummary === undefined) { - throw new Error("Pre-write validation failure receipt must include failureSummary"); - } - } - return receipt; -} - -export function validateValidationStatusPayload(payload: ValidationStatusPayload): ValidationStatusPayload { - if (!payload || typeof payload !== "object") { - throw new Error("Validation status payload is required"); - } - if (payload.schemaVersion !== 1) { - throw new Error("Validation status payload schemaVersion must be 1"); - } - if (typeof payload.ready !== "boolean") { - throw new Error("Validation status payload ready must be boolean"); - } - validateNonEmptyString(payload.generatedAt, "Validation status payload generatedAt"); - if (!payload.adapterRegistry || typeof payload.adapterRegistry !== "object") { - throw new Error("Validation status payload adapterRegistry is required"); - } - validateExactStringSet( - payload.adapterRegistry.checkRoutes, - ["files", "staged", "changed", "tree", "all", "manifest"], - "Validation status payload checkRoutes" - ); - validateExactStringSet( - payload.adapterRegistry.validateRoutes, - ["request", "hypothetical", "pre-write", "manifest"], - "Validation status payload validateRoutes" - ); - validateValidationChecks(payload.adapterRegistry.checkIds, "Validation status payload checkIds"); - if (!Array.isArray(payload.adapterRegistry.entries)) { - throw new Error("Validation status payload entries must be an array"); - } - for (const entry of payload.adapterRegistry.entries) validateValidationCheckManifestEntry(entry); - if (payload.adapterRegistry.adapters !== undefined) { - if (!Array.isArray(payload.adapterRegistry.adapters)) { - throw new Error("Validation status payload adapters must be an array"); - } - for (const adapter of payload.adapterRegistry.adapters) validateValidationAdapterRuntimeStatus(adapter); - } - if (!payload.graph || typeof payload.graph !== "object") { - throw new Error("Validation status payload graph is required"); - } - if (!includesString(graphProviderModes, payload.graph.mode)) { - throw new Error(`Unknown validation status graph mode: ${String(payload.graph.mode)}`); - } - const graphStatus = validateProviderStatus(payload.graph.status); - if (graphStatus.mode !== payload.graph.mode) { - throw new Error("Validation status graph status mode must match graph mode"); - } - if (payload.daemon !== undefined) { - if (!payload.daemon || typeof payload.daemon !== "object") { - throw new Error("Validation status daemon must be an object"); - } - if (!includesString(validationDaemonReadinessStates, payload.daemon.state)) { - throw new Error(`Unknown validation daemon readiness state: ${String(payload.daemon.state)}`); - } - if (payload.daemon.message !== undefined) validateNonEmptyString(payload.daemon.message, "Validation status daemon message"); - } - return payload; -} - -function validateValidationAdapterRuntimeStatus(status: ValidationAdapterRuntimeStatus): ValidationAdapterRuntimeStatus { - if (!status || typeof status !== "object") { - throw new Error("Validation adapter runtime status is required"); - } - validateNonEmptyString(status.adapter, "Validation adapter runtime status adapter"); - if (!includesString(validationAdapterRuntimeStates, status.status)) { - throw new Error(`Unknown validation adapter runtime status: ${String(status.status)}`); - } - validateValidationChecks(status.checkIds, "Validation adapter runtime status checkIds"); - if (status.toolchain !== undefined) { - if (!Array.isArray(status.toolchain)) { - throw new Error("Validation adapter runtime status toolchain must be an array"); - } - for (const tool of status.toolchain) validateValidationAdapterToolchainStatus(tool); - } - if (status.degradedChecks !== undefined) { - if (!Array.isArray(status.degradedChecks)) { - throw new Error("Validation adapter runtime status degradedChecks must be an array"); - } - for (const degradedCheck of status.degradedChecks) validateValidationAdapterDegradedCheckStatus(degradedCheck); - } - if (status.tempWorkspaceRequired !== undefined && typeof status.tempWorkspaceRequired !== "boolean") { - throw new Error("Validation adapter runtime status tempWorkspaceRequired must be boolean"); - } - return status; -} - -function validateValidationAdapterToolchainStatus(status: ValidationAdapterToolchainStatus): ValidationAdapterToolchainStatus { - if (!status || typeof status !== "object") { - throw new Error("Validation adapter toolchain status is required"); - } - validateNonEmptyString(status.tool, "Validation adapter toolchain status tool"); - if (typeof status.available !== "boolean") { - throw new Error("Validation adapter toolchain status available must be boolean"); - } - if (status.command !== undefined) validateNonEmptyString(status.command, "Validation adapter toolchain status command"); - if (status.version !== undefined) validateNonEmptyString(status.version, "Validation adapter toolchain status version"); - if (status.failureMessage !== undefined) { - validateNonEmptyString(status.failureMessage, "Validation adapter toolchain status failureMessage"); - } - if (status.cwd !== undefined) validateNonEmptyString(status.cwd, "Validation adapter toolchain status cwd"); - if (status.configFile !== undefined) { - validateNonEmptyString(status.configFile, "Validation adapter toolchain status configFile"); - } - if (status.source !== undefined) validateNonEmptyString(status.source, "Validation adapter toolchain status source"); - return status; -} - -function validateValidationAdapterDegradedCheckStatus( - status: ValidationAdapterDegradedCheckStatus -): ValidationAdapterDegradedCheckStatus { - if (!status || typeof status !== "object") { - throw new Error("Validation adapter degraded check status is required"); - } - validateValidationCheckId(status.checkId, "Validation adapter degraded check status checkId"); - if (!includesString(validationCheckRunStatuses, status.status)) { - throw new Error(`Unknown validation adapter degraded check status: ${String(status.status)}`); - } - validateNonEmptyString(status.reason, "Validation adapter degraded check status reason"); - validateNonEmptyString(status.message, "Validation adapter degraded check status message"); - if (status.requiredTool !== undefined) { - validateNonEmptyString(status.requiredTool, "Validation adapter degraded check status requiredTool"); - } - if (status.retainedCompatibility !== undefined && typeof status.retainedCompatibility !== "boolean") { - throw new Error("Validation adapter degraded check status retainedCompatibility must be boolean"); - } - if (status.followUpIssue !== undefined) { - validateNonEmptyString(status.followUpIssue, "Validation adapter degraded check status followUpIssue"); - } - if (status.currentUsage !== undefined) { - validateValidationAdapterCurrentUsage(status.currentUsage); - } - return status; -} - -function validateValidationAdapterCurrentUsage( - currentUsage: ValidationAdapterDegradedCheckStatus["currentUsage"] -): NonNullable { - if (!currentUsage || typeof currentUsage !== "object") { - throw new Error("Validation adapter degraded check status currentUsage is required when present"); - } - for (const key of ["opcore", "orchestra", "covibes", "gateway"] as const) { - if (typeof currentUsage[key] !== "boolean") { - throw new Error(`Validation adapter degraded check status currentUsage.${key} must be boolean`); - } - } - return currentUsage; -} - -function validateValidationScope(scope: ValidationScope): ValidationScope { - if (!scope || typeof scope !== "object") { - throw new Error("Validation scope is required"); - } - if (!includesString(validationScopeKinds, scope.kind)) { - throw new Error(`Unknown validation scope kind: ${String((scope as { kind?: unknown }).kind)}`); - } - if (scope.kind === "files") { - validateStringArray(scope.files, "Validation scope files", { allowEmpty: false }); - for (const file of scope.files) validateRepoRelativePath(file); - } - if (scope.kind === "changed") { - validateNonEmptyString(scope.baseRef, "Validation changed scope baseRef"); - } - if (scope.kind === "tree") { - validateNonEmptyString(scope.treeRef, "Validation tree scope treeRef"); - validateNonEmptyString(scope.changedFrom, "Validation tree scope changedFrom"); - } - if (scope.kind === "package") { - validateNonEmptyString(scope.packageName, "Validation package scope packageName"); - validateRepoRelativePath(scope.packageRoot); - } - return scope; -} - -function validateValidationGraphConfig(graph: ValidationGraphConfig): ValidationGraphConfig { - if (!graph || typeof graph !== "object") { - throw new Error("Validation graph config is required"); - } - if (!includesString(graphProviderModes, graph.mode)) { - throw new Error(`Unknown validation graph mode: ${String(graph.mode)}`); - } - if (graph.provider !== undefined) validateNonEmptyString(graph.provider, "Validation graph provider"); - if (graph.maxAgeMs !== undefined && (typeof graph.maxAgeMs !== "number" || graph.maxAgeMs < 0)) { - throw new Error("Validation graph maxAgeMs must be non-negative"); - } - if (graph.status !== undefined) { - validateProviderStatus(graph.status); - if (graph.status.mode !== graph.mode) { - throw new Error("Validation graph status mode must match graph mode"); - } - if (graph.provider !== undefined && graph.status.provider !== graph.provider) { - throw new Error("Validation graph status provider must match graph provider"); - } - } - return graph; -} - -function validateHypotheticalOverlays(overlays: readonly HypotheticalOverlay[]): readonly HypotheticalOverlay[] { - if (!Array.isArray(overlays)) { - throw new Error("Validation request overlays must be an array"); - } - const normalizedPaths = new Set(); - for (const overlay of overlays) { - validateHypotheticalOverlay(overlay); - const normalizedPath = validateRepoRelativePath(overlay.path); - if (normalizedPaths.has(normalizedPath)) { - throw new Error(`Validation request overlays include duplicate path: ${normalizedPath}`); - } - normalizedPaths.add(normalizedPath); - } - return overlays; -} - -function validateHypotheticalOverlay(overlay: HypotheticalOverlay): HypotheticalOverlay { - if (!overlay || typeof overlay !== "object") { - throw new Error("Validation request overlay is required"); - } - validateRepoRelativePath(overlay.path); - if (!includesString(["write", "delete"] as const, overlay.action)) { - throw new Error(`Unknown validation overlay action: ${String((overlay as { action?: unknown }).action)}`); - } - if (overlay.action === "write") { - if (typeof overlay.content !== "string") { - throw new Error("Validation write overlay must include content"); - } - } - if (overlay.action === "delete" && Object.hasOwn(overlay, "content")) { - throw new Error("Validation delete overlay must not include content"); - } - if (overlay.checksumBefore !== undefined) { - validateNonEmptyString(overlay.checksumBefore, "Validation overlay checksumBefore"); - } - return overlay; -} - -function validateValidationDiagnostics(diagnostics: readonly ValidationDiagnostic[]): readonly ValidationDiagnostic[] { - if (!Array.isArray(diagnostics)) { - throw new Error("Validation result diagnostics must be an array"); - } - for (const diagnostic of diagnostics) validateValidationDiagnostic(diagnostic); - return diagnostics; -} - -function validateValidationDiagnostic(diagnostic: ValidationDiagnostic): ValidationDiagnostic { - if (!diagnostic || typeof diagnostic !== "object") { - throw new Error("Validation diagnostic is required"); - } - if (!includesString(validationDiagnosticCategories, diagnostic.category)) { - throw new Error(`Unknown validation diagnostic category: ${String(diagnostic.category)}`); - } - validateNonEmptyString(diagnostic.message, "Validation diagnostic message"); - if (diagnostic.path !== undefined) validateRepoRelativePath(diagnostic.path); - if (!includesString(["info", "warning", "error"] as const, diagnostic.severity)) { - throw new Error(`Unknown validation diagnostic severity: ${String(diagnostic.severity)}`); - } - if (diagnostic.code !== undefined) validateNonEmptyString(diagnostic.code, "Validation diagnostic code"); - validateValidationDiagnosticLocation(diagnostic); - if (diagnostic.tool !== undefined) validateValidationDiagnosticTool(diagnostic.tool); - return diagnostic; -} - -function validateValidationDiagnosticLocation(diagnostic: ValidationDiagnostic): void { - for (const field of ["line", "column", "endLine", "endColumn"] as const) { - if (diagnostic[field] !== undefined) validatePositiveInteger(diagnostic[field], `Validation diagnostic ${field}`); - } - if (diagnostic.column !== undefined && diagnostic.line === undefined) { - throw new Error("Validation diagnostic column requires line"); - } - if ((diagnostic.endLine !== undefined || diagnostic.endColumn !== undefined) && diagnostic.line === undefined) { - throw new Error("Validation diagnostic end location requires line"); - } - if (diagnostic.endColumn !== undefined && diagnostic.endLine === undefined) { - throw new Error("Validation diagnostic endColumn requires endLine"); - } - if (diagnostic.line !== undefined && diagnostic.endLine !== undefined) { - const startsAfterEnd = diagnostic.endLine < diagnostic.line || - (diagnostic.endLine === diagnostic.line && diagnostic.column !== undefined && diagnostic.endColumn !== undefined && diagnostic.endColumn < diagnostic.column); - if (startsAfterEnd) throw new Error("Validation diagnostic end location must not precede start location"); - } -} - -function validateValidationDiagnosticTool(tool: ValidationDiagnosticToolProvenance): void { - if (!tool || typeof tool !== "object") throw new Error("Validation diagnostic tool provenance is required"); - validateNonEmptyString(tool.name, "Validation diagnostic tool name"); - validateNonEmptyString(tool.command, "Validation diagnostic tool command"); - if (tool.version !== undefined) validateNonEmptyString(tool.version, "Validation diagnostic tool version"); - if (tool.source !== undefined) validateNonEmptyString(tool.source, "Validation diagnostic tool source"); - if (tool.cwd !== undefined) validateNonEmptyString(tool.cwd, "Validation diagnostic tool cwd"); -} - -function validateValidationResultManifest(manifest: ValidationResultManifest): ValidationResultManifest { - if (!manifest || typeof manifest !== "object") { - throw new Error("Validation result manifest is required"); - } - if (manifest.schemaVersion !== GRAPH_SCHEMA_VERSION) { - throw new Error(`Validation result manifest schemaVersion must be ${GRAPH_SCHEMA_VERSION}`); - } - validateValidationChecks(manifest.checks, "Validation result manifest checks"); - validateNonEmptyString(manifest.generatedAt, "Validation result manifest generatedAt"); - if (manifest.durationMs !== undefined) validateNonNegativeNumber(manifest.durationMs, "Validation result manifest durationMs"); - if (manifest.entries !== undefined) { - if (!Array.isArray(manifest.entries)) { - throw new Error("Validation result manifest entries must be an array"); - } - for (const entry of manifest.entries) validateValidationCheckManifestEntry(entry); - } - if (manifest.runs !== undefined) { - if (!Array.isArray(manifest.runs)) { - throw new Error("Validation result manifest runs must be an array"); - } - for (const run of manifest.runs) validateValidationCheckRunSummary(run); - } - if (manifest.skippedChecks !== undefined) { - if (!Array.isArray(manifest.skippedChecks)) { - throw new Error("Validation result manifest skippedChecks must be an array"); - } - for (const skippedCheck of manifest.skippedChecks) validateValidationSkippedCheck(skippedCheck); - } - return manifest; -} - -function validatePreWriteValidationGraph(graph: PreWriteValidationReceipt["graph"]): void { - if (!graph || typeof graph !== "object") { - throw new Error("Pre-write validation receipt graph is required"); - } - if (!includesString(graphProviderModes, graph.mode)) { - throw new Error(`Unknown pre-write validation receipt graph mode: ${String(graph.mode)}`); - } - if (graph.provider !== undefined) validateNonEmptyString(graph.provider, "Pre-write validation receipt graph provider"); - if (graph.status !== undefined) { - validateProviderStatus(graph.status); - if (graph.status.mode !== graph.mode) { - throw new Error("Pre-write validation receipt graph status mode must match graph mode"); - } - if (graph.provider !== undefined && graph.status.provider !== graph.provider) { - throw new Error("Pre-write validation receipt graph status provider must match graph provider"); - } - } -} - -function validatePreWriteValidationOverlaySummary(summary: PreWriteValidationOverlaySummary): void { - if (!summary || typeof summary !== "object") { - throw new Error("Pre-write validation receipt overlays are required"); - } - for (const key of ["count", "writeCount", "deleteCount"] as const) { - if (!Number.isInteger(summary[key]) || summary[key] < 0) { - throw new Error(`Pre-write validation receipt overlays ${key} must be a non-negative integer`); - } - } - validateStringArray(summary.paths, "Pre-write validation receipt overlay paths", { allowEmpty: true }); - for (const path of summary.paths) validateRepoRelativePath(path); - if (summary.count !== summary.writeCount + summary.deleteCount) { - throw new Error("Pre-write validation receipt overlay count must equal writeCount plus deleteCount"); - } - if (summary.count !== summary.paths.length) { - throw new Error("Pre-write validation receipt overlay count must equal paths length"); - } -} - -function validatePreWriteValidationFailureSummary(summary: PreWriteValidationFailureSummary): void { - if (!summary || typeof summary !== "object") { - throw new Error("Pre-write validation receipt failureSummary is required"); - } - if (!includesString(validationResultStatuses, summary.category)) { - throw new Error(`Unknown pre-write validation receipt failure category: ${String(summary.category)}`); - } - if (summary.category === "passed") { - throw new Error("Pre-write validation receipt failure category must not be passed"); - } - validateNonEmptyString(summary.message, "Pre-write validation receipt failureSummary message"); - if (summary.cause !== undefined) validateNonEmptyString(summary.cause, "Pre-write validation receipt failureSummary cause"); - if (summary.retryable !== undefined && typeof summary.retryable !== "boolean") { - throw new Error("Pre-write validation receipt failureSummary retryable must be boolean"); - } -} - -function validateValidationCheckManifestEntry(entry: ValidationCheckManifestEntry): ValidationCheckManifestEntry { - if (!entry || typeof entry !== "object") { - throw new Error("Validation check manifest entry is required"); - } - validateValidationCheckId(entry.checkId, "Validation check manifest entry checkId"); - validateNonEmptyString(entry.owner, "Validation check manifest entry owner"); - validateNonEmptyString(entry.adapter, "Validation check manifest entry adapter"); - if (!includesString(["info", "warning", "error"] as const, entry.defaultSeverity)) { - throw new Error(`Unknown validation check manifest entry defaultSeverity: ${String(entry.defaultSeverity)}`); - } - if (!Array.isArray(entry.supportedScopes) || entry.supportedScopes.length === 0) { - throw new Error("Validation check manifest entry supportedScopes must be a non-empty array"); - } - for (const scopeKind of entry.supportedScopes) { - if (!includesString(validationScopeKinds, scopeKind)) { - throw new Error(`Unknown validation check manifest entry supported scope: ${String(scopeKind)}`); - } - } - if (typeof entry.requiresGraph !== "boolean") { - throw new Error("Validation check manifest entry requiresGraph must be boolean"); - } - return entry; -} - -function validateValidationCheckRunSummary(run: ValidationCheckRunSummary): ValidationCheckRunSummary { - if (!run || typeof run !== "object") { - throw new Error("Validation check run summary is required"); - } - validateValidationCheckId(run.checkId, "Validation check run summary checkId"); - if (!includesString(validationCheckRunStatuses, run.status)) { - throw new Error(`Unknown validation check run status: ${String(run.status)}`); - } - if (run.outcome !== undefined && !includesString(validationCheckOutcomes, run.outcome)) { - throw new Error(`Unknown validation check outcome: ${String(run.outcome)}`); - } - validateValidationCheckOutcomeStatus(run); - if (run.durationMs !== undefined) validateNonNegativeNumber(run.durationMs, "Validation check run summary durationMs"); - if (run.diagnosticCount !== undefined) validateNonNegativeInteger(run.diagnosticCount, "Validation check run summary diagnosticCount"); - if (run.failureMessage !== undefined) validateNonEmptyString(run.failureMessage, "Validation check run summary failureMessage"); - if ( - includesString(["infrastructure_failure", "provider_failure", "unsupported_request"] as const, run.status) && - run.failureMessage === undefined - ) { - throw new Error("Validation check run summary failureMessage is required for failure statuses"); - } - if (run.pythonCapabilityRuns !== undefined) { - if (!Array.isArray(run.pythonCapabilityRuns)) { - throw new Error("Validation check run summary pythonCapabilityRuns must be an array"); - } - for (const capabilityRun of run.pythonCapabilityRuns) validatePythonValidationCapabilityRun(capabilityRun); - } - return run; -} - -function validateValidationCheckOutcomeStatus(run: ValidationCheckRunSummary): void { - if (run.outcome === undefined) return; - const expectedStatus: Record = { - passed: "passed", - findings: "policy_failure", - tool_unavailable: "unsupported_request", - invalid_config: "unsupported_request", - timeout: "infrastructure_failure", - unsupported_target: "unsupported_request", - tool_failure: "infrastructure_failure" - }; - if (run.status !== expectedStatus[run.outcome]) { - throw new Error(`Validation check outcome ${run.outcome} requires status ${expectedStatus[run.outcome]}`); - } -} - -function validatePythonRuffValidationCapabilityRun( - run: PythonRuffValidationCapabilityRun -): PythonRuffValidationCapabilityRun { - if (!run || typeof run !== "object") { - throw new Error("Python validation capability run is required"); - } - validateExactObjectKeys(run, [ - "schemaId", "schemaVersion", "checkId", "capability", "state", "projectKey", "contextFingerprint", - "afterStateManifestFingerprint", "sourcePaths", "configPaths", "executable", "command", "argv", "cwd", - "configPath", "toolVersion", "toolSource", "termination", "exitCode", "signal", "invocations", - "durationMs", "diagnosticCount", "failureMessage" - ], "Python Ruff validation capability run"); - if (run.schemaId !== PYTHON_VALIDATION_CAPABILITY_RUN_SCHEMA_ID) { - throw new Error(`Python validation capability run schemaId must be ${PYTHON_VALIDATION_CAPABILITY_RUN_SCHEMA_ID}`); - } - if (run.schemaVersion !== 1) throw new Error("Python validation capability run schemaVersion must be 1"); - validateValidationCheckId(run.checkId, "Python validation capability run checkId"); - if (!includesString(["ruff_lint", "ruff_format"] as const, run.capability)) { - throw new Error(`Unknown Python Ruff validation capability: ${String(run.capability)}`); - } - if (!includesString(pythonValidationCapabilityStates, run.state)) { - throw new Error(`Unknown Python validation capability state: ${String(run.state)}`); - } - if (run.projectKey !== undefined) validateSha256Identity(run.projectKey, "Python validation capability run projectKey"); - if (run.contextFingerprint !== undefined) { - validateSha256Identity(run.contextFingerprint, "Python validation capability run contextFingerprint"); - } - if (run.afterStateManifestFingerprint !== undefined) { - validateSha256Identity( - run.afterStateManifestFingerprint, - "Python validation capability run afterStateManifestFingerprint" - ); - } - if (run.sourcePaths !== undefined) { - validateStringArray(run.sourcePaths, "Python validation capability run sourcePaths", { allowEmpty: true }); - for (const path of run.sourcePaths) validateRepoRelativePath(path); - } - if (run.configPaths !== undefined) { - validateStringArray(run.configPaths, "Python validation capability run configPaths", { allowEmpty: true }); - for (const path of run.configPaths) validateRepoRelativePath(path); - } - if (run.executable !== undefined) validatePortablePythonCapabilityExecutable(run.executable); - if (run.command !== undefined) validateNonEmptyString(run.command, "Python validation capability run command"); - if (run.argv !== undefined) { - validateStringArray(run.argv, "Python validation capability run argv", { allowEmpty: false }); - validatePortablePythonCapabilityArgv(run.argv); - } - if (run.cwd !== undefined) validateNonEmptyString(run.cwd, "Python validation capability run cwd"); - if (run.configPath !== undefined) validateRepoRelativePath(run.configPath); - if (run.toolVersion !== undefined) { - validateNonEmptyString(run.toolVersion, "Python validation capability run toolVersion"); - if (!/^[0-9]+\.[0-9][-+._A-Za-z0-9]*$/u.test(run.toolVersion)) { - throw new Error("Python validation capability run toolVersion must be exact version provenance"); - } - } - if (run.toolSource !== undefined && !includesString(pythonProjectExecutableSources, run.toolSource)) { - throw new Error(`Unknown Python validation capability run toolSource: ${String(run.toolSource)}`); - } - if (run.termination !== undefined && !includesString(pythonValidationCapabilityTerminations, run.termination)) { - throw new Error(`Unknown Python validation capability termination: ${String(run.termination)}`); - } - if (run.exitCode !== undefined) validateNonNegativeInteger(run.exitCode, "Python validation capability run exitCode"); - if (run.signal !== undefined) validateNonEmptyString(run.signal, "Python validation capability run signal"); - if (run.invocations !== undefined) { - if (!Array.isArray(run.invocations) || run.invocations.length === 0) { - throw new Error("Python validation capability run invocations must be a non-empty array"); - } - for (const invocation of run.invocations) { - validatePythonValidationCapabilityInvocation(invocation); - if (run.executable !== undefined && invocation.argv[0] !== run.executable) { - throw new Error("Python validation capability invocation argv must start with executable"); - } - } - } - validateNonNegativeNumber(run.durationMs, "Python validation capability run durationMs"); - validateNonNegativeInteger(run.diagnosticCount, "Python validation capability run diagnosticCount"); - if (run.failureMessage !== undefined) { - validateNonEmptyString(run.failureMessage, "Python validation capability run failureMessage"); - if (containsHostAbsolutePath(run.failureMessage)) { - throw new Error("Python validation capability run failureMessage must not contain host-absolute paths"); - } - } - if (run.state === "not_applicable" || run.state === "disabled") { - if ( - run.termination !== undefined || - run.exitCode !== undefined || - run.signal !== undefined || - run.command !== undefined || - run.argv !== undefined || - run.invocations !== undefined - ) { - throw new Error(`Python validation capability state ${run.state} must not record a process invocation`); - } - } - if (run.signal !== undefined && run.termination !== "signal") { - throw new Error("Python validation capability run signal requires signal termination"); - } - if (run.exitCode !== undefined && run.termination !== "exited") { - throw new Error("Python validation capability run exitCode requires exited termination"); - } - validateRuffCapabilityRun(run); - return run; -} - -function validatePythonValidationCapabilityInvocation(invocation: PythonValidationCapabilityInvocation): void { - if (!invocation || typeof invocation !== "object") { - throw new Error("Python validation capability invocation is required"); - } - validateStringArray(invocation.argv, "Python validation capability invocation argv", { allowEmpty: false }); - validatePortablePythonCapabilityArgv(invocation.argv); - if (!includesString(pythonValidationCapabilityTerminations, invocation.termination)) { - throw new Error(`Unknown Python validation capability invocation termination: ${String(invocation.termination)}`); - } - if (invocation.exitCode !== undefined) { - validateNonNegativeInteger(invocation.exitCode, "Python validation capability invocation exitCode"); - if (invocation.termination !== "exited") { - throw new Error("Python validation capability invocation exitCode requires exited termination"); - } - } - if (invocation.signal !== undefined) { - validateNonEmptyString(invocation.signal, "Python validation capability invocation signal"); - if (invocation.termination !== "signal") { - throw new Error("Python validation capability invocation signal requires signal termination"); - } - } - validateRuffTerminationEvidence(invocation, "Python validation capability invocation"); - validateNonNegativeNumber(invocation.durationMs, "Python validation capability invocation durationMs"); -} - -function validateRuffCapabilityRun(run: PythonRuffValidationCapabilityRun): void { - const expectedCheckId = - run.capability === "ruff_lint" - ? "python.ruff-lint" - : run.capability === "ruff_format" - ? "python.ruff-format" - : undefined; - if (expectedCheckId === undefined) return; - if (run.checkId !== expectedCheckId) { - throw new Error(`Python validation capability ${run.capability} requires checkId ${expectedCheckId}`); - } - if (run.state === "not_applicable" || run.state === "disabled") return; - const exactStateFields: readonly (keyof PythonRuffValidationCapabilityRun)[] = [ - "projectKey", - "contextFingerprint", - "afterStateManifestFingerprint", - "sourcePaths", - "configPaths", - "cwd" - ]; - for (const field of exactStateFields) { - if (run[field] === undefined) { - throw new Error(`Activated Ruff capability run requires ${field}`); - } - } - if ((run.sourcePaths?.length ?? 0) === 0) { - throw new Error("Activated Ruff capability run requires at least one source path"); - } - if (run.cwd !== "." && run.cwd !== undefined) validateRepoRelativePath(run.cwd); - if ( - run.state === "tool_unavailable" || - run.state === "unsupported_target" - ) { - requireRuffFailureMessage(run); - rejectRuffProcessEvidence(run); - return; - } - if (run.state === "timeout") { - requireRuffFailureMessage(run); - requireExecutedRuffCapability(run); - if (run.termination !== "timeout") { - throw new Error("Ruff capability timeout requires timeout termination"); - } - validateExecutedRuffCapabilityCoherence(run); - return; - } - if (run.state === "invalid_config") { - requireRuffFailureMessage(run); - if (!hasRuffProcessEvidence(run)) return; - requireExecutedRuffCapability(run); - if (run.termination !== "exited" || run.exitCode !== 2) { - throw new Error("Executed Ruff capability invalid_config requires exited configuration-rejection evidence"); - } - validateExecutedRuffCapabilityCoherence(run); - return; - } - if (run.state === "tool_failure") { - requireRuffFailureMessage(run); - if (!hasRuffProcessEvidence(run)) return; - requireExecutedRuffCapability(run); - if (run.termination === "timeout") { - throw new Error("Executed Ruff capability tool_failure must not use timeout termination"); - } - validateExecutedRuffCapabilityCoherence(run); - return; - } - if (run.state !== "passed" && run.state !== "findings") return; - requireExecutedRuffCapability(run); - if (run.termination !== "exited") { - throw new Error("Executed Ruff capability run requires exited termination"); - } - for (const invocation of run.invocations ?? []) { - if (invocation.termination !== "exited" || (invocation.exitCode !== 0 && invocation.exitCode !== 1)) { - throw new Error("Executed Ruff capability invocation requires exited Ruff result code 0 or 1"); - } - } - validateExecutedRuffCapabilityCoherence(run); - const expectedExitCode = run.state === "passed" ? 0 : 1; - if (run.exitCode !== expectedExitCode) { - throw new Error(`Executed Ruff capability state ${run.state} requires exitCode ${expectedExitCode}`); - } - if (run.state === "passed" && run.diagnosticCount !== 0) { - throw new Error("Passed Ruff capability run requires zero diagnostics"); - } - if (run.state === "findings" && run.diagnosticCount <= 0) { - throw new Error("Ruff findings capability run requires positive diagnosticCount"); - } -} - -function requireRuffFailureMessage(run: PythonRuffValidationCapabilityRun): void { - if (run.failureMessage === undefined) { - throw new Error(`Ruff capability state ${run.state} requires failureMessage`); - } -} - -function hasRuffProcessEvidence(run: PythonRuffValidationCapabilityRun): boolean { - return ( - run.command !== undefined || - run.argv !== undefined || - run.termination !== undefined || - run.exitCode !== undefined || - run.signal !== undefined || - run.invocations !== undefined - ); -} - -function rejectRuffProcessEvidence(run: PythonRuffValidationCapabilityRun): void { - if (hasRuffProcessEvidence(run)) { - throw new Error(`Ruff capability state ${run.state} must not record process evidence`); - } -} - -function requireExecutedRuffCapability(run: PythonRuffValidationCapabilityRun): void { - const executionFields: readonly (keyof PythonRuffValidationCapabilityRun)[] = [ - "executable", - "command", - "argv", - "toolVersion", - "toolSource", - "termination", - "invocations" - ]; - for (const field of executionFields) { - if (run[field] === undefined) { - throw new Error(`Executed Ruff capability run requires ${field}`); - } - } - if (run.durationMs <= 0) { - throw new Error("Executed Ruff capability run requires positive durationMs"); - } - if (run.argv?.[0] !== run.executable) { - throw new Error("Executed Ruff capability run argv must start with executable"); - } - if (run.command !== run.argv?.join(" ")) { - throw new Error("Executed Ruff capability run command must match argv"); - } - validateRuffTerminationEvidence(run, "Executed Ruff capability run"); - for (const invocation of run.invocations ?? []) { - if (invocation.argv[0] !== run.executable) { - throw new Error("Executed Ruff capability invocation argv must start with executable"); - } - if (invocation.durationMs <= 0) { - throw new Error("Executed Ruff capability invocation requires positive durationMs"); - } - } -} - -function validateExecutedRuffCapabilityCoherence(run: PythonRuffValidationCapabilityRun): void { - const matchingInvocation = run.invocations?.some((invocation) => - invocation.termination === run.termination && - invocation.exitCode === run.exitCode && - invocation.signal === run.signal && - invocation.argv.length === run.argv?.length && - invocation.argv.every((argument, index) => argument === run.argv?.[index]) - ); - if (matchingInvocation !== true) { - throw new Error("Executed Ruff capability run requires an invocation matching its argv and termination evidence"); - } -} - -function validateRuffTerminationEvidence( - evidence: Pick< - PythonRuffValidationCapabilityRun | PythonValidationCapabilityInvocation, - "termination" | "exitCode" | "signal" - >, - label: string -): void { - if (evidence.termination === "exited") { - if (evidence.exitCode === undefined || evidence.signal !== undefined) { - throw new Error(`${label} exited termination requires exitCode without signal`); - } - return; - } - if (evidence.termination === "signal") { - if (evidence.signal === undefined || evidence.exitCode !== undefined) { - throw new Error(`${label} signal termination requires signal without exitCode`); - } - return; - } - if (evidence.exitCode !== undefined || evidence.signal !== undefined) { - throw new Error(`${label} ${String(evidence.termination)} termination must not record exitCode or signal`); - } -} - -function validatePortablePythonCapabilityArgv(argv: readonly string[]): void { - for (const argument of argv) { - if (containsHostAbsolutePath(argument)) { - throw new Error("Python validation capability run requires portable argv without host-absolute paths"); - } - } -} - -function validateValidationSkippedCheck(skippedCheck: ValidationSkippedCheck): ValidationSkippedCheck { - if (!skippedCheck || typeof skippedCheck !== "object") { - throw new Error("Validation skipped check is required"); - } - validateValidationCheckId(skippedCheck.checkId, "Validation skipped check checkId"); - if (!includesString(validationSkippedCheckReasons, skippedCheck.reason)) { - throw new Error(`Unknown validation skipped reason: ${String(skippedCheck.reason)}`); - } - validateNonEmptyString(skippedCheck.message, "Validation skipped check message"); - return skippedCheck; -} - -function validateValidationFailure(failure: ValidationFailure): ValidationFailure { - if (!failure || typeof failure !== "object") { - throw new Error("Validation failure is required"); - } - if (!includesString(validationFailureCategories, failure.category)) { - throw new Error(`Unknown validation failure category: ${String(failure.category)}`); - } - validateNonEmptyString(failure.message, "Validation failure message"); - if (failure.retryable !== undefined && typeof failure.retryable !== "boolean") { - throw new Error("Validation failure retryable must be boolean"); - } - if (failure.cause !== undefined) validateNonEmptyString(failure.cause, "Validation failure cause"); - return failure; -} - -function validateContextDocFilename(filename: string): string { - validateRepoRelativePath(filename); - if (filename.includes("/")) { - throw new Error(`Required context doc policy filename must be a basename: ${filename}`); - } - return filename; -} - -function validateContextDocRequiredPath(path: string): string { - if (path === ".") return path; - return validateRepoRelativePath(path); -} - -export function validateEditPlanPayload(plan: EditPlan): EditPlan { - if (!plan || typeof plan !== "object") { - throw new Error("Edit plan is required"); - } - validateNonEmptyString(plan.planId, "Edit plan planId"); - validateRepoIdentity(plan.repo); - if (!Array.isArray(plan.changes)) { - throw new Error("Edit plan changes must be an array"); - } - for (const change of plan.changes) validateRepoRelativeChange(change); - if (!plan.atomic || typeof plan.atomic !== "object") { - throw new Error("Edit plan atomic metadata is required"); - } - if (plan.atomic.strategy !== "all_or_nothing") { - throw new Error("Edit plan atomic strategy must be all_or_nothing"); - } - if (plan.atomic.planHash !== undefined) validateNonEmptyString(plan.atomic.planHash, "Edit plan planHash"); - if (plan.atomic.expectedBaseSha !== undefined) validateNonEmptyString(plan.atomic.expectedBaseSha, "Edit plan expectedBaseSha"); - if (!plan.validation || typeof plan.validation !== "object") { - throw new Error("Edit plan validation requirement is required"); - } - if (typeof plan.validation.required !== "boolean") { - throw new Error("Edit plan validation required must be boolean"); - } - validateValidationRequestPayload(plan.validation.request); - return plan; -} - -export function validateEditCommandResult(result: EditCommandResult): EditCommandResult { - if (!result || typeof result !== "object") { - throw new Error("Edit command result is required"); - } - if (typeof result.ok !== "boolean") { - throw new Error("Edit command result ok must be boolean"); - } - if (typeof result.applied !== "boolean") { - throw new Error("Edit command result applied must be boolean"); - } - if (result.planId !== undefined) validateNonEmptyString(result.planId, "Edit command result planId"); - if (result.planHash !== undefined) validateNonEmptyString(result.planHash, "Edit command result planHash"); - if (result.appliedAt !== undefined) validateNonEmptyString(result.appliedAt, "Edit command result appliedAt"); - if (result.matchCount !== undefined) validateNonNegativeInteger(result.matchCount, "Edit command result matchCount"); - if (result.afterState !== undefined) validateEditAfterState(result.afterState); - if (result.validationRequest !== undefined) validateValidationRequestPayload(result.validationRequest); - if (result.validation !== undefined) validateValidationResultPayload(result.validation); - if (result.refusal !== undefined) validateEditRefusal(result.refusal); - if (result.rollback !== undefined) validateEditPlanRollbackState(result.rollback); - if (!result.ok && result.refusal === undefined) { - throw new Error("Edit command result refusal is required when ok=false"); - } - if (result.ok && result.refusal !== undefined) { - throw new Error("Edit command result ok=true must not include refusal"); - } - return result; -} - -function validateEditPlanRollbackState(rollback: EditPlanRollbackState): EditPlanRollbackState { - if (!rollback || typeof rollback !== "object") { - throw new Error("Edit rollback state is required"); - } - if (typeof rollback.completed !== "boolean") { - throw new Error("Edit rollback completed must be boolean"); - } - validateRepoRelativePaths(rollback.restoredPaths, "Edit rollback restoredPaths"); - validateRepoRelativePaths(rollback.failedPaths, "Edit rollback failedPaths"); - if (!Array.isArray(rollback.cleanupFailedPaths)) { - throw new Error("Edit rollback cleanupFailedPaths must be an array"); - } - for (const path of rollback.cleanupFailedPaths) { - validateNonEmptyString(path, "Edit rollback cleanupFailedPaths path"); - } - return rollback; -} - -function validateRepoRelativeChange(change: RepoRelativeChange): RepoRelativeChange { - if (!change || typeof change !== "object") { - throw new Error("Repo-relative change is required"); - } - if (change.kind === "create" || change.kind === "replace") { - validateRepoRelativePath(change.path); - if (typeof change.content !== "string") throw new Error("Repo-relative write change content must be string"); - if (change.checksumBefore !== undefined) validateNonEmptyString(change.checksumBefore, "Repo-relative change checksumBefore"); - if (change.checksumAfter !== undefined) validateNonEmptyString(change.checksumAfter, "Repo-relative change checksumAfter"); - return change; - } - if (change.kind === "delete") { - validateRepoRelativePath(change.path); - if (change.checksumBefore !== undefined) validateNonEmptyString(change.checksumBefore, "Repo-relative change checksumBefore"); - return change; - } - if (change.kind === "rename") { - validateRepoRelativePath(change.path); - validateRepoRelativePath(change.toPath); - if (change.checksumBefore !== undefined) validateNonEmptyString(change.checksumBefore, "Repo-relative change checksumBefore"); - return change; - } - throw new Error(`Unknown repo-relative change kind: ${String((change as { kind?: unknown }).kind)}`); -} - -function validateEditAfterState(afterState: Readonly>): void { - if (!afterState || typeof afterState !== "object" || Array.isArray(afterState)) { - throw new Error("Edit command result afterState must be an object"); - } - for (const [path, content] of Object.entries(afterState)) { - validateRepoRelativePath(path); - if (typeof content !== "string" && content !== null) { - throw new Error(`Edit command result afterState for ${path} must be string or null`); - } - } -} - -function validateEditRefusal(refusal: EditRefusal): EditRefusal { - if (!refusal || typeof refusal !== "object") { - throw new Error("Edit refusal is required"); - } - if (!includesString(editRefusalCategories, refusal.category)) { - throw new Error(`Unknown edit refusal category: ${String(refusal.category)}`); - } - validateNonEmptyString(refusal.message, "Edit refusal message"); - if (refusal.path !== undefined) validateRepoRelativePath(refusal.path); - return refusal; -} - -function includesString(values: T, value: unknown): value is T[number] { - return typeof value === "string" && values.includes(value); -} - -function validateGraphDaemonOperation(operation: unknown): GraphDaemonOperation { - if (!includesString(graphDaemonOperations, operation)) { - throw new Error(`Unknown graph daemon operation: ${String(operation)}`); - } - return operation; -} - -function validateGraphFactQueryKind(kind: unknown): GraphFactQuerySelector["kind"] { - if (!includesString(graphFactQueryKinds, kind)) { - throw new Error(`Unknown graph fact query kind: ${String(kind)}`); - } - return kind; -} - -function validateGraphNamedQueryKind(kind: unknown): GraphNamedQueryKind { - if (!includesString(graphNamedQueryKinds, kind)) { - throw new Error(`Unknown graph named query kind: ${String(kind)}`); - } - return kind; -} - -function validateGraphProviderQueryKind(kind: unknown): GraphProviderQueryKind { - if ( - !includesString(graphFactQueryKinds, kind) && - !includesString(graphNamedQueryKinds, kind) && - kind !== "review_context" && - kind !== "detect_changes" && - kind !== "search" - ) { - throw new Error(`Unknown graph provider query kind: ${String(kind)}`); - } - return kind; -} - -function validateGraphQueryRequestBase( - request: { requestId?: string; repo: RepoIdentity; schemaVersion: number; mode: GraphProviderMode }, - label: string -): void { - if (!request || typeof request !== "object") throw new Error(`${label} is required`); - if (request.requestId !== undefined) validateNonEmptyString(request.requestId, `${label} requestId`); - validateRepoIdentity(request.repo); - if (request.schemaVersion !== GRAPH_SCHEMA_VERSION) throw new Error(`${label} schemaVersion must be ${GRAPH_SCHEMA_VERSION}`); - if (!includesString(graphProviderModes, request.mode)) throw new Error(`Unknown ${label} mode: ${String(request.mode)}`); -} - -function validateTraversalOptions(maxDepth: number | undefined, limit: number | undefined, label: string): void { - if (maxDepth !== undefined && (!Number.isFinite(maxDepth) || maxDepth < 0)) { - throw new Error(`${label} maxDepth must be a non-negative number`); - } - if (limit !== undefined && (!Number.isFinite(limit) || limit < 1)) { - throw new Error(`${label} limit must be a positive number`); - } -} - -function validateGraphTraversalMetadata(metadata: GraphTraversalMetadata): GraphTraversalMetadata { - if (!metadata || typeof metadata !== "object") throw new Error("Graph traversal metadata is required"); - if (typeof metadata.maxDepth !== "number" || metadata.maxDepth < 0) { - throw new Error("Graph traversal metadata maxDepth must be non-negative"); - } - if (typeof metadata.truncated !== "boolean") throw new Error("Graph traversal metadata truncated must be boolean"); - if (typeof metadata.total !== "number" || metadata.total < 0) throw new Error("Graph traversal metadata total must be non-negative"); - if (typeof metadata.empty !== "boolean") throw new Error("Graph traversal metadata empty must be boolean"); - return metadata; -} - -function validateGraphSearchMode(mode: GraphSearchMode): GraphSearchMode { - if (!mode || typeof mode !== "object") throw new Error("Graph search mode is required"); - validateNonEmptyString(mode.engine, "Graph search mode engine"); - validateNonEmptyString(mode.querySyntax, "Graph search mode querySyntax"); - if (!Number.isFinite(mode.limit) || mode.limit < 1) throw new Error("Graph search mode limit must be a positive number"); - validateRepoRelativePaths(mode.contextFiles, "Graph search mode contextFiles"); - return mode; -} - -function validateGraphSearchSummary(summary: GraphSearchSummary): GraphSearchSummary { - if (!summary || typeof summary !== "object") throw new Error("Graph search summary is required"); - validateNonEmptyString(summary.query, "Graph search summary query"); - for (const key of ["total", "returned", "limit"] as const) { - if (!Number.isFinite(summary[key]) || summary[key] < (key === "limit" ? 1 : 0)) { - throw new Error(`Graph search summary ${key} must be a non-negative number`); - } - } - validateStringArray(summary.indexedNodeKinds, "Graph search summary indexedNodeKinds", { allowEmpty: true }); - validateRepoRelativePaths(summary.contextFiles, "Graph search summary contextFiles"); - return summary; -} - -function validateGraphSearchResultEntry(entry: GraphSearchResultEntry): GraphSearchResultEntry { - if (!entry || typeof entry !== "object") throw new Error("Graph search result entry is required"); - validateNonEmptyString(entry.nodeId, "Graph search result entry nodeId"); - validateNonEmptyString(entry.kind, "Graph search result entry kind"); - if (entry.path !== undefined) validateRepoRelativePath(entry.path); - if (entry.name !== undefined) validateNonEmptyString(entry.name, "Graph search result entry name"); - validateNonEmptyString(entry.qualifiedName, "Graph search result entry qualifiedName"); - if (entry.filePath !== undefined) validateRepoRelativePath(entry.filePath); - validateNonEmptyString(entry.signature, "Graph search result entry signature"); - if (!Number.isFinite(entry.score)) throw new Error("Graph search result entry score must be numeric"); - if (!Number.isFinite(entry.rank) || entry.rank < 1) throw new Error("Graph search result entry rank must be a positive number"); - validateStringArray(entry.matches, "Graph search result entry matches", { allowEmpty: true }); - return entry; -} - -function validateInspectRouteName(route: unknown): InspectRouteResult["route"] { - if (!includesString(["references", "signature", "implementations"] as const, route)) { - throw new Error(`Unknown inspect route result route: ${String(route)}`); - } - return route; -} - -function validateInspectRoutePayload(result: InspectRouteResult, route: InspectRouteResult["route"]): void { - const payloadFields = ["references", "signatures", "implementations"] as const; - const expectedField = - route === "references" ? "references" : route === "signature" ? "signatures" : "implementations"; - for (const field of payloadFields) { - const hasField = Object.hasOwn(result, field); - if (field !== expectedField && hasField) { - throw new Error(`Inspect ${route} result must not include ${field}`); - } - } - const payload = result as unknown as { - references?: readonly InspectReferenceEntry[]; - signatures?: readonly InspectSignatureEntry[]; - implementations?: readonly InspectImplementationEntry[]; - }; - if (route === "references") { - if (!Array.isArray(payload.references)) throw new Error("Inspect references result references must be an array"); - for (const reference of payload.references) validateInspectReferenceEntry(reference); - return; - } - if (route === "signature") { - if (!Array.isArray(payload.signatures)) throw new Error("Inspect signature result signatures must be an array"); - for (const signature of payload.signatures) validateInspectSignatureEntry(signature); - return; - } - if (!Array.isArray(payload.implementations)) throw new Error("Inspect implementations result implementations must be an array"); - for (const implementation of payload.implementations) validateInspectImplementationEntry(implementation); -} - -function validateInspectSymbolTarget(target: InspectSymbolTarget, label: string): InspectSymbolTarget { - if (!target || typeof target !== "object") throw new Error(`${label} is required`); - if (!includesString(["node", "file_symbol"] as const, target.kind)) { - throw new Error(`Unknown ${label} kind: ${String((target as { kind?: unknown }).kind)}`); - } - if (target.kind === "node") { - validateNonEmptyString(target.nodeId, `${label} nodeId`); - if (target.path !== undefined || target.symbolName !== undefined || target.line !== undefined || target.column !== undefined) { - throw new Error(`${label} node target must not include file-symbol fields`); - } - } else { - validateRepoRelativePath(validateNonEmptyString(target.path, `${label} path`)); - validateNonEmptyString(target.symbolName, `${label} symbolName`); - if (target.line !== undefined) validatePositiveInteger(target.line, `${label} line`); - if (target.column !== undefined) validatePositiveInteger(target.column, `${label} column`); - if (target.nodeId !== undefined) validateNonEmptyString(target.nodeId, `${label} nodeId`); - } - return target; -} - -const validateInspectReferenceTarget = validateInspectSymbolTarget; - -function validateInspectReferenceEntry(entry: InspectReferenceEntry): InspectReferenceEntry { - if (!entry || typeof entry !== "object") throw new Error("Inspect reference entry is required"); - validateRepoRelativePath(entry.file); - validatePositiveInteger(entry.line, "Inspect reference entry line"); - validatePositiveInteger(entry.column, "Inspect reference entry column"); - validateNonEmptyString(entry.text, "Inspect reference entry text"); - validateInspectTextSpan(entry.span, "Inspect reference span"); - validateInspectSymbolSummary(entry.symbol, "Inspect reference entry symbol"); - if (typeof entry.isDefinition !== "boolean") throw new Error("Inspect reference entry isDefinition must be boolean"); - if (entry.isDeclaration !== undefined && typeof entry.isDeclaration !== "boolean") { - throw new Error("Inspect reference entry isDeclaration must be boolean"); - } - validateInspectSymbolEvidence(entry.evidence, "Inspect reference entry evidence"); - return entry; -} - -function validateInspectSignatureEntry(entry: InspectSignatureEntry): InspectSignatureEntry { - if (!entry || typeof entry !== "object") throw new Error("Inspect signature entry is required"); - validateRepoRelativePath(entry.file); - validatePositiveInteger(entry.line, "Inspect signature entry line"); - validatePositiveInteger(entry.column, "Inspect signature entry column"); - validateNonEmptyString(entry.text, "Inspect signature entry text"); - validateNonEmptyString(entry.signature, "Inspect signature entry signature"); - if (!includesString(inspectSignatureKinds, entry.kind)) { - throw new Error(`Unknown inspect signature entry kind: ${String(entry.kind)}`); - } - if (!Array.isArray(entry.parameters)) throw new Error("Inspect signature entry parameters must be an array"); - for (const parameter of entry.parameters) validateInspectSignatureParameter(parameter); - if (!Array.isArray(entry.typeParameters)) throw new Error("Inspect signature entry typeParameters must be an array"); - for (const typeParameter of entry.typeParameters) validateInspectSignatureTypeParameter(typeParameter); - if (typeof entry.exported !== "boolean") throw new Error("Inspect signature entry exported must be boolean"); - if (typeof entry.async !== "boolean") throw new Error("Inspect signature entry async must be boolean"); - if (entry.returnType !== undefined) validateNonEmptyString(entry.returnType, "Inspect signature entry returnType"); - validateInspectTextSpan(entry.span, "Inspect signature span"); - validateInspectSymbolSummary(entry.symbol, "Inspect signature entry symbol"); - if (entry.overloadIndex !== undefined) validateNonNegativeInteger(entry.overloadIndex, "Inspect signature entry overloadIndex"); - validateInspectSymbolEvidence(entry.evidence, "Inspect signature entry evidence"); - return entry; -} - -function validateInspectSignatureParameter(parameter: InspectSignatureParameter): InspectSignatureParameter { - if (!parameter || typeof parameter !== "object") throw new Error("Inspect signature parameter is required"); - validateNonEmptyString(parameter.name, "Inspect signature parameter name"); - validateNonEmptyString(parameter.type, "Inspect signature parameter type"); - if (typeof parameter.optional !== "boolean") throw new Error("Inspect signature parameter optional must be boolean"); - if (parameter.rest !== undefined && typeof parameter.rest !== "boolean") { - throw new Error("Inspect signature parameter rest must be boolean"); - } - if (parameter.defaultValue !== undefined) validateNonEmptyString(parameter.defaultValue, "Inspect signature parameter defaultValue"); - return parameter; -} - -function validateInspectSignatureTypeParameter(typeParameter: InspectSignatureTypeParameter): InspectSignatureTypeParameter { - if (!typeParameter || typeof typeParameter !== "object") throw new Error("Inspect signature typeParameter is required"); - validateNonEmptyString(typeParameter.name, "Inspect signature typeParameter name"); - if (typeParameter.constraint !== undefined) { - validateNonEmptyString(typeParameter.constraint, "Inspect signature typeParameter constraint"); - } - if (typeParameter.default !== undefined) { - validateNonEmptyString(typeParameter.default, "Inspect signature typeParameter default"); - } - return typeParameter; -} - -function validateInspectImplementationEntry(entry: InspectImplementationEntry): InspectImplementationEntry { - if (!entry || typeof entry !== "object") throw new Error("Inspect implementation entry is required"); - validateRepoRelativePath(entry.file); - validatePositiveInteger(entry.line, "Inspect implementation entry line"); - validatePositiveInteger(entry.column, "Inspect implementation entry column"); - validateNonEmptyString(entry.text, "Inspect implementation entry text"); - validateInspectTextSpan(entry.span, "Inspect implementation span"); - if (Object.hasOwn(entry, "implements")) throw new Error("Inspect implementation entry must use target, not implements"); - if (!includesString(inspectImplementationKinds, entry.kind)) { - throw new Error(`Unknown Inspect implementation entry kind: ${String(entry.kind)}`); - } - validateInspectSymbolSummary(entry.symbol, "Inspect implementation entry symbol"); - validateInspectSymbolSummary(entry.target, "Inspect implementation entry target"); - if (entry.isDeclaration !== undefined && typeof entry.isDeclaration !== "boolean") { - throw new Error("Inspect implementation entry isDeclaration must be boolean"); - } - validateInspectSymbolEvidence(entry.evidence, "Inspect implementation entry evidence"); - return entry; -} - -function validateInspectTextSpan(span: InspectTextSpan, label: string): InspectTextSpan { - if (!span || typeof span !== "object") throw new Error(`${label} is required`); - validatePositiveInteger(span.startLine, `${label} startLine`); - validatePositiveInteger(span.startColumn, `${label} startColumn`); - validatePositiveInteger(span.endLine, `${label} endLine`); - validatePositiveInteger(span.endColumn, `${label} endColumn`); - if (span.endLine < span.startLine || (span.endLine === span.startLine && span.endColumn < span.startColumn)) { - throw new Error(`${label} end must be after start`); - } - if (span.startOffset !== undefined) validateNonNegativeInteger(span.startOffset, `${label} startOffset`); - if (span.endOffset !== undefined) validateNonNegativeInteger(span.endOffset, `${label} endOffset`); - return span; -} - -function validateInspectSymbolSummary(symbol: InspectSymbolSummary, label: string): InspectSymbolSummary { - if (!symbol || typeof symbol !== "object") throw new Error(`${label} is required`); - validateNonEmptyString(symbol.id, `${label} id`); - validateNonEmptyString(symbol.name, `${label} name`); - if (symbol.kind !== undefined) validateNonEmptyString(symbol.kind, `${label} kind`); - return symbol; -} - -function validateInspectSymbolEvidence(evidence: InspectSymbolEvidence, label: string): InspectSymbolEvidence { - if (!evidence || typeof evidence !== "object") throw new Error(`${label} is required`); - validateStringArray(evidence.graphNodeIds, `${label} graphNodeIds`, { allowEmpty: true }); - if (!includesString(["graph", "language_service"] as const, evidence.resolver)) { - throw new Error(`Unknown ${label} resolver: ${String(evidence.resolver)}`); - } - return evidence; -} - -function validateInspectRouteFailure(failure: InspectRouteFailure): InspectRouteFailure { - if (!failure || typeof failure !== "object") throw new Error("Inspect route failure is required"); - if (!includesString(inspectFailureCategories, failure.category)) { - throw new Error(`Unknown inspect route failure category: ${String(failure.category)}`); - } - validateNonEmptyString(failure.message, "Inspect route failure message"); - if (failure.candidates !== undefined) { - if (!Array.isArray(failure.candidates)) throw new Error("Inspect route failure candidates must be an array"); - for (const candidate of failure.candidates) validateInspectSymbolTarget(candidate, "Inspect route failure candidate"); - } - return failure; -} - -function validateGraphPayloadResult( - result: - | GraphNamedQueryResult - | GraphImpactResult - | GraphDetectChangesResult - | GraphReviewContextResult, - label: string, - validatePayload: (payload: Record) => void -): void { - if (!result || typeof result !== "object") throw new Error(`${label} is required`); - if (result.requestId !== undefined) validateNonEmptyString(result.requestId, `${label} requestId`); - const status = validateProviderStatus(result.status); - const payload = result as unknown as Record; - const payloadKeys = Object.keys(payload).filter((key) => key !== "requestId" && key !== "status"); - if (status.state !== "available") { - if (payloadKeys.length > 0) throw new Error(`${label} ${status.state} result must not include graph data`); - return; - } - validatePayload(payload); - if (payload.diagnostics !== undefined) { - validateGraphExtractionDiagnostics(payload.diagnostics as readonly GraphExtractionDiagnostic[]); - } -} - -function validateRepoRelativePaths(paths: unknown, label: string): readonly string[] { - validateStringArray(paths as readonly string[] | undefined, label, { allowEmpty: true }); - for (const path of paths as readonly string[]) validateRepoRelativePath(path); - return paths as readonly string[]; -} - -function validateRenamedFiles(renamedFiles: readonly GraphRenamedFile[]): void { - if (!Array.isArray(renamedFiles)) throw new Error("Graph renamedFiles must be an array"); - for (const renamed of renamedFiles) { - validateRepoRelativePath(renamed.fromPath); - validateRepoRelativePath(renamed.toPath); - } -} - -function isNamedQueryResult(result: GraphFactQueryResult | GraphNamedQueryResult): result is GraphNamedQueryResult { - return Object.hasOwn(result, "queryKind") || Object.hasOwn(result, "traversal"); -} - -function validateGraphFactNode(node: GraphFactNode): GraphFactNode { - if (!node || typeof node !== "object") { - throw new Error("Graph fact node is required"); - } - validateNonEmptyString(node.id, "Graph fact node id"); - validateNonEmptyString(node.kind, "Graph fact node kind"); - if (node.path !== undefined) validateRepoRelativePath(node.path); - if (node.name !== undefined) validateNonEmptyString(node.name, "Graph fact node name"); - return node; -} - -function validateGraphFactEdge(edge: GraphFactEdge): GraphFactEdge { - if (!edge || typeof edge !== "object") { - throw new Error("Graph fact edge is required"); - } - if (edge.id !== undefined) validateNonEmptyString(edge.id, "Graph fact edge id"); - validateNonEmptyString(edge.kind, "Graph fact edge kind"); - validateNonEmptyString(edge.from, "Graph fact edge from"); - validateNonEmptyString(edge.to, "Graph fact edge to"); - return edge; -} - -function validateGraphExtractionDiagnostics( - diagnostics: readonly GraphExtractionDiagnostic[] -): readonly GraphExtractionDiagnostic[] { - if (!Array.isArray(diagnostics)) { - throw new Error("Graph extraction diagnostics must be an array"); - } - for (const diagnostic of diagnostics) validateGraphExtractionDiagnostic(diagnostic); - return diagnostics; -} - -function validateGraphExtractionDiagnostic(diagnostic: GraphExtractionDiagnostic): GraphExtractionDiagnostic { - if (!diagnostic || typeof diagnostic !== "object") { - throw new Error("Graph extraction diagnostic is required"); - } - if (!includesString(graphExtractionDiagnosticCategories, diagnostic.category)) { - throw new Error(`Unknown graph extraction diagnostic category: ${String(diagnostic.category)}`); - } - if (!includesString(["info", "warning", "error"] as const, diagnostic.severity)) { - throw new Error(`Unknown graph extraction diagnostic severity: ${String(diagnostic.severity)}`); - } - validateNonEmptyString(diagnostic.message, "Graph extraction diagnostic message"); - if (diagnostic.path !== undefined) validateRepoRelativePath(diagnostic.path); - if (diagnostic.language !== undefined) validateNonEmptyString(diagnostic.language, "Graph extraction diagnostic language"); - return diagnostic; -} - -function validateProviderFailure(failure: ProviderFailure): ProviderFailure { - if (!failure || typeof failure !== "object") { - throw new Error("Provider failure is required"); - } - if (!includesString(providerFailureCategories, failure.category)) { - throw new Error(`Unknown provider failure category: ${String(failure.category)}`); - } - validateNonEmptyString(failure.message, "Provider failure message"); - if (failure.retryable !== undefined && typeof failure.retryable !== "boolean") { - throw new Error("Provider failure retryable must be boolean"); - } - if (failure.cause !== undefined) validateNonEmptyString(failure.cause, "Provider failure cause"); - return failure; -} - -function validateCommandOwner(owner: unknown): CommandOwner { - if (!includesString(commandOwners, owner)) { - throw new Error(`Unknown command owner: ${String(owner)}`); - } - return owner; -} - -function validateCommandRouteStatus(status: unknown): CommandRouteStatus { - if (!includesString(commandRouteStatuses, status)) { - throw new Error(`Unknown command route status: ${String(status)}`); - } - return status; -} - -function validateGraphReleaseSurfaceClassification(classification: unknown): GraphReleaseSurfaceClassification { - if (!includesString(graphReleaseSurfaceClassifications, classification)) { - throw new Error(`Unknown graph release surface classification: ${String(classification)}`); - } - return classification; -} - -function validateGraphReleasePackageVersions(versions: readonly GraphReleasePackageVersion[]): void { - validateNonEmptyArray(versions, "Graph release graphPackageVersions"); - for (const version of versions) { - if (!version || typeof version !== "object") throw new Error("Graph release package version is required"); - validateNonEmptyString(version.packageName, "Graph release package version packageName"); - validateNonEmptyString(version.version, "Graph release package version version"); - } - if (!versions.some((version) => version.packageName === "@the-open-engine/opcore-graph")) { - throw new Error("Graph release package versions must include @the-open-engine/opcore-graph"); - } -} - -function validateGraphReleaseCommandCoverage(coverage: readonly GraphReleaseCommandCoverage[]): void { - validateNonEmptyArray(coverage, "Graph release commandCoverage"); - validateExactStringSet( - coverage.map((entry) => entry.id), - graphReleaseCoreCommandIds, - "Graph release command coverage ids" - ); - for (const entry of coverage) { - if (!entry || typeof entry !== "object") throw new Error("Graph release command coverage entry is required"); - validateGraphReleaseCoreCommandId(entry.id); - if (entry.bin !== "opcore") throw new Error(`Unknown graph release command bin: ${String(entry.bin)}`); - validateStringArray(entry.command, "Graph release command coverage command", { allowEmpty: false }); - validateStringArray(entry.canonicalCommand, "Graph release command coverage canonicalCommand", { allowEmpty: false }); - if (entry.status !== "passed") throw new Error("Graph release command coverage status must be passed"); - if (entry.exitCode !== 0) throw new Error("Graph release command coverage exitCode must be 0"); - validateNonEmptyString(entry.fixture, "Graph release command coverage fixture"); - if (typeof entry.durationMs !== "number" || entry.durationMs <= 0) { - throw new Error("Graph release command coverage durationMs must be positive"); - } - const route = graphReleaseRouteForCommandId(entry.id); - if (entry.bin !== route.bin) throw new Error(`Graph release command ${entry.id} must use ${route.bin}`); - if (entry.command.join("\0") !== route.command.join("\0")) { - throw new Error(`Graph release command ${entry.id} command must be ${route.command.join(" ")}`); - } - if (entry.canonicalCommand.join("\0") !== route.canonicalCommand.join("\0")) { - throw new Error(`Graph release command ${entry.id} canonicalCommand must be ${route.canonicalCommand.join(" ")}`); - } - } -} - -function validateGraphReleaseRustCommandCoverage(coverage: readonly GraphReleaseRustCommandCoverage[]): void { - validateNonEmptyArray(coverage, "Graph release rustCommandCoverage"); - validateExactStringSet( - coverage.map((entry) => entry.id), - graphReleaseRustCommandIds, - "Graph release Rust command coverage ids" - ); - for (const entry of coverage) { - if (!entry || typeof entry !== "object") throw new Error("Graph release Rust command coverage entry is required"); - validateGraphReleaseRustCommandId(entry.id); - if (entry.bin !== "opcore") throw new Error(`Unknown graph release Rust command bin: ${String(entry.bin)}`); - validateStringArray(entry.command, "Graph release Rust command coverage command", { allowEmpty: false }); - validateStringArray(entry.canonicalCommand, "Graph release Rust command coverage canonicalCommand", { allowEmpty: false }); - if (entry.status !== "passed") throw new Error("Graph release Rust command coverage status must be passed"); - if (entry.exitCode !== 0) throw new Error("Graph release Rust command coverage exitCode must be 0"); - validateNonEmptyString(entry.fixture, "Graph release Rust command coverage fixture"); - if (typeof entry.durationMs !== "number" || entry.durationMs <= 0) { - throw new Error("Graph release Rust command coverage durationMs must be positive"); - } - const route = graphReleaseRouteForRustCommandId(entry.id); - if (entry.bin !== route.bin) throw new Error(`Graph release Rust command ${entry.id} must use ${route.bin}`); - if (entry.command.join("\0") !== route.command.join("\0")) { - throw new Error(`Graph release Rust command ${entry.id} route must match ${route.command.join(" ")}`); - } - if (entry.canonicalCommand.join("\0") !== route.canonicalCommand.join("\0")) { - throw new Error(`Graph release Rust command ${entry.id} route must match ${route.canonicalCommand.join(" ")}`); - } - } -} - -function validateGraphReleaseDirectSqliteQueries(queries: readonly GraphReleaseDirectSqliteQueryReceipt[]): void { - validateNonEmptyArray(queries, "Graph release directSqliteQueries"); - validateExactStringSet( - queries.map((entry) => entry.id), - graphReleaseDirectSqliteQueryIds, - "Graph release direct SQLite query ids" - ); - for (const query of queries) { - if (!query || typeof query !== "object") throw new Error("Graph release direct SQLite query receipt is required"); - if (!includesString(graphReleaseDirectSqliteQueryIds, query.id)) { - throw new Error(`Unknown graph release direct SQLite query id: ${String(query.id)}`); - } - validateNonEmptyString(query.query, "Graph release direct SQLite query query"); - if (query.status !== "passed") throw new Error("Graph release direct SQLite query status must be passed"); - if (typeof query.rowCount !== "number" || query.rowCount < 0) { - throw new Error("Graph release direct SQLite query rowCount must be non-negative"); - } - validateNonEmptyString(query.fixture, "Graph release direct SQLite query fixture"); - } -} - -function validateGraphReleaseServeTransport(receipts: readonly GraphReleaseServeTransportReceipt[]): void { - validateNonEmptyArray(receipts, "Graph release serveTransport"); - validateExactStringSet( - receipts.map((entry) => entry.id), - graphReleaseServeTransportIds, - "Graph release serve transport ids" - ); - for (const receipt of receipts) { - if (!receipt || typeof receipt !== "object") throw new Error("Graph release serve transport receipt is required"); - validateGraphReleaseServeTransportId(receipt.id); - if (receipt.protocol !== "opcore.graph.daemon") { - throw new Error("Graph release serve transport protocol must be opcore.graph.daemon"); - } - validateNonEmptyString(receipt.operation, "Graph release serve transport operation"); - if (receipt.operation !== graphReleaseOperationForServeTransportId(receipt.id)) { - throw new Error(`Graph release serve transport ${receipt.id} operation must be ${graphReleaseOperationForServeTransportId(receipt.id)}`); - } - if (receipt.status !== "passed") throw new Error("Graph release serve transport status must be passed"); - if (receipt.exitCode !== 0) throw new Error("Graph release serve transport exitCode must be 0"); - } -} - -function validateGraphReleaseBenchmarks(benchmarks: readonly GraphReleaseBenchmarkReceipt[]): void { - validateNonEmptyArray(benchmarks, "Graph release benchmarks"); - validateExactStringSet( - benchmarks.map((entry) => entry.metric), - graphReleaseBenchmarkMetrics, - "Graph release benchmark metrics" - ); - for (const benchmark of benchmarks) { - if (!benchmark || typeof benchmark !== "object") throw new Error("Graph release benchmark receipt is required"); - validateGraphReleaseBenchmarkMetric(benchmark.metric); - if (typeof benchmark.value !== "number" || benchmark.value <= 0) { - throw new Error("Graph release benchmark value must be positive"); - } - if (benchmark.unit !== "ms" && benchmark.unit !== "bytes") { - throw new Error("Graph release benchmark unit must be ms or bytes"); - } - if (benchmark.metric.endsWith("_bytes") && benchmark.unit !== "bytes") { - throw new Error(`Graph release benchmark ${benchmark.metric} must use bytes`); - } - if (!benchmark.metric.endsWith("_bytes") && benchmark.unit !== "ms") { - throw new Error(`Graph release benchmark ${benchmark.metric} must use ms`); - } - if (benchmark.baselineIssue !== "#19") throw new Error("Graph release benchmark baselineIssue must be #19"); - validateNonEmptyString(benchmark.baselineReceipt, "Graph release benchmark baselineReceipt"); - if (!["recorded", "within_baseline", "above_baseline", "below_baseline"].includes(benchmark.comparison)) { - throw new Error(`Unknown graph release benchmark comparison: ${String(benchmark.comparison)}`); - } - } -} - -function validateGraphReleasePackageInspection(inspection: GraphReleasePackageInspection): void { - if (!inspection || typeof inspection !== "object") throw new Error("Graph release packageInspection is required"); - if (inspection.packageName !== "@the-open-engine/opcore-graph") { - throw new Error("Graph release packageInspection packageName must be @the-open-engine/opcore-graph"); - } - validateNonEmptyString(inspection.tarballName, "Graph release packageInspection tarballName"); - if (typeof inspection.fileCount !== "number" || inspection.fileCount <= 0) { - throw new Error("Graph release packageInspection fileCount must be positive"); - } - validateStringArray(inspection.files, "Graph release packageInspection files", { allowEmpty: false }); - if (inspection.fileCount !== inspection.files.length) { - throw new Error("Graph release packageInspection fileCount must equal files length"); - } - validateStringArray(inspection.inspections, "Graph release packageInspection inspections", { allowEmpty: false }); - for (const key of [ - "forbiddenMarkersAbsent", - "generatedBuildMetadataAbsent", - "privatePathsAbsent", - "sourceProvenanceAbsent", - "packageMetadataAbsent", - "gitHistoryAbsent", - "foreignImplementationNamesAbsent" - ] as const) { - if (inspection[key] !== true) throw new Error(`Graph release packageInspection ${key} must be true`); - } -} - -function validateGraphReleaseNativeArtifacts(nativeArtifacts: readonly GraphReleaseNativeArtifactEvidence[]): void { - validateNonEmptyArray(nativeArtifacts, "Graph release nativeArtifacts"); - validateExactStringSet( - nativeArtifacts.map((artifact) => artifact.targetPlatform), - graphCoreNativeSupportedTargets, - "Graph release native artifact targets" - ); - for (const nativeArtifact of nativeArtifacts) { - if (!nativeArtifact || typeof nativeArtifact !== "object") throw new Error("Graph release native artifact evidence is required"); - const expectedPackageName = graphCoreNativePackageNameForTarget(nativeArtifact.targetPlatform); - if (nativeArtifact.packageName !== expectedPackageName) { - throw new Error(`Graph release native artifact packageName for ${nativeArtifact.targetPlatform} must be ${expectedPackageName}`); - } - validateGraphProviderArtifactMetadata(nativeArtifact.metadata); - if (nativeArtifact.metadata.targetPlatform !== nativeArtifact.targetPlatform) { - throw new Error("Graph release native artifact targetPlatform must match metadata"); - } - if (nativeArtifact.binaryPath !== "opcore-graph-core") throw new Error("Graph release native binaryPath must be opcore-graph-core"); - if (nativeArtifact.checksumPath !== "opcore-graph-core.sha256") { - throw new Error("Graph release native checksumPath must be opcore-graph-core.sha256"); - } - if (nativeArtifact.metadataPath !== "metadata.json") throw new Error("Graph release native metadataPath must be metadata.json"); - if (nativeArtifact.metadata.binaryPath !== nativeArtifact.binaryPath) { - throw new Error("Graph release native artifact binaryPath must match metadata"); - } - if (nativeArtifact.metadata.checksumPath !== nativeArtifact.checksumPath) { - throw new Error("Graph release native artifact checksumPath must match metadata"); - } - if (nativeArtifact.metadata.checksumSha256 !== nativeArtifact.binarySha256) { - throw new Error("Graph release native artifact metadata checksum must match binary sha256"); - } - validateSha256(nativeArtifact.binarySha256, "Graph release native artifact binarySha256"); - validateSha256(nativeArtifact.checksumFileSha256, "Graph release native artifact checksumFileSha256"); - validateSha256(nativeArtifact.metadataSha256, "Graph release native artifact metadataSha256"); - validateExactStringSet( - nativeArtifact.packageFiles, - ["package.json", "README.md", "opcore-graph-core", "opcore-graph-core.sha256", "metadata.json"], - `Graph release native package files ${nativeArtifact.targetPlatform}` - ); - } -} - -function validateGraphReleaseReportReceipts(receipts: readonly GraphReleaseReportReceipt[]): void { - validateNonEmptyArray(receipts, "Graph release reportReceipts"); - validateExactStringSet( - receipts.map((entry) => entry.id), - graphReleaseReportReceiptIds, - "Graph release report receipt ids" - ); - for (const receipt of receipts) { - if (!receipt || typeof receipt !== "object") throw new Error("Graph release report receipt is required"); - if (!includesString(graphReleaseReportReceiptIds, receipt.id)) { - throw new Error(`Unknown graph release report receipt id: ${String(receipt.id)}`); - } - validateStringArray(receipt.command, "Graph release report receipt command", { allowEmpty: false }); - if (receipt.status !== "passed") throw new Error("Graph release report receipt status must be passed"); - if (receipt.exitCode !== 0) throw new Error("Graph release report receipt exitCode must be 0"); - validateNonEmptyString(receipt.path, "Graph release report receipt path"); - if (receipt.checksumSha256 !== undefined) validateNonEmptyString(receipt.checksumSha256, "Graph release report receipt checksumSha256"); - } -} - -function validateGraphReleaseOptionalSurfaces(surfaces: readonly GraphReleaseOptionalSurfaceReceipt[]): void { - validateNonEmptyArray(surfaces, "Graph release optionalSurfaces"); - for (const surface of surfaces) { - if (!surface || typeof surface !== "object") throw new Error("Graph release optional surface is required"); - validateGraphReleaseDeferredChild(surface.issue, "Graph release optional surface issue"); - validateNonEmptyString(surface.id, "Graph release optional surface id"); - validateGraphReleaseSurfaceClassification(surface.classification); - if (surface.status !== "unsupported" && surface.status !== "deferred") { - throw new Error("Graph release optional surface status must be unsupported or deferred"); - } - if (surface.classification === "required") { - throw new Error("Graph release optional surfaces must not mark staged graph release surfaces as required"); - } - } - validateGraphReleaseOptionalAnalysisSurfaceSet(surfaces, "Graph release optional surfaces"); -} - -function validateGraphReleaseOptionalAnalysisSurfaceSet( - surfaces: readonly Pick[], - label: string -): void { - const actual = surfaces.map(graphReleaseOptionalSurfaceKey).sort(); - const expected = graphReleaseOptionalAnalysisSurfaces.map(graphReleaseOptionalSurfaceKey).sort(); - if (actual.length !== expected.length || actual.some((value, index) => value !== expected[index])) { - throw new Error(`${label} must match staged graph release surfaces`); - } -} - -function graphReleaseOptionalSurfaceKey( - surface: Pick -): string { - return `${surface.issue}:${surface.id}:${surface.classification}:${surface.status}`; -} - -function validateGraphReleaseDeferredChild(issue: unknown, label = "Graph release deferred child"): GraphReleaseDeferredChild { - if (!includesString(graphReleaseDeferredChildren, issue)) { - throw new Error(`${label} must be one of ${graphReleaseDeferredChildren.join(", ")}`); - } - return issue; -} - -function validateGraphReleaseHandoff(handoff: readonly GraphReleaseHandoffReceipt[]): void { - validateNonEmptyArray(handoff, "Graph release handoff"); - validateExactStringSet( - handoff.map((entry) => entry.issue), - graphReleaseHandoffIssues, - "Graph release handoff issues" - ); - for (const entry of handoff) { - if (!entry || typeof entry !== "object") throw new Error("Graph release handoff entry is required"); - validateGraphReleaseHandoffIssue(entry.issue); - validateNonEmptyString(entry.receiptPath, "Graph release handoff receiptPath"); - validateNonEmptyString(entry.checksumSha256, "Graph release handoff checksumSha256"); - validateNonEmptyString(entry.rollbackNote, "Graph release handoff rollbackNote"); - } -} - -function validateGraphReleaseCoreCommandId(id: unknown): GraphReleaseCoreCommandId { - if (!includesString(graphReleaseCoreCommandIds, id)) { - throw new Error(`Unknown graph release command id: ${String(id)}`); - } - return id; -} - -function validateGraphReleaseRustCommandId(id: unknown): GraphReleaseRustCommandId { - if (!includesString(graphReleaseRustCommandIds, id)) { - throw new Error(`Unknown graph release Rust command id: ${String(id)}`); - } - return id; -} - -function validateGraphReleaseBenchmarkMetric(metric: unknown): GraphReleaseBenchmarkMetric { - if (!includesString(graphReleaseBenchmarkMetrics, metric)) { - throw new Error(`Unknown graph release benchmark metric: ${String(metric)}`); - } - return metric; -} - -function validateGraphReleaseHandoffIssue(issue: unknown): GraphReleaseHandoffIssue { - if (!includesString(graphReleaseHandoffIssues, issue)) { - throw new Error(`Unknown graph release handoff issue: ${String(issue)}`); - } - return issue; -} - -function validateGraphReleaseServeTransportId(id: unknown): GraphReleaseServeTransportId { - if (!includesString(graphReleaseServeTransportIds, id)) { - throw new Error(`Unknown graph release serve transport id: ${String(id)}`); - } - return id; -} - -function graphReleaseOperationForServeTransportId(id: GraphReleaseServeTransportId): string { - return id.replace("serve-jsonl-", ""); -} - -function graphReleaseRouteForCommandId(id: GraphReleaseCoreCommandId): { - bin: "opcore"; - command: readonly string[]; - canonicalCommand: readonly string[]; -} { - const command = id.replace("opcore-graph-", ""); - return { - bin: "opcore", - command: ["graph", command], - canonicalCommand: ["opcore", "graph", command] - }; -} - -function graphReleaseRouteForRustCommandId(id: GraphReleaseRustCommandId): { - bin: "opcore"; - command: readonly string[]; - canonicalCommand: readonly string[]; -} { - const command = id.replace("opcore-graph-rust-", ""); - return { - bin: "opcore", - command: ["graph", command], - canonicalCommand: ["opcore", "graph", command] - }; -} - -function validateReleaseReceiptPackages(packages: readonly ReleaseReceiptPackageEvidence[]): void { - validateNonEmptyArray(packages, "Release receipt package evidence"); - validateExactStringSet( - packages.map((entry) => entry.packageName), - releaseReceiptPackageNames, - "Release receipt package evidence" - ); - for (const packageEvidence of packages) validateReleaseReceiptPackage(packageEvidence); -} - -function validateReleaseReceiptPackage(packageEvidence: ReleaseReceiptPackageEvidence): void { - if (!packageEvidence || typeof packageEvidence !== "object") throw new Error("Release receipt package evidence entry is required"); - validateReleaseReceiptPackageName(packageEvidence.packageName, "Release receipt package evidence packageName"); - validateRepoRelativePath(packageEvidence.packageRoot); - validateNonEmptyString(packageEvidence.version, "Release receipt package evidence version"); - validateReleaseReceiptPackageManifest(packageEvidence.manifest, packageEvidence.packageName); - validateReleaseReceiptTarball(packageEvidence.tarball); - validateStringArray(packageEvidence.files, "Release receipt package evidence files", { allowEmpty: false }); - validateStringArray(packageEvidence.expectedFiles, "Release receipt package evidence expectedFiles", { allowEmpty: false }); - if (!Number.isInteger(packageEvidence.fileCount) || packageEvidence.fileCount !== packageEvidence.files.length) { - throw new Error("Release receipt package evidence fileCount must equal files length"); - } - if (!Number.isInteger(packageEvidence.expectedFileCount) || packageEvidence.expectedFileCount !== packageEvidence.expectedFiles.length) { - throw new Error("Release receipt package evidence expectedFileCount must equal expectedFiles length"); - } - validateExactStringSet(packageEvidence.files, packageEvidence.expectedFiles, `${packageEvidence.packageName} packed files`); - validateReleaseReceiptBins(packageEvidence.bins, packageEvidence.packageName); - for (const descriptorReference of packageEvidence.descriptorReferences) { - validateManagedToolDescriptorPackageReference(descriptorReference, packageEvidence.packageName); - if (!packageEvidence.files.includes(descriptorReference.path)) { - throw new Error(`Release receipt descriptor reference ${descriptorReference.id} is not in ${packageEvidence.packageName} packed files`); - } - } - if (packageEvidence.packageName === "opcore") { - validateNonEmptyArray(packageEvidence.nativeArtifacts, "Release receipt native package artifacts"); - validateExactStringSet( - packageEvidence.nativeArtifacts.map((entry) => entry.targetPlatform), - graphCoreNativeSupportedTargets, - "Release receipt Opcore bundled native artifact targets" - ); - } else if (isGraphCoreNativePackageName(packageEvidence.packageName)) { - throw new Error("Release receipt must not publish native graph-core packages separately"); - } else if (packageEvidence.nativeArtifacts.length > 0) { - throw new Error(`${packageEvidence.packageName} must not report native graph artifacts`); - } - for (const nativeArtifact of packageEvidence.nativeArtifacts) validateReleaseReceiptNativeArtifact(nativeArtifact); -} - -function validateReleaseReceiptPackageManifest( - manifest: ReleaseReceiptPackageManifestMetadata, - packageName: ReleaseReceiptPackageName -): void { - if (!manifest || typeof manifest !== "object") throw new Error("Release receipt package manifest is required"); - if (manifest.name !== packageName) throw new Error(`Release receipt package manifest name must match ${packageName}`); - validateNonEmptyString(manifest.version, "Release receipt package manifest version"); - validateNonEmptyString(manifest.license, "Release receipt package manifest license"); - if (isGraphCoreNativePackageName(packageName)) { - if (manifest.main !== undefined || manifest.types !== undefined) { - throw new Error("Release receipt native package manifest must not declare main or types"); - } - } else { - if (manifest.main === undefined || manifest.types === undefined) { - throw new Error("Release receipt package manifest must declare main and types"); - } - validateRepoRelativePath(manifest.main); - validateRepoRelativePath(manifest.types); - } - validateStringArray(manifest.files, "Release receipt package manifest files", { allowEmpty: false }); - validateReleaseReceiptBins(manifest.bins, packageName); - validateStringRecord(manifest.dependencies, "Release receipt package manifest dependencies"); - if (manifest.optionalDependencies !== undefined) { - validateStringRecord(manifest.optionalDependencies, "Release receipt package manifest optionalDependencies"); - } - validateStringArray(manifest.bundledDependencies, "Release receipt package manifest bundledDependencies", { allowEmpty: true }); -} - -function validateReleaseReceiptTarball(tarball: ReleaseReceiptTarballEvidence): void { - if (!tarball || typeof tarball !== "object") throw new Error("Release receipt tarball evidence is required"); - validateNonEmptyString(tarball.filename, "Release receipt tarball filename"); - validateRepoRelativePath(tarball.path); - validateSha256(tarball.sha256, "Release receipt tarball sha256"); - if (tarball.integrity !== undefined) validateNonEmptyString(tarball.integrity, "Release receipt tarball integrity"); - if (tarball.shasum !== undefined) validateNonEmptyString(tarball.shasum, "Release receipt tarball shasum"); -} - -function validateReleaseReceiptDescriptor( - descriptorEvidence: ReleaseReceiptDescriptorEvidence, - packages: readonly ReleaseReceiptPackageEvidence[] -): void { - if (!descriptorEvidence || typeof descriptorEvidence !== "object") throw new Error("Release receipt descriptor evidence is required"); - validateRepoRelativePath(descriptorEvidence.path); - if (descriptorEvidence.packageName !== "opcore") { - throw new Error("Release receipt descriptor packageName must be opcore"); - } - validateSha256(descriptorEvidence.checksumSha256, "Release receipt descriptor checksumSha256"); - const descriptor = validateManagedToolDescriptor(descriptorEvidence.descriptor); - validateExactStringSet( - descriptorEvidence.commandGroups.map((entry) => entry.name), - releaseReceiptCommandGroups, - "Release receipt descriptor command groups" - ); - for (const group of descriptorEvidence.commandGroups) { - validateReleaseReceiptCommandGroupName(group.name, "Release receipt descriptor command group name"); - validateExactStringSequence(group.canonicalCommand, ["opcore", group.name], `Release receipt descriptor ${group.name} canonicalCommand`); - const descriptorGroup = descriptor.commandGroups.find((entry) => entry.name === group.name); - if (!descriptorGroup) throw new Error(`Release receipt descriptor command group missing from descriptor: ${group.name}`); - if (group.packageName !== descriptorGroup.packageName) { - throw new Error(`Release receipt descriptor command group ${group.name} packageName must match descriptor`); - } - } - validateReleaseResolvedArtifacts(descriptorEvidence.resolvedArtifacts, descriptor.artifacts, packages); - validateReleaseResolvedChecksums(descriptorEvidence.resolvedChecksums, descriptor.checksums, packages); -} - -function validateReleaseResolvedArtifacts( - resolvedArtifacts: readonly ReleaseReceiptResolvedArtifactEvidence[], - descriptorArtifacts: readonly ManagedToolDescriptorArtifactReference[], - packages: readonly ReleaseReceiptPackageEvidence[] -): void { - validateNonEmptyArray(resolvedArtifacts, "Release receipt descriptor resolvedArtifacts"); - validateExactStringSet( - resolvedArtifacts.map((entry) => entry.id), - descriptorArtifacts.map((entry) => entry.id), - "Release receipt descriptor resolved artifact ids" - ); - for (const resolved of resolvedArtifacts) { - validateReleaseReceiptPackageName(resolved.packageName, "Release receipt descriptor resolved artifact packageName"); - validateRepoRelativePath(resolved.path); - if (!includesString(managedToolDescriptorArtifactTypes, resolved.type)) { - throw new Error(`Unknown release receipt descriptor resolved artifact type: ${String(resolved.type)}`); - } - if (resolved.packageFile !== true) throw new Error(`Release receipt resolved artifact ${resolved.id} must resolve to a package file`); - const descriptorArtifact = descriptorArtifacts.find((entry) => entry.id === resolved.id); - if (!descriptorArtifact) throw new Error(`Release receipt resolved artifact is not declared by descriptor: ${resolved.id}`); - if ( - descriptorArtifact.packageName !== resolved.packageName || - descriptorArtifact.path !== resolved.path || - descriptorArtifact.type !== resolved.type || - descriptorArtifact.required !== resolved.required || - descriptorArtifact.checksumRef !== resolved.checksumRef - ) { - throw new Error(`Release receipt resolved artifact must mirror descriptor: ${resolved.id}`); - } - if (!packageEvidenceIncludesFile(packages, resolved.packageName, resolved.path)) { - throw new Error(`Release receipt resolved artifact ${resolved.id} is not present in packed package files`); - } - } -} - -function validateReleaseResolvedChecksums( - resolvedChecksums: readonly ReleaseReceiptResolvedChecksumEvidence[], - descriptorChecksums: readonly ManagedToolDescriptorChecksumReference[], - packages: readonly ReleaseReceiptPackageEvidence[] -): void { - validateNonEmptyArray(resolvedChecksums, "Release receipt descriptor resolvedChecksums"); - validateExactStringSet( - resolvedChecksums.map((entry) => entry.id), - descriptorChecksums.map((entry) => entry.id), - "Release receipt descriptor resolved checksum ids" - ); - for (const resolved of resolvedChecksums) { - validateReleaseReceiptPackageName(resolved.packageName, "Release receipt descriptor resolved checksum packageName"); - validateRepoRelativePath(resolved.path); - if (resolved.algorithm !== "sha256") throw new Error("Release receipt descriptor checksum algorithm must be sha256"); - if (resolved.packageFile !== true) throw new Error(`Release receipt resolved checksum ${resolved.id} must resolve to a package file`); - validateSha256(resolved.value, "Release receipt descriptor checksum value"); - const descriptorChecksum = descriptorChecksums.find((entry) => entry.id === resolved.id); - if (!descriptorChecksum) throw new Error(`Release receipt resolved checksum is not declared by descriptor: ${resolved.id}`); - if ( - descriptorChecksum.packageName !== resolved.packageName || - descriptorChecksum.path !== resolved.path || - descriptorChecksum.algorithm !== resolved.algorithm || - descriptorChecksum.artifactRef !== resolved.artifactRef || - descriptorChecksum.required !== resolved.required - ) { - throw new Error(`Release receipt resolved checksum must mirror descriptor: ${resolved.id}`); - } - if (descriptorChecksum.value !== undefined && descriptorChecksum.value !== resolved.value) { - throw new Error(`Release receipt resolved checksum value must match descriptor: ${resolved.id}`); - } - if (!packageEvidenceIncludesFile(packages, resolved.packageName, resolved.path)) { - throw new Error(`Release receipt resolved checksum ${resolved.id} is not present in packed package files`); - } - } -} - -function validateReleaseReceiptNativeArtifacts( - nativeArtifacts: readonly ReleaseReceiptNativeArtifactEvidence[], - packages: readonly ReleaseReceiptPackageEvidence[], - descriptorEvidence: ReleaseReceiptDescriptorEvidence -): void { - validateNonEmptyArray(nativeArtifacts, "Release receipt native artifacts"); - validateExactStringSet( - nativeArtifacts.map((artifact) => artifact.targetPlatform), - graphCoreNativeSupportedTargets, - "Release receipt native artifact targets" - ); - for (const nativeArtifact of nativeArtifacts) { - validateReleaseReceiptNativeArtifact(nativeArtifact); - if (!packageEvidenceIncludesFile(packages, nativeArtifact.packageName, nativeArtifact.binaryPath)) { - throw new Error("Release receipt native artifact binary must be present in native package files"); - } - if (!packageEvidenceIncludesFile(packages, nativeArtifact.packageName, nativeArtifact.checksumPath)) { - throw new Error("Release receipt native artifact checksum must be present in native package files"); - } - if (!packageEvidenceIncludesFile(packages, nativeArtifact.packageName, nativeArtifact.metadataPath)) { - throw new Error("Release receipt native artifact metadata must be present in native package files"); - } - const binaryArtifact = descriptorEvidence.resolvedArtifacts.find((artifact) => artifact.id === nativeArtifact.descriptorArtifactId); - if (!binaryArtifact || binaryArtifact.packageName !== nativeArtifact.packageName || binaryArtifact.path !== nativeArtifact.binaryPath) { - throw new Error("Release receipt native artifact binary must resolve from descriptor artifacts"); - } - const checksum = descriptorEvidence.resolvedChecksums.find((entry) => entry.id === nativeArtifact.descriptorChecksumId); - if ( - !checksum || - checksum.packageName !== nativeArtifact.packageName || - checksum.path !== nativeArtifact.checksumPath || - checksum.value !== nativeArtifact.binarySha256 - ) { - throw new Error("Release receipt native artifact checksum must resolve from descriptor checksum evidence"); - } - } -} - -function validateReleaseReceiptNativeArtifact(nativeArtifact: ReleaseReceiptNativeArtifactEvidence): void { - if (!nativeArtifact || typeof nativeArtifact !== "object") throw new Error("Release receipt native artifact evidence is required"); - if (nativeArtifact.packageName !== "opcore") { - throw new Error("Release receipt native artifact packageName must be opcore"); - } - if (!isGraphCoreNativePackageName(nativeArtifact.bundledPackageName)) { - throw new Error("Release receipt native artifact bundledPackageName must be an Opcore graph-core native package"); - } - const expectedTarget = graphCoreNativeTargetForPackageName(nativeArtifact.bundledPackageName); - if (nativeArtifact.targetPlatform !== expectedTarget) { - throw new Error(`Release receipt native artifact targetPlatform must be ${expectedTarget}`); - } - if (nativeArtifact.binaryPath !== bundledGraphCoreNativePath(nativeArtifact.bundledPackageName, "opcore-graph-core")) { - throw new Error("Release receipt native artifact binaryPath must point at the bundled native binary"); - } - if (nativeArtifact.checksumPath !== bundledGraphCoreNativePath(nativeArtifact.bundledPackageName, "opcore-graph-core.sha256")) { - throw new Error("Release receipt native artifact checksumPath must point at the bundled native checksum"); - } - if (nativeArtifact.metadataPath !== bundledGraphCoreNativePath(nativeArtifact.bundledPackageName, "metadata.json")) { - throw new Error("Release receipt native artifact metadataPath must point at the bundled native metadata"); - } - validateGraphProviderArtifactMetadata(nativeArtifact.metadata); - validateRepoRelativePath(nativeArtifact.binaryPath); - validateRepoRelativePath(nativeArtifact.checksumPath); - validateRepoRelativePath(nativeArtifact.metadataPath); - validateSha256(nativeArtifact.binarySha256, "Release receipt native artifact binarySha256"); - validateSha256(nativeArtifact.checksumFileSha256, "Release receipt native artifact checksumFileSha256"); - validateSha256(nativeArtifact.metadataSha256, "Release receipt native artifact metadataSha256"); - validateNonEmptyString(nativeArtifact.descriptorArtifactId, "Release receipt native artifact descriptorArtifactId"); - validateNonEmptyString(nativeArtifact.descriptorChecksumId, "Release receipt native artifact descriptorChecksumId"); - if (nativeArtifact.metadata.targetPlatform !== nativeArtifact.targetPlatform) { - throw new Error("Release receipt native artifact targetPlatform must match metadata"); - } - if (nativeArtifact.metadata.binaryPath !== "opcore-graph-core") { - throw new Error("Release receipt native artifact binaryPath must match metadata"); - } - if (nativeArtifact.metadata.checksumPath !== "opcore-graph-core.sha256") { - throw new Error("Release receipt native artifact checksumPath must match metadata"); - } - if (nativeArtifact.metadata.checksumSha256 !== nativeArtifact.binarySha256) { - throw new Error("Release receipt native artifact binary sha256 must match metadata checksum"); - } -} - -function validateReleaseReceiptLicense(license: ReleaseReceiptLicenseEvidence): void { - if (!license || typeof license !== "object") throw new Error("Release receipt license evidence is required"); - validateRepoRelativePath(license.reportPath); - validateSha256(license.reportSha256, "Release receipt license reportSha256"); - validateNonNegativeInteger(license.productionDependencyCount, "Release receipt license productionDependencyCount"); - validateNonNegativeInteger(license.bundledDependencyCount, "Release receipt license bundledDependencyCount"); - validateNonNegativeInteger(license.workspacePackageCount, "Release receipt license workspacePackageCount"); - if (license.workspacePackageCount < releaseReceiptPackageNames.length) { - throw new Error(`Release receipt license workspacePackageCount must be at least ${releaseReceiptPackageNames.length}`); - } - if (license.unresolvedLicenseCount !== 0) throw new Error("Release receipt license unresolvedLicenseCount must be 0"); - if (!Array.isArray(license.packages)) throw new Error("Release receipt license packages must be an array"); - for (const packageEvidence of license.packages) { - validateNonEmptyString(packageEvidence.name, "Release receipt license package name"); - validateNonEmptyString(packageEvidence.version, "Release receipt license package version"); - validateNonEmptyString(packageEvidence.license, "Release receipt license package license"); - validateNonEmptyString(packageEvidence.source, "Release receipt license package source"); - if (typeof packageEvidence.bundled !== "boolean") throw new Error("Release receipt license package bundled must be boolean"); - } -} - -function validateReleaseReceiptProvenance(provenance: ReleaseReceiptProvenanceEvidence): void { - if (!provenance || typeof provenance !== "object") throw new Error("Release receipt provenance evidence is required"); - validateRepoRelativePath(provenance.reportPath); - validateSha256(provenance.reportSha256, "Release receipt provenance reportSha256"); - validateNonNegativeInteger(provenance.scannedFileCount, "Release receipt provenance scannedFileCount"); - validateNonNegativeInteger(provenance.historyCommitCount, "Release receipt provenance historyCommitCount"); - if (provenance.findingCount !== 0 || provenance.findings.length !== 0) { - throw new Error("Release receipt provenance findings must be empty"); - } -} - -function validateReleaseReceiptSecretHistory(secretHistory: ReleaseReceiptSecretHistoryEvidence): void { - if (!secretHistory || typeof secretHistory !== "object") throw new Error("Release receipt secret history evidence is required"); - validateRepoRelativePath(secretHistory.allowlistPath); - validateSha256(secretHistory.allowlistSha256, "Release receipt secret history allowlistSha256"); - validateNonNegativeInteger(secretHistory.currentTreeScannedFileCount, "Release receipt secret history currentTreeScannedFileCount"); - validateNonNegativeInteger(secretHistory.gitHistoryScannedCommitCount, "Release receipt secret history gitHistoryScannedCommitCount"); - if (secretHistory.findingCount !== 0 || secretHistory.findings.length !== 0) { - throw new Error("Release receipt secret findings must be empty"); - } -} - -function validateReleaseReceiptReports(reports: readonly ReleaseReceiptReport[]): void { - validateNonEmptyArray(reports, "Release receipt reports"); - validateExactStringSet( - reports.map((entry) => entry.id), - releaseReceiptReportIds, - "Release receipt reports" - ); - for (const report of reports) { - validateReleaseReceiptReportId(report.id, "Release receipt report id"); - validateStringArray(report.command, "Release receipt report command", { allowEmpty: false }); - if (report.status !== "passed") throw new Error("Release receipt report status must be passed"); - if (report.exitCode !== 0) throw new Error("Release receipt report exitCode must be 0"); - if (report.path !== undefined) validateRepoRelativePath(report.path); - if (report.checksumSha256 !== undefined) validateSha256(report.checksumSha256, "Release receipt report checksumSha256"); - validateNonEmptyString(report.summary, "Release receipt report summary"); - } -} - -function validateReleaseReceiptGraphReleaseEvidence(evidence: ReleaseReceiptGraphReleaseEvidence): void { - if (!evidence || typeof evidence !== "object") throw new Error("Release receipt graph release evidence is required"); - validateRepoRelativePath(evidence.path); - if (evidence.issue !== "#17") throw new Error("Release receipt graph release evidence issue must be #17"); - validateSha256(evidence.checksumSha256, "Release receipt graph release checksumSha256"); -} - -function validateReleaseReceiptBins(bins: Readonly>, packageName: ReleaseReceiptPackageName): void { - if (!bins || typeof bins !== "object" || Array.isArray(bins)) throw new Error("Release receipt bins must be an object"); - const binNames = Object.keys(bins); - for (const bin of binNames) { - validateNonEmptyString(bin, "Release receipt bin name"); - validateRepoRelativePath(bins[bin]); - } - if (packageName === "opcore") { - validateExactStringSet(binNames, ["opcore", "opcore-asp-provider"], "Release receipt Opcore package bins"); - } else if (binNames.length > 0) { - throw new Error(`${packageName} must not expose CLI bins`); - } -} - -function validateReleaseCutoverInstalledPackages(packages: readonly ReleaseCutoverInstalledPackageEvidence[]): void { - validateNonEmptyArray(packages, "Release cutover installed package evidence"); - validateReleaseCutoverInstalledPackageSet(packages.map((entry) => entry.packageName)); - for (const entry of packages) { - if (!entry || typeof entry !== "object") throw new Error("Release cutover installed package evidence entry is required"); - validateReleaseReceiptPackageName(entry.packageName, "Release cutover installed package packageName"); - validateNonEmptyString(entry.version, "Release cutover installed package version"); - if (!entry.tarball || typeof entry.tarball !== "object") throw new Error("Release cutover tarball evidence is required"); - validateNonEmptyString(entry.tarball.filename, "Release cutover tarball filename"); - validateSha256(entry.tarball.sha256, "Release cutover tarball sha256"); - if (!entry.installedManifest || typeof entry.installedManifest !== "object") { - throw new Error("Release cutover installed manifest evidence is required"); - } - validateNonEmptyString(entry.installedManifest.path, "Release cutover installed manifest path"); - if (!entry.installedManifest.path.includes("node_modules/") || !entry.installedManifest.path.endsWith("package.json")) { - throw new Error("Release cutover installed manifest path must be inside node_modules and end with package.json"); - } - validateSha256(entry.installedManifest.sha256, "Release cutover installed manifest sha256"); - validateReleaseReceiptBins(entry.installedManifest.bins, entry.packageName); - validateReleaseCutoverInstalledFiles(entry); - } -} - -function validateReleaseCutoverInstalledFiles(entry: ReleaseCutoverInstalledPackageEvidence): void { - validateNonEmptyArray(entry.installedFiles, "Release cutover installed files"); - const prefix = `node_modules/${entry.packageName}/`; - const paths = []; - for (const file of entry.installedFiles) { - if (!file || typeof file !== "object") throw new Error("Release cutover installed file evidence entry is required"); - validateNonEmptyString(file.path, "Release cutover installed file path"); - if (!file.path.startsWith(prefix)) { - throw new Error(`Release cutover installed file path must be inside ${prefix}`); - } - validateSha256(file.sha256, "Release cutover installed file sha256"); - paths.push(file.path); - } - if (new Set(paths).size !== paths.length) { - throw new Error("Release cutover installed file paths must be unique"); - } - if (!paths.includes(entry.installedManifest.path)) { - throw new Error("Release cutover installed files must include package.json"); - } - const binPaths = Object.values(entry.installedManifest.bins).map((path) => `${prefix}${path}`); - for (const binPath of binPaths) { - if (!paths.includes(binPath)) throw new Error(`Release cutover installed files must include bin target ${binPath}`); - } - if (entry.packageName === "opcore") { - if (!paths.includes("node_modules/opcore/node_modules/@the-open-engine/opcore-asp-provider/dist/manifests/asp-server.json")) { - throw new Error("Release cutover Opcore installed files must include bundled canonical asp-server.json"); - } - for (const target of graphCoreNativeSupportedTargets) { - const bundledPackageName = graphCoreNativePackageNameForTarget(target); - if (!paths.includes(`node_modules/opcore/${bundledGraphCoreNativePath(bundledPackageName, "opcore-graph-core")}`)) { - throw new Error(`Release cutover Opcore installed files must include bundled native binary for ${target}`); - } - } - } -} - -function validateReleaseCutoverInstalledPackageSet(packageNames: readonly string[]): void { - validateExactStringSet(packageNames, releaseReceiptPackageNames, "Release cutover installed package evidence"); - for (const packageName of packageNames) { - validateReleaseReceiptPackageName(packageName, "Release cutover installed package packageName"); - } -} - -function validateReleaseCutoverDescriptor(descriptorEvidence: ReleaseCutoverDescriptorEvidence): void { - if (!descriptorEvidence || typeof descriptorEvidence !== "object") throw new Error("Release cutover descriptor evidence is required"); - validateNonEmptyString(descriptorEvidence.path, "Release cutover descriptor path"); - if (descriptorEvidence.packageName !== "opcore") { - throw new Error("Release cutover descriptor packageName must be opcore"); - } - validateSha256(descriptorEvidence.checksumSha256, "Release cutover descriptor checksumSha256"); - const descriptor = validateManagedToolDescriptor(descriptorEvidence.descriptor); - validateNonEmptyArray(descriptorEvidence.resolvedArtifacts, "Release cutover descriptor resolvedArtifacts"); - validateExactStringSet( - descriptorEvidence.resolvedArtifacts.map((entry) => entry.id), - descriptor.artifacts.map((entry) => entry.id), - "Release cutover descriptor resolved artifact ids" - ); - for (const artifact of descriptorEvidence.resolvedArtifacts) { - validateReleaseReceiptPackageName(artifact.packageName, "Release cutover descriptor resolved artifact packageName"); - validateNonEmptyString(artifact.path, "Release cutover descriptor resolved artifact path"); - validateNonEmptyString(artifact.id, "Release cutover descriptor resolved artifact id"); - if (!includesString(managedToolDescriptorArtifactTypes, artifact.type)) { - throw new Error(`Unknown release cutover descriptor resolved artifact type: ${String(artifact.type)}`); - } - if (artifact.packageFile !== true) throw new Error("Release cutover descriptor resolved artifacts must be package files"); - } - validateNonEmptyArray(descriptorEvidence.resolvedChecksums, "Release cutover descriptor resolvedChecksums"); - validateExactStringSet( - descriptorEvidence.resolvedChecksums.map((entry) => entry.id), - descriptor.checksums.map((entry) => entry.id), - "Release cutover descriptor resolved checksum ids" - ); - for (const checksum of descriptorEvidence.resolvedChecksums) { - validateReleaseReceiptPackageName(checksum.packageName, "Release cutover descriptor resolved checksum packageName"); - validateNonEmptyString(checksum.path, "Release cutover descriptor resolved checksum path"); - validateNonEmptyString(checksum.id, "Release cutover descriptor resolved checksum id"); - if (checksum.algorithm !== "sha256") throw new Error("Release cutover descriptor checksum algorithm must be sha256"); - validateSha256(checksum.value, "Release cutover descriptor resolved checksum value"); - if (checksum.packageFile !== true) throw new Error("Release cutover descriptor resolved checksums must be package files"); - } -} - -function validateReleaseCutoverEnvironmentIsolation(environment: ReleaseCutoverEnvironmentIsolationEvidence): void { - if (!environment || typeof environment !== "object") throw new Error("Release cutover environmentIsolation is required"); - if (environment.pathSanitized !== true) throw new Error("Release cutover PATH must be sanitized"); - if (environment.siblingRepositoriesExcluded !== true) { - throw new Error("Release cutover sibling repository paths must be excluded"); - } - if (environment.opcoreBinsVerified !== true) { - throw new Error("Release cutover installed project bins must be verified"); - } -} - -function validateReleaseCutoverCommandReceipts(receipts: readonly ReleaseCutoverCommandReceipt[]): void { - validateNonEmptyArray(receipts, "Release cutover command receipts"); - validateExactStringSet( - receipts.map((entry) => entry.id), - releaseCutoverRequiredCommandIds, - "Release cutover command receipts" - ); - for (const receipt of receipts) { - if (!receipt || typeof receipt !== "object") throw new Error("Release cutover command receipt is required"); - if (!includesString(releaseCutoverRequiredCommandIds, receipt.id)) { - throw new Error(`Unknown release cutover command receipt id: ${String(receipt.id)}`); - } - validateStringArray(receipt.command, "Release cutover command receipt command", { allowEmpty: false }); - validateStringArray(receipt.canonicalCommand, "Release cutover command receipt canonicalCommand", { allowEmpty: false }); - validateExactStringSequence(receipt.command, receipt.canonicalCommand, `Release cutover ${receipt.id} command`); - const expected = releaseCutoverCommandExpectations[receipt.id]; - if (receipt.command[0] !== expected.bin) { - throw new Error(`Release cutover command ${receipt.id} command must use canonical ${expected.bin} bin`); - } - if (receipt.canonicalCommand[0] !== expected.bin) { - throw new Error(`Release cutover command ${receipt.id} canonicalCommand must use canonical ${expected.bin} bin`); - } - validateCommandOwner(receipt.owner); - validateReleaseCutoverExpectedCommand(receipt.canonicalCommand, expected, receipt.id); - if (receipt.owner !== expected.owner) { - throw new Error(`Release cutover command ${receipt.id} owner must match expected ${expected.owner}`); - } - const status = validateCommandRouteStatus(receipt.status); - if (receipt.status === "not_implemented") { - throw new Error("Release cutover command receipts must not be not_implemented"); - } - if (status !== expected.status) { - throw new Error(`Release cutover command ${receipt.id} status must match expected ${expected.status}`); - } - validateExitCodeForStatus(receipt.exitCode, status); - if (receipt.exitCode !== expected.exitCode) { - throw new Error(`Release cutover command ${receipt.id} exitCode must match expected ${expected.exitCode}`); - } - validateNonEmptyString(receipt.binPath, "Release cutover command receipt binPath"); - if (!receipt.binPath.endsWith(`node_modules/.bin/${expected.bin}`)) { - throw new Error(`Release cutover command receipt binPath must use installed node_modules/.bin/${expected.bin}`); - } - validateSha256(receipt.stdoutSha256, "Release cutover command receipt stdoutSha256"); - validateSha256(receipt.stderrSha256, "Release cutover command receipt stderrSha256"); - validateNonEmptyString(receipt.assertion, "Release cutover command receipt assertion"); - } -} - -function validateReleaseCutoverRustCommandReceipts(receipts: readonly ReleaseCutoverRustCommandReceipt[]): void { - validateNonEmptyArray(receipts, "Release cutover Rust command receipts"); - validateExactStringSet( - receipts.map((entry) => entry.id), - releaseCutoverRustCommandIds, - "Release cutover Rust command receipts" - ); - for (const receipt of receipts) { - if (!receipt || typeof receipt !== "object") throw new Error("Release cutover Rust command receipt is required"); - if (!includesString(releaseCutoverRustCommandIds, receipt.id)) { - throw new Error(`Unknown release cutover Rust command receipt id: ${String(receipt.id)}`); - } - validateStringArray(receipt.command, "Release cutover Rust command receipt command", { allowEmpty: false }); - validateStringArray(receipt.canonicalCommand, "Release cutover Rust command receipt canonicalCommand", { allowEmpty: false }); - validateExactStringSequence(receipt.command, receipt.canonicalCommand, `Release cutover Rust ${receipt.id} command`); - const expected = releaseCutoverRustCommandExpectations[receipt.id]; - validateReleaseCutoverExpectedCommand(receipt.canonicalCommand, expected, receipt.id); - if (receipt.owner !== "graph") throw new Error(`Release cutover Rust command ${receipt.id} owner must be graph`); - if (receipt.status !== "ok") throw new Error(`Release cutover Rust command ${receipt.id} status must be ok`); - if (receipt.exitCode !== 0) throw new Error(`Release cutover Rust command ${receipt.id} exitCode must be 0`); - validateNonEmptyString(receipt.binPath, "Release cutover Rust command receipt binPath"); - if (!receipt.binPath.endsWith("node_modules/.bin/opcore")) { - throw new Error("Release cutover Rust command receipt binPath must use installed node_modules/.bin/opcore"); - } - validateSha256(receipt.stdoutSha256, "Release cutover Rust command receipt stdoutSha256"); - validateSha256(receipt.stderrSha256, "Release cutover Rust command receipt stderrSha256"); - validateNonEmptyString(receipt.assertion, "Release cutover Rust command receipt assertion"); - } -} - -function validateReleaseCutoverPythonCommandReceipts(receipts: readonly ReleaseCutoverPythonCommandReceipt[]): void { - validateNonEmptyArray(receipts, "Release cutover Python command receipts"); - validateExactStringSet( - receipts.map((entry) => entry.id), - releaseCutoverPythonCommandIds, - "Release cutover Python command receipts" - ); - for (const receipt of receipts) { - if (!receipt || typeof receipt !== "object") throw new Error("Release cutover Python command receipt is required"); - if (!includesString(releaseCutoverPythonCommandIds, receipt.id)) { - throw new Error(`Unknown release cutover Python command receipt id: ${String(receipt.id)}`); - } - validateStringArray(receipt.command, "Release cutover Python command receipt command", { allowEmpty: false }); - validateStringArray(receipt.canonicalCommand, "Release cutover Python command receipt canonicalCommand", { allowEmpty: false }); - validateStringArray(receipt.evidence, "Release cutover Python command receipt evidence", { allowEmpty: false }); - validateExactStringSequence(receipt.command, receipt.canonicalCommand, `Release cutover Python ${receipt.id} command`); - const expected = releaseCutoverPythonCommandExpectations[receipt.id]; - validateExactStringSet( - receipt.evidence, - releaseCutoverPythonEvidenceExpectations[receipt.id], - `Release cutover Python command ${receipt.id} evidence` - ); - if (receipt.command[0] !== expected.bin) { - throw new Error(`Release cutover Python command ${receipt.id} command must use canonical ${expected.bin} bin`); - } - if (receipt.canonicalCommand[0] !== expected.bin) { - throw new Error(`Release cutover Python command ${receipt.id} canonicalCommand must use canonical ${expected.bin} bin`); - } - validateCommandOwner(receipt.owner); - validateReleaseCutoverExpectedCommand(receipt.canonicalCommand, expected, receipt.id); - if (receipt.owner !== expected.owner) { - throw new Error(`Release cutover Python command ${receipt.id} owner must match expected ${expected.owner}`); - } - if (receipt.status !== "ok") throw new Error(`Release cutover Python command ${receipt.id} status must be ok`); - if (receipt.exitCode !== 0) throw new Error(`Release cutover Python command ${receipt.id} exitCode must be 0`); - validateNonEmptyString(receipt.binPath, "Release cutover Python command receipt binPath"); - if (!receipt.binPath.endsWith(`node_modules/.bin/${expected.bin}`)) { - throw new Error(`Release cutover Python command receipt binPath must use installed node_modules/.bin/${expected.bin}`); - } - validateSha256(receipt.stdoutSha256, "Release cutover Python command receipt stdoutSha256"); - validateSha256(receipt.stderrSha256, "Release cutover Python command receipt stderrSha256"); - validateNonEmptyString(receipt.assertion, "Release cutover Python command receipt assertion"); - } -} - -function validateReleaseCutoverExpectedCommand( - command: readonly string[], - expectation: ReleaseCutoverCommandExpectation, - id: ReleaseCutoverCommandId | ReleaseCutoverRustCommandId | ReleaseCutoverPythonCommandId -): void { - if (!releaseCutoverCommandMatchesExpectation(command, expectation)) { - throw new Error(`Release cutover command ${id} canonicalCommand must match expected ${formatReleaseCutoverCommand(expectation)}`); - } -} - -function releaseCutoverCommandMatchesExpectation( - command: readonly string[], - expectation: ReleaseCutoverCommandExpectation -): boolean { - if (command.length !== expectation.canonicalCommand.length) return false; - return expectation.canonicalCommand.every((expected, index) => { - const actual = command[index]; - if (expected !== releaseCutoverRequestFilePlaceholder) return actual === expected; - return releaseCutoverPathBasename(actual) === expectation.requestFileBasename; - }); -} - -function releaseCutoverPathBasename(path: string): string { - const normalized = path.replaceAll("\\", "/"); - const parts = normalized.split("/"); - return parts[parts.length - 1] ?? normalized; -} - -function formatReleaseCutoverCommand(expectation: ReleaseCutoverCommandExpectation): string { - return expectation.canonicalCommand - .map((part) => (part === releaseCutoverRequestFilePlaceholder ? `<${expectation.requestFileBasename}>` : part)) - .join(" "); -} - -function validateReleaseCutoverNegativeChecks(checks: readonly ReleaseCutoverNegativeCheck[]): void { - validateNonEmptyArray(checks, "Release cutover negative checks"); - validateExactStringSet( - checks.map((entry) => entry.id), - releaseCutoverNegativeCheckIds, - "Release cutover negative checks" - ); - for (const check of checks) { - if (!check || typeof check !== "object") throw new Error("Release cutover negative check is required"); - validateNonEmptyString(check.id, "Release cutover negative check id"); - if (!includesString(releaseCutoverNegativeCheckIds, check.id)) { - throw new Error(`Unknown release cutover negative check id: ${String(check.id)}`); - } - validateStringArray(check.command, "Release cutover negative check command", { allowEmpty: false }); - validateExactStringSequence( - check.command, - releaseCutoverNegativeCheckExpectations[check.id], - `Release cutover negative check ${check.id} command` - ); - if (check.status !== "passed") throw new Error("Release cutover negative check status must be passed"); - if (check.exitCode !== 0) throw new Error("Release cutover negative check exitCode must be 0"); - validateNonEmptyString(check.assertion, "Release cutover negative check assertion"); - } -} - -function validateReleaseCutoverForbiddenMarkerScan(scan: ReleaseCutoverForbiddenMarkerScan): void { - if (!scan || typeof scan !== "object") throw new Error("Release cutover forbiddenMarkerScan is required"); - validateNonNegativeInteger(scan.scannedTextCount, "Release cutover forbidden marker scannedTextCount"); - if (scan.scannedTextCount === 0) throw new Error("Release cutover forbidden marker scan must scan at least one text"); - if (scan.findingCount !== 0) throw new Error("Release cutover forbidden marker findingCount must be 0"); - validateStringArray(scan.markersBlocked, "Release cutover forbidden marker labels", { allowEmpty: false }); -} - -function validateReleaseCutoverInputEvidence(evidence: readonly ReleaseCutoverInputEvidence[]): void { - validateNonEmptyArray(evidence, "Release cutover input evidence"); - validateExactStringSet( - evidence.map((entry) => entry.issue), - releaseCutoverInputIssues, - "Release cutover input evidence issues" - ); - for (const entry of evidence) { - if (!entry || typeof entry !== "object") throw new Error("Release cutover input evidence entry is required"); - if (!includesString(releaseCutoverInputIssues, entry.issue)) { - throw new Error(`Unknown release cutover input evidence issue: ${String(entry.issue)}`); - } - validateNonEmptyString(entry.path, "Release cutover input evidence path"); - validateSha256(entry.checksumSha256, "Release cutover input evidence checksumSha256"); - } -} - -function validateAspDogfoodManager(manager: AspDogfoodManagerEvidence): void { - if (!manager || typeof manager !== "object") throw new Error("ASP dogfood manager evidence is required"); - if (manager.bootstrapSource !== "local-sibling") throw new Error("ASP dogfood bootstrapSource must be local-sibling"); - validateNonEmptyString(manager.aspRepoPath, "ASP dogfood manager aspRepoPath"); - validateNonEmptyString(manager.aspBinPath, "ASP dogfood manager aspBinPath"); - validateNonEmptyString(manager.cliPath, "ASP dogfood manager cliPath"); - validateNonEmptyString(manager.commitSha, "ASP dogfood manager commitSha"); -} - -function validateAspDogfoodAspHome(aspHome: AspDogfoodAspHomeEvidence): void { - if (!aspHome || typeof aspHome !== "object") throw new Error("ASP dogfood ASP_HOME evidence is required"); - validateNonEmptyString(aspHome.path, "ASP dogfood ASP_HOME path"); - if (aspHome.temp !== true) throw new Error("ASP dogfood ASP_HOME must be temporary"); - if (aspHome.isolated !== true) throw new Error("ASP dogfood ASP_HOME must be isolated"); - if (aspHome.sharedStateMutated !== false) throw new Error("ASP dogfood shared ASP state must not be mutated"); - if (aspHome.pathSanitized !== true) throw new Error("ASP dogfood PATH must be sanitized for manager execution"); -} - -function validateAspDogfoodHostFixture(fixture: AspDogfoodHostFixtureEvidence): void { - if (!fixture || typeof fixture !== "object") throw new Error("ASP dogfood host fixture evidence is required"); - validateNonEmptyString(fixture.repo, "ASP dogfood host fixture repo"); - if (fixture.temp !== true) throw new Error("ASP dogfood host fixture repo must be temporary"); - if (fixture.sourceRepoMutated !== false) throw new Error("ASP dogfood host fixture must not mutate the source repo"); - if (fixture.baselineCommitted !== true) throw new Error("ASP dogfood host fixture must commit a baseline"); - validateStringArray(fixture.changedPaths, "ASP dogfood host fixture changedPaths", { allowEmpty: false }); - for (const path of fixture.changedPaths) validateRepoRelativePath(path); -} - -function validateAspDogfoodProvider(provider: AspDogfoodProviderEvidence): void { - if (!provider || typeof provider !== "object") throw new Error("ASP dogfood provider evidence is required"); - if (provider.providerId !== "opcore") throw new Error("ASP dogfood providerId must be opcore"); - if (provider.packageName !== "opcore") { - throw new Error("ASP dogfood provider package must be opcore"); - } - validateNonEmptyString(provider.binPath, "ASP dogfood provider binPath"); - if (!provider.binPath.endsWith("node_modules/.bin/opcore-asp-provider")) { - throw new Error("ASP dogfood provider binPath must use installed node_modules/.bin/opcore-asp-provider"); - } - validateNonEmptyString(provider.indexPath, "ASP dogfood provider indexPath"); - if (!provider.indexPath.endsWith("node_modules/opcore/node_modules/@the-open-engine/opcore-asp-provider/dist/index.js")) { - throw new Error("ASP dogfood provider indexPath must be bundled opcore-asp-provider dist/index.js"); - } - validateSha256(provider.indexSha256, "ASP dogfood provider indexSha256"); - validateExactStringSequence(provider.command, ["opcore-asp-provider", "--stdio"], "ASP dogfood provider command"); - if (!provider.entrypoint || typeof provider.entrypoint !== "object") throw new Error("ASP dogfood provider entrypoint is required"); - if (provider.entrypoint.transport !== "stdio") throw new Error("ASP dogfood provider entrypoint transport must be stdio"); - validateAspDogfoodProviderBinPath(provider.entrypoint.bin, "ASP dogfood provider entrypoint bin"); - validateExactStringSequence(provider.entrypoint.args, ["--stdio"], "ASP dogfood provider entrypoint args"); - validateAspDogfoodProviderManifest(provider.manifest); -} - -function validateAspDogfoodProviderManifest(manifestEvidence: AspDogfoodProviderManifestEvidence): void { - if (!manifestEvidence || typeof manifestEvidence !== "object") throw new Error("ASP dogfood provider manifest evidence is required"); - validateNonEmptyString(manifestEvidence.manifestPath, "ASP dogfood provider manifestPath"); - validateSha256(manifestEvidence.manifestSha256, "ASP dogfood provider manifestSha256"); - if (!manifestEvidence.manifest || typeof manifestEvidence.manifest !== "object") { - throw new Error("ASP dogfood provider manifest must be structured metadata"); - } - const manifest = manifestEvidence.manifest as Record; - if (manifest.manifestVersion !== "asp-server/0.1") throw new Error("ASP dogfood provider manifestVersion must be asp-server/0.1"); - const server = manifest.server as Record | undefined; - if (!server || server.id !== "opcore") throw new Error("ASP dogfood provider manifest server.id must be opcore"); - const entrypoint = manifest.entrypoint as Record | undefined; - if (!entrypoint || entrypoint.transport !== "stdio" || typeof entrypoint.bin !== "string") { - throw new Error("ASP dogfood provider manifest entrypoint must be opcore-asp-provider --stdio"); - } - validateAspDogfoodProviderBinPath(entrypoint.bin, "ASP dogfood provider manifest entrypoint bin"); - validateExactStringSequence(entrypoint.args as readonly string[], ["--stdio"], "ASP dogfood provider manifest entrypoint args"); - validateExactStringSet((manifest.capabilities as readonly string[]) ?? [], ["check"], "ASP dogfood provider manifest capabilities"); -} - -function validateAspDogfoodProviderBinPath(value: unknown, label: string): void { - validateNonEmptyString(value, label); - const normalized = String(value).replaceAll("\\", "/"); - if (!/node_modules\/\.bin\/opcore-asp-provider(?:\.cmd)?$/.test(normalized)) { - throw new Error(`${label} must use installed node_modules/.bin/opcore-asp-provider`); - } -} - -function validateAspDogfoodManagerState(state: AspDogfoodManagerStateEvidence): void { - if (!state || typeof state !== "object") throw new Error("ASP dogfood managerState is required"); - validateAspDogfoodPassedCommandRun(state.status, "asp-status", "ASP dogfood manager status"); - validateAspDogfoodPassedCommandRun(state.serverAdd, "asp-server-add", "ASP dogfood manager server add"); - validateAspDogfoodPassedCommandRun(state.serverStatus, "asp-server-status", "ASP dogfood manager server status"); - if (!state.serverStatus.output || typeof state.serverStatus.output !== "object") { - throw new Error("ASP dogfood server status output is required"); - } -} - -function validateAspDogfoodRepoEnrollment(enrollment: AspDogfoodRepoEnrollmentEvidence): void { - if (!enrollment || typeof enrollment !== "object") throw new Error("ASP dogfood repo enrollment is required"); - validateNonEmptyString(enrollment.repo, "ASP dogfood repo enrollment repo"); - if (enrollment.mode !== "advisory" && enrollment.mode !== "shadow") { - throw new Error("ASP dogfood repo enrollment mode must be advisory or shadow"); - } - validateAspDogfoodPassedCommandRun(enrollment.repoAdd, "asp-repo-add", "ASP dogfood repo add"); - validateAspDogfoodPassedCommandRun(enrollment.repoEnable, "asp-repo-enable", "ASP dogfood repo enable"); - validateAspDogfoodPassedCommandRun(enrollment.repoStatus, "asp-repo-status", "ASP dogfood repo status"); -} - -function validateAspDogfoodHostEvaluation(evaluation: AspDogfoodHostEvaluationEvidence): void { - if (!evaluation || typeof evaluation !== "object") throw new Error("ASP dogfood host evaluation is required"); - validateAspDogfoodPassedCommandRun(evaluation.check, "asp-check-changed", "ASP dogfood host check"); - if (!evaluation.check.command.includes("check")) throw new Error("ASP dogfood host check must run asp check"); - if (!evaluation.check.hostDecision || typeof evaluation.check.hostDecision !== "object") { - throw new Error("ASP dogfood host decision is required"); - } - if (!evaluation.check.receipt || typeof evaluation.check.receipt !== "object") { - throw new Error("ASP dogfood host receipt is required"); - } - validateAspDogfoodHostAuthorityEvidence(evaluation.check.hostDecision, "ASP dogfood host decision", { requireProviderProvenance: false }); - validateAspDogfoodHostAuthorityEvidence(evaluation.check.receipt, "ASP dogfood host receipt", { requireProviderProvenance: true }); - if (!evaluation.check.assurance || typeof evaluation.check.assurance !== "object") { - throw new Error("ASP dogfood host assurance is required"); - } - validateNonEmptyString(evaluation.check.assurance.mode, "ASP dogfood host assurance mode"); - validateNonEmptyString(evaluation.check.assurance.transactionGuarantee, "ASP dogfood host transactionGuarantee"); - if (evaluation.ciVerify !== undefined) { - validateAspDogfoodCommandRun(evaluation.ciVerify, "asp-ci-verify", "ASP dogfood CI verify"); - if (!evaluation.ciVerify.command.includes("ci") || !evaluation.ciVerify.command.includes("verify")) { - throw new Error("ASP dogfood CI verifier must run asp ci verify"); - } - } -} - -function validateAspDogfoodHostAuthorityEvidence( - value: unknown, - label: string, - options: { requireProviderProvenance: boolean } -): void { - if (!value || typeof value !== "object") throw new Error(`${label} is required`); - const record = value as Record; - const authorityEvidence = record.authorityEvidence; - if (!Array.isArray(authorityEvidence) || authorityEvidence.length === 0) { - throw new Error(`${label} must include host authorityEvidence`); - } - const providerProvenance = record.providerProvenance; - if (options.requireProviderProvenance && (!Array.isArray(providerProvenance) || providerProvenance.length === 0)) { - throw new Error(`${label} must include providerProvenance`); - } -} - -function validateAspDogfoodProviderProbe(probe: AspDogfoodProviderProbeEvidence): void { - if (!probe || typeof probe !== "object") throw new Error("ASP dogfood provider probe is required"); - validateAspDogfoodPassedCommandRun(probe, "provider-probe", "ASP dogfood provider probe"); - validateExactStringSequence(probe.command, ["opcore-asp-provider", "--stdio"], "ASP dogfood provider probe command"); - if (!probe.assessment || typeof probe.assessment !== "object") throw new Error("ASP dogfood provider probe assessment is required"); - if (!probe.validAsOf || typeof probe.validAsOf !== "object") throw new Error("ASP dogfood provider probe validAsOf is required"); - if (!probe.coverage || typeof probe.coverage !== "object") throw new Error("ASP dogfood provider probe coverage is required"); - validateNonNegativeInteger(probe.diagnosticsCount, "ASP dogfood provider probe diagnosticsCount"); - if (probe.hostOwnedFieldLeak !== false) throw new Error("ASP dogfood provider output must not contain host-owned decision fields"); - assertNoAspDogfoodHostOwnedFields(probe.assessment); -} - -function validateAspDogfoodUnsupportedSurfaces(surfaces: readonly AspDogfoodUnsupportedSurfaceEvidence[]): void { - validateNonEmptyArray(surfaces, "ASP dogfood unsupported surfaces"); - validateExactStringSet( - surfaces.map((entry) => entry.surface), - aspDogfoodUnsupportedSurfaceIds, - "ASP dogfood unsupported surfaces" - ); - for (const entry of surfaces) { - if (!entry || typeof entry !== "object") throw new Error("ASP dogfood unsupported surface entry is required"); - if (!includesString(aspDogfoodUnsupportedSurfaceIds, entry.surface)) { - throw new Error(`Unknown ASP dogfood unsupported surface: ${String(entry.surface)}`); - } - if (!includesString(["degraded", "parity-blocker"] as const, entry.status)) { - throw new Error("ASP dogfood unsupported surface status must be degraded or parity-blocker"); - } - if (entry.cleanCoverage !== false) throw new Error("ASP dogfood unsupported inspect/edit surfaces must not be represented as clean coverage"); - validateNonEmptyString(entry.blocker, "ASP dogfood unsupported surface blocker"); - } -} - -function validateAspDogfoodParityBlockers(blockers: readonly AspDogfoodParityBlocker[]): void { - if (!Array.isArray(blockers)) throw new Error("ASP dogfood parity blockers must be an array"); - for (const blocker of blockers) { - if (!blocker || typeof blocker !== "object") throw new Error("ASP dogfood parity blocker is required"); - validateNonEmptyString(blocker.source, "ASP dogfood parity blocker source"); - validateNonEmptyString(blocker.detail, "ASP dogfood parity blocker detail"); - } -} - -function validateAspDogfoodAuthority(authority: AspDogfoodAuthorityEvidence): void { - if (!authority || typeof authority !== "object") throw new Error("ASP dogfood authority evidence is required"); - if (authority.hostOwnsDecisions !== true) throw new Error("ASP dogfood host must own decisions"); - if (authority.providerOutputIsHostDecision !== false) throw new Error("ASP dogfood provider output must not be treated as host decision"); - if (!authority.localAuthorityOverride || typeof authority.localAuthorityOverride !== "object") { - throw new Error("ASP dogfood local authority override evidence is required"); - } - if (authority.localAuthorityOverride.present !== false || authority.localAuthorityOverride.sharedAuthorityWeakened !== false) { - throw new Error("ASP dogfood must not silently weaken shared authority through local override"); - } -} - -function validateAspDogfoodForbiddenMarkerScan(scan: AspDogfoodForbiddenMarkerScan): void { - if (!scan || typeof scan !== "object") throw new Error("ASP dogfood forbidden marker scan is required"); - validatePositiveInteger(scan.scannedTextCount, "ASP dogfood forbidden marker scannedTextCount"); - if (scan.findingCount !== 0) throw new Error("ASP dogfood forbidden marker findingCount must be 0"); - validateExactStringSet(scan.markersBlocked, aspDogfoodForbiddenProviderMarkers, "ASP dogfood forbidden provider markers"); -} - -function validateAspDogfoodCommandRun(receipt: AspDogfoodCommandRunReceipt, expectedId: string, label: string): void { - if (!receipt || typeof receipt !== "object") throw new Error(`${label} receipt is required`); - if (receipt.id !== expectedId) throw new Error(`${label} id must be ${expectedId}`); - validateStringArray(receipt.command, `${label} command`, { allowEmpty: false }); - if (!includesString(["passed", "failed", "retained-not-run"] as const, receipt.status)) { - throw new Error(`${label} status must be passed, failed, or retained-not-run`); - } - if (receipt.status === "passed" && receipt.exitCode !== 0) throw new Error(`${label} passed status must use exitCode 0`); - if (receipt.status === "retained-not-run" && receipt.exitCode !== null) { - throw new Error(`${label} retained-not-run status must use null exitCode`); - } - if (receipt.status === "failed") validateNonNegativeInteger(receipt.exitCode, `${label} exitCode`); - validateSha256(receipt.stdoutSha256, `${label} stdoutSha256`); - validateSha256(receipt.stderrSha256, `${label} stderrSha256`); - validateNonEmptyString(receipt.assertion, `${label} assertion`); -} - -function validateAspDogfoodPassedCommandRun(receipt: AspDogfoodCommandRunReceipt, expectedId: string, label: string): void { - validateAspDogfoodCommandRun(receipt, expectedId, label); - if (receipt.status !== "passed") throw new Error(`${label} status must be passed`); - if (receipt.exitCode !== 0) throw new Error(`${label} passed status must use exitCode 0`); -} - -function validateAspDogfoodForbiddenProviderEntrypoint(receipt: AspDogfoodReceipt): void { - const providerTexts = collectStrings(receipt.provider); - const findings: string[] = []; - for (const text of providerTexts) { - const normalized = text.replaceAll("\\", "/").toLowerCase(); - for (const marker of aspDogfoodForbiddenProviderMarkers) { - if (normalized.includes(marker.toLowerCase())) findings.push(marker); - } - } - if (findings.length > 0) { - throw new Error(`ASP dogfood provider entrypoint contains forbidden marker: ${[...new Set(findings)].join(", ")}`); - } -} - -function assertNoAspDogfoodHostOwnedFields(value: unknown, path = "$"): void { - if (!value || typeof value !== "object") return; - if (Array.isArray(value)) { - value.forEach((entry, index) => assertNoAspDogfoodHostOwnedFields(entry, `${path}[${index}]`)); - return; - } - const forbidden = new Set(["decision", "verdict", "pass", "authority", "authorityEvidence", "assurance", "transactionGuarantee", "applyReceipt"]); - for (const [key, child] of Object.entries(value)) { - if (forbidden.has(key)) throw new Error(`ASP dogfood provider output contains host-owned field ${path}.${key}`); - assertNoAspDogfoodHostOwnedFields(child, `${path}.${key}`); - } -} - -function validateManagedToolDescriptorPackageReference( - reference: ManagedToolDescriptorArtifactReference, - packageName: ReleaseReceiptPackageName -): void { - if (!reference || typeof reference !== "object") throw new Error("Release receipt descriptor reference is required"); - validateNonEmptyString(reference.id, "Release receipt descriptor reference id"); - if (reference.packageName !== packageName) { - throw new Error(`Release receipt descriptor reference packageName must be ${packageName}`); - } - validateRepoRelativePath(reference.path); - if (!includesString(managedToolDescriptorArtifactTypes, reference.type)) { - throw new Error(`Unknown release receipt descriptor reference type: ${String(reference.type)}`); - } - if (typeof reference.required !== "boolean") throw new Error("Release receipt descriptor reference required must be boolean"); - if (reference.checksumRef !== undefined) validateNonEmptyString(reference.checksumRef, "Release receipt descriptor reference checksumRef"); -} - -function validateReleaseReceiptPackageName(value: unknown, label: string): ReleaseReceiptPackageName { - if (!includesString(releaseReceiptPackageNames, value)) { - throw new Error(`${label} must be one of ${releaseReceiptPackageNames.join(", ")}`); - } - return value; -} - -function isGraphCoreNativePackageName(value: unknown): value is GraphCoreNativePackageName { - return includesString(graphCoreNativePackageNames, value); -} - -function bundledGraphCoreNativePath(packageName: GraphCoreNativePackageName, file: "metadata.json" | "opcore-graph-core" | "opcore-graph-core.sha256"): string { - return `node_modules/${packageName}/${file}`; -} - -function graphCoreNativeTargetForPackageName(packageName: GraphCoreNativePackageName): GraphCoreNativeSupportedTarget { - const target = graphCoreNativeSupportedTargets.find((entry) => graphCoreNativePackageNamesByTarget[entry] === packageName); - if (!target) throw new Error(`Unknown Opcore graph-core native package: ${packageName}`); - return target; -} - -function validateReleaseReceiptCommandGroupName(value: unknown, label: string): ReleaseReceiptCommandGroupName { - if (!includesString(releaseReceiptCommandGroups, value)) { - throw new Error(`${label} must be one of ${releaseReceiptCommandGroups.join(", ")}`); - } - return value; -} - -function validateReleaseReceiptReportId(value: unknown, label: string): ReleaseReceiptReportId { - if (!includesString(releaseReceiptReportIds, value)) { - throw new Error(`${label} must be one of ${releaseReceiptReportIds.join(", ")}`); - } - return value; -} - -function validateStringRecord(value: Readonly>, label: string): void { - if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${label} must be an object`); - for (const [key, recordValue] of Object.entries(value)) { - validateNonEmptyString(key, `${label} key`); - validateNonEmptyString(recordValue, `${label}.${key}`); - } -} - -function packageEvidenceIncludesFile( - packages: readonly ReleaseReceiptPackageEvidence[], - packageName: ReleaseReceiptPackageName, - path: string -): boolean { - return packages.find((entry) => entry.packageName === packageName)?.files.includes(path) ?? false; -} - -function validateSha256(value: unknown, label: string): string { - const text = validateNonEmptyString(value, label); - if (!/^[a-f0-9]{64}$/i.test(text)) throw new Error(`${label} must be a sha256 hex digest`); - return text; -} - -function validateExactStringSet(actual: readonly string[], expected: readonly string[], label: string): void { - validateStringArray(actual, label, { allowEmpty: false }); - const actualSorted = [...actual].sort(); - const expectedSorted = [...expected].sort(); - if ( - actualSorted.length !== expectedSorted.length || - actualSorted.some((value, index) => value !== expectedSorted[index]) - ) { - throw new Error(`${label} must exactly match ${expected.join(", ")}`); - } -} - -function validateExactStringSequence(actual: readonly string[], expected: readonly string[], label: string): void { - validateStringArray(actual, label, { allowEmpty: false }); - if (!sameStringArray(actual, expected)) { - throw new Error(`${label} must exactly match ${expected.join(" ")}`); - } -} - -function validateGraphReleaseSourceFreeStrings(value: unknown): void { - const forbidden = [/tirth8205/i, /pyproject\.toml/i, /setup\.py/i, /setup\.cfg/i, /Pipfile/i, /git clone/i]; - for (const text of collectStrings(value)) { - const pattern = forbidden.find((entry) => entry.test(text)); - if (pattern) throw new Error(`Graph release receipt contains forbidden source provenance: ${text}`); - } -} - -function validateCommandExitSemantics(exitSemantics: CommandExitSemantics): CommandExitSemantics { - if (!exitSemantics || typeof exitSemantics !== "object") { - throw new Error("Command router manifest must include exitSemantics"); - } - if (exitSemantics.ok !== 0) throw new Error("Command exit semantics ok must be 0"); - if (exitSemantics.error !== 1) throw new Error("Command exit semantics error must be 1"); - if (exitSemantics.notImplemented !== 2) throw new Error("Command exit semantics notImplemented must be 2"); - if (exitSemantics.unsupported !== 64) throw new Error("Command exit semantics unsupported must be 64"); - if (typeof exitSemantics.jsonStable !== "boolean") { - throw new Error("Command exit semantics jsonStable must be boolean"); - } - return exitSemantics; -} - -function validateExitCodeForStatus(exitCode: unknown, status: CommandRouteStatus): number { - if (typeof exitCode !== "number" || !Number.isInteger(exitCode) || exitCode < 0) { - throw new Error("Command route exitCode must be a non-negative integer"); - } - if (status === "ok" && exitCode !== 0) throw new Error("Command route ok status must use exitCode 0"); - if (status === "error" && exitCode !== 1) throw new Error("Command route error status must use exitCode 1"); - if (status === "not_implemented" && exitCode !== 2) { - throw new Error("Command route not_implemented status must use exitCode 2"); - } - if (status === "unsupported" && exitCode !== 64) { - throw new Error("Command route unsupported status must use exitCode 64"); - } - return exitCode; -} - -function validateStringArray( - values: readonly string[] | undefined, - label: string, - options: { allowEmpty: boolean; allowEmptyValues?: boolean } -): readonly string[] { - if (!Array.isArray(values)) { - throw new Error(`${label} must be an array`); - } - if (!options.allowEmpty && values.length === 0) { - throw new Error(`${label} must not be empty`); - } - for (const value of values) { - if (options.allowEmptyValues === true) { - if (typeof value !== "string") throw new Error(`${label} must contain only strings`); - } else { - validateNonEmptyString(value, label); - } - } - return values; -} - -function validateValidationChecks(checks: readonly string[], label: string): readonly string[] { - validateStringArray(checks, label, { allowEmpty: true }); - for (const check of checks) { - if (check.trim().length === 0) { - throw new Error(`${label} entries must include non-whitespace content`); - } - validateValidationCheckId(check, `${label} entry`); - } - return checks; -} - -function validateValidationCheckId(checkId: unknown, label: string): string { - const value = validateNonEmptyString(checkId, label); - if (!validationCheckIdRegex.test(value)) { - throw new Error(`${label} must be a stable validation check id`); - } - return value; -} - -function validateNonNegativeNumber(value: unknown, label: string): number { - if (typeof value !== "number" || !Number.isFinite(value) || value < 0) { - throw new Error(`${label} must be a non-negative number`); - } - return value; -} - -function validateNonNegativeInteger(value: unknown, label: string): number { - if (!Number.isInteger(value) || (value as number) < 0) { - throw new Error(`${label} must be a non-negative integer`); - } - return value as number; -} - -function validatePositiveInteger(value: unknown, label: string): number { - if (!Number.isInteger(value) || (value as number) < 1) { - throw new Error(`${label} must be a positive integer`); - } - return value as number; -} - -function validateNonEmptyArray(values: readonly unknown[] | undefined, label: string): readonly unknown[] { - if (!Array.isArray(values) || values.length === 0) { - throw new Error(`${label} must be a non-empty array`); - } - return values; -} - -function validateNonEmptyString(value: unknown, label: string): string { - if (typeof value !== "string" || value.length === 0) { - throw new Error(`${label} must be a non-empty string`); - } - return value; -} - -function validateGraphFreshness(freshness: GraphFreshness | undefined, label: string): GraphFreshness { - if (!freshness || typeof freshness !== "object") { - throw new Error(`${label} graph provider status must include freshness`); - } - if (typeof freshness.generatedAt !== "string" || freshness.generatedAt.length === 0) { - throw new Error(`${label} graph provider freshness must include generatedAt`); - } - if (typeof freshness.ageMs !== "number") { - throw new Error(`${label} graph provider freshness must include numeric ageMs`); - } - if (typeof freshness.stale !== "boolean") { - throw new Error(`${label} graph provider freshness must include stale`); - } - return freshness; -} - -function validateGraphSnapshotMetadata(metadata: GraphSnapshotMetadata): GraphSnapshotMetadata { - if (!metadata || typeof metadata !== "object") { - throw new Error("Graph snapshot metadata is required"); - } - if (typeof metadata.schemaVersion !== "number") { - throw new Error("Graph snapshot metadata must include numeric schemaVersion"); - } - if (typeof metadata.provider !== "string" || metadata.provider.length === 0) { - throw new Error("Graph snapshot metadata must include provider"); - } - validateRepoIdentity(metadata.repo); - validateGraphFreshness(metadata.freshness, "Graph snapshot"); - if (!Array.isArray(metadata.nodeKinds) || !Array.isArray(metadata.edgeKinds)) { - throw new Error("Graph snapshot metadata must include nodeKinds and edgeKinds"); - } - return metadata; -} - -function collectStrings(value: unknown): string[] { - if (typeof value === "string") return [value]; - if (Array.isArray(value)) return value.flatMap((entry) => collectStrings(entry)); - if (!value || typeof value !== "object") return []; - return Object.values(value).flatMap((entry) => collectStrings(entry)); -} +export { + GRAPH_SCHEMA_VERSION, CLONE_PROTOCOL, graphProviderModes, graphProviderStatusStates, requiredGraphNodeKinds, + requiredGraphEdgeKinds, graphSnapshotMetadataKeys, providerFailureCategories, + graphProviderFailureCategoriesByState, } from "./graph/vocabulary-01.js"; +export type { + GraphProviderMode, GraphProviderStatusState, GraphNodeKind, GraphEdgeKind, GraphSnapshotMetadataKey, + ProviderFailureCategory, GraphProviderErrorFailureCategory, } from "./graph/vocabulary-01.js"; +export { graphExtractionDiagnosticCategories } from "./graph/vocabulary-02.js"; +export type { GraphExtractionDiagnosticCategory } from "./graph/vocabulary-02.js"; +export { editRefusalCategories } from "./edit/vocabulary.js"; +export type { EditRefusalCategory } from "./edit/vocabulary.js"; +export { + validationDiagnosticCategories, validationResultStatuses, validationFailureCategories, validationReportModes, + validationCheckRunStatuses, validationCheckOutcomes, pythonValidationCapabilityRunStatuses, + pythonValidationAuthorities, } from "./validation/vocabulary-01.js"; +export type { + ValidationDiagnosticCategory, ValidationResultStatus, ValidationFailureCategory, ValidationReportMode, + ValidationCheckRunStatus, ValidationCheckOutcome, PythonValidationCapabilityRunStatus, + PythonValidationAuthority, } from "./validation/vocabulary-01.js"; +export { + pythonValidationAuthoritySources, pythonValidationCapabilityTerminationKinds, pythonValidationCapabilities, + pythonValidationCapabilityStates, pythonValidationCapabilityTerminations, validationSkippedCheckReasons, + validationCheckIdPattern, } from "./validation/vocabulary-02.js"; +export type { + PythonValidationAuthoritySource, PythonValidationCapabilityTerminationKind, PythonValidationCapability, + PythonValidationCapabilityState, PythonValidationCapabilityTermination, ValidationSkippedCheckReason, +} from "./validation/vocabulary-02.js"; +export type { JsonPrimitive, JsonValue } from "./shared/json.js"; +export type { + RepoIdentity, GraphFreshness, GraphProviderArtifactMetadata, GraphProviderCapabilityHandshake, + ProviderFailure, ProviderFailureWithCategory, GraphExtractionDiagnostic, GraphProviderStatusBase, + GraphProviderAvailableStatus, GraphProviderWarmingStatus, GraphProviderSkippedStatus, + GraphProviderRequiredMissingStatus, GraphProviderStaleStatus, GraphProviderSchemaMismatchStatus, + GraphProviderDaemonUnavailableStatus, GraphProviderErrorStatus, } from "./graph/provider-contracts-01.js"; +export type { + GraphProviderStatus, GraphProviderFailureStatus, GraphProviderNonAvailableStatus, GraphFactNode, + GraphFactEdge, GraphSnapshotMetadata, } from "./graph/provider-contracts-02.js"; +export { graphFactQueryKinds, graphNamedQueryKinds } from "./graph/query-contracts-01.js"; +export type { + GraphFactQuerySelector, GraphNamedQueryKind, GraphProviderQueryKind, GraphFactQueryRequest, + GraphFactQueryAvailableResult, GraphFactQueryFailureResult, GraphFactQueryResult, GraphTraversalMetadata, + GraphNamedQueryRequest, GraphNamedQueryAvailableResult, GraphNamedQueryFailureResult, GraphNamedQueryResult, + GraphImpactRequest, GraphImpactAvailableResult, } from "./graph/query-contracts-01.js"; +export type { + GraphImpactFailureResult, GraphImpactResult, GraphRenamedFile, GraphDetectChangesRequest, + GraphDetectChangesAvailableResult, GraphDetectChangesFailureResult, GraphDetectChangesResult, + GraphReviewContextRequest, GraphReviewContextAvailableResult, GraphReviewContextFailureResult, + GraphReviewContextResult, } from "./graph/query-contracts-02.js"; +export { + inspectSignatureKinds, inspectImplementationKinds, inspectFailureCategories, +} from "./inspect/contracts-01.js"; +export type { + InspectSymbolTarget, InspectReferenceTarget, InspectTextSpan, InspectReferenceSpan, InspectSymbolSummary, + InspectSymbolEvidence, InspectReferenceEntry, InspectSignatureKind, InspectSignatureParameter, + InspectSignatureTypeParameter, InspectSignatureEntry, InspectImplementationKind, InspectImplementationEntry, +} from "./inspect/contracts-01.js"; +export type { + InspectFailureCategory, InspectRouteFailure, InspectReferenceResult, InspectSignatureResult, + InspectImplementationResult, InspectRouteErrorResult, InspectRouteResult, } from "./inspect/contracts-02.js"; +export { aspWarmMethodNames } from "./inspect/warm-contracts.js"; +export type { + AspWarmMethodName, AspWarmProviderSummary, AspWarmInspectReferencesParams, AspWarmInspectReferencesOkResult, + AspWarmInspectReferencesErrorResult, AspWarmInspectReferencesResponse, SymbolEditTarget, + AspWarmEditRenameParams, AspWarmAffectedChecksum, AspWarmEditRenamePreviewResult, + AspWarmEditRenameRefusedResult, AspWarmEditRenameResponse, AspWarmSessionShutdownResponse, +} from "./inspect/warm-contracts.js"; +export type { + GraphSearchRequest, GraphSearchMode, GraphSearchResultEntry, GraphSearchSummary, GraphSearchAvailableResult, + GraphSearchFailureResult, GraphSearchResult, } from "./graph/search-contracts.js"; +export { graphDaemonOperations } from "./graph/pipeline-contracts.js"; +export type { + GraphPipelineOperation, GraphPipelinePhaseTiming, GraphWalCheckpointSummary, GraphPipelineSummary, + GraphWatchLifecycle, GraphServeTransportStatus, GraphPipelineResult, GraphDaemonOperation, + GraphDaemonRequest, GraphDaemonResponse, } from "./graph/pipeline-contracts.js"; +export type { + RepoRelativeChangeBase, RepoRelativeChange, AtomicApplyMetadata, EditPlanValidationRequirement, EditPlan, + EditRefusal, EditPlanResult, EditPlanRollbackState, EditCommandResult, } from "./edit/contracts.js"; +export { + validationScopeKinds, cloneReportModes, cloneSourceReadModes, } from "./validation/request-contracts.js"; +export type { + ValidationScopeKind, ValidationScope, HypotheticalOverlay, CloneReportMode, CloneSourceReadMode, + CloneAnalysisRequest, CloneFinding, CloneAnalysisSummary, CloneAnalysisResult, ValidationFailure, + ValidationGraphConfig, ValidationRequest, } from "./validation/request-contracts.js"; +export { + PYTHON_PROJECT_CONTEXT_SCHEMA_ID, PYTHON_VALIDATION_CAPABILITY_RUN_SCHEMA_ID, pythonProjectContextOutcomes, + pythonProjectContextReasonCodes, pythonProjectManagerKinds, pythonProjectLayoutKinds, + pythonProjectExecutableSources, pythonProjectToolKinds, } from "./validation/python-project-contracts-01.js"; +export type { + PythonProjectContextOutcome, PythonProjectContextReasonCode, PythonProjectManagerKind, + PythonProjectLayoutKind, PythonProjectExecutableSource, PythonProjectToolKind, PythonProjectContextReason, + PythonProjectFileEvidence, } from "./validation/python-project-contracts-01.js"; +export type { + PythonProjectManagerEvidence, PythonProjectExecutableProvenance, PythonInterpreterProvenance, + PythonProjectToolProvenance, PythonProjectTarget, PythonProjectLayoutEvidence, PythonProjectBuildSystem, + PythonProjectContext, PythonValidationCapabilityToolProvenance, PythonValidationCapabilityExecution, + PythonTypesValidationCapabilityRun, } from "./validation/python-project-contracts-02.js"; +export type { + ValidationDiagnostic, ValidationDiagnosticToolProvenance, ValidationCheckManifestEntry, + PythonRuffValidationCapabilityRun, PythonValidationCapabilityRun, PythonValidationCapabilityInvocation, + ValidationCheckRunSummary, ValidationSkippedCheck, ValidationResultManifest, +} from "./validation/diagnostic-contracts.js"; +export { + pythonCapabilityActivations, pythonPytestSelectionModes, pythonCapabilityProcessTerminations, +} from "./validation/capability-contracts.js"; +export type { + PythonCapabilityActivation, PythonPytestSelectionMode, PythonCapabilityProcessTermination, + PythonCapabilityCounts, PythonCapabilityCleanupEvidence, PythonCapabilityInvocation, + PythonPytestValidationCapabilityRun, ValidationResult, } from "./validation/capability-contracts.js"; +export { + requiredContextDocPolicy, validationDaemonReadinessStates, validationAdapterRuntimeStates, +} from "./validation/status-contracts.js"; +export type { + RequiredContextDocPolicy, PreWriteValidationOverlaySummary, PreWriteValidationFailureSummary, + PreWriteValidationReceipt, ValidationDaemonReadinessState, ValidationAdapterRuntimeState, + ValidationAdapterToolchainStatus, ValidationAdapterDegradedCheckStatus, ValidationAdapterRuntimeStatus, + ValidationStatusPayload, } from "./validation/status-contracts.js"; +export { managedToolDescriptorCommandGroups, managedToolDescriptorArtifactTypes } from "./managed/contracts.js"; +export type { + ManagedToolDescriptorCommandGroupName, ManagedToolDescriptorArtifactType, ManagedToolDescriptor, + ManagedToolDescriptorEntrypoint, ManagedToolDescriptorCommandGroup, ManagedToolDescriptorHealthProbe, + ManagedToolDescriptorCapabilities, ManagedToolDescriptorNativeArtifact, + ManagedToolDescriptorArtifactReference, ManagedToolDescriptorChecksumReference, + ManagedToolDescriptorProvenanceHook, } from "./managed/contracts.js"; +export { + commandOwners, commandRouteStatuses, commandTimingProcessStates, commandTimingDegradationReasons, + latencyBudgetResultStatuses, commandLatencyTelemetryBins, commandLatencyTelemetryArtifactPolicy, +} from "./command/vocabulary.js"; +export type { + CommandOwner, CommandRouteStatus, CommandTimingProcessState, CommandTimingDegradationReason, + LatencyBudgetResultStatus, CommandLatencyTelemetryBin, } from "./command/vocabulary.js"; +export { + graphReleaseSurfaceClassifications, graphReleaseCoreCommandIds, graphReleaseRustCommandIds, + graphReleaseBenchmarkMetrics, graphReleaseRequiredChildren, graphReleaseDeferredChildren, + graphReleaseOptionalAnalysisSurfaces, graphReleaseHandoffIssues, } from "./release/graph-vocabulary-01.js"; +export type { + GraphReleaseSurfaceClassification, GraphReleaseCoreCommandId, GraphReleaseRustCommandId, + GraphReleaseBenchmarkMetric, GraphReleaseRequiredChild, GraphReleaseDeferredChild, + GraphReleaseOptionalAnalysisSurface, GraphReleaseHandoffIssue, } from "./release/graph-vocabulary-01.js"; +export { + graphReleaseDirectSqliteQueryIds, graphReleaseServeTransportIds, graphReleaseReportReceiptIds, + graphCoreNativeSupportedTargets, graphCoreNativePackageNames, graphCoreNativePackageNamesByTarget, + graphCoreNativePackageNameForTarget, } from "./release/graph-vocabulary-02.js"; +export type { + GraphReleaseDirectSqliteQueryId, GraphReleaseServeTransportId, GraphReleaseReportReceiptId, + GraphCoreNativeSupportedTarget, GraphCoreNativePackageName, } from "./release/graph-vocabulary-02.js"; +export { + releaseReceiptPackageNames, releaseReceiptBundledPackageNames, releaseReceiptCommandGroups, + releaseReceiptReportIds, releaseReceiptSecretFindingScopes, releaseCutoverRequiredCommandIds, + releaseCutoverRustCommandIds, releaseCutoverPythonCommandIds, releaseCutoverNegativeCheckIds, +} from "./release/vocabulary-01.js"; +export type { + ReleaseReceiptPackageName, ReleaseReceiptCommandGroupName, ReleaseReceiptReportId, + ReleaseReceiptSecretFindingScope, ReleaseCutoverCommandId, ReleaseCutoverRustCommandId, + ReleaseCutoverPythonCommandId, } from "./release/vocabulary-01.js"; +export { + releaseCutoverInputIssues, aspDogfoodUnsupportedSurfaceIds, aspDogfoodForbiddenProviderMarkers, +} from "./release/vocabulary-02.js"; +export type { + ReleaseCutoverNegativeCheckId, ReleaseCutoverInputIssue, AspDogfoodUnsupportedSurfaceId, + AspDogfoodForbiddenProviderMarker, } from "./release/vocabulary-02.js"; +export type { CommandExitSemantics, CommandGroupContract, CommandRouterManifest } from "./command/contracts.js"; +export { opcoreRuntimeArtifactSources } from "./product/status-contracts.js"; +export type { + OpcoreRepoStatePayload, OpcoreValidationPolicySummary, OpcoreRuntimeArtifactSource, OpcoreRuntimeInfoPayload, + OpcoreDoctorPayload, } from "./product/status-contracts.js"; +export { opcoreInitScopes } from "./product/init-contracts.js"; +export type { + OpcoreInitScope, OpcoreInitAction, OpcoreInitScanSummary, OpcoreInitLanguageSetting, + OpcoreInitPythonEnvironment, OpcoreInitSettings, OpcoreInitInteraction, OpcoreInitTiming, + OpcoreInitPlanPayload, } from "./product/init-contracts.js"; +export { + opcoreMeasureLatencyStatuses, opcoreMeasureLatencyFindingStatuses, } from "./product/metrics-contracts-01.js"; +export type { + OpcoreMetricEvidence, OpcoreMetricSignal, OpcoreMetricDegradation, OpcoreMetricReport, + OpcoreMetricHistoryEntry, OpcoreMeasureSignalCount, OpcoreMeasureSignalDelta, OpcoreMeasureLatencyStatus, + OpcoreMeasureLatencyFindingStatus, OpcoreMeasureLatencyPhase, OpcoreMeasureLatencyFinding, + OpcoreMeasureLatencyReport, OpcoreMeasureComparison, OpcoreMeasureDelta, +} from "./product/metrics-contracts-01.js"; +export type { + OpcoreTrySignalSummary, OpcoreTryScenario, OpcoreTryCommandSummary, OpcoreTryPayload, +} from "./product/metrics-contracts-02.js"; +export type { + CommandTimingPhase, CommandTiming, RepoShapeFingerprint, CommandLatencyRecord, LatencyPhaseBudget, + LatencyBudget, LatencyBudgetResult, } from "./product/latency-contracts.js"; +export type { + CommandRouterResult, ParsedCommandArgv, CommandRouterResultInput, CommandAdapterRequest, CommandAdapter, + CommandRouterWriter, RouteCommandAdapterOptions, RunCommandAdapterCliOptions, +} from "./command/router-contracts.js"; +export { commandExitSemantics, commandRouterManifest } from "./command/manifest.js"; +export type { + GraphReleaseCommandCoverage, GraphReleaseRustCommandCoverage, GraphReleaseDirectSqliteQueryReceipt, + GraphReleaseServeTransportReceipt, GraphReleaseBenchmarkReceipt, GraphReleasePackageInspection, + GraphReleaseNativeArtifactEvidence, GraphReleaseReportReceipt, GraphReleaseOptionalSurfaceReceipt, + GraphReleaseHandoffReceipt, GraphReleasePackageVersion, GraphReleaseReceipt, +} from "./release/graph-contracts.js"; +export type { + ReleaseReceiptTarballEvidence, ReleaseReceiptPackageManifestMetadata, ReleaseReceiptNativeArtifactEvidence, + ReleaseReceiptPackageEvidence, ReleaseReceiptDescriptorCommandGroupEvidence, + ReleaseReceiptResolvedArtifactEvidence, ReleaseReceiptResolvedChecksumEvidence, + ReleaseReceiptDescriptorEvidence, ReleaseReceiptLicensePackageEvidence, ReleaseReceiptLicenseEvidence, + ReleaseReceiptProvenanceFinding, ReleaseReceiptProvenanceEvidence, ReleaseReceiptSecretFinding, + ReleaseReceiptSecretHistoryEvidence, ReleaseReceiptReport, ReleaseReceiptGraphReleaseEvidence, +} from "./release/receipt-contracts-01.js"; +export type { ReleaseReceipt } from "./release/receipt-contracts-02.js"; +export type { + ReleaseCutoverTarballEvidence, ReleaseCutoverInstalledManifestEvidence, ReleaseCutoverInstalledFileEvidence, + ReleaseCutoverInstalledPackageEvidence, ReleaseCutoverDescriptorEvidence, + ReleaseCutoverEnvironmentIsolationEvidence, ReleaseCutoverCommandReceipt, ReleaseCutoverRustCommandReceipt, + ReleaseCutoverPythonCommandReceipt, ReleaseCutoverNegativeCheck, OpcoreSelfValidationReceipt, + ReleaseCutoverForbiddenMarkerScan, ReleaseCutoverInputEvidence, ReleaseCutoverReceipt, +} from "./release/cutover-contracts.js"; +export type { + AspDogfoodManagerEvidence, AspDogfoodAspHomeEvidence, AspDogfoodHostFixtureEvidence, + AspDogfoodCommandRunReceipt, AspDogfoodProviderManifestEvidence, AspDogfoodProviderEvidence, + AspDogfoodRepoEnrollmentEvidence, AspDogfoodManagerStateEvidence, AspDogfoodHostCheckEvidence, + AspDogfoodHostEvaluationEvidence, AspDogfoodProviderProbeEvidence, AspDogfoodUnsupportedSurfaceEvidence, + AspDogfoodParityBlocker, AspDogfoodAuthorityEvidence, AspDogfoodForbiddenMarkerScan, AspDogfoodReceipt, +} from "./release/asp-contracts-01.js"; +export { + parseCommandArgv, normalizeCommandBin, commandExitCodeForStatus, createCommandRouterResult, + routeCommandAdapter, runCommandAdapterCli, } from "./command/router-01.js"; +export { commandGroupByName } from "./command/router-02.js"; +export { validateCommandRouterManifest, validateManagedToolDescriptor } from "./managed/validators-01.js"; +export { validateCommandRouterResult } from "./command/validators.js"; +export { validateOpcoreRepoStatePayload } from "./product/status-validators.js"; +export { + validateOpcoreRuntimeInfoPayload, validateOpcoreDoctorPayload, validateOpcoreInitPlanPayload, +} from "./product/init-validators-01.js"; +export { + validateCommandTiming, validateRepoShapeFingerprint, validateCommandLatencyRecord, validateLatencyBudget, +} from "./product/metrics-validators-01.js"; +export { validateLatencyBudgetResult, validateOpcoreMetricReport } from "./product/metrics-validators-02.js"; +export { + validateOpcoreMetricHistoryEntry, validateOpcoreMeasureDelta, validateOpcoreTryPayload, +} from "./product/metrics-validators-03.js"; +export { validateCommandAdapterRequest } from "./command/adapter-validator.js"; +export { + validateGraphReleaseReceipt, validateReleaseReceipt, validateReleaseCutoverReceipt, + validateAspDogfoodReceipt, } from "./release/public-validators.js"; +export { + validateRepoRelativePath, validateHomeRelativePath, validateRepoIdentity, +} from "./shared/path-validators.js"; +export { + validateProviderStatus, validateGraphProviderCapabilityHandshake, +} from "./graph/provider-validators.js"; +export { validateGraphProviderArtifactMetadata } from "./graph/protocol-validators.js"; +export { + validateGraphFactQueryRequest, validateGraphFactQueryResult, validateGraphNamedQueryRequest, + validateGraphNamedQueryResult, validateGraphImpactRequest, validateGraphImpactResult, + validateGraphDetectChangesRequest, validateGraphDetectChangesResult, validateGraphReviewContextRequest, + validateGraphReviewContextResult, } from "./graph/query-validators.js"; +export { validateGraphSearchRequest, validateGraphSearchResult } from "./graph/search-validators.js"; +export { validateInspectRouteResult } from "./inspect/validators.js"; +export { + validateGraphDaemonRequest, validateGraphDaemonResponse, validateGraphPipelineResult, + validateGraphPipelineSummary, } from "./graph/daemon-validators-01.js"; +export { validateGraphServeTransportStatus } from "./graph/daemon-validators-02.js"; +export { validateGraphWatchLifecycle } from "./graph/protocol-validators.js"; +export { validateCloneAnalysisRequest, validateCloneAnalysisResult } from "./clone/validators.js"; +export { + validateValidationRequestPayload, validateValidationResultPayload, } from "./validation/result-validator.js"; +export { + validatePythonValidationCapabilityRun, validatePythonValidationCapabilityRuns, +} from "./validation/python-types-validators.js"; +export { + validatePythonProjectContext, validatePythonProjectContexts, +} from "./validation/python-project-validators-01.js"; +export { validateRequiredContextDocPolicy } from "./validation/python-project-validators-02.js"; +export { + validatePreWriteValidationReceipt, validateValidationStatusPayload, +} from "./validation/prewrite-status-validators-01.js"; +export { validateEditPlanPayload, validateEditCommandResult } from "./edit/validators.js"; diff --git a/packages/contracts/src/inspect/contracts-01.ts b/packages/contracts/src/inspect/contracts-01.ts new file mode 100644 index 0000000..602979c --- /dev/null +++ b/packages/contracts/src/inspect/contracts-01.ts @@ -0,0 +1,149 @@ +import type { GraphNodeKind } from "../graph/vocabulary-01.js"; + +interface InspectSymbolTarget { + kind: "node" | "file_symbol"; + nodeId?: string; + path?: string; + symbolName?: string; + line?: number; + column?: number; +} + +export type { InspectSymbolTarget }; + +type InspectReferenceTarget = InspectSymbolTarget; + +export type { InspectReferenceTarget }; + +interface InspectTextSpan { + startLine: number; + startColumn: number; + endLine: number; + endColumn: number; + startOffset?: number; + endOffset?: number; +} + +export type { InspectTextSpan }; + +type InspectReferenceSpan = InspectTextSpan; + +export type { InspectReferenceSpan }; + +interface InspectSymbolSummary { + id: string; + name: string; + kind?: GraphNodeKind; +} + +export type { InspectSymbolSummary }; + +interface InspectSymbolEvidence { + graphNodeIds: readonly string[]; + resolver: "graph" | "language_service"; +} + +export type { InspectSymbolEvidence }; + +interface InspectReferenceEntry { + file: string; + line: number; + column: number; + text: string; + span: InspectTextSpan; + symbol: InspectSymbolSummary; + isDefinition: boolean; + isDeclaration?: boolean; + evidence: InspectSymbolEvidence; +} + +export type { InspectReferenceEntry }; + +const inspectSignatureKinds = [ + "function", + "method", + "constructor", + "interface", + "type_alias", + "class", + "variable_function", +] as const; + +export { inspectSignatureKinds }; + +type InspectSignatureKind = (typeof inspectSignatureKinds)[number]; + +export type { InspectSignatureKind }; + +interface InspectSignatureParameter { + name: string; + type: string; + optional: boolean; + rest?: boolean; + defaultValue?: string; +} + +export type { InspectSignatureParameter }; + +interface InspectSignatureTypeParameter { + name: string; + constraint?: string; + default?: string; +} + +export type { InspectSignatureTypeParameter }; + +interface InspectSignatureEntry { + file: string; + line: number; + column: number; + text: string; + signature: string; + kind: InspectSignatureKind; + parameters: readonly InspectSignatureParameter[]; + typeParameters: readonly InspectSignatureTypeParameter[]; + exported: boolean; + async: boolean; + returnType?: string; + span: InspectTextSpan; + symbol: InspectSymbolSummary; + overloadIndex?: number; + evidence: InspectSymbolEvidence; +} + +export type { InspectSignatureEntry }; + +const inspectImplementationKinds = ["implements", "inherited_implements", "extends", "interface_extends"] as const; + +export { inspectImplementationKinds }; + +type InspectImplementationKind = (typeof inspectImplementationKinds)[number]; + +export type { InspectImplementationKind }; + +interface InspectImplementationEntry { + file: string; + line: number; + column: number; + text: string; + span: InspectTextSpan; + kind: InspectImplementationKind; + symbol: InspectSymbolSummary; + target: InspectSymbolSummary; + isDeclaration?: boolean; + evidence: InspectSymbolEvidence; +} + +export type { InspectImplementationEntry }; + +const inspectFailureCategories = [ + "graph_unavailable", + "target_ambiguous", + "target_not_found", + "unsupported_language", + "malformed_target", + "language_service_error", + "unsupported_route", +] as const; + +export { inspectFailureCategories }; diff --git a/packages/contracts/src/inspect/contracts-02.ts b/packages/contracts/src/inspect/contracts-02.ts new file mode 100644 index 0000000..af3847a --- /dev/null +++ b/packages/contracts/src/inspect/contracts-02.ts @@ -0,0 +1,71 @@ +import type { GraphProviderStatus } from "../graph/provider-contracts-02.js"; +import type { + InspectImplementationEntry, + InspectReferenceEntry, + InspectSignatureEntry, + InspectSymbolTarget, + inspectFailureCategories, +} from "./contracts-01.js"; + +type InspectFailureCategory = (typeof inspectFailureCategories)[number]; + +export type { InspectFailureCategory }; + +interface InspectRouteFailure { + category: InspectFailureCategory; + message: string; + candidates?: readonly InspectSymbolTarget[]; +} + +export type { InspectRouteFailure }; + +interface InspectReferenceResult { + route: "references"; + status: "ok" | "degraded"; + target: InspectSymbolTarget; + providerStatus: GraphProviderStatus; + failure?: InspectRouteFailure; + references: readonly InspectReferenceEntry[]; +} + +export type { InspectReferenceResult }; + +interface InspectSignatureResult { + route: "signature"; + status: "ok" | "degraded"; + target: InspectSymbolTarget; + providerStatus: GraphProviderStatus; + failure?: InspectRouteFailure; + signatures: readonly InspectSignatureEntry[]; +} + +export type { InspectSignatureResult }; + +interface InspectImplementationResult { + route: "implementations"; + status: "ok" | "degraded"; + target: InspectSymbolTarget; + providerStatus: GraphProviderStatus; + failure?: InspectRouteFailure; + implementations: readonly InspectImplementationEntry[]; +} + +export type { InspectImplementationResult }; + +interface InspectRouteErrorResult { + route: "references" | "signature" | "implementations"; + status: "error" | "degraded"; + target?: InspectSymbolTarget; + providerStatus?: GraphProviderStatus; + failure: InspectRouteFailure; +} + +export type { InspectRouteErrorResult }; + +type InspectRouteResult = + | InspectReferenceResult + | InspectSignatureResult + | InspectImplementationResult + | InspectRouteErrorResult; + +export type { InspectRouteResult }; diff --git a/packages/contracts/src/inspect/helper-validators-01.ts b/packages/contracts/src/inspect/helper-validators-01.ts new file mode 100644 index 0000000..3e1c5c2 --- /dev/null +++ b/packages/contracts/src/inspect/helper-validators-01.ts @@ -0,0 +1,263 @@ +import { includesString } from "../shared/primitives.js"; +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { + validateNonEmptyString, + validateNonNegativeInteger, + validatePositiveInteger, + validateStringArray, +} from "../shared/validators-01.js"; +import { + validateBoolean, + validateOptional, + validateRequiredObject, +} from "../shared/validators-02.js"; +import type { + InspectImplementationEntry, + InspectReferenceEntry, + InspectSignatureEntry, + InspectSignatureParameter, + InspectSignatureTypeParameter, + InspectSymbolEvidence, + InspectSymbolSummary, + InspectSymbolTarget, + InspectTextSpan} from "./contracts-01.js"; +import { + inspectImplementationKinds, + inspectSignatureKinds, +} from "./contracts-01.js"; +import type { InspectRouteResult } from "./contracts-02.js"; + +function validateInspectRouteName(route: unknown): InspectRouteResult["route"] { + if (!includesString(["references", "signature", "implementations"] as const, route)) { + throw new Error(`Unknown inspect route result route: ${String(route)}`); + } + return route; +} + +export { validateInspectRouteName }; + +function validateInspectRoutePayload(result: InspectRouteResult, route: InspectRouteResult["route"]): void { + validateInspectRoutePayloadFields(result, route); + if (route === "references") { + return validateInspectReferencesPayload("references" in result ? result.references : undefined); + } + if (route === "signature") { + return validateInspectSignaturesPayload("signatures" in result ? result.signatures : undefined); + } + validateInspectImplementationsPayload("implementations" in result ? result.implementations : undefined); +} + +export { validateInspectRoutePayload }; + +function validateInspectRoutePayloadFields(result: InspectRouteResult, route: InspectRouteResult["route"]): void { + const payloadFields = ["references", "signatures", "implementations"] as const; + const expectedFields = { + references: "references", + signature: "signatures", + implementations: "implementations", + } as const; + const expectedField = expectedFields[route]; + for (const field of payloadFields) { + if (field !== expectedField && Object.hasOwn(result, field)) { + throw new Error(`Inspect ${route} result must not include ${field}`); + } + } +} + +function validateInspectReferencesPayload(references: readonly InspectReferenceEntry[] | undefined): void { + if (!Array.isArray(references)) throw new Error("Inspect references result references must be an array"); + for (const reference of references) validateInspectReferenceEntry(reference); +} + +function validateInspectSignaturesPayload(signatures: readonly InspectSignatureEntry[] | undefined): void { + if (!Array.isArray(signatures)) throw new Error("Inspect signature result signatures must be an array"); + for (const signature of signatures) validateInspectSignatureEntry(signature); +} + +function validateInspectImplementationsPayload( + implementations: readonly InspectImplementationEntry[] | undefined, +): void { + if (!Array.isArray(implementations)) + throw new Error("Inspect implementations result implementations must be an array"); + for (const implementation of implementations) validateInspectImplementationEntry(implementation); +} + +function validateInspectSymbolTarget(target: InspectSymbolTarget, label: string): InspectSymbolTarget { + validateRequiredObject(target, `${label} is required`); + if (!includesString(["node", "file_symbol"] as const, target.kind)) { + throw new Error(`Unknown ${label} kind: ${String((target as { kind?: unknown }).kind)}`); + } + if (target.kind === "node") validateInspectNodeTarget(target, label); + else validateInspectFileSymbolTarget(target, label); + return target; +} + +export { validateInspectSymbolTarget }; + +function validateInspectNodeTarget(target: InspectSymbolTarget, label: string): void { + validateNonEmptyString(target.nodeId, `${label} nodeId`); + const fileSymbolFields = [target.path, target.symbolName, target.line, target.column]; + if (fileSymbolFields.some((value) => value !== undefined)) { + throw new Error(`${label} node target must not include file-symbol fields`); + } +} + +function validateInspectFileSymbolTarget(target: InspectSymbolTarget, label: string): void { + validateRepoRelativePath(validateNonEmptyString(target.path, `${label} path`)); + validateNonEmptyString(target.symbolName, `${label} symbolName`); + validateOptional(target.line, (value) => validatePositiveInteger(value, `${label} line`)); + validateOptional(target.column, (value) => validatePositiveInteger(value, `${label} column`)); + validateOptional(target.nodeId, (value) => validateNonEmptyString(value, `${label} nodeId`)); +} + +const validateInspectReferenceTarget = validateInspectSymbolTarget; + +export { validateInspectReferenceTarget }; + +function validateInspectReferenceEntry(entry: InspectReferenceEntry): InspectReferenceEntry { + if (!entry || typeof entry !== "object") throw new Error("Inspect reference entry is required"); + validateRepoRelativePath(entry.file); + validatePositiveInteger(entry.line, "Inspect reference entry line"); + validatePositiveInteger(entry.column, "Inspect reference entry column"); + validateNonEmptyString(entry.text, "Inspect reference entry text"); + validateInspectTextSpan(entry.span, "Inspect reference span"); + validateInspectSymbolSummary(entry.symbol, "Inspect reference entry symbol"); + if (typeof entry.isDefinition !== "boolean") throw new Error("Inspect reference entry isDefinition must be boolean"); + if (entry.isDeclaration !== undefined && typeof entry.isDeclaration !== "boolean") { + throw new Error("Inspect reference entry isDeclaration must be boolean"); + } + validateInspectSymbolEvidence(entry.evidence, "Inspect reference entry evidence"); + return entry; +} + +export { validateInspectReferenceEntry }; + +function validateInspectSymbolEvidence(evidence: InspectSymbolEvidence, label: string): InspectSymbolEvidence { + if (!evidence || typeof evidence !== "object") throw new Error(`${label} is required`); + validateStringArray(evidence.graphNodeIds, `${label} graphNodeIds`, { + allowEmpty: true, + }); + if (!includesString(["graph", "language_service"] as const, evidence.resolver)) { + throw new Error(`Unknown ${label} resolver: ${String(evidence.resolver)}`); + } + return evidence; +} + +export { validateInspectSymbolEvidence }; + +function validateInspectSignatureEntry(entry: InspectSignatureEntry): InspectSignatureEntry { + validateRequiredObject(entry, "Inspect signature entry is required"); + validateRepoRelativePath(entry.file); + validatePositiveInteger(entry.line, "Inspect signature entry line"); + validatePositiveInteger(entry.column, "Inspect signature entry column"); + validateNonEmptyString(entry.text, "Inspect signature entry text"); + validateNonEmptyString(entry.signature, "Inspect signature entry signature"); + if (!includesString(inspectSignatureKinds, entry.kind)) { + throw new Error(`Unknown inspect signature entry kind: ${String(entry.kind)}`); + } + validateInspectSignatureParameters(entry); + validateBoolean(entry.exported, "Inspect signature entry exported"); + validateBoolean(entry.async, "Inspect signature entry async"); + validateOptional(entry.returnType, (value) => + validateNonEmptyString(value, "Inspect signature entry returnType"), + ); + validateInspectTextSpan(entry.span, "Inspect signature span"); + validateInspectSymbolSummary(entry.symbol, "Inspect signature entry symbol"); + validateOptional(entry.overloadIndex, (value) => + validateNonNegativeInteger(value, "Inspect signature entry overloadIndex"), + ); + validateInspectSymbolEvidence(entry.evidence, "Inspect signature entry evidence"); + return entry; +} + +export { validateInspectSignatureEntry }; + +function validateInspectSignatureParameters(entry: InspectSignatureEntry): void { + if (!Array.isArray(entry.parameters)) throw new Error("Inspect signature entry parameters must be an array"); + for (const parameter of entry.parameters) validateInspectSignatureParameter(parameter); + if (!Array.isArray(entry.typeParameters)) { + throw new Error("Inspect signature entry typeParameters must be an array"); + } + for (const typeParameter of entry.typeParameters) validateInspectSignatureTypeParameter(typeParameter); +} + +function validateInspectSignatureParameter(parameter: InspectSignatureParameter): InspectSignatureParameter { + if (!parameter || typeof parameter !== "object") throw new Error("Inspect signature parameter is required"); + validateNonEmptyString(parameter.name, "Inspect signature parameter name"); + validateNonEmptyString(parameter.type, "Inspect signature parameter type"); + if (typeof parameter.optional !== "boolean") throw new Error("Inspect signature parameter optional must be boolean"); + if (parameter.rest !== undefined && typeof parameter.rest !== "boolean") { + throw new Error("Inspect signature parameter rest must be boolean"); + } + if (parameter.defaultValue !== undefined) + validateNonEmptyString(parameter.defaultValue, "Inspect signature parameter defaultValue"); + return parameter; +} + +export { validateInspectSignatureParameter }; + +function validateInspectSignatureTypeParameter( + typeParameter: InspectSignatureTypeParameter, +): InspectSignatureTypeParameter { + if (!typeParameter || typeof typeParameter !== "object") + throw new Error("Inspect signature typeParameter is required"); + validateNonEmptyString(typeParameter.name, "Inspect signature typeParameter name"); + if (typeParameter.constraint !== undefined) { + validateNonEmptyString(typeParameter.constraint, "Inspect signature typeParameter constraint"); + } + if (typeParameter.default !== undefined) { + validateNonEmptyString(typeParameter.default, "Inspect signature typeParameter default"); + } + return typeParameter; +} + +export { validateInspectSignatureTypeParameter }; + +function validateInspectImplementationEntry(entry: InspectImplementationEntry): InspectImplementationEntry { + if (!entry || typeof entry !== "object") throw new Error("Inspect implementation entry is required"); + validateRepoRelativePath(entry.file); + validatePositiveInteger(entry.line, "Inspect implementation entry line"); + validatePositiveInteger(entry.column, "Inspect implementation entry column"); + validateNonEmptyString(entry.text, "Inspect implementation entry text"); + validateInspectTextSpan(entry.span, "Inspect implementation span"); + if (Object.hasOwn(entry, "implements")) + throw new Error("Inspect implementation entry must use target, not implements"); + if (!includesString(inspectImplementationKinds, entry.kind)) { + throw new Error(`Unknown Inspect implementation entry kind: ${String(entry.kind)}`); + } + validateInspectSymbolSummary(entry.symbol, "Inspect implementation entry symbol"); + validateInspectSymbolSummary(entry.target, "Inspect implementation entry target"); + if (entry.isDeclaration !== undefined && typeof entry.isDeclaration !== "boolean") { + throw new Error("Inspect implementation entry isDeclaration must be boolean"); + } + validateInspectSymbolEvidence(entry.evidence, "Inspect implementation entry evidence"); + return entry; +} + +export { validateInspectImplementationEntry }; + +function validateInspectTextSpan(span: InspectTextSpan, label: string): InspectTextSpan { + if (!span || typeof span !== "object") throw new Error(`${label} is required`); + validatePositiveInteger(span.startLine, `${label} startLine`); + validatePositiveInteger(span.startColumn, `${label} startColumn`); + validatePositiveInteger(span.endLine, `${label} endLine`); + validatePositiveInteger(span.endColumn, `${label} endColumn`); + if (span.endLine < span.startLine || (span.endLine === span.startLine && span.endColumn < span.startColumn)) { + throw new Error(`${label} end must be after start`); + } + if (span.startOffset !== undefined) validateNonNegativeInteger(span.startOffset, `${label} startOffset`); + if (span.endOffset !== undefined) validateNonNegativeInteger(span.endOffset, `${label} endOffset`); + return span; +} + +export { validateInspectTextSpan }; + +function validateInspectSymbolSummary(symbol: InspectSymbolSummary, label: string): InspectSymbolSummary { + if (!symbol || typeof symbol !== "object") throw new Error(`${label} is required`); + validateNonEmptyString(symbol.id, `${label} id`); + validateNonEmptyString(symbol.name, `${label} name`); + if (symbol.kind !== undefined) validateNonEmptyString(symbol.kind, `${label} kind`); + return symbol; +} + +export { validateInspectSymbolSummary }; diff --git a/packages/contracts/src/inspect/helper-validators-02.ts b/packages/contracts/src/inspect/helper-validators-02.ts new file mode 100644 index 0000000..a58c30f --- /dev/null +++ b/packages/contracts/src/inspect/helper-validators-02.ts @@ -0,0 +1,21 @@ +import { includesString } from "../shared/primitives.js"; +import { validateNonEmptyString } from "../shared/validators-01.js"; +import { inspectFailureCategories } from "./contracts-01.js"; +import type { InspectRouteFailure } from "./contracts-02.js"; +import { validateInspectSymbolTarget } from "./helper-validators-01.js"; + +function validateInspectRouteFailure(failure: InspectRouteFailure): InspectRouteFailure { + if (!failure || typeof failure !== "object") throw new Error("Inspect route failure is required"); + if (!includesString(inspectFailureCategories, failure.category)) { + throw new Error(`Unknown inspect route failure category: ${String(failure.category)}`); + } + validateNonEmptyString(failure.message, "Inspect route failure message"); + if (failure.candidates !== undefined) { + if (!Array.isArray(failure.candidates)) throw new Error("Inspect route failure candidates must be an array"); + for (const candidate of failure.candidates) + validateInspectSymbolTarget(candidate, "Inspect route failure candidate"); + } + return failure; +} + +export { validateInspectRouteFailure }; diff --git a/packages/contracts/src/inspect/validators.ts b/packages/contracts/src/inspect/validators.ts new file mode 100644 index 0000000..5bdec23 --- /dev/null +++ b/packages/contracts/src/inspect/validators.ts @@ -0,0 +1,72 @@ +import { includesString } from "../shared/primitives.js"; +import { validateProviderStatus } from "../graph/provider-validators.js"; +import { validateOptional, validateRequiredObject } from "../shared/validators-02.js"; +import type { InspectRouteResult } from "./contracts-02.js"; +import { + validateInspectRouteName, + validateInspectRoutePayload, + validateInspectSymbolTarget, +} from "./helper-validators-01.js"; +import { validateInspectRouteFailure } from "./helper-validators-02.js"; + +function validateInspectRouteResult(result: InspectRouteResult): InspectRouteResult { + validateRequiredObject(result, "Inspect route result is required"); + const route = validateInspectRouteName((result as { route?: unknown }).route); + if (!includesString(["ok", "error", "degraded"] as const, result.status)) { + throw new Error(`Unknown inspect route result status: ${String((result as { status?: unknown }).status)}`); + } + validateOptional(result.providerStatus, validateProviderStatus); + if (result.status === "ok") validateSuccessfulInspectResult(result, route); + else if (result.status === "degraded" && inspectResultHasPayload(result, route)) + validateDegradedInspectResult(result, route); + else validateFailedInspectResult(result, route); + return result; +} + +export { validateInspectRouteResult }; + +function validateSuccessfulInspectResult(result: InspectRouteResult, route: InspectRouteResult["route"]): void { + const target = result.target; + if (target === undefined) throw new Error(`Successful inspect ${route} result requires target`); + validateInspectSymbolTarget(target, `Inspect ${route} target`); + if (result.providerStatus === undefined || result.providerStatus.state !== "available") { + throw new Error(`Successful inspect ${route} result requires available providerStatus`); + } + validateInspectRoutePayload(result, route); + if (Object.hasOwn(result, "failure")) { + throw new Error(`Successful inspect ${route} result must not include failure`); + } +} + +function validateDegradedInspectResult(result: InspectRouteResult, route: InspectRouteResult["route"]): void { + const target = result.target; + if (target === undefined) throw new Error(`Degraded inspect ${route} result requires target`); + validateInspectSymbolTarget(target, `Inspect ${route} target`); + if (result.providerStatus === undefined) { + throw new Error(`Degraded inspect ${route} result requires providerStatus`); + } + validateInspectRoutePayload(result, route); + const failure = result.failure; + if (failure === undefined) throw new Error(`Degraded inspect ${route} result requires failure`); + validateInspectRouteFailure(failure); +} + +function validateFailedInspectResult(result: InspectRouteResult, route: InspectRouteResult["route"]): void { + validateOptional(result.target, (target) => validateInspectSymbolTarget(target, `Inspect ${route} target`)); + const failure = result.failure; + if (failure === undefined) throw new Error(`Failed inspect ${route} result requires failure`); + validateInspectRouteFailure(failure); + for (const field of ["references", "signatures", "implementations"] as const) { + if (Object.hasOwn(result, field)) throw new Error(`Failed inspect ${route} result must not include ${field}`); + } +} + +function inspectResultHasPayload(result: InspectRouteResult, route: InspectRouteResult["route"]): boolean { + return ( + (route === "references" && Object.hasOwn(result, "references")) || + (route === "signature" && Object.hasOwn(result, "signatures")) || + (route === "implementations" && Object.hasOwn(result, "implementations")) + ); +} + +export { inspectResultHasPayload }; diff --git a/packages/contracts/src/inspect/warm-contracts.ts b/packages/contracts/src/inspect/warm-contracts.ts new file mode 100644 index 0000000..b06e760 --- /dev/null +++ b/packages/contracts/src/inspect/warm-contracts.ts @@ -0,0 +1,115 @@ +import type { EditRefusal, RepoRelativeChange } from "../edit/contracts.js"; +import type { CommandTiming } from "../product/latency-contracts.js"; +import type { InspectReferenceEntry, InspectReferenceTarget } from "./contracts-01.js"; +import type { InspectRouteFailure } from "./contracts-02.js"; + +const aspWarmMethodNames = ["inspect/references", "edit/rename", "check/evaluate", "session/shutdown"] as const; + +export { aspWarmMethodNames }; + +type AspWarmMethodName = (typeof aspWarmMethodNames)[number]; + +export type { AspWarmMethodName }; + +interface AspWarmProviderSummary { + id: "opcore"; + capabilityFamily: "inspect" | "edit" | "session"; +} + +export type { AspWarmProviderSummary }; + +interface AspWarmInspectReferencesParams { + path: string; + symbolName: string; + line?: number; + column?: number; + limit?: number; +} + +export type { AspWarmInspectReferencesParams }; + +interface AspWarmInspectReferencesOkResult { + route: "references"; + status: "ok"; + target: InspectReferenceTarget; + references: readonly InspectReferenceEntry[]; +} + +export type { AspWarmInspectReferencesOkResult }; + +interface AspWarmInspectReferencesErrorResult { + route: "references"; + status: "error"; + target?: InspectReferenceTarget; + failure: InspectRouteFailure; +} + +export type { AspWarmInspectReferencesErrorResult }; + +interface AspWarmInspectReferencesResponse { + provider: AspWarmProviderSummary; + inspectResult: AspWarmInspectReferencesOkResult | AspWarmInspectReferencesErrorResult; + timing: CommandTiming; +} + +export type { AspWarmInspectReferencesResponse }; + +interface SymbolEditTarget { + path: string; + name: string; + line?: number; + column?: number; + nodeId?: string; +} + +export type { SymbolEditTarget }; + +interface AspWarmEditRenameParams { + target: SymbolEditTarget; + newName: string; +} + +export type { AspWarmEditRenameParams }; + +interface AspWarmAffectedChecksum { + path: string; + checksumBefore?: string; + checksumAfter?: string; +} + +export type { AspWarmAffectedChecksum }; + +interface AspWarmEditRenamePreviewResult { + route: "rename"; + status: "preview"; + changes: readonly RepoRelativeChange[]; + affectedChecksums: readonly AspWarmAffectedChecksum[]; +} + +export type { AspWarmEditRenamePreviewResult }; + +interface AspWarmEditRenameRefusedResult { + route: "rename"; + status: "refused"; + refusal: EditRefusal; +} + +export type { AspWarmEditRenameRefusedResult }; + +interface AspWarmEditRenameResponse { + provider: AspWarmProviderSummary; + editResult: AspWarmEditRenamePreviewResult | AspWarmEditRenameRefusedResult; + timing: CommandTiming; +} + +export type { AspWarmEditRenameResponse }; + +interface AspWarmSessionShutdownResponse { + provider: AspWarmProviderSummary; + session: { + state: "shutdown"; + }; + timing: CommandTiming; +} + +export type { AspWarmSessionShutdownResponse }; diff --git a/packages/contracts/src/managed/contracts.ts b/packages/contracts/src/managed/contracts.ts new file mode 100644 index 0000000..b3ce395 --- /dev/null +++ b/packages/contracts/src/managed/contracts.ts @@ -0,0 +1,198 @@ +import type { GraphProviderMode } from "../graph/vocabulary-01.js"; +import type { OpcoreInitScope } from "../product/init-contracts.js"; +import type { GraphReleaseOptionalSurfaceReceipt } from "../release/graph-contracts.js"; +import type { GraphCoreNativePackageName, GraphCoreNativeSupportedTarget } from "../release/graph-vocabulary-02.js"; +import type { + PYTHON_PROJECT_CONTEXT_SCHEMA_ID, + PythonProjectContextOutcome, +} from "../validation/python-project-contracts-01.js"; +import type { ValidationScopeKind } from "../validation/request-contracts.js"; + +const managedToolDescriptorCommandGroups = [ + "graph", + "inspect", + "edit", + "check", + "validate", + "status", + "doctor", +] as const; + +export { managedToolDescriptorCommandGroups }; + +type ManagedToolDescriptorCommandGroupName = (typeof managedToolDescriptorCommandGroups)[number]; + +export type { ManagedToolDescriptorCommandGroupName }; + +const managedToolDescriptorCommandGroupPackageNames: Record = { + graph: "opcore", + inspect: "opcore", + edit: "opcore", + check: "opcore", + validate: "opcore", + status: "opcore", + doctor: "opcore", +}; + +export { managedToolDescriptorCommandGroupPackageNames }; + +const managedToolDescriptorArtifactTypes = [ + "entrypoint", + "descriptor", + "schema", + "manifest", + "native_binary", + "checksum", + "receipt", +] as const; + +export { managedToolDescriptorArtifactTypes }; + +type ManagedToolDescriptorArtifactType = (typeof managedToolDescriptorArtifactTypes)[number]; + +export type { ManagedToolDescriptorArtifactType }; + +interface ManagedToolDescriptor { + schemaVersion: 1; + descriptorKind: "aggregate_opcore"; + aggregateIdentity: { + name: "opcore"; + releaseLine: "opcore"; + packageName: "opcore"; + version?: string; + }; + packageIdentity: { + packageName: "opcore"; + artifactName: "opcore"; + version?: string; + }; + entrypoints: readonly ManagedToolDescriptorEntrypoint[]; + commandGroups: readonly ManagedToolDescriptorCommandGroup[]; + healthProbes: readonly ManagedToolDescriptorHealthProbe[]; + capabilities: ManagedToolDescriptorCapabilities; + artifacts: readonly ManagedToolDescriptorArtifactReference[]; + checksums: readonly ManagedToolDescriptorChecksumReference[]; + provenanceHooks: readonly ManagedToolDescriptorProvenanceHook[]; + optionalSurfaces: readonly GraphReleaseOptionalSurfaceReceipt[]; +} + +export type { ManagedToolDescriptor }; + +interface ManagedToolDescriptorEntrypoint { + bin: "opcore"; + packageName: "opcore"; + path: string; + command: readonly string[]; +} + +export type { ManagedToolDescriptorEntrypoint }; + +interface ManagedToolDescriptorCommandGroup { + name: ManagedToolDescriptorCommandGroupName; + canonicalCommand: readonly string[]; + commands: readonly string[]; + packageName: string; +} + +export type { ManagedToolDescriptorCommandGroup }; + +interface ManagedToolDescriptorHealthProbe { + id: string; + command: readonly string[]; + expectedExitCode: 0; + output: "json"; +} + +export type { ManagedToolDescriptorHealthProbe }; + +interface ManagedToolDescriptorCapabilities { + graph: { + provider: "opcore-graph"; + schemaVersion: 1; + commands: readonly string[]; + queryKinds: readonly string[]; + daemonOperations: readonly string[]; + nativeArtifacts: readonly ManagedToolDescriptorNativeArtifact[]; + }; + edit: { + commands: readonly string[]; + safeEditModes: readonly string[]; + symbolEditModes: readonly string[]; + validationRequiredForApply: true; + dryRun: true; + }; + validation: { + checkRoutes: readonly string[]; + validateRoutes: readonly string[]; + scopeModes: readonly ValidationScopeKind[]; + graphModes: readonly GraphProviderMode[]; + hypothetical: true; + statusSurfaces: readonly ("status" | "doctor")[]; + pythonProjectContext: { + schemaId: typeof PYTHON_PROJECT_CONTEXT_SCHEMA_ID; + outcomes: readonly PythonProjectContextOutcome[]; + readOnly: true; + installs: false; + }; + writeGate: { + initScopes: readonly OpcoreInitScope[]; + harnesses: readonly ("claude-code" | "codex")[]; + adapterPath: string; + validationCommand: readonly string[]; + adapterErrorPolicy: "fail_open"; + validationErrorPolicy: "fail_closed"; + codexBoundary: "pretooluse_guardrail"; + }; + checkIds: readonly string[]; + }; +} + +export type { ManagedToolDescriptorCapabilities }; + +interface ManagedToolDescriptorNativeArtifact { + targetPlatform: GraphCoreNativeSupportedTarget; + packageName: "opcore"; + bundledPackageName: GraphCoreNativePackageName; + binaryPath: string; + metadataPath: string; + checksumPath: string; + artifactIds: { + binaryArtifactId: string; + metadataArtifactId: string; + checksumId: string; + checksumArtifactId: string; + }; +} + +export type { ManagedToolDescriptorNativeArtifact }; + +interface ManagedToolDescriptorArtifactReference { + id: string; + packageName: string; + path: string; + type: ManagedToolDescriptorArtifactType; + required: boolean; + checksumRef?: string; +} + +export type { ManagedToolDescriptorArtifactReference }; + +interface ManagedToolDescriptorChecksumReference { + id: string; + packageName: string; + path: string; + algorithm: "sha256"; + artifactRef: string; + required: boolean; + value?: string; +} + +export type { ManagedToolDescriptorChecksumReference }; + +interface ManagedToolDescriptorProvenanceHook { + id: string; + command: readonly string[]; + expectedExitCode: 0; +} + +export type { ManagedToolDescriptorProvenanceHook }; diff --git a/packages/contracts/src/managed/helper-validators.ts b/packages/contracts/src/managed/helper-validators.ts new file mode 100644 index 0000000..9ee3877 --- /dev/null +++ b/packages/contracts/src/managed/helper-validators.ts @@ -0,0 +1,105 @@ +import { includesString } from "../shared/primitives.js"; +import type { + GraphCoreNativePackageName, + GraphCoreNativeSupportedTarget} from "../release/graph-vocabulary-02.js"; +import { + graphCoreNativePackageNames, + graphCoreNativePackageNamesByTarget, + graphCoreNativeSupportedTargets, +} from "../release/graph-vocabulary-02.js"; +import type { + ReleaseReceiptCommandGroupName, + ReleaseReceiptPackageName, + ReleaseReceiptReportId} from "../release/vocabulary-01.js"; +import { + releaseReceiptCommandGroups, + releaseReceiptPackageNames, + releaseReceiptReportIds, +} from "../release/vocabulary-01.js"; +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { validateNonEmptyString } from "../shared/validators-01.js"; +import type { ManagedToolDescriptorArtifactReference} from "./contracts.js"; +import { managedToolDescriptorArtifactTypes } from "./contracts.js"; + +function validateManagedToolDescriptorPackageReference( + reference: ManagedToolDescriptorArtifactReference, + packageName: ReleaseReceiptPackageName, +): void { + if (!reference || typeof reference !== "object") throw new Error("Release receipt descriptor reference is required"); + validateNonEmptyString(reference.id, "Release receipt descriptor reference id"); + if (reference.packageName !== packageName) { + throw new Error(`Release receipt descriptor reference packageName must be ${packageName}`); + } + validateRepoRelativePath(reference.path); + if (!includesString(managedToolDescriptorArtifactTypes, reference.type)) { + throw new Error(`Unknown release receipt descriptor reference type: ${String(reference.type)}`); + } + if (typeof reference.required !== "boolean") + throw new Error("Release receipt descriptor reference required must be boolean"); + if (reference.checksumRef !== undefined) + validateNonEmptyString(reference.checksumRef, "Release receipt descriptor reference checksumRef"); +} + +export { validateManagedToolDescriptorPackageReference }; + +function validateReleaseReceiptPackageName(value: unknown, label: string): ReleaseReceiptPackageName { + if (!includesString(releaseReceiptPackageNames, value)) { + throw new Error(`${label} must be one of ${releaseReceiptPackageNames.join(", ")}`); + } + return value; +} + +export { validateReleaseReceiptPackageName }; + +function isGraphCoreNativePackageName(value: unknown): value is GraphCoreNativePackageName { + return includesString(graphCoreNativePackageNames, value); +} + +export { isGraphCoreNativePackageName }; + +function bundledGraphCoreNativePath( + packageName: GraphCoreNativePackageName, + file: "metadata.json" | "opcore-graph-core" | "opcore-graph-core.sha256", +): string { + return `node_modules/${packageName}/${file}`; +} + +export { bundledGraphCoreNativePath }; + +function graphCoreNativeTargetForPackageName(packageName: GraphCoreNativePackageName): GraphCoreNativeSupportedTarget { + const target = graphCoreNativeSupportedTargets.find( + (entry) => graphCoreNativePackageNamesByTarget[entry] === packageName, + ); + if (!target) throw new Error(`Unknown Opcore graph-core native package: ${packageName}`); + return target; +} + +export { graphCoreNativeTargetForPackageName }; + +function validateReleaseReceiptCommandGroupName(value: unknown, label: string): ReleaseReceiptCommandGroupName { + if (!includesString(releaseReceiptCommandGroups, value)) { + throw new Error(`${label} must be one of ${releaseReceiptCommandGroups.join(", ")}`); + } + return value; +} + +export { validateReleaseReceiptCommandGroupName }; + +function validateReleaseReceiptReportId(value: unknown, label: string): ReleaseReceiptReportId { + if (!includesString(releaseReceiptReportIds, value)) { + throw new Error(`${label} must be one of ${releaseReceiptReportIds.join(", ")}`); + } + return value; +} + +export { validateReleaseReceiptReportId }; + +function validateStringRecord(value: Readonly>, label: string): void { + if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${label} must be an object`); + for (const [key, recordValue] of Object.entries(value)) { + validateNonEmptyString(key, `${label} key`); + validateNonEmptyString(recordValue, `${label}.${key}`); + } +} + +export { validateStringRecord }; diff --git a/packages/contracts/src/managed/validators-01.ts b/packages/contracts/src/managed/validators-01.ts new file mode 100644 index 0000000..701ae27 --- /dev/null +++ b/packages/contracts/src/managed/validators-01.ts @@ -0,0 +1,198 @@ +import { validateRequiredObject } from "../shared/validators-02.js"; +import type { CommandRouterManifest } from "../command/contracts.js"; +import { + validateCommandRouterManifestHeader, + validateManifestBins, + validateManifestGroups, + validateManifestOwnershipBoundaries, +} from "../command/validators.js"; +import { includesString, sameStringArray } from "../shared/primitives.js"; +import { validateGraphReleaseOptionalSurfaces } from "../release/graph-optional-validators.js"; +import { + validateCommandExitSemantics, + validateExactStringSequence, + validateExactStringSet, + validateNonEmptyArray, + validateNonEmptyString, + validateStringArray, +} from "../shared/validators-01.js"; +import type { + ManagedToolDescriptor, + ManagedToolDescriptorCapabilities, + ManagedToolDescriptorCommandGroup, + ManagedToolDescriptorEntrypoint, + ManagedToolDescriptorHealthProbe} from "./contracts.js"; +import { + managedToolDescriptorCommandGroupPackageNames, + managedToolDescriptorCommandGroups, +} from "./contracts.js"; +import { + validateManagedToolEditCapabilities, + validateManagedToolGraphCapabilities, + validateManagedToolValidationCapabilities, +} from "./validators-02.js"; +import { + validateManagedToolArtifacts, + validateManagedToolChecksums, + validateManagedToolCommandTokens, + validateManagedToolDescriptorForbiddenStrings, + validateManagedToolPackagePath, + validateManagedToolProvenanceHooks, +} from "./validators-03.js"; + +function validateCommandRouterManifest(manifest: CommandRouterManifest): CommandRouterManifest { + validateCommandRouterManifestHeader(manifest); + validateManifestBins(manifest.bins); + validateCommandExitSemantics(manifest.exitSemantics); + + validateManifestGroups(manifest.commandGroups); + validateManifestOwnershipBoundaries(manifest.ownershipBoundaries); + + return manifest; +} + +export { validateCommandRouterManifest }; + +function validateManagedToolDescriptor(descriptor: ManagedToolDescriptor): ManagedToolDescriptor { + validateRequiredObject(descriptor, "Managed tool descriptor is required"); + if (descriptor.schemaVersion !== 1) throw new Error("Managed tool descriptor schemaVersion must be 1"); + if (descriptor.descriptorKind !== "aggregate_opcore") { + throw new Error("Managed tool descriptor descriptorKind must be aggregate_opcore"); + } + validateManagedToolIdentity(descriptor); + validateManagedToolEntrypoints(descriptor.entrypoints); + validateManagedToolCommandGroups(descriptor.commandGroups); + validateManagedToolHealthProbes(descriptor.healthProbes); + validateManagedToolCapabilities(descriptor.capabilities); + const artifactReferences = validateManagedToolArtifacts(descriptor.artifacts); + validateManagedToolChecksums(descriptor.checksums, artifactReferences); + validateManagedToolProvenanceHooks(descriptor.provenanceHooks); + validateGraphReleaseOptionalSurfaces(descriptor.optionalSurfaces); + validateManagedToolDescriptorForbiddenStrings(descriptor); + return descriptor; +} + +export { validateManagedToolDescriptor }; + +function validateManagedToolIdentity(descriptor: ManagedToolDescriptor): void { + const aggregate = descriptor.aggregateIdentity; + if (!aggregate || typeof aggregate !== "object") + throw new Error("Managed tool descriptor aggregateIdentity is required"); + if (aggregate.name !== "opcore") throw new Error("Managed tool descriptor aggregateIdentity.name must be opcore"); + if (aggregate.releaseLine !== "opcore") + throw new Error("Managed tool descriptor aggregateIdentity.releaseLine must be opcore"); + if (aggregate.packageName !== "opcore") { + throw new Error("Managed tool descriptor aggregateIdentity.packageName must be opcore"); + } + if (aggregate.version !== undefined) + validateNonEmptyString(aggregate.version, "Managed tool descriptor aggregateIdentity.version"); + + const packageIdentity = descriptor.packageIdentity; + validateRequiredObject(packageIdentity, "Managed tool descriptor packageIdentity is required"); + if (packageIdentity.packageName !== "opcore") { + throw new Error("Managed tool descriptor packageIdentity.packageName must be opcore"); + } + if (packageIdentity.artifactName !== "opcore") { + throw new Error("Managed tool descriptor packageIdentity.artifactName must be opcore"); + } + if (packageIdentity.version !== undefined) + validateNonEmptyString(packageIdentity.version, "Managed tool descriptor packageIdentity.version"); +} + +export { validateManagedToolIdentity }; + +function validateManagedToolEntrypoints(entrypoints: readonly ManagedToolDescriptorEntrypoint[]): void { + validateNonEmptyArray(entrypoints, "Managed tool descriptor entrypoints"); + validateExactStringSet( + entrypoints.map((entrypoint) => entrypoint.bin), + ["opcore"], + "Managed tool descriptor entrypoint bins", + ); + for (const entrypoint of entrypoints) { + if (!entrypoint || typeof entrypoint !== "object") + throw new Error("Managed tool descriptor entrypoint is required"); + if (entrypoint.bin !== "opcore") throw new Error("Managed tool descriptor must expose the opcore entrypoint"); + if (entrypoint.packageName !== "opcore") { + throw new Error("Managed tool descriptor entrypoint packageName must be opcore"); + } + validateManagedToolPackagePath(entrypoint.path, "Managed tool descriptor entrypoint path"); + if (entrypoint.path !== "dist/index.js") { + throw new Error("Managed tool descriptor entrypoint path must be dist/index.js"); + } + validateExactStringSequence(entrypoint.command, ["opcore"], "Managed tool descriptor entrypoint command"); + } +} + +export { validateManagedToolEntrypoints }; + +function validateManagedToolCommandGroups(commandGroups: readonly ManagedToolDescriptorCommandGroup[]): void { + validateNonEmptyArray(commandGroups, "Managed tool descriptor command groups"); + validateExactStringSet( + commandGroups.map((group) => group.name), + managedToolDescriptorCommandGroups, + "Managed tool descriptor command groups", + ); + for (const group of commandGroups) { + if (!group || typeof group !== "object") throw new Error("Managed tool descriptor command group is required"); + if (!includesString(managedToolDescriptorCommandGroups, group.name)) { + throw new Error(`Unknown managed tool descriptor command group: ${String(group.name)}`); + } + const expectedCanonical = ["opcore", group.name]; + validateExactStringSequence( + group.canonicalCommand, + expectedCanonical, + `Managed tool descriptor ${group.name} canonicalCommand`, + ); + validateStringArray(group.commands, `Managed tool descriptor ${group.name} commands`, { allowEmpty: false }); + const expectedPackageName = managedToolDescriptorCommandGroupPackageNames[group.name]; + if (group.packageName !== expectedPackageName) { + throw new Error(`Managed tool descriptor ${group.name} packageName must be ${expectedPackageName}`); + } + validateManagedToolCommandTokens(group.commands, `Managed tool descriptor ${group.name} commands`); + } +} + +export { validateManagedToolCommandGroups }; + +function validateManagedToolHealthProbes(healthProbes: readonly ManagedToolDescriptorHealthProbe[]): void { + validateNonEmptyArray(healthProbes, "Managed tool descriptor health probes"); + for (const probe of healthProbes) validateManagedToolHealthProbe(probe); + validateRequiredHealthProbe(healthProbes, ["opcore", "status", "--json"], "status"); + validateRequiredHealthProbe(healthProbes, ["opcore", "doctor", "--json"], "doctor"); +} + +export { validateManagedToolHealthProbes }; + +function validateManagedToolHealthProbe(probe: ManagedToolDescriptorHealthProbe): void { + if (!probe || typeof probe !== "object") throw new Error("Managed tool descriptor health probe is required"); + validateNonEmptyString(probe.id, "Managed tool descriptor health probe id"); + validateStringArray(probe.command, "Managed tool descriptor health probe command", { allowEmpty: false }); + validateManagedToolCommandTokens(probe.command, "Managed tool descriptor health probe command"); + if (probe.command[0] !== "opcore") { + throw new Error("Managed tool descriptor health probes must use opcore commands"); + } + if (probe.expectedExitCode !== 0) { + throw new Error("Managed tool descriptor health probe expectedExitCode must be 0"); + } + if (probe.output !== "json") throw new Error("Managed tool descriptor health probe output must be json"); +} + +function validateRequiredHealthProbe( + probes: readonly ManagedToolDescriptorHealthProbe[], + command: readonly string[], + name: string, +): void { + if (!probes.some((probe) => sameStringArray(probe.command, command))) { + throw new Error(`Managed tool descriptor health probes must include ${name}`); + } +} + +function validateManagedToolCapabilities(capabilities: ManagedToolDescriptorCapabilities): void { + if (!capabilities || typeof capabilities !== "object") + throw new Error("Managed tool descriptor capabilities are required"); + validateManagedToolGraphCapabilities(capabilities.graph); + validateManagedToolEditCapabilities(capabilities.edit); + validateManagedToolValidationCapabilities(capabilities.validation); +} + +export { validateManagedToolCapabilities }; diff --git a/packages/contracts/src/managed/validators-02.ts b/packages/contracts/src/managed/validators-02.ts new file mode 100644 index 0000000..ae53e1b --- /dev/null +++ b/packages/contracts/src/managed/validators-02.ts @@ -0,0 +1,236 @@ +import { + validateExactValue, + validateRequiredObject, +} from "../shared/validators-02.js"; +import { graphProviderModes } from "../graph/vocabulary-01.js"; +import { opcoreInitScopes } from "../product/init-contracts.js"; +import { + graphCoreNativePackageNameForTarget, + graphCoreNativeSupportedTargets, +} from "../release/graph-vocabulary-02.js"; +import { + validateExactStringSequence, + validateExactStringSet, + validateStringArray, + validateValidationChecks, +} from "../shared/validators-01.js"; +import { + PYTHON_PROJECT_CONTEXT_SCHEMA_ID, + pythonProjectContextOutcomes, +} from "../validation/python-project-contracts-01.js"; +import { validationScopeKinds } from "../validation/request-contracts.js"; +import type { ManagedToolDescriptorCapabilities } from "./contracts.js"; +import { bundledGraphCoreNativePath } from "./helper-validators.js"; +import { validateManagedToolPackagePath } from "./validators-03.js"; + +function validateManagedToolGraphCapabilities(graph: ManagedToolDescriptorCapabilities["graph"]): void { + if (!graph || typeof graph !== "object") throw new Error("Managed tool descriptor graph capabilities are required"); + if (graph.provider !== "opcore-graph") throw new Error("Managed tool descriptor graph provider must be opcore-graph"); + if (graph.schemaVersion !== 1) throw new Error("Managed tool descriptor graph schemaVersion must be 1"); + validateExactStringSet( + graph.commands, + ["build", "update", "watch", "status", "query", "impact", "review-context", "detect-changes", "search", "serve"], + "Managed tool descriptor graph commands", + ); + validateStringArray(graph.queryKinds, "Managed tool descriptor graph queryKinds", { allowEmpty: false }); + validateExactStringSet( + graph.daemonOperations, + ["ping", "status", "query", "search", "shutdown"], + "Managed tool descriptor graph daemonOperations", + ); + validateManagedToolGraphNativeArtifacts(graph.nativeArtifacts); +} + +export { validateManagedToolGraphCapabilities }; + +function validateManagedToolGraphNativeArtifacts( + nativeArtifacts: ManagedToolDescriptorCapabilities["graph"]["nativeArtifacts"], +): void { + if (!Array.isArray(nativeArtifacts)) throw new Error("Managed tool descriptor graph native artifacts are required"); + validateExactStringSet( + nativeArtifacts.map((artifact) => artifact?.targetPlatform), + graphCoreNativeSupportedTargets, + "Managed tool descriptor graph native targets", + ); + for (const artifact of nativeArtifacts) validateManagedToolGraphNativeArtifact(artifact); +} + +export { validateManagedToolGraphNativeArtifacts }; + +function validateManagedToolGraphNativeArtifact( + artifact: ManagedToolDescriptorCapabilities["graph"]["nativeArtifacts"][number], +): void { + validateRequiredObject(artifact, "Managed tool descriptor graph native artifact is required"); + const target = artifact.targetPlatform; + const bundledPackage = graphCoreNativePackageNameForTarget(target); + validateExactValue( + artifact.packageName, + "opcore", + `Managed tool descriptor graph native packageName for ${target} must be opcore`, + ); + validateExactValue( + artifact.bundledPackageName, + bundledPackage, + `Managed tool descriptor graph native bundledPackageName for ${target} must be ${bundledPackage}`, + ); + validateGraphNativeArtifactPaths(artifact, bundledPackage); + validateGraphNativeArtifactIds(artifact); +} + +export { validateManagedToolGraphNativeArtifact }; + +function validateGraphNativeArtifactPaths( + artifact: ManagedToolDescriptorCapabilities["graph"]["nativeArtifacts"][number], + bundledPackage: ReturnType, +): void { + validateExactValue( + artifact.binaryPath, + bundledGraphCoreNativePath(bundledPackage, "opcore-graph-core"), + "Managed tool descriptor graph native binaryPath must point at bundled opcore-graph-core", + ); + validateExactValue( + artifact.metadataPath, + bundledGraphCoreNativePath(bundledPackage, "metadata.json"), + "Managed tool descriptor graph native metadataPath must point at bundled metadata.json", + ); + validateExactValue( + artifact.checksumPath, + bundledGraphCoreNativePath(bundledPackage, "opcore-graph-core.sha256"), + "Managed tool descriptor graph native checksumPath must point at bundled opcore-graph-core.sha256", + ); +} + +export { validateGraphNativeArtifactPaths }; + +function validateGraphNativeArtifactIds( + artifact: ManagedToolDescriptorCapabilities["graph"]["nativeArtifacts"][number], +): void { + validateRequiredObject(artifact.artifactIds, "Managed tool descriptor graph native artifact ids are required"); + const suffix = artifact.targetPlatform; + validateExactValue( + artifact.artifactIds.binaryArtifactId, + `graph-core-binary-${suffix}`, + `Managed tool descriptor graph native binary artifact id must be graph-core-binary-${suffix}`, + ); + validateExactValue( + artifact.artifactIds.metadataArtifactId, + `graph-core-metadata-${suffix}`, + `Managed tool descriptor graph native metadata artifact id must be graph-core-metadata-${suffix}`, + ); + validateExactValue( + artifact.artifactIds.checksumArtifactId, + `graph-core-checksum-${suffix}`, + `Managed tool descriptor graph native checksum artifact id must be graph-core-checksum-${suffix}`, + ); + validateExactValue( + artifact.artifactIds.checksumId, + `graph-core-binary-sha256-${suffix}`, + `Managed tool descriptor graph native checksum id must be graph-core-binary-sha256-${suffix}`, + ); +} + +export { validateGraphNativeArtifactIds }; + +function validateManagedToolEditCapabilities(edit: ManagedToolDescriptorCapabilities["edit"]): void { + if (!edit || typeof edit !== "object") throw new Error("Managed tool descriptor edit capabilities are required"); + validateExactStringSet( + edit.commands, + ["exact", "multi", "search-replace", "patch", "tree", "rename", "move", "signature", "check", "apply"], + "Managed tool descriptor edit commands", + ); + validateExactStringSet( + edit.safeEditModes, + ["exact", "multi", "search-replace", "patch", "tree"], + "Managed tool descriptor safe edit modes", + ); + validateExactStringSet( + edit.symbolEditModes, + ["rename", "move", "signature"], + "Managed tool descriptor symbol edit modes", + ); + if (edit.validationRequiredForApply !== true) { + throw new Error("Managed tool descriptor edit validationRequiredForApply must be true"); + } + if (edit.dryRun !== true) throw new Error("Managed tool descriptor edit dryRun must be true"); +} + +export { validateManagedToolEditCapabilities }; + +function validateManagedToolValidationCapabilities(validation: ManagedToolDescriptorCapabilities["validation"]): void { + if (!validation || typeof validation !== "object") + throw new Error("Managed tool descriptor validation capabilities are required"); + validateExactStringSet( + validation.checkRoutes, + ["files", "staged", "changed", "tree", "all", "manifest"], + "Managed tool descriptor check routes", + ); + validateExactStringSet( + validation.validateRoutes, + ["request", "hypothetical", "pre-write", "manifest"], + "Managed tool descriptor validate routes", + ); + validateExactStringSet(validation.scopeModes, validationScopeKinds, "Managed tool descriptor validation scope modes"); + validateExactStringSet(validation.graphModes, graphProviderModes, "Managed tool descriptor validation graph modes"); + if (validation.hypothetical !== true) throw new Error("Managed tool descriptor validation hypothetical must be true"); + validateExactStringSet( + validation.statusSurfaces, + ["status", "doctor"], + "Managed tool descriptor validation status surfaces", + ); + validateRequiredObject( + validation.pythonProjectContext, + "Managed tool descriptor Python project context capability is required", + ); + if (validation.pythonProjectContext.schemaId !== PYTHON_PROJECT_CONTEXT_SCHEMA_ID) { + throw new Error( + `Managed tool descriptor Python project context schemaId must be ${PYTHON_PROJECT_CONTEXT_SCHEMA_ID}`, + ); + } + validateExactStringSet( + validation.pythonProjectContext.outcomes, + pythonProjectContextOutcomes, + "Managed tool descriptor Python project context outcomes", + ); + if (validation.pythonProjectContext.readOnly !== true || validation.pythonProjectContext.installs !== false) { + throw new Error("Managed tool descriptor Python project context must be read-only and no-install"); + } + validateManagedToolValidationWriteGate(validation.writeGate); + validateValidationChecks(validation.checkIds, "Managed tool descriptor validation checkIds"); +} + +export { validateManagedToolValidationCapabilities }; + +function validateManagedToolValidationWriteGate( + writeGate: ManagedToolDescriptorCapabilities["validation"]["writeGate"], +): void { + validateRequiredObject(writeGate, "Managed tool descriptor validation writeGate is required"); + validateExactStringSet(writeGate.initScopes, opcoreInitScopes, "Managed tool descriptor writeGate initScopes"); + validateExactStringSet(writeGate.harnesses, ["claude-code", "codex"], "Managed tool descriptor writeGate harnesses"); + validateManagedToolPackagePath(writeGate.adapterPath, "Managed tool descriptor writeGate adapterPath"); + if (writeGate.adapterPath !== "dist/agent-gate.js") { + throw new Error("Managed tool descriptor writeGate adapterPath must be dist/agent-gate.js"); + } + validateExactStringSequence( + writeGate.validationCommand, + ["opcore", "validate", "pre-write", "--request-file", "", "--timeout-ms", "30000", "--json"], + "Managed tool descriptor writeGate validationCommand", + ); + if (writeGate.adapterErrorPolicy !== "fail_open") { + throw new Error("Managed tool descriptor writeGate adapterErrorPolicy must be fail_open"); + } + if (writeGate.validationErrorPolicy !== "fail_closed") { + throw new Error("Managed tool descriptor writeGate validationErrorPolicy must be fail_closed"); + } + if (writeGate.codexBoundary !== "pretooluse_guardrail") { + throw new Error("Managed tool descriptor writeGate codexBoundary must be pretooluse_guardrail"); + } +} + +export { validateManagedToolValidationWriteGate }; + +interface ManagedToolDescriptorArtifactValidationState { + artifactIds: Set; + artifactChecksumRefs: readonly { artifactId: string; checksumRef: string }[]; +} + +export type { ManagedToolDescriptorArtifactValidationState }; diff --git a/packages/contracts/src/managed/validators-03.ts b/packages/contracts/src/managed/validators-03.ts new file mode 100644 index 0000000..b162f16 --- /dev/null +++ b/packages/contracts/src/managed/validators-03.ts @@ -0,0 +1,267 @@ +import { includesString } from "../shared/primitives.js"; +import { + graphCoreNativePackageNameForTarget, + graphCoreNativeSupportedTargets, +} from "../release/graph-vocabulary-02.js"; +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { validateNonEmptyArray, validateNonEmptyString, validateStringArray } from "../shared/validators-01.js"; +import { + collectStrings, + validateBoolean, + validateExactValue, + validateOptional, + validateRequiredObject, +} from "../shared/validators-02.js"; +import type { + ManagedToolDescriptorArtifactReference, + ManagedToolDescriptorChecksumReference, + ManagedToolDescriptorProvenanceHook} from "./contracts.js"; +import { + managedToolDescriptorArtifactTypes, +} from "./contracts.js"; +import { bundledGraphCoreNativePath } from "./helper-validators.js"; +import type { ManagedToolDescriptorArtifactValidationState } from "./validators-02.js"; + +function validateManagedToolArtifacts( + artifacts: readonly ManagedToolDescriptorArtifactReference[], +): ManagedToolDescriptorArtifactValidationState { + validateNonEmptyArray(artifacts, "Managed tool descriptor artifacts"); + const artifactIds = new Set(); + const artifactChecksumRefs: { artifactId: string; checksumRef: string }[] = []; + for (const artifact of artifacts) { + validateManagedToolArtifact(artifact, artifactIds, artifactChecksumRefs); + } + + for (const target of graphCoreNativeSupportedTargets) validateGraphNativeArtifacts(artifacts, target); + if (!artifacts.some(isPackagedManagedToolDescriptor)) { + throw new Error("Managed tool descriptor must include packaged descriptor artifact"); + } + return { artifactIds, artifactChecksumRefs }; +} + +export { validateManagedToolArtifacts }; + +function validateManagedToolArtifact( + artifact: ManagedToolDescriptorArtifactReference, + artifactIds: Set, + artifactChecksumRefs: { artifactId: string; checksumRef: string }[], +): void { + validateRequiredObject(artifact, "Managed tool descriptor artifact is required"); + validateNonEmptyString(artifact.id, "Managed tool descriptor artifact id"); + if (artifactIds.has(artifact.id)) { + throw new Error(`Managed tool descriptor artifact id must be unique: ${artifact.id}`); + } + artifactIds.add(artifact.id); + validateNonEmptyString(artifact.packageName, "Managed tool descriptor artifact packageName"); + validateManagedToolPackagePath(artifact.path, "Managed tool descriptor artifact path"); + if (!includesString(managedToolDescriptorArtifactTypes, artifact.type)) { + throw new Error(`Unknown managed tool descriptor artifact type: ${String(artifact.type)}`); + } + validateBoolean(artifact.required, "Managed tool descriptor artifact required"); + validateOptional(artifact.checksumRef, (checksumRef) => { + validateNonEmptyString(checksumRef, "Managed tool descriptor artifact checksumRef"); + artifactChecksumRefs.push({ artifactId: artifact.id, checksumRef }); + }); +} + +export { validateManagedToolArtifact }; + +function validateGraphNativeArtifacts( + artifacts: readonly ManagedToolDescriptorArtifactReference[], + target: (typeof graphCoreNativeSupportedTargets)[number], +): void { + const bundledPackage = graphCoreNativePackageNameForTarget(target); + validateGraphNativeArtifact( + artifacts.find((artifact) => artifact.id === `graph-core-binary-${target}`), + { + type: "native_binary", + path: bundledGraphCoreNativePath(bundledPackage, "opcore-graph-core"), + checksumRef: `graph-core-binary-sha256-${target}`, + }, + `Managed tool descriptor must include graph native binary artifact for ${target}`, + ); + validateGraphNativeArtifact( + artifacts.find((artifact) => artifact.id === `graph-core-metadata-${target}`), + { + type: "manifest", + path: bundledGraphCoreNativePath(bundledPackage, "metadata.json"), + }, + `Managed tool descriptor must include graph native metadata artifact for ${target}`, + ); + validateGraphNativeArtifact( + artifacts.find((artifact) => artifact.id === `graph-core-checksum-${target}`), + { + type: "checksum", + path: bundledGraphCoreNativePath(bundledPackage, "opcore-graph-core.sha256"), + }, + `Managed tool descriptor must include graph native checksum artifact for ${target}`, + ); +} + +export { validateGraphNativeArtifacts }; + +interface ExpectedGraphNativeArtifact { + type: ManagedToolDescriptorArtifactReference["type"]; + path: string; + checksumRef?: string; +} + +function validateGraphNativeArtifact( + artifact: ManagedToolDescriptorArtifactReference | undefined, + expected: ExpectedGraphNativeArtifact, + message: string, +): void { + if (!artifact) throw new Error(message); + validateExactValue(artifact.packageName, "opcore", message); + validateExactValue(artifact.type, expected.type, message); + validateExactValue(artifact.required, true, message); + validateExactValue(artifact.path, expected.path, message); + validateOptional(expected.checksumRef, (checksumRef) => + validateExactValue(artifact.checksumRef, checksumRef, message), + ); +} + +export { validateGraphNativeArtifact }; + +function isPackagedManagedToolDescriptor(artifact: ManagedToolDescriptorArtifactReference): boolean { + return ( + artifact.id === "descriptor" && + artifact.packageName === "opcore" && + artifact.path === "dist/descriptors/opcore.managed-tool.json" && + artifact.type === "descriptor" && + artifact.required + ); +} + +export { isPackagedManagedToolDescriptor }; + +function validateManagedToolChecksums( + checksums: readonly ManagedToolDescriptorChecksumReference[], + artifactReferences: ManagedToolDescriptorArtifactValidationState, +): void { + validateNonEmptyArray(checksums, "Managed tool descriptor checksums"); + const checksumIds = new Set(); + for (const checksum of checksums) { + validateManagedToolChecksum(checksum, checksumIds, artifactReferences.artifactIds); + } + for (const target of graphCoreNativeSupportedTargets) { + if ( + !checksums.some( + (checksum) => + checksum.id === `graph-core-binary-sha256-${target}` && + checksum.artifactRef === `graph-core-binary-${target}`, + ) + ) { + throw new Error(`Managed tool descriptor must include graph native checksum reference for ${target}`); + } + } + for (const artifactChecksumRef of artifactReferences.artifactChecksumRefs) { + if (!checksumIds.has(artifactChecksumRef.checksumRef)) { + throw new Error( + `Managed tool descriptor artifact checksumRef must reference a checksum: ${artifactChecksumRef.artifactId} ` + + `-> ${artifactChecksumRef.checksumRef}`, + ); + } + } +} + +export { validateManagedToolChecksums }; + +function validateManagedToolChecksum( + checksum: ManagedToolDescriptorChecksumReference, + checksumIds: Set, + artifactIds: Set, +): void { + validateRequiredObject(checksum, "Managed tool descriptor checksum is required"); + validateNonEmptyString(checksum.id, "Managed tool descriptor checksum id"); + if (checksumIds.has(checksum.id)) { + throw new Error(`Managed tool descriptor checksum id must be unique: ${checksum.id}`); + } + checksumIds.add(checksum.id); + validateNonEmptyString(checksum.packageName, "Managed tool descriptor checksum packageName"); + validateManagedToolPackagePath(checksum.path, "Managed tool descriptor checksum path"); + validateExactValue( + checksum.algorithm, + "sha256", + "Managed tool descriptor checksum algorithm must be sha256", + ); + validateNonEmptyString(checksum.artifactRef, "Managed tool descriptor checksum artifactRef"); + if (!artifactIds.has(checksum.artifactRef)) { + throw new Error(`Managed tool descriptor checksum artifactRef must reference an artifact: ${checksum.artifactRef}`); + } + validateBoolean(checksum.required, "Managed tool descriptor checksum required"); + validateOptional(checksum.value, validateManagedToolChecksumValue); +} + +export { validateManagedToolChecksum }; + +function validateManagedToolChecksumValue(value: string): void { + if (!/^[a-f0-9]{64}$/i.test(value)) { + throw new Error("Managed tool descriptor checksum value must be a sha256 hex digest"); + } +} + +export { validateManagedToolChecksumValue }; + +function validateManagedToolProvenanceHooks(provenanceHooks: readonly ManagedToolDescriptorProvenanceHook[]): void { + validateNonEmptyArray(provenanceHooks, "Managed tool descriptor provenance hooks"); + for (const hook of provenanceHooks) { + if (!hook || typeof hook !== "object") throw new Error("Managed tool descriptor provenance hook is required"); + validateNonEmptyString(hook.id, "Managed tool descriptor provenance hook id"); + validateStringArray(hook.command, "Managed tool descriptor provenance hook command", { allowEmpty: false }); + validateManagedToolCommandTokens(hook.command, "Managed tool descriptor provenance hook command"); + if (hook.expectedExitCode !== 0) + throw new Error("Managed tool descriptor provenance hook expectedExitCode must be 0"); + } +} + +export { validateManagedToolProvenanceHooks }; + +const managedToolPrivateRuntimePathPattern = /(?:^|\/)(?:\.agents|\.claude|\.codex|\.gemini|\.opencode)(?:\/|$)/; + +export { managedToolPrivateRuntimePathPattern }; + +function normalizeManagedToolDescriptorString(value: string): string { + return value.replaceAll("\\", "/"); +} + +export { normalizeManagedToolDescriptorString }; + +function validateManagedToolPackagePath(path: string, label: string): string { + const normalized = validateRepoRelativePath(path); + if (normalized === "~" || normalized.startsWith("~/")) { + throw new Error(`${label} must not reference private home paths`); + } + if (managedToolPrivateRuntimePathPattern.test(normalized)) { + throw new Error(`${label} must not reference private runtime paths`); + } + return normalized; +} + +export { validateManagedToolPackagePath }; + +function validateManagedToolCommandTokens(tokens: readonly string[], label: string): void { + for (const token of tokens) { + validateNonEmptyString(token, label); + const normalizedToken = normalizeManagedToolDescriptorString(token); + if (managedToolPrivateRuntimePathPattern.test(normalizedToken)) { + throw new Error("Managed tool descriptor must not reference private runtime paths"); + } + } +} + +export { validateManagedToolCommandTokens }; + +function validateManagedToolDescriptorForbiddenStrings(value: unknown): void { + for (const text of collectStrings(value)) { + const normalizedText = normalizeManagedToolDescriptorString(text); + if (managedToolPrivateRuntimePathPattern.test(normalizedText)) { + throw new Error("Managed tool descriptor must not reference private runtime paths"); + } + if (normalizedText.includes("/Users/tom")) { + throw new Error("Managed tool descriptor must not reference private paths"); + } + } +} + +export { validateManagedToolDescriptorForbiddenStrings }; diff --git a/packages/contracts/src/product/init-contracts.ts b/packages/contracts/src/product/init-contracts.ts new file mode 100644 index 0000000..bb35ec8 --- /dev/null +++ b/packages/contracts/src/product/init-contracts.ts @@ -0,0 +1,132 @@ +import type { GraphProviderStatusState } from "../graph/vocabulary-01.js"; +import type { PythonProjectContext } from "../validation/python-project-contracts-02.js"; +import type { ValidationResultStatus } from "../validation/vocabulary-01.js"; + +const opcoreInitScopes = ["repo", "global"] as const; + +export { opcoreInitScopes }; + +type OpcoreInitScope = (typeof opcoreInitScopes)[number]; + +export type { OpcoreInitScope }; + +interface OpcoreInitAction { + kind: "write" | "upsert_block" | "create_hook" | "wire_harness" | "restore" | "remove"; + path: string; + targetScope: OpcoreInitScope; + summary: string; + requiresApproval: boolean; + outsideOpcore: boolean; +} + +export type { OpcoreInitAction }; + +interface OpcoreInitScanSummary { + totalFiles: number; + graphSupportedFiles: number; + validationSupportedFiles: number; + validationRetainedFiles: number; + unsupportedFiles: number; + languages: readonly { + language: string; + files: number; + graphSupported: boolean; + validationSupported: boolean; + }[]; + unsupportedStacks: readonly { + extension: string; + language: string; + count: number; + examples: readonly string[]; + }[]; + degradedRustTools: readonly { + adapter: string; + tool: string; + failureMessage?: string; + }[]; + diagnosticCount: number; + validationStatus: ValidationResultStatus; + failedChecks: readonly string[]; + graphState: GraphProviderStatusState; + activationLevel: "ready" | "degraded" | "blocked"; +} + +export type { OpcoreInitScanSummary }; + +interface OpcoreInitLanguageSetting { + language: string; + files: number; + state: "supported" | "retained" | "unsupported" | "degraded"; + graph: "supported" | "unsupported"; + validation: "supported" | "retained" | "unsupported" | "degraded"; + checks: readonly string[]; + notes: readonly string[]; +} + +export type { OpcoreInitLanguageSetting }; + +interface OpcoreInitPythonEnvironment { + dependencyManagers: readonly { + kind: "pyproject" | "requirements" | "pipfile" | "poetry" | "uv"; + path: string; + }[]; + virtualEnvironments: readonly { + kind: "venv"; + path: string; + }[]; + notes: readonly string[]; + contexts?: readonly PythonProjectContext[]; +} + +export type { OpcoreInitPythonEnvironment }; + +interface OpcoreInitSettings { + languages: readonly OpcoreInitLanguageSetting[]; + python?: OpcoreInitPythonEnvironment; +} + +export type { OpcoreInitSettings }; + +interface OpcoreInitInteraction { + tty: boolean; + promptState: "not_requested" | "requested" | "approved" | "declined"; +} + +export type { OpcoreInitInteraction }; + +interface OpcoreInitTiming { + scanMs: number; + planMs: number; + promptMs: number; + applyMs: number; + totalMs: number; + firstOutputMs: number; +} + +export type { OpcoreInitTiming }; + +interface OpcoreInitPlanPayload { + schemaVersion: 1; + mode: "plan" | "apply" | "undo"; + approved: boolean; + repo: { + root: string; + requestedPath: string; + }; + options: { + scope: OpcoreInitScope; + failClosedHook: boolean; + dryRun: boolean; + }; + agentFiles: readonly string[]; + actions: readonly OpcoreInitAction[]; + warnings: readonly string[]; + nextActions: readonly string[]; + undoAvailable: boolean; + scan: OpcoreInitScanSummary; + settings: OpcoreInitSettings; + interaction: OpcoreInitInteraction; + timings: OpcoreInitTiming; +} + +export type { OpcoreInitPlanPayload }; diff --git a/packages/contracts/src/product/init-validators-01.ts b/packages/contracts/src/product/init-validators-01.ts new file mode 100644 index 0000000..6794c91 --- /dev/null +++ b/packages/contracts/src/product/init-validators-01.ts @@ -0,0 +1,180 @@ +import { + includesString, + validateBoolean, + validateOptional, + validateRequiredObject, +} from "../shared/primitives.js"; +import { validateProviderStatus } from "../graph/provider-validators.js"; +import { graphProviderStatusStates } from "../graph/vocabulary-01.js"; +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { + validateNonEmptyArray, + validateNonEmptyString, + validateNonNegativeInteger, + validateStringArray, +} from "../shared/validators-01.js"; +import { validationResultStatuses } from "../validation/vocabulary-01.js"; +import type { OpcoreInitPlanPayload, OpcoreInitScanSummary} from "./init-contracts.js"; +import { opcoreInitScopes } from "./init-contracts.js"; +import { + validateOpcoreInitInteraction, + validateOpcoreInitSettings, + validateOpcoreInitTiming, +} from "./init-validators-02.js"; +import { validateOpcoreInitAction } from "./metrics-validators-02.js"; +import { + validateOpcoreCoverageLanguages, + validateOpcoreUnsupportedStacks, +} from "./metrics-coverage-validators.js"; +import type { OpcoreDoctorPayload, OpcoreRuntimeInfoPayload} from "./status-contracts.js"; +import { opcoreRuntimeArtifactSources } from "./status-contracts.js"; +import { validateOpcoreValidationPolicySummary } from "./status-validators.js"; + +function validateOpcoreRuntimeInfoPayload(payload: OpcoreRuntimeInfoPayload): OpcoreRuntimeInfoPayload { + validateRequiredObject(payload, "Opcore runtime info payload is required"); + if (payload.schemaVersion !== 1) { + throw new Error("Opcore runtime info schemaVersion must be 1"); + } + if (payload.packageName !== "opcore") { + throw new Error("Opcore runtime info packageName must be opcore"); + } + validateNonEmptyString(payload.version, "Opcore runtime info version"); + if (payload.bin !== "opcore") { + throw new Error("Opcore runtime info bin must be opcore"); + } + if (!includesString(opcoreRuntimeArtifactSources, payload.artifactSource)) { + throw new Error(`Unknown Opcore runtime artifact source: ${String(payload.artifactSource)}`); + } + validateNonEmptyString(payload.packageRoot, "Opcore runtime info packageRoot"); + validateNonEmptyString(payload.entrypoint, "Opcore runtime info entrypoint"); + return payload; +} + +export { validateOpcoreRuntimeInfoPayload }; + +function validateOpcoreDoctorPayload(payload: OpcoreDoctorPayload): OpcoreDoctorPayload { + validateRequiredObject(payload, "Opcore doctor payload is required"); + if (payload.schemaVersion !== 1) { + throw new Error("Opcore doctor payload schemaVersion must be 1"); + } + validateOpcoreRuntimeInfoPayload(payload.runtime); + validateRequiredObject(payload.repo, "Opcore doctor repo is required"); + validateNonEmptyString(payload.repo.root, "Opcore doctor repo root"); + validateNonEmptyString(payload.repo.requestedPath, "Opcore doctor repo requestedPath"); + validateRequiredObject(payload.config, "Opcore doctor config is required"); + if (payload.config.path !== ".opcore/config") { + throw new Error("Opcore doctor config path must be .opcore/config"); + } + if (!includesString(["found", "missing", "unreadable"] as const, payload.config.state)) { + throw new Error(`Unknown Opcore doctor config state: ${String(payload.config.state)}`); + } + if (payload.config.message !== undefined) + validateNonEmptyString(payload.config.message, "Opcore doctor config message"); + validateRequiredObject(payload.checks, "Opcore doctor checks are required"); + validateNonNegativeInteger(payload.checks.count, "Opcore doctor checks count"); + validateStringArray(payload.checks.ids, "Opcore doctor checks ids", { + allowEmpty: false, + }); + if (payload.checks.ids.length !== payload.checks.count) { + throw new Error("Opcore doctor checks count must match ids length"); + } + validateOpcoreValidationPolicySummary(payload.policy, "Opcore doctor policy"); + validateProviderStatus(payload.graph); + validateRequiredObject(payload.generatedState, "Opcore doctor generatedState is required"); + validateStringArray(payload.generatedState.ignored, "Opcore doctor generatedState ignored", { allowEmpty: false }); + validateNonEmptyString(payload.generatedState.guidance, "Opcore doctor generatedState guidance"); + validateStringArray(payload.nextActions, "Opcore doctor nextActions", { + allowEmpty: false, + }); + return payload; +} + +export { validateOpcoreDoctorPayload }; + +function validateOpcoreInitPlanPayload(payload: OpcoreInitPlanPayload): OpcoreInitPlanPayload { + validateRequiredObject(payload, "Opcore init payload is required"); + if (payload.schemaVersion !== 1) { + throw new Error("Opcore init payload schemaVersion must be 1"); + } + if (!includesString(["plan", "apply", "undo"] as const, payload.mode)) { + throw new Error(`Unknown Opcore init mode: ${String(payload.mode)}`); + } + validateBoolean(payload.approved, "Opcore init approved"); + validateOpcoreInitApproval(payload); + validateRequiredObject(payload.repo, "Opcore init repo is required"); + validateNonEmptyString(payload.repo.root, "Opcore init repo root"); + validateNonEmptyString(payload.repo.requestedPath, "Opcore init requested path"); + validateRequiredObject(payload.options, "Opcore init options are required"); + if (!includesString(opcoreInitScopes, payload.options.scope)) { + throw new Error(`Unknown Opcore init scope: ${String(payload.options.scope)}`); + } + validateBoolean(payload.options.failClosedHook, "Opcore init failClosedHook option"); + validateBoolean(payload.options.dryRun, "Opcore init dryRun option"); + validateStringArray(payload.agentFiles, "Opcore init agentFiles", { + allowEmpty: true, + }); + for (const agentFile of payload.agentFiles) validateRepoRelativePath(agentFile); + validateNonEmptyArray(payload.actions, "Opcore init actions"); + for (const action of payload.actions) validateOpcoreInitAction(action); + validateStringArray(payload.warnings, "Opcore init warnings", { + allowEmpty: true, + }); + validateStringArray(payload.nextActions, "Opcore init nextActions", { + allowEmpty: false, + }); + validateBoolean(payload.undoAvailable, "Opcore init undoAvailable"); + validateOpcoreInitScanSummary(payload.scan); + validateOpcoreInitSettings(payload.settings); + validateOpcoreInitInteraction(payload.interaction); + validateOpcoreInitTiming(payload.timings); + return payload; +} + +export { validateOpcoreInitPlanPayload }; + +function validateOpcoreInitApproval(payload: OpcoreInitPlanPayload): void { + if (payload.mode === "plan" && payload.approved) { + throw new Error("Opcore init approved plan must use apply mode"); + } + if (payload.mode === "apply" && !payload.approved) { + throw new Error("Opcore init apply mode requires approval"); + } +} + +function validateOpcoreInitScanSummary(scan: OpcoreInitScanSummary): OpcoreInitScanSummary { + validateRequiredObject(scan, "Opcore init scan summary is required"); + validateNonNegativeInteger(scan.totalFiles, "Opcore init scan totalFiles"); + validateNonNegativeInteger(scan.graphSupportedFiles, "Opcore init scan graphSupportedFiles"); + validateNonNegativeInteger(scan.validationSupportedFiles, "Opcore init scan validationSupportedFiles"); + validateNonNegativeInteger(scan.validationRetainedFiles, "Opcore init scan validationRetainedFiles"); + validateNonNegativeInteger(scan.unsupportedFiles, "Opcore init scan unsupportedFiles"); + validateOpcoreCoverageLanguages(scan.languages, "Opcore init scan"); + validateOpcoreUnsupportedStacks(scan.unsupportedStacks, "Opcore init scan"); + if (!Array.isArray(scan.degradedRustTools)) { + throw new Error("Opcore init scan degradedRustTools must be an array"); + } + for (const tool of scan.degradedRustTools) { + validateRequiredObject(tool, "Opcore init scan degraded Rust tool is required"); + validateNonEmptyString(tool.adapter, "Opcore init scan degraded Rust adapter"); + validateNonEmptyString(tool.tool, "Opcore init scan degraded Rust tool"); + validateOptional(tool.failureMessage, (value) => + validateNonEmptyString(value, "Opcore init scan degraded Rust failureMessage"), + ); + } + validateNonNegativeInteger(scan.diagnosticCount, "Opcore init scan diagnosticCount"); + if (!includesString(validationResultStatuses, scan.validationStatus)) { + throw new Error(`Unknown Opcore init scan validationStatus: ${String(scan.validationStatus)}`); + } + validateStringArray(scan.failedChecks, "Opcore init scan failedChecks", { + allowEmpty: true, + }); + if (!includesString(graphProviderStatusStates, scan.graphState)) { + throw new Error(`Unknown Opcore init scan graphState: ${String(scan.graphState)}`); + } + if (!includesString(["ready", "degraded", "blocked"] as const, scan.activationLevel)) { + throw new Error(`Unknown Opcore init scan activationLevel: ${String(scan.activationLevel)}`); + } + return scan; +} + +export { validateOpcoreInitScanSummary }; diff --git a/packages/contracts/src/product/init-validators-02.ts b/packages/contracts/src/product/init-validators-02.ts new file mode 100644 index 0000000..3332311 --- /dev/null +++ b/packages/contracts/src/product/init-validators-02.ts @@ -0,0 +1,109 @@ +import { validateRequiredObject } from "../shared/validators-02.js"; +import { includesString } from "../shared/primitives.js"; +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { + validateNonEmptyString, + validateNonNegativeInteger, + validateNonNegativeNumber, + validateStringArray, + validateValidationChecks, +} from "../shared/validators-01.js"; +import { validatePythonProjectContexts } from "../validation/python-project-validators-01.js"; +import type { + OpcoreInitInteraction, + OpcoreInitLanguageSetting, + OpcoreInitPythonEnvironment, + OpcoreInitSettings, + OpcoreInitTiming, +} from "./init-contracts.js"; + +function validateOpcoreInitSettings(settings: OpcoreInitSettings): OpcoreInitSettings { + validateRequiredObject(settings, "Opcore init settings are required"); + if (!Array.isArray(settings.languages)) { + throw new Error("Opcore init settings languages must be an array"); + } + for (const language of settings.languages) { + validateOpcoreInitLanguageSetting(language); + } + if (settings.python !== undefined) validateOpcoreInitPythonEnvironment(settings.python); + return settings; +} + +export { validateOpcoreInitSettings }; + +function validateOpcoreInitLanguageSetting(setting: OpcoreInitLanguageSetting): OpcoreInitLanguageSetting { + validateRequiredObject(setting, "Opcore init language setting is required"); + validateNonEmptyString(setting.language, "Opcore init language setting language"); + validateNonNegativeInteger(setting.files, "Opcore init language setting files"); + if (!includesString(["supported", "retained", "unsupported", "degraded"] as const, setting.state)) { + throw new Error(`Unknown Opcore init language setting state: ${String(setting.state)}`); + } + if (!includesString(["supported", "unsupported"] as const, setting.graph)) { + throw new Error(`Unknown Opcore init language setting graph: ${String(setting.graph)}`); + } + if (!includesString(["supported", "retained", "unsupported", "degraded"] as const, setting.validation)) { + throw new Error(`Unknown Opcore init language setting validation: ${String(setting.validation)}`); + } + validateValidationChecks(setting.checks, "Opcore init language setting checks"); + validateStringArray(setting.notes, "Opcore init language setting notes", { + allowEmpty: true, + }); + return setting; +} + +export { validateOpcoreInitLanguageSetting }; + +function validateOpcoreInitPythonEnvironment(environment: OpcoreInitPythonEnvironment): OpcoreInitPythonEnvironment { + validateRequiredObject(environment, "Opcore init Python environment is required"); + if (!Array.isArray(environment.dependencyManagers)) { + throw new Error("Opcore init Python dependencyManagers must be an array"); + } + for (const manager of environment.dependencyManagers) { + validateRequiredObject(manager, "Opcore init Python dependency manager is required"); + if (!includesString(["pyproject", "requirements", "pipfile", "poetry", "uv"] as const, manager.kind)) { + throw new Error(`Unknown Opcore init Python dependency manager kind: ${String(manager.kind)}`); + } + validateRepoRelativePath(manager.path); + } + if (!Array.isArray(environment.virtualEnvironments)) { + throw new Error("Opcore init Python virtualEnvironments must be an array"); + } + for (const virtualEnvironment of environment.virtualEnvironments) { + validateRequiredObject(virtualEnvironment, "Opcore init Python virtual environment is required"); + if (virtualEnvironment.kind !== "venv") { + throw new Error(`Unknown Opcore init Python virtual environment kind: ${String(virtualEnvironment.kind)}`); + } + validateRepoRelativePath(virtualEnvironment.path); + } + validateStringArray(environment.notes, "Opcore init Python environment notes", { allowEmpty: true }); + if (environment.contexts !== undefined) validatePythonProjectContexts(environment.contexts); + return environment; +} + +export { validateOpcoreInitPythonEnvironment }; + +function validateOpcoreInitInteraction(interaction: OpcoreInitInteraction): OpcoreInitInteraction { + validateRequiredObject(interaction, "Opcore init interaction is required"); + if (typeof interaction.tty !== "boolean") { + throw new Error("Opcore init interaction tty must be boolean"); + } + if (!includesString(["not_requested", "requested", "approved", "declined"] as const, interaction.promptState)) { + throw new Error(`Unknown Opcore init interaction promptState: ${String(interaction.promptState)}`); + } + return interaction; +} + +export { validateOpcoreInitInteraction }; + +function validateOpcoreInitTiming(timing: OpcoreInitTiming): OpcoreInitTiming { + validateRequiredObject(timing, "Opcore init timings are required"); + validateNonNegativeNumber(timing.scanMs, "Opcore init timing scanMs"); + validateNonNegativeNumber(timing.planMs, "Opcore init timing planMs"); + validateNonNegativeNumber(timing.promptMs, "Opcore init timing promptMs"); + validateNonNegativeNumber(timing.applyMs, "Opcore init timing applyMs"); + validateNonNegativeNumber(timing.totalMs, "Opcore init timing totalMs"); + validateNonNegativeNumber(timing.firstOutputMs, "Opcore init timing firstOutputMs"); + return timing; +} + +export { validateOpcoreInitTiming }; diff --git a/packages/contracts/src/product/latency-contracts.ts b/packages/contracts/src/product/latency-contracts.ts new file mode 100644 index 0000000..24a7cc7 --- /dev/null +++ b/packages/contracts/src/product/latency-contracts.ts @@ -0,0 +1,93 @@ +import type { + CommandOwner, + CommandRouteStatus, + CommandTimingDegradationReason, + CommandTimingProcessState, + LatencyBudgetResultStatus, +} from "../command/vocabulary.js"; +import type { GraphPipelinePhaseTiming } from "../graph/pipeline-contracts.js"; + +type CommandTimingPhase = Pick; + +export type { CommandTimingPhase }; + +interface CommandTiming { + durationMs: number; + phases: readonly CommandTimingPhase[]; + processState: CommandTimingProcessState; + degradations?: readonly CommandTimingDegradationReason[]; +} + +export type { CommandTiming }; + +interface RepoShapeFingerprint { + totalFiles: number; + languages: readonly { + language: string; + files: number; + }[]; + graph: { + supportedFiles: number; + unsupportedFiles: number; + }; + git: { + available: boolean; + clean?: boolean; + }; +} + +export type { RepoShapeFingerprint }; + +interface CommandLatencyRecord { + schemaVersion: 1; + recordedAt: string; + bin: string; + canonicalCommand: readonly string[]; + owner: CommandOwner; + status: CommandRouteStatus; + exitCode: number; + repo: RepoShapeFingerprint; + timing: CommandTiming; + opcoreVersion: string; +} + +export type { CommandLatencyRecord }; + +interface LatencyPhaseBudget { + phase: string; + budgetMs: number; +} + +export type { LatencyPhaseBudget }; + +interface LatencyBudget { + schemaVersion: 1; + canonicalCommand: readonly string[]; + scope: string; + repoShapeBucket: string; + budgetMs: number; + phaseBudgets?: readonly LatencyPhaseBudget[]; +} + +export type { LatencyBudget }; + +interface LatencyBudgetResult { + schemaVersion: 1; + status: LatencyBudgetResultStatus; + budget: LatencyBudget; + observed: { + canonicalCommand: readonly string[]; + phase: string; + durationMs: number; + }; + evidence: { + canonicalCommand: readonly string[]; + phase: string; + repoShapeBucket: string; + observedMs: number; + budgetMs: number; + overByMs: number; + }; +} + +export type { LatencyBudgetResult }; diff --git a/packages/contracts/src/product/metrics-contracts-01.ts b/packages/contracts/src/product/metrics-contracts-01.ts new file mode 100644 index 0000000..50f1af2 --- /dev/null +++ b/packages/contracts/src/product/metrics-contracts-01.ts @@ -0,0 +1,175 @@ +import type { CommandTimingProcessState } from "../command/vocabulary.js"; +import type { GraphProviderMode, GraphProviderStatusState } from "../graph/vocabulary-01.js"; +import type { PythonProjectContext } from "../validation/python-project-contracts-02.js"; +import type { ValidationResultStatus } from "../validation/vocabulary-01.js"; +import type { OpcoreRepoStatePayload, OpcoreValidationPolicySummary } from "./status-contracts.js"; + +interface OpcoreMetricEvidence { + source: string; + path: string; + message: string; + checkId?: string; + code?: string; + line?: number; + column?: number; +} + +export type { OpcoreMetricEvidence }; + +interface OpcoreMetricSignal { + id: string; + title: string; + category: "coverage" | "typescript" | "rust" | "graph" | (string & {}); + severity: "info" | "warning" | "error"; + count: number; + evidence: readonly OpcoreMetricEvidence[]; +} + +export type { OpcoreMetricSignal }; + +interface OpcoreMetricDegradation { + id: string; + title: string; + source: string; + severity: "info" | "warning" | "error"; + message: string; + checkId?: string; + requiredTool?: string; +} + +export type { OpcoreMetricDegradation }; + +interface OpcoreMetricReport { + schemaVersion: 1; + kind: "opcore_metric_report"; + generatedAt: string; + repo: { + root: string; + requestedPath: string; + git: OpcoreRepoStatePayload["repo"]["git"]; + }; + coverage: OpcoreRepoStatePayload["coverage"]; + graph: { + state: GraphProviderStatusState; + mode: GraphProviderMode; + provider: string; + }; + validation: { + status?: ValidationResultStatus; + diagnosticCount: number; + checkCount: number; + policy?: OpcoreValidationPolicySummary; + pythonProjectContexts?: readonly PythonProjectContext[]; + }; + signals: readonly OpcoreMetricSignal[]; + degradations: readonly OpcoreMetricDegradation[]; + warnings: readonly string[]; + nextActions: readonly string[]; +} + +export type { OpcoreMetricReport }; + +interface OpcoreMetricHistoryEntry { + schemaVersion: 1; + kind: "opcore_metric_history_entry"; + recordedAt: string; + report: OpcoreMetricReport; +} + +export type { OpcoreMetricHistoryEntry }; + +interface OpcoreMeasureSignalCount { + id: string; + title: string; + count: number; +} + +export type { OpcoreMeasureSignalCount }; + +interface OpcoreMeasureSignalDelta { + id: string; + title: string; + currentCount: number; + comparisonCount: number; + delta: number; +} + +export type { OpcoreMeasureSignalDelta }; + +const opcoreMeasureLatencyStatuses = ["ok", "slower", "over_budget"] as const; + +export { opcoreMeasureLatencyStatuses }; + +type OpcoreMeasureLatencyStatus = (typeof opcoreMeasureLatencyStatuses)[number]; + +export type { OpcoreMeasureLatencyStatus }; + +const opcoreMeasureLatencyFindingStatuses = ["slower", "over_budget"] as const; + +export { opcoreMeasureLatencyFindingStatuses }; + +type OpcoreMeasureLatencyFindingStatus = (typeof opcoreMeasureLatencyFindingStatuses)[number]; + +export type { OpcoreMeasureLatencyFindingStatus }; + +interface OpcoreMeasureLatencyPhase { + phase: string; + durationMs: number; +} + +export type { OpcoreMeasureLatencyPhase }; + +interface OpcoreMeasureLatencyFinding { + canonicalCommand: readonly string[]; + repoShapeBucket: string; + processState: CommandTimingProcessState; + status: OpcoreMeasureLatencyFindingStatus; + currentDurationMs: number; + dominantPhase?: OpcoreMeasureLatencyPhase; + baselineDurationMs?: number; + previousDurationMs?: number; + baselineDeltaMs?: number; + previousDeltaMs?: number; + budgetMs?: number; + overBudgetMs?: number; +} + +export type { OpcoreMeasureLatencyFinding }; + +interface OpcoreMeasureLatencyReport { + kind: "opcore_latency_report"; + recordCount: number; + budgetCount: number; + findings: readonly OpcoreMeasureLatencyFinding[]; +} + +export type { OpcoreMeasureLatencyReport }; + +interface OpcoreMeasureComparison { + recordedAt: string; + generatedAt: string; + coverage: OpcoreMetricReport["coverage"]; + signals: readonly OpcoreMeasureSignalCount[]; + deltas: readonly OpcoreMeasureSignalDelta[]; +} + +export type { OpcoreMeasureComparison }; + +interface OpcoreMeasureDelta { + schemaVersion: 1; + kind: "opcore_measure_delta"; + generatedAt: string; + current: { + generatedAt: string; + coverage: OpcoreMetricReport["coverage"]; + signals: readonly OpcoreMeasureSignalCount[]; + }; + latency?: OpcoreMeasureLatencyReport; + baseline?: OpcoreMeasureComparison; + previous?: OpcoreMeasureComparison; + warnings: readonly string[]; + degradations: readonly OpcoreMetricDegradation[]; + nextActions: readonly string[]; +} + +export type { OpcoreMeasureDelta }; diff --git a/packages/contracts/src/product/metrics-contracts-02.ts b/packages/contracts/src/product/metrics-contracts-02.ts new file mode 100644 index 0000000..12a9454 --- /dev/null +++ b/packages/contracts/src/product/metrics-contracts-02.ts @@ -0,0 +1,46 @@ +import type { CommandOwner, CommandRouteStatus } from "../command/vocabulary.js"; + +interface OpcoreTrySignalSummary { + id: string; + title: string; + count: number; + delta: number; +} + +export type { OpcoreTrySignalSummary }; + +interface OpcoreTryScenario { + id: string; + repoRoot: string; + title: string; + commands: readonly string[]; + coverage: { + totalFiles: number; + validationSupportedFiles: number; + unsupportedFiles: number; + }; + signals: readonly OpcoreTrySignalSummary[]; +} + +export type { OpcoreTryScenario }; + +interface OpcoreTryCommandSummary { + scenarioId: string; + command: readonly string[]; + canonicalCommand: readonly string[]; + owner: CommandOwner; + status: CommandRouteStatus; + exitCode: number; +} + +export type { OpcoreTryCommandSummary }; + +interface OpcoreTryPayload { + schemaVersion: 1; + sampleRoot: string; + published: false; + scenarios: readonly OpcoreTryScenario[]; + commands: readonly OpcoreTryCommandSummary[]; +} + +export type { OpcoreTryPayload }; diff --git a/packages/contracts/src/product/metrics-coverage-validators.ts b/packages/contracts/src/product/metrics-coverage-validators.ts new file mode 100644 index 0000000..561dd33 --- /dev/null +++ b/packages/contracts/src/product/metrics-coverage-validators.ts @@ -0,0 +1,68 @@ +import { validateRequiredObject } from "../shared/validators-02.js"; +import { + validateNonEmptyString, + validateNonNegativeInteger, + validateStringArray, +} from "../shared/validators-01.js"; +import { validateOpcoreCoverageCounts } from "./metrics-validators-05.js"; +import type { OpcoreRepoStatePayload } from "./status-contracts.js"; + +function validateOpcoreMetricCoverage(coverage: OpcoreRepoStatePayload["coverage"], label: string): void { + validateRequiredObject(coverage, `${label} is required`); + validateNonNegativeInteger(coverage.totalFiles, `${label} totalFiles`); + validateOpcoreCoverageLanguages(coverage.languages, label); + validateOpcoreCoverageCounts(coverage.graph, "graph"); + validateOpcoreCoverageCounts(coverage.validation, "validation"); + validateNonNegativeInteger(coverage.validation.retainedFiles, `${label} validation retainedFiles`); + validateOpcoreUnsupportedSection(coverage.unsupported, label); +} + +export { validateOpcoreMetricCoverage }; + +function validateOpcoreCoverageLanguages( + languages: OpcoreRepoStatePayload["coverage"]["languages"], + label: string, +): void { + if (!Array.isArray(languages)) { + throw new Error(`${label} languages must be an array`); + } + for (const language of languages) { + validateNonEmptyString(language.language, `${label} language`); + validateNonNegativeInteger(language.files, `${label} language files`); + if (typeof language.graphSupported !== "boolean" || typeof language.validationSupported !== "boolean") { + throw new Error(`${label} language support flags must be boolean`); + } + } +} + +export { validateOpcoreCoverageLanguages }; + +function validateOpcoreUnsupportedSection( + unsupported: OpcoreRepoStatePayload["coverage"]["unsupported"], + label: string, +): void { + validateRequiredObject(unsupported, `${label} unsupported is required`); + validateNonNegativeInteger(unsupported.totalFiles, `${label} unsupported totalFiles`); + validateOpcoreUnsupportedStacks(unsupported.stacks, label); +} + +export { validateOpcoreUnsupportedSection }; + +function validateOpcoreUnsupportedStacks( + stacks: OpcoreRepoStatePayload["coverage"]["unsupported"]["stacks"], + label: string, +): void { + if (!Array.isArray(stacks)) { + throw new Error(`${label} unsupported stacks must be an array`); + } + for (const stack of stacks) { + validateNonEmptyString(stack.extension, `${label} unsupported extension`); + validateNonEmptyString(stack.language, `${label} unsupported language`); + validateNonNegativeInteger(stack.count, `${label} unsupported count`); + validateStringArray(stack.examples, `${label} unsupported examples`, { + allowEmpty: true, + }); + } +} + +export { validateOpcoreUnsupportedStacks }; diff --git a/packages/contracts/src/product/metrics-validators-01.ts b/packages/contracts/src/product/metrics-validators-01.ts new file mode 100644 index 0000000..c082a8f --- /dev/null +++ b/packages/contracts/src/product/metrics-validators-01.ts @@ -0,0 +1,125 @@ +import { validateRequiredObject } from "../shared/validators-02.js"; +import { validateCommandOwner, validateCommandRouteStatus } from "../command/helper-validators.js"; +import { commandTimingDegradationReasons, commandTimingProcessStates } from "../command/vocabulary.js"; +import { includesString } from "../shared/primitives.js"; +import { + validateExitCodeForStatus, + validateNonEmptyString, + validateNonNegativeInteger, + validateNonNegativeNumber, + validateStringArray, +} from "../shared/validators-01.js"; +import type { CommandLatencyRecord, CommandTiming, LatencyBudget, RepoShapeFingerprint } from "./latency-contracts.js"; +import { + assertNoOpaqueScoreFields, + assertNoTelemetrySourceFields, + validateCommandTimingPhase, + validateLatencyCanonicalCommand, + validateLatencyPhaseBudget, + validateLatencyStableId, + validateLatencyTelemetryCommandBin, +} from "./metrics-validators-05.js"; + +function validateCommandTiming(timing: CommandTiming): CommandTiming { + assertNoOpaqueScoreFields(timing, "Command timing"); + assertNoTelemetrySourceFields(timing, "Command timing"); + validateRequiredObject(timing, "Command timing is required"); + validateNonNegativeNumber(timing.durationMs, "Command timing durationMs"); + if (!Array.isArray(timing.phases)) { + throw new Error("Command timing phases must be an array"); + } + for (const phase of timing.phases) validateCommandTimingPhase(phase); + if (!includesString(commandTimingProcessStates, timing.processState)) { + throw new Error(`Unknown command timing processState: ${String(timing.processState)}`); + } + if (timing.degradations !== undefined) { + validateStringArray(timing.degradations, "Command timing degradations", { + allowEmpty: true, + }); + for (const degradation of timing.degradations) { + if (!includesString(commandTimingDegradationReasons, degradation)) { + throw new Error(`Unknown command timing degradation: ${String(degradation)}`); + } + } + } + return timing; +} + +export { validateCommandTiming }; + +function validateRepoShapeFingerprint(fingerprint: RepoShapeFingerprint): RepoShapeFingerprint { + assertNoOpaqueScoreFields(fingerprint, "Repo shape fingerprint"); + assertNoTelemetrySourceFields(fingerprint, "Repo shape fingerprint"); + validateRequiredObject(fingerprint, "Repo shape fingerprint is required"); + validateNonNegativeInteger(fingerprint.totalFiles, "Repo shape fingerprint totalFiles"); + if (!Array.isArray(fingerprint.languages)) { + throw new Error("Repo shape fingerprint languages must be an array"); + } + for (const language of fingerprint.languages) { + validateNonEmptyString(language.language, "Repo shape fingerprint language"); + validateNonNegativeInteger(language.files, "Repo shape fingerprint language files"); + } + validateRequiredObject(fingerprint.graph, "Repo shape fingerprint graph is required"); + validateNonNegativeInteger(fingerprint.graph.supportedFiles, "Repo shape fingerprint graph supportedFiles"); + validateNonNegativeInteger(fingerprint.graph.unsupportedFiles, "Repo shape fingerprint graph unsupportedFiles"); + validateRequiredObject(fingerprint.git, "Repo shape fingerprint git is required"); + if (typeof fingerprint.git.available !== "boolean") { + throw new Error("Repo shape fingerprint git available must be boolean"); + } + if (fingerprint.git.clean !== undefined && typeof fingerprint.git.clean !== "boolean") { + throw new Error("Repo shape fingerprint git clean must be boolean"); + } + return fingerprint; +} + +export { validateRepoShapeFingerprint }; + +function validateCommandLatencyRecord(record: CommandLatencyRecord): CommandLatencyRecord { + assertNoOpaqueScoreFields(record, "Command latency record"); + assertNoTelemetrySourceFields(record, "Command latency record"); + validateRequiredObject(record, "Command latency record is required"); + if (record.schemaVersion !== 1) { + throw new Error("Command latency record schemaVersion must be 1"); + } + validateNonEmptyString(record.recordedAt, "Command latency record recordedAt"); + validateLatencyTelemetryCommandBin(record.bin, "Command latency record bin"); + validateLatencyCanonicalCommand(record.canonicalCommand, "Command latency record canonicalCommand"); + validateCommandOwner(record.owner); + const status = validateCommandRouteStatus(record.status); + validateExitCodeForStatus(record.exitCode, status); + validateRepoShapeFingerprint(record.repo); + validateCommandTiming(record.timing); + validateNonEmptyString(record.opcoreVersion, "Command latency record opcoreVersion"); + return record; +} + +export { validateCommandLatencyRecord }; + +function validateLatencyBudget(budget: LatencyBudget): LatencyBudget { + assertNoOpaqueScoreFields(budget, "Latency budget"); + assertNoTelemetrySourceFields(budget, "Latency budget"); + validateRequiredObject(budget, "Latency budget is required"); + if (budget.schemaVersion !== 1) { + throw new Error("Latency budget schemaVersion must be 1"); + } + validateLatencyCanonicalCommand(budget.canonicalCommand, "Latency budget canonicalCommand"); + validateLatencyStableId(budget.scope, "Latency budget scope"); + validateLatencyStableId(budget.repoShapeBucket, "Latency budget repoShapeBucket"); + validateNonNegativeNumber(budget.budgetMs, "Latency budget budgetMs"); + if (budget.phaseBudgets !== undefined) { + if (!Array.isArray(budget.phaseBudgets)) { + throw new Error("Latency budget phaseBudgets must be an array"); + } + const phases = new Set(); + for (const phaseBudget of budget.phaseBudgets) { + const validatedPhaseBudget = validateLatencyPhaseBudget(phaseBudget); + if (phases.has(validatedPhaseBudget.phase)) { + throw new Error("Latency budget phaseBudgets must not include duplicate phases"); + } + phases.add(validatedPhaseBudget.phase); + } + } + return budget; +} + +export { validateLatencyBudget }; diff --git a/packages/contracts/src/product/metrics-validators-02.ts b/packages/contracts/src/product/metrics-validators-02.ts new file mode 100644 index 0000000..a763314 --- /dev/null +++ b/packages/contracts/src/product/metrics-validators-02.ts @@ -0,0 +1,212 @@ +import { + validateBoolean, + validateOptional, + validateRequiredObject, +} from "../shared/validators-02.js"; +import { latencyBudgetResultStatuses } from "../command/vocabulary.js"; +import { includesString, sameStringArray } from "../shared/primitives.js"; +import { graphProviderModes, graphProviderStatusStates } from "../graph/vocabulary-01.js"; +import { validateHomeRelativePath, validateRepoRelativePath } from "../shared/path-validators.js"; +import { + validateNonEmptyString, + validateNonNegativeInteger, + validateNonNegativeNumber, + validateStringArray, +} from "../shared/validators-01.js"; +import { validatePythonProjectContexts } from "../validation/python-project-validators-01.js"; +import { validationResultStatuses } from "../validation/vocabulary-01.js"; +import type { OpcoreInitAction} from "./init-contracts.js"; +import { opcoreInitScopes } from "./init-contracts.js"; +import type { LatencyBudgetResult } from "./latency-contracts.js"; +import type { OpcoreMetricReport } from "./metrics-contracts-01.js"; +import { validateOpcoreMetricCoverage } from "./metrics-coverage-validators.js"; +import { validateLatencyBudget } from "./metrics-validators-01.js"; +import { validateOpcoreMetricDegradation, validateOpcoreMetricSignal } from "./metrics-validators-04.js"; +import { + assertNoOpaqueScoreFields, + assertNoTelemetrySourceFields, + resolveLatencyAppliedBudgetMs, + validateLatencyCanonicalCommand, + validateLatencyStableId, +} from "./metrics-validators-05.js"; +import { validateOpcoreValidationPolicySummary } from "./status-validators.js"; + +function validateLatencyBudgetResult(result: LatencyBudgetResult): LatencyBudgetResult { + assertNoOpaqueScoreFields(result, "Latency budget result"); + assertNoTelemetrySourceFields(result, "Latency budget result"); + validateRequiredObject(result, "Latency budget result is required"); + if (result.schemaVersion !== 1) { + throw new Error("Latency budget result schemaVersion must be 1"); + } + if (!includesString(latencyBudgetResultStatuses, result.status)) { + throw new Error(`Unknown latency budget result status: ${String(result.status)}`); + } + const budget = validateLatencyBudget(result.budget); + validateLatencyBudgetResultEvidence(result); + validateLatencyBudgetResultConsistency(result, budget); + validateLatencyBudgetResultStatus(result); + return result; +} + +export { validateLatencyBudgetResult }; + +function validateLatencyBudgetResultEvidence(result: LatencyBudgetResult): void { + validateRequiredObject(result.observed, "Latency budget result observed is required"); + validateLatencyCanonicalCommand(result.observed.canonicalCommand, "Latency budget result observed canonicalCommand"); + validateLatencyStableId(result.observed.phase, "Latency budget result observed phase"); + validateNonNegativeNumber(result.observed.durationMs, "Latency budget result observed durationMs"); + validateRequiredObject(result.evidence, "Latency budget result evidence is required"); + validateLatencyCanonicalCommand(result.evidence.canonicalCommand, "Latency budget result evidence canonicalCommand"); + validateLatencyStableId(result.evidence.phase, "Latency budget result evidence phase"); + validateLatencyStableId(result.evidence.repoShapeBucket, "Latency budget result evidence repoShapeBucket"); + validateNonNegativeNumber(result.evidence.observedMs, "Latency budget result evidence observedMs"); + validateNonNegativeNumber(result.evidence.budgetMs, "Latency budget result evidence budgetMs"); + validateNonNegativeNumber(result.evidence.overByMs, "Latency budget result evidence overByMs"); +} + +function validateLatencyBudgetResultConsistency( + result: LatencyBudgetResult, + budget: ReturnType, +): void { + if (!sameStringArray(result.observed.canonicalCommand, result.evidence.canonicalCommand)) { + throw new Error("Latency budget result observed and evidence commands must match"); + } + if (!sameStringArray(budget.canonicalCommand, result.evidence.canonicalCommand)) { + throw new Error("Latency budget result evidence command must match budget command"); + } + if (result.observed.phase !== result.evidence.phase) { + throw new Error("Latency budget result observed and evidence phases must match"); + } + if (budget.repoShapeBucket !== result.evidence.repoShapeBucket) { + throw new Error("Latency budget result evidence bucket must match budget bucket"); + } + if (result.observed.durationMs !== result.evidence.observedMs) { + throw new Error("Latency budget result observed duration must match evidence observedMs"); + } + const appliedBudgetMs = resolveLatencyAppliedBudgetMs(budget, result.evidence.phase); + if (result.evidence.budgetMs !== appliedBudgetMs) { + throw new Error("Latency budget result evidence budgetMs must match the applied budget"); + } + const computedOverByMs = Math.max(0, result.evidence.observedMs - appliedBudgetMs); + if (result.evidence.overByMs !== computedOverByMs) { + throw new Error("Latency budget result overByMs must equal observedMs over budgetMs"); + } +} + +function validateLatencyBudgetResultStatus(result: LatencyBudgetResult): void { + if (result.status === "pass" && result.evidence.overByMs !== 0) { + throw new Error("Latency budget pass result must not exceed budget"); + } + if (result.status === "over" && result.evidence.overByMs <= 0) { + throw new Error("Latency budget over result must exceed budget"); + } +} + +function validateOpcoreInitAction(action: OpcoreInitAction): OpcoreInitAction { + validateRequiredObject(action, "Opcore init action is required"); + if ( + !includesString(["write", "upsert_block", "create_hook", "wire_harness", "restore", "remove"] as const, action.kind) + ) { + throw new Error(`Unknown Opcore init action kind: ${String(action.kind)}`); + } + if (!includesString(opcoreInitScopes, action.targetScope)) { + throw new Error(`Unknown Opcore init action targetScope: ${String(action.targetScope)}`); + } + const rawPath = validateNonEmptyString(action.path, "Opcore init action path"); + const path = action.targetScope === "global" ? validateHomeRelativePath(rawPath) : validateRepoRelativePath(rawPath); + validateNonEmptyString(action.summary, "Opcore init action summary"); + validateBoolean(action.requiresApproval, "Opcore init action requiresApproval"); + validateBoolean(action.outsideOpcore, "Opcore init action outsideOpcore"); + const insideOpcore = isOpcoreInitPath(action.targetScope, path); + if (action.outsideOpcore === insideOpcore) { + throw new Error("Opcore init action outsideOpcore must match action path"); + } + if (action.outsideOpcore && !action.requiresApproval) { + throw new Error("Opcore init action outside .opcore requires approval"); + } + return action; +} + +export { validateOpcoreInitAction }; + +function isOpcoreInitPath(scope: OpcoreInitAction["targetScope"], path: string): boolean { + if (scope === "global") return path === "~/.opcore" || path.startsWith("~/.opcore/"); + return path === ".opcore" || path.startsWith(".opcore/"); +} + +function validateOpcoreMetricReport(report: OpcoreMetricReport): OpcoreMetricReport { + assertNoOpaqueScoreFields(report, "Opcore metric report"); + validateRequiredObject(report, "Opcore metric report is required"); + if (report.schemaVersion !== 1) { + throw new Error("Opcore metric report schemaVersion must be 1"); + } + if (report.kind !== "opcore_metric_report") { + throw new Error("Opcore metric report kind must be opcore_metric_report"); + } + validateNonEmptyString(report.generatedAt, "Opcore metric report generatedAt"); + validateOpcoreMetricReportRepo(report); + validateOpcoreMetricCoverage(report.coverage, "Opcore metric report coverage"); + validateOpcoreMetricReportGraph(report); + validateOpcoreMetricReportValidation(report); + validateOpcoreMetricReportFindings(report); + validateStringArray(report.warnings, "Opcore metric report warnings", { + allowEmpty: true, + }); + validateStringArray(report.nextActions, "Opcore metric report nextActions", { + allowEmpty: false, + }); + return report; +} + +export { validateOpcoreMetricReport }; + +function validateOpcoreMetricReportRepo(report: OpcoreMetricReport): void { + validateRequiredObject(report.repo, "Opcore metric report repo is required"); + validateNonEmptyString(report.repo.root, "Opcore metric report repo root"); + validateNonEmptyString(report.repo.requestedPath, "Opcore metric report repo requestedPath"); + validateRequiredObject(report.repo.git, "Opcore metric report repo git is required"); + validateBoolean(report.repo.git.available, "Opcore metric report repo git available"); + validateOptional(report.repo.git.branch, (value) => + validateNonEmptyString(value, "Opcore metric report git branch"), + ); + for (const [key, value] of Object.entries(report.repo.git)) { + if (key === "available" || key === "branch" || key === "clean") continue; + validateNonNegativeInteger(value, `Opcore metric report git ${key}`); + } + validateOptional(report.repo.git.clean, (value) => validateBoolean(value, "Opcore metric report git clean")); +} + +function validateOpcoreMetricReportGraph(report: OpcoreMetricReport): void { + validateRequiredObject(report.graph, "Opcore metric report graph is required"); + if (!includesString(graphProviderStatusStates, report.graph.state)) { + throw new Error(`Unknown Opcore metric graph state: ${String(report.graph.state)}`); + } + if (!includesString(graphProviderModes, report.graph.mode)) { + throw new Error(`Unknown Opcore metric graph mode: ${String(report.graph.mode)}`); + } + validateNonEmptyString(report.graph.provider, "Opcore metric report graph provider"); +} + +function validateOpcoreMetricReportValidation(report: OpcoreMetricReport): void { + validateRequiredObject(report.validation, "Opcore metric report validation is required"); + if (report.validation.status !== undefined && !includesString(validationResultStatuses, report.validation.status)) { + throw new Error(`Unknown Opcore metric validation status: ${String(report.validation.status)}`); + } + validateNonNegativeInteger(report.validation.diagnosticCount, "Opcore metric report diagnosticCount"); + validateNonNegativeInteger(report.validation.checkCount, "Opcore metric report checkCount"); + validateOptional(report.validation.policy, (value) => + validateOpcoreValidationPolicySummary(value, "Opcore metric report validation policy"), + ); + validateOptional(report.validation.pythonProjectContexts, validatePythonProjectContexts); +} + +function validateOpcoreMetricReportFindings(report: OpcoreMetricReport): void { + if (!Array.isArray(report.signals)) { + throw new Error("Opcore metric report signals must be an array"); + } + for (const signal of report.signals) validateOpcoreMetricSignal(signal); + if (!Array.isArray(report.degradations)) { + throw new Error("Opcore metric report degradations must be an array"); + } + for (const degradation of report.degradations) validateOpcoreMetricDegradation(degradation); +} diff --git a/packages/contracts/src/product/metrics-validators-03.ts b/packages/contracts/src/product/metrics-validators-03.ts new file mode 100644 index 0000000..61a25cd --- /dev/null +++ b/packages/contracts/src/product/metrics-validators-03.ts @@ -0,0 +1,144 @@ +import { validateRequiredObject } from "../shared/validators-02.js"; +import { validateCommandOwner, validateCommandRouteStatus } from "../command/helper-validators.js"; +import { + validateExitCodeForStatus, + validateNonEmptyArray, + validateNonEmptyString, + validateNonNegativeInteger, + validateStringArray, +} from "../shared/validators-01.js"; +import type { OpcoreMeasureDelta, OpcoreMetricHistoryEntry } from "./metrics-contracts-01.js"; +import type { + OpcoreTryCommandSummary, + OpcoreTryPayload, + OpcoreTryScenario, + OpcoreTrySignalSummary, +} from "./metrics-contracts-02.js"; +import { validateOpcoreMetricCoverage } from "./metrics-coverage-validators.js"; +import { validateOpcoreMetricReport } from "./metrics-validators-02.js"; +import { + validateOpcoreMeasureComparison, + validateOpcoreMeasureLatencyReport, + validateOpcoreMeasureSignalCounts, + validateOpcoreMetricDegradation, +} from "./metrics-validators-04.js"; +import { assertNoOpaqueScoreFields } from "./metrics-validators-05.js"; + +function validateOpcoreMetricHistoryEntry(entry: OpcoreMetricHistoryEntry): OpcoreMetricHistoryEntry { + assertNoOpaqueScoreFields(entry, "Opcore metric history entry"); + validateRequiredObject(entry, "Opcore metric history entry is required"); + if (entry.schemaVersion !== 1) { + throw new Error("Opcore metric history entry schemaVersion must be 1"); + } + if (entry.kind !== "opcore_metric_history_entry") { + throw new Error("Opcore metric history entry kind must be opcore_metric_history_entry"); + } + validateNonEmptyString(entry.recordedAt, "Opcore metric history entry recordedAt"); + validateOpcoreMetricReport(entry.report); + return entry; +} + +export { validateOpcoreMetricHistoryEntry }; + +function validateOpcoreMeasureDelta(delta: OpcoreMeasureDelta): OpcoreMeasureDelta { + assertNoOpaqueScoreFields(delta, "Opcore measure delta"); + validateRequiredObject(delta, "Opcore measure delta is required"); + if (delta.schemaVersion !== 1) { + throw new Error("Opcore measure delta schemaVersion must be 1"); + } + if (delta.kind !== "opcore_measure_delta") { + throw new Error("Opcore measure delta kind must be opcore_measure_delta"); + } + validateNonEmptyString(delta.generatedAt, "Opcore measure delta generatedAt"); + validateRequiredObject(delta.current, "Opcore measure delta current is required"); + validateNonEmptyString(delta.current.generatedAt, "Opcore measure delta current generatedAt"); + validateOpcoreMetricCoverage(delta.current.coverage, "Opcore measure delta current coverage"); + validateOpcoreMeasureSignalCounts(delta.current.signals, "Opcore measure delta current signals"); + if (delta.latency !== undefined) validateOpcoreMeasureLatencyReport(delta.latency); + if (delta.baseline !== undefined) validateOpcoreMeasureComparison(delta.baseline, "baseline"); + if (delta.previous !== undefined) validateOpcoreMeasureComparison(delta.previous, "previous"); + validateStringArray(delta.warnings, "Opcore measure delta warnings", { + allowEmpty: true, + }); + if (!Array.isArray(delta.degradations)) { + throw new Error("Opcore measure delta degradations must be an array"); + } + for (const degradation of delta.degradations) validateOpcoreMetricDegradation(degradation); + validateStringArray(delta.nextActions, "Opcore measure delta nextActions", { + allowEmpty: false, + }); + return delta; +} + +export { validateOpcoreMeasureDelta }; + +function validateOpcoreTryPayload(payload: OpcoreTryPayload): OpcoreTryPayload { + assertNoOpaqueScoreFields(payload, "Opcore try payload"); + validateRequiredObject(payload, "Opcore try payload is required"); + if (payload.schemaVersion !== 1) { + throw new Error("Opcore try payload schemaVersion must be 1"); + } + validateNonEmptyString(payload.sampleRoot, "Opcore try sampleRoot"); + if (payload.published !== false) { + throw new Error("Opcore try published must be false"); + } + validateNonEmptyArray(payload.scenarios, "Opcore try scenarios"); + for (const scenario of payload.scenarios) validateOpcoreTryScenario(scenario); + validateNonEmptyArray(payload.commands, "Opcore try commands"); + for (const command of payload.commands) validateOpcoreTryCommand(command); + return payload; +} + +export { validateOpcoreTryPayload }; + +function validateOpcoreTryScenario(scenario: OpcoreTryScenario): OpcoreTryScenario { + validateRequiredObject(scenario, "Opcore try scenario is required"); + validateNonEmptyString(scenario.id, "Opcore try scenario id"); + validateNonEmptyString(scenario.repoRoot, "Opcore try scenario repoRoot"); + validateNonEmptyString(scenario.title, "Opcore try scenario title"); + validateStringArray(scenario.commands, "Opcore try scenario commands", { + allowEmpty: false, + }); + validateRequiredObject(scenario.coverage, "Opcore try scenario coverage is required"); + validateNonNegativeInteger(scenario.coverage.totalFiles, "Opcore try scenario totalFiles"); + validateNonNegativeInteger( + scenario.coverage.validationSupportedFiles, + "Opcore try scenario validationSupportedFiles", + ); + validateNonNegativeInteger(scenario.coverage.unsupportedFiles, "Opcore try scenario unsupportedFiles"); + if (!Array.isArray(scenario.signals)) { + throw new Error("Opcore try scenario signals must be an array"); + } + for (const signal of scenario.signals) validateOpcoreTrySignal(signal); + return scenario; +} + +export { validateOpcoreTryScenario }; + +function validateOpcoreTrySignal(signal: OpcoreTrySignalSummary): OpcoreTrySignalSummary { + validateRequiredObject(signal, "Opcore try signal is required"); + validateNonEmptyString(signal.id, "Opcore try signal id"); + validateNonEmptyString(signal.title, "Opcore try signal title"); + validateNonNegativeInteger(signal.count, "Opcore try signal count"); + if (!Number.isInteger(signal.delta)) { + throw new Error("Opcore try signal delta must be an integer"); + } + return signal; +} + +export { validateOpcoreTrySignal }; + +function validateOpcoreTryCommand(command: OpcoreTryCommandSummary): OpcoreTryCommandSummary { + validateRequiredObject(command, "Opcore try command is required"); + validateNonEmptyString(command.scenarioId, "Opcore try command scenarioId"); + validateStringArray(command.command, "Opcore try command", { + allowEmpty: false, + }); + validateStringArray(command.canonicalCommand, "Opcore try command canonicalCommand", { allowEmpty: false }); + validateCommandOwner(command.owner); + validateCommandRouteStatus(command.status); + validateExitCodeForStatus(command.exitCode, command.status); + return command; +} + +export { validateOpcoreTryCommand }; diff --git a/packages/contracts/src/product/metrics-validators-04.ts b/packages/contracts/src/product/metrics-validators-04.ts new file mode 100644 index 0000000..a7a27bf --- /dev/null +++ b/packages/contracts/src/product/metrics-validators-04.ts @@ -0,0 +1,203 @@ +import { validateOptional, validateRequiredObject } from "../shared/validators-02.js"; +import { commandTimingProcessStates } from "../command/vocabulary.js"; +import { includesString } from "../shared/primitives.js"; +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { + validateNonEmptyString, + validateNonNegativeInteger, + validateNonNegativeNumber, + validateValidationCheckId, +} from "../shared/validators-01.js"; +import type { + OpcoreMeasureComparison, + OpcoreMeasureLatencyFinding, + OpcoreMeasureLatencyPhase, + OpcoreMeasureLatencyReport, + OpcoreMeasureSignalCount, + OpcoreMeasureSignalDelta, + OpcoreMetricDegradation, + OpcoreMetricEvidence, + OpcoreMetricSignal} from "./metrics-contracts-01.js"; +import { + opcoreMeasureLatencyFindingStatuses, +} from "./metrics-contracts-01.js"; +import { validateOpcoreMetricCoverage } from "./metrics-coverage-validators.js"; +import { + assertNoOpaqueScoreFields, + assertNoTelemetrySourceFields, + validateLatencyCanonicalCommand, + validateLatencyStableId, +} from "./metrics-validators-05.js"; + +function validateOpcoreMetricSignal(signal: OpcoreMetricSignal): OpcoreMetricSignal { + validateRequiredObject(signal, "Opcore metric signal is required"); + validateNonEmptyString(signal.id, "Opcore metric signal id"); + validateNonEmptyString(signal.title, "Opcore metric signal title"); + validateNonEmptyString(signal.category, "Opcore metric signal category"); + if (!includesString(["info", "warning", "error"] as const, signal.severity)) { + throw new Error(`Unknown Opcore metric signal severity: ${String(signal.severity)}`); + } + if (!Number.isInteger(signal.count) || signal.count <= 0) { + throw new Error("Opcore metric signal count must be a positive integer"); + } + if (!Array.isArray(signal.evidence) || signal.evidence.length === 0) { + throw new Error("Opcore metric signal evidence must be a non-empty array"); + } + for (const evidence of signal.evidence) validateOpcoreMetricEvidence(evidence); + return signal; +} + +export { validateOpcoreMetricSignal }; + +function validateOpcoreMetricEvidence(evidence: OpcoreMetricEvidence): OpcoreMetricEvidence { + validateRequiredObject(evidence, "Opcore metric evidence is required"); + validateNonEmptyString(evidence.source, "Opcore metric evidence source"); + validateRepoRelativePath(validateNonEmptyString(evidence.path, "Opcore metric evidence path")); + validateNonEmptyString(evidence.message, "Opcore metric evidence message"); + if (evidence.checkId !== undefined) validateValidationCheckId(evidence.checkId, "Opcore metric evidence checkId"); + if (evidence.code !== undefined) validateNonEmptyString(evidence.code, "Opcore metric evidence code"); + if (evidence.line !== undefined && (!Number.isInteger(evidence.line) || evidence.line < 1)) { + throw new Error("Opcore metric evidence line must be a positive integer"); + } + if (evidence.column !== undefined && (!Number.isInteger(evidence.column) || evidence.column < 1)) { + throw new Error("Opcore metric evidence column must be a positive integer"); + } + return evidence; +} + +export { validateOpcoreMetricEvidence }; + +function validateOpcoreMetricDegradation(degradation: OpcoreMetricDegradation): OpcoreMetricDegradation { + validateRequiredObject(degradation, "Opcore metric degradation is required"); + validateNonEmptyString(degradation.id, "Opcore metric degradation id"); + validateNonEmptyString(degradation.title, "Opcore metric degradation title"); + validateNonEmptyString(degradation.source, "Opcore metric degradation source"); + if (!includesString(["info", "warning", "error"] as const, degradation.severity)) { + throw new Error(`Unknown Opcore metric degradation severity: ${String(degradation.severity)}`); + } + validateNonEmptyString(degradation.message, "Opcore metric degradation message"); + if (degradation.checkId !== undefined) + validateValidationCheckId(degradation.checkId, "Opcore metric degradation checkId"); + if (degradation.requiredTool !== undefined) { + validateNonEmptyString(degradation.requiredTool, "Opcore metric degradation requiredTool"); + } + return degradation; +} + +export { validateOpcoreMetricDegradation }; + +function validateOpcoreMeasureLatencyReport(report: OpcoreMeasureLatencyReport): OpcoreMeasureLatencyReport { + assertNoOpaqueScoreFields(report, "Opcore measure latency report"); + assertNoTelemetrySourceFields(report, "Opcore measure latency report"); + validateRequiredObject(report, "Opcore measure latency report is required"); + if (report.kind !== "opcore_latency_report") { + throw new Error("Opcore measure latency report kind must be opcore_latency_report"); + } + validateNonNegativeInteger(report.recordCount, "Opcore measure latency report recordCount"); + validateNonNegativeInteger(report.budgetCount, "Opcore measure latency report budgetCount"); + if (!Array.isArray(report.findings)) { + throw new Error("Opcore measure latency report findings must be an array"); + } + for (const finding of report.findings) validateOpcoreMeasureLatencyFinding(finding); + return report; +} + +export { validateOpcoreMeasureLatencyReport }; + +function validateOpcoreMeasureLatencyFinding(finding: OpcoreMeasureLatencyFinding): OpcoreMeasureLatencyFinding { + assertNoOpaqueScoreFields(finding, "Opcore measure latency finding"); + assertNoTelemetrySourceFields(finding, "Opcore measure latency finding"); + validateRequiredObject(finding, "Opcore measure latency finding is required"); + validateLatencyCanonicalCommand(finding.canonicalCommand, "Opcore measure latency finding canonicalCommand"); + validateLatencyStableId(finding.repoShapeBucket, "Opcore measure latency finding repoShapeBucket"); + if (!includesString(commandTimingProcessStates, finding.processState)) { + throw new Error(`Unknown Opcore measure latency processState: ${String(finding.processState)}`); + } + if (!includesString(opcoreMeasureLatencyFindingStatuses, finding.status)) { + throw new Error(`Unknown Opcore measure latency status: ${String(finding.status)}`); + } + validateNonNegativeNumber(finding.currentDurationMs, "Opcore measure latency finding currentDurationMs"); + validateOptional(finding.dominantPhase, validateOpcoreMeasureLatencyPhase); + validateOptional(finding.baselineDurationMs, (value) => + validateNonNegativeNumber(value, "Opcore measure latency finding baselineDurationMs"), + ); + validateOptional(finding.previousDurationMs, (value) => + validateNonNegativeNumber(value, "Opcore measure latency finding previousDurationMs"), + ); + validateOptional(finding.baselineDeltaMs, (value) => + validateFiniteLatencyDelta(value, "baselineDeltaMs"), + ); + validateOptional(finding.previousDeltaMs, (value) => + validateFiniteLatencyDelta(value, "previousDeltaMs"), + ); + validateOptional(finding.budgetMs, (value) => + validateNonNegativeNumber(value, "Opcore measure latency finding budgetMs"), + ); + validateOptional(finding.overBudgetMs, (value) => + validateNonNegativeNumber(value, "Opcore measure latency finding overBudgetMs"), + ); + if (finding.status === "over_budget" && (finding.overBudgetMs ?? 0) <= 0) { + throw new Error("Opcore measure latency over_budget finding must include overBudgetMs"); + } + return finding; +} + +export { validateOpcoreMeasureLatencyFinding }; + +function validateFiniteLatencyDelta(value: number, field: "baselineDeltaMs" | "previousDeltaMs"): void { + if (!Number.isFinite(value)) { + throw new Error(`Opcore measure latency finding ${field} must be a finite number`); + } +} + +function validateOpcoreMeasureLatencyPhase(phase: OpcoreMeasureLatencyPhase): OpcoreMeasureLatencyPhase { + validateRequiredObject(phase, "Opcore measure latency phase is required"); + validateLatencyStableId(phase.phase, "Opcore measure latency phase"); + validateNonNegativeNumber(phase.durationMs, "Opcore measure latency phase durationMs"); + return phase; +} + +export { validateOpcoreMeasureLatencyPhase }; + +function validateOpcoreMeasureComparison(comparison: OpcoreMeasureComparison, label: string): OpcoreMeasureComparison { + validateRequiredObject(comparison, `Opcore measure delta ${label} comparison is required`); + validateNonEmptyString(comparison.recordedAt, `Opcore measure delta ${label} recordedAt`); + validateNonEmptyString(comparison.generatedAt, `Opcore measure delta ${label} generatedAt`); + validateOpcoreMetricCoverage(comparison.coverage, `Opcore measure delta ${label} coverage`); + validateOpcoreMeasureSignalCounts(comparison.signals, `Opcore measure delta ${label} signals`); + if (!Array.isArray(comparison.deltas)) { + throw new Error(`Opcore measure delta ${label} deltas must be an array`); + } + for (const entry of comparison.deltas) validateOpcoreMeasureSignalDelta(entry, label); + return comparison; +} + +export { validateOpcoreMeasureComparison }; + +function validateOpcoreMeasureSignalCounts(counts: readonly OpcoreMeasureSignalCount[], label: string): void { + if (!Array.isArray(counts)) { + throw new Error(`${label} must be an array`); + } + for (const count of counts) { + validateRequiredObject(count, `${label} entry is required`); + validateNonEmptyString(count.id, `${label} id`); + validateNonEmptyString(count.title, `${label} title`); + validateNonNegativeInteger(count.count, `${label} count`); + } +} + +export { validateOpcoreMeasureSignalCounts }; + +function validateOpcoreMeasureSignalDelta(delta: OpcoreMeasureSignalDelta, label: string): OpcoreMeasureSignalDelta { + validateRequiredObject(delta, `Opcore measure delta ${label} entry is required`); + validateNonEmptyString(delta.id, `Opcore measure delta ${label} id`); + validateNonEmptyString(delta.title, `Opcore measure delta ${label} title`); + validateNonNegativeInteger(delta.currentCount, `Opcore measure delta ${label} currentCount`); + validateNonNegativeInteger(delta.comparisonCount, `Opcore measure delta ${label} comparisonCount`); + if (!Number.isInteger(delta.delta)) { + throw new Error(`Opcore measure delta ${label} delta must be an integer`); + } + return delta; +} + +export { validateOpcoreMeasureSignalDelta }; diff --git a/packages/contracts/src/product/metrics-validators-05.ts b/packages/contracts/src/product/metrics-validators-05.ts new file mode 100644 index 0000000..cc0fe89 --- /dev/null +++ b/packages/contracts/src/product/metrics-validators-05.ts @@ -0,0 +1,164 @@ +import { validateRequiredObject } from "../shared/validators-02.js"; +import type { CommandLatencyTelemetryBin} from "../command/vocabulary.js"; +import { commandLatencyTelemetryBins } from "../command/vocabulary.js"; +import { includesString } from "../shared/primitives.js"; +import { + validateNonEmptyString, + validateNonNegativeInteger, + validateNonNegativeNumber, + validateStringArray, +} from "../shared/validators-01.js"; +import { latencyStableIdRegex, latencyTelemetryCommandTokenRegex } from "../validation/vocabulary-02.js"; +import { latencyTelemetrySourceFileExtensionRegex } from "../validation/vocabulary-03.js"; +import type { CommandTimingPhase, LatencyBudget, LatencyPhaseBudget } from "./latency-contracts.js"; +import type { OpcoreRepoStatePayload } from "./status-contracts.js"; + +function assertNoOpaqueScoreFields(value: unknown, label: string): void { + if (!value || typeof value !== "object") return; + if (Array.isArray(value)) { + for (const entry of value) assertNoOpaqueScoreFields(entry, label); + return; + } + for (const [key, entry] of Object.entries(value)) { + if (key === "score" || key === "blendedScore") { + throw new Error(`${label} must not include opaque score fields`); + } + assertNoOpaqueScoreFields(entry, label); + } +} + +export { assertNoOpaqueScoreFields }; + +function assertNoTelemetrySourceFields(value: unknown, label: string): void { + const blockedKeys = new Set([ + "root", + "requestedPath", + "path", + "paths", + "examples", + "content", + "contents", + "source", + "secret", + "secrets", + "token", + "tokens", + "apiKey", + "password", + ]); + visitTelemetryValue(value); + + function visitTelemetryValue(entry: unknown): void { + if (!entry || typeof entry !== "object") return; + if (Array.isArray(entry)) { + for (const item of entry) visitTelemetryValue(item); + return; + } + for (const [key, child] of Object.entries(entry)) { + if (blockedKeys.has(key)) { + throw new Error(`${label} must remain source-safe and must not include ${key}`); + } + visitTelemetryValue(child); + } + } +} + +export { assertNoTelemetrySourceFields }; + +function validateCommandTimingPhase(phase: CommandTimingPhase): CommandTimingPhase { + validateRequiredObject(phase, "Command timing phase is required"); + validateLatencyStableId(phase.phase, "Command timing phase"); + validateNonNegativeNumber(phase.durationMs, "Command timing phase durationMs"); + if (phase.fileCount !== undefined) validateNonNegativeInteger(phase.fileCount, "Command timing phase fileCount"); + return phase; +} + +export { validateCommandTimingPhase }; + +function validateLatencyPhaseBudget(phaseBudget: LatencyPhaseBudget): LatencyPhaseBudget { + validateRequiredObject(phaseBudget, "Latency phase budget is required"); + validateLatencyStableId(phaseBudget.phase, "Latency phase budget phase"); + validateNonNegativeNumber(phaseBudget.budgetMs, "Latency phase budget budgetMs"); + return phaseBudget; +} + +export { validateLatencyPhaseBudget }; + +function resolveLatencyAppliedBudgetMs(budget: LatencyBudget, phase: string): number { + if (phase === "total") return budget.budgetMs; + const phaseBudget = budget.phaseBudgets?.find((entry) => entry.phase === phase); + if (!phaseBudget) { + throw new Error("Latency budget result phase must match total or a configured phase budget"); + } + return phaseBudget.budgetMs; +} + +export { resolveLatencyAppliedBudgetMs }; + +function validateLatencyStableId(value: unknown, label: string): string { + const stableId = validateNonEmptyString(value, label); + if (!latencyStableIdRegex.test(stableId)) { + throw new Error(`${label} must be a stable latency id`); + } + return stableId; +} + +export { validateLatencyStableId }; + +function validateLatencyTelemetryCommandBin(value: unknown, label: string): CommandLatencyTelemetryBin { + const bin = validateNonEmptyString(value, label); + if (!includesString(commandLatencyTelemetryBins, bin)) { + throw new Error(`${label} must be a source-safe command bin`); + } + return bin; +} + +export { validateLatencyTelemetryCommandBin }; + +function validateLatencyCanonicalCommand(command: readonly string[], label: string): readonly string[] { + const parts = validateStringArray(command, label, { allowEmpty: false }); + for (const [index, part] of parts.entries()) { + validateLatencyCanonicalCommandToken(part, `${label} entry ${index}`); + } + return parts; +} + +export { validateLatencyCanonicalCommand }; + +function validateLatencyCanonicalCommandToken(value: string, label: string): string { + if (!latencyTelemetryCommandTokenRegex.test(value)) { + throw new Error(`${label} must be a source-safe canonicalCommand token`); + } + if ( + value.includes("/") || + value.includes("\\") || + value === "." || + value === ".." || + value.startsWith("~") || + /^[A-Za-z]:/.test(value) || + /^file:/i.test(value) || + latencyTelemetrySourceFileExtensionRegex.test(value) + ) { + throw new Error(`${label} must be a source-safe canonicalCommand token`); + } + return value; +} + +export { validateLatencyCanonicalCommandToken }; + +function validateOpcoreCoverageCounts( + section: OpcoreRepoStatePayload["coverage"]["graph"] | OpcoreRepoStatePayload["coverage"]["validation"], + label: string, +): void { + validateRequiredObject(section, `Opcore repo state ${label} coverage is required`); + validateNonNegativeInteger(section.supportedFiles, `Opcore repo state ${label} supportedFiles`); + if (!Array.isArray(section.extensions)) { + throw new Error(`Opcore repo state ${label} extensions must be an array`); + } + for (const entry of section.extensions) { + validateNonEmptyString(entry.extension, `Opcore repo state ${label} extension`); + validateNonNegativeInteger(entry.count, `Opcore repo state ${label} count`); + } +} + +export { validateOpcoreCoverageCounts }; diff --git a/packages/contracts/src/product/status-contracts.ts b/packages/contracts/src/product/status-contracts.ts new file mode 100644 index 0000000..ca36067 --- /dev/null +++ b/packages/contracts/src/product/status-contracts.ts @@ -0,0 +1,154 @@ +import type { GraphProviderStatus } from "../graph/provider-contracts-02.js"; +import type { GraphProviderMode, GraphProviderStatusState } from "../graph/vocabulary-01.js"; +import type { PythonProjectContext } from "../validation/python-project-contracts-02.js"; +import type { ValidationAdapterRuntimeState } from "../validation/status-contracts.js"; + +interface OpcoreRepoStatePayload { + schemaVersion: 1; + repo: { + root: string; + requestedPath: string; + git: { + available: boolean; + branch?: string; + changed?: number; + staged?: number; + unstaged?: number; + untracked?: number; + conflicted?: number; + clean?: boolean; + }; + }; + coverage: { + totalFiles: number; + languages: readonly { + language: string; + files: number; + graphSupported: boolean; + validationSupported: boolean; + }[]; + graph: { + supportedFiles: number; + extensions: readonly { + extension: string; + count: number; + }[]; + }; + validation: { + supportedFiles: number; + retainedFiles: number; + extensions: readonly { + extension: string; + count: number; + }[]; + }; + unsupported: { + totalFiles: number; + stacks: readonly { + extension: string; + language: string; + count: number; + examples: readonly string[]; + }[]; + }; + }; + graph: { + state: GraphProviderStatusState; + mode: GraphProviderMode; + provider: string; + action: string; + message?: string; + status: GraphProviderStatus; + }; + validation: { + ready: boolean; + checkCount: number; + policy: OpcoreValidationPolicySummary; + adapters: readonly { + adapter: string; + status: ValidationAdapterRuntimeState; + checkCount: number; + degradedChecks: readonly string[]; + missingTools: readonly string[]; + }[]; + degradedToolchains: readonly { + adapter: string; + tool: string; + failureMessage?: string; + }[]; + pythonProjectContexts?: readonly PythonProjectContext[]; + }; + activation: { + ready: boolean; + level: "ready" | "degraded" | "blocked"; + summary: string; + asp: { + state: "enrolled" | "not_enrolled"; + paths: readonly string[]; + }; + }; + warnings: readonly string[]; + blockers: readonly string[]; + nextActions: readonly string[]; +} + +export type { OpcoreRepoStatePayload }; + +interface OpcoreValidationPolicySummary { + path: ".opcore/config"; + state: "missing" | "loaded"; + adapters: readonly string[]; + packs: readonly string[]; + disabledChecks: readonly string[]; + defaultChecks: readonly string[]; + configuredChecks: readonly string[]; +} + +export type { OpcoreValidationPolicySummary }; + +const opcoreRuntimeArtifactSources = ["source_checkout", "installed_package", "unknown"] as const; + +export { opcoreRuntimeArtifactSources }; + +type OpcoreRuntimeArtifactSource = (typeof opcoreRuntimeArtifactSources)[number]; + +export type { OpcoreRuntimeArtifactSource }; + +interface OpcoreRuntimeInfoPayload { + schemaVersion: 1; + packageName: "opcore"; + version: string; + bin: "opcore"; + artifactSource: OpcoreRuntimeArtifactSource; + packageRoot: string; + entrypoint: string; +} + +export type { OpcoreRuntimeInfoPayload }; + +interface OpcoreDoctorPayload { + schemaVersion: 1; + runtime: OpcoreRuntimeInfoPayload; + repo: { + root: string; + requestedPath: string; + }; + config: { + path: ".opcore/config"; + state: "found" | "missing" | "unreadable"; + message?: string; + }; + checks: { + count: number; + ids: readonly string[]; + }; + policy: OpcoreValidationPolicySummary; + graph: GraphProviderStatus; + generatedState: { + ignored: readonly string[]; + guidance: string; + }; + nextActions: readonly string[]; +} + +export type { OpcoreDoctorPayload }; diff --git a/packages/contracts/src/product/status-validators.ts b/packages/contracts/src/product/status-validators.ts new file mode 100644 index 0000000..7417cb3 --- /dev/null +++ b/packages/contracts/src/product/status-validators.ts @@ -0,0 +1,180 @@ +import { validateRequiredObject } from "../shared/validators-02.js"; +import { includesString } from "../shared/primitives.js"; +import { validateProviderStatus } from "../graph/provider-validators.js"; +import { graphProviderModes, graphProviderStatusStates } from "../graph/vocabulary-01.js"; +import { validateNonEmptyString, validateNonNegativeInteger, validateStringArray } from "../shared/validators-01.js"; +import { validateArray, validateBoolean, validateObject, validateOptional } from "../shared/validators-02.js"; +import { validatePythonProjectContexts } from "../validation/python-project-validators-01.js"; +import { validationAdapterRuntimeStates } from "../validation/status-contracts.js"; +import { validateOpcoreCoverageCounts } from "./metrics-validators-05.js"; +import type { OpcoreRepoStatePayload, OpcoreValidationPolicySummary } from "./status-contracts.js"; + +function validateOpcoreRepoStatePayload(payload: OpcoreRepoStatePayload): OpcoreRepoStatePayload { + validateObject(payload, "Opcore repo state payload"); + if (payload.schemaVersion !== 1) { + throw new Error("Opcore repo state payload schemaVersion must be 1"); + } + validateOpcoreRepo(payload.repo); + validateOpcoreCoverage(payload.coverage); + validateOpcoreGraph(payload.graph); + validateOpcoreValidation(payload.validation); + validateOpcoreActivation(payload.activation); + validateStringArray(payload.warnings, "Opcore repo state warnings", { allowEmpty: true }); + validateStringArray(payload.blockers, "Opcore repo state blockers", { allowEmpty: true }); + validateStringArray(payload.nextActions, "Opcore repo state nextActions", { allowEmpty: false }); + return payload; +} + +export { validateOpcoreRepoStatePayload }; + +function validateOpcoreRepo(repo: OpcoreRepoStatePayload["repo"]): void { + validateObject(repo, "Opcore repo state repo"); + validateNonEmptyString(repo.root, "Opcore repo state repo root"); + validateNonEmptyString(repo.requestedPath, "Opcore repo state requested path"); + validateObject(repo.git, "Opcore repo state git payload"); + validateBoolean(repo.git.available, "Opcore repo state git available"); + validateOptional(repo.git.branch, (branch) => validateNonEmptyString(branch, "Opcore repo state git branch")); + for (const [key, value] of Object.entries(repo.git)) { + if (key === "available" || key === "branch" || key === "clean") continue; + validateNonNegativeInteger(value, `Opcore repo state git ${key}`); + } + validateOptional(repo.git.clean, (clean) => validateBoolean(clean, "Opcore repo state git clean")); +} + +export { validateOpcoreRepo }; + +function validateOpcoreCoverage(coverage: OpcoreRepoStatePayload["coverage"]): void { + validateObject(coverage, "Opcore repo state coverage"); + validateNonNegativeInteger(coverage.totalFiles, "Opcore repo state coverage totalFiles"); + validateArray(coverage.languages, "Opcore repo state coverage languages"); + for (const language of coverage.languages) validateOpcoreLanguageCoverage(language); + validateOpcoreCoverageCounts(coverage.graph, "graph"); + validateOpcoreCoverageCounts(coverage.validation, "validation"); + validateNonNegativeInteger(coverage.validation.retainedFiles, "Opcore repo state validation retainedFiles"); + validateObject(coverage.unsupported, "Opcore repo state unsupported coverage"); + validateNonNegativeInteger(coverage.unsupported.totalFiles, "Opcore repo state unsupported totalFiles"); + validateArray(coverage.unsupported.stacks, "Opcore repo state unsupported stacks"); + for (const stack of coverage.unsupported.stacks) validateOpcoreUnsupportedStack(stack); +} + +export { validateOpcoreCoverage }; + +function validateOpcoreLanguageCoverage(language: OpcoreRepoStatePayload["coverage"]["languages"][number]): void { + validateNonEmptyString(language.language, "Opcore repo state language"); + validateNonNegativeInteger(language.files, "Opcore repo state language files"); + validateBoolean(language.graphSupported, "Opcore repo state language graphSupported"); + validateBoolean(language.validationSupported, "Opcore repo state language validationSupported"); +} + +export { validateOpcoreLanguageCoverage }; + +function validateOpcoreUnsupportedStack( + stack: OpcoreRepoStatePayload["coverage"]["unsupported"]["stacks"][number], +): void { + validateNonEmptyString(stack.extension, "Opcore repo state unsupported extension"); + validateNonEmptyString(stack.language, "Opcore repo state unsupported language"); + validateNonNegativeInteger(stack.count, "Opcore repo state unsupported count"); + validateStringArray(stack.examples, "Opcore repo state unsupported examples", { allowEmpty: true }); +} + +export { validateOpcoreUnsupportedStack }; + +function validateOpcoreGraph(graph: OpcoreRepoStatePayload["graph"]): void { + validateObject(graph, "Opcore repo state graph"); + if (!includesString(graphProviderStatusStates, graph.state)) { + throw new Error(`Unknown Opcore repo state graph state: ${String(graph.state)}`); + } + if (!includesString(graphProviderModes, graph.mode)) { + throw new Error(`Unknown Opcore repo state graph mode: ${String(graph.mode)}`); + } + validateNonEmptyString(graph.provider, "Opcore repo state graph provider"); + validateNonEmptyString(graph.action, "Opcore repo state graph action"); + validateOptional(graph.message, (message) => validateNonEmptyString(message, "Opcore repo state graph message")); + const graphStatus = validateProviderStatus(graph.status); + if (graphStatus.state !== graph.state || graphStatus.mode !== graph.mode || graphStatus.provider !== graph.provider) { + throw new Error("Opcore repo state graph summary must match provider status"); + } +} + +export { validateOpcoreGraph }; + +function validateOpcoreValidation(validation: OpcoreRepoStatePayload["validation"]): void { + validateObject(validation, "Opcore repo state validation"); + validateBoolean(validation.ready, "Opcore repo state validation ready"); + validateNonNegativeInteger(validation.checkCount, "Opcore repo state validation checkCount"); + validateOpcoreValidationPolicySummary(validation.policy, "Opcore repo state validation policy"); + validateArray(validation.adapters, "Opcore repo state validation adapters"); + for (const adapter of validation.adapters) validateOpcoreAdapter(adapter); + validateArray(validation.degradedToolchains, "Opcore repo state validation degradedToolchains"); + for (const tool of validation.degradedToolchains) validateOpcoreDegradedTool(tool); + validateOptional(validation.pythonProjectContexts, validatePythonProjectContexts); +} + +export { validateOpcoreValidation }; + +function validateOpcoreAdapter(adapter: OpcoreRepoStatePayload["validation"]["adapters"][number]): void { + validateNonEmptyString(adapter.adapter, "Opcore repo state validation adapter"); + if (!includesString(validationAdapterRuntimeStates, adapter.status)) { + throw new Error(`Unknown Opcore validation adapter status: ${String(adapter.status)}`); + } + validateNonNegativeInteger(adapter.checkCount, "Opcore repo state validation adapter checkCount"); + validateStringArray(adapter.degradedChecks, "Opcore repo state validation degradedChecks", { allowEmpty: true }); + validateStringArray(adapter.missingTools, "Opcore repo state validation missingTools", { allowEmpty: true }); +} + +export { validateOpcoreAdapter }; + +function validateOpcoreDegradedTool(tool: OpcoreRepoStatePayload["validation"]["degradedToolchains"][number]): void { + validateNonEmptyString(tool.adapter, "Opcore repo state validation degraded adapter"); + validateNonEmptyString(tool.tool, "Opcore repo state validation degraded tool"); + validateOptional(tool.failureMessage, (message) => + validateNonEmptyString(message, "Opcore repo state validation degraded failureMessage"), + ); +} + +export { validateOpcoreDegradedTool }; + +function validateOpcoreActivation(activation: OpcoreRepoStatePayload["activation"]): void { + validateObject(activation, "Opcore repo state activation"); + validateBoolean(activation.ready, "Opcore repo state activation ready"); + if (!includesString(["ready", "degraded", "blocked"] as const, activation.level)) { + throw new Error(`Unknown Opcore activation level: ${String(activation.level)}`); + } + validateNonEmptyString(activation.summary, "Opcore repo state activation summary"); + validateObject(activation.asp, "Opcore repo state ASP status"); + if (!includesString(["enrolled", "not_enrolled"] as const, activation.asp.state)) { + throw new Error(`Unknown Opcore ASP state: ${String(activation.asp.state)}`); + } + validateStringArray(activation.asp.paths, "Opcore repo state ASP paths", { allowEmpty: true }); +} + +export { validateOpcoreActivation }; + +function validateOpcoreValidationPolicySummary( + summary: OpcoreValidationPolicySummary, + label: string, +): OpcoreValidationPolicySummary { + validateRequiredObject(summary, `${label} is required`); + if (summary.path !== ".opcore/config") { + throw new Error(`${label} path must be .opcore/config`); + } + if (!includesString(["missing", "loaded"] as const, summary.state)) { + throw new Error(`Unknown ${label} state: ${String(summary.state)}`); + } + validateStringArray(summary.adapters, `${label} adapters`, { + allowEmpty: true, + }); + validateStringArray(summary.packs, `${label} packs`, { allowEmpty: true }); + validateStringArray(summary.disabledChecks, `${label} disabledChecks`, { + allowEmpty: true, + }); + validateStringArray(summary.defaultChecks, `${label} defaultChecks`, { + allowEmpty: true, + }); + validateStringArray(summary.configuredChecks, `${label} configuredChecks`, { + allowEmpty: true, + }); + return summary; +} + +export { validateOpcoreValidationPolicySummary }; diff --git a/packages/contracts/src/release/asp-contracts-01.ts b/packages/contracts/src/release/asp-contracts-01.ts new file mode 100644 index 0000000..cd27c37 --- /dev/null +++ b/packages/contracts/src/release/asp-contracts-01.ts @@ -0,0 +1,189 @@ +import type { OpcoreSelfValidationReceipt, ReleaseCutoverInstalledPackageEvidence } from "./cutover-contracts.js"; +import type { ReleaseReceiptPackageName } from "./vocabulary-01.js"; +import type { AspDogfoodForbiddenProviderMarker, AspDogfoodUnsupportedSurfaceId } from "./vocabulary-02.js"; + +interface AspDogfoodManagerEvidence { + bootstrapSource: "local-sibling"; + aspRepoPath: string; + aspBinPath: string; + cliPath: string; + commitSha: string; +} + +export type { AspDogfoodManagerEvidence }; + +interface AspDogfoodAspHomeEvidence { + path: string; + temp: true; + isolated: true; + sharedStateMutated: false; + pathSanitized: true; +} + +export type { AspDogfoodAspHomeEvidence }; + +interface AspDogfoodHostFixtureEvidence { + repo: string; + temp: true; + sourceRepoMutated: false; + baselineCommitted: true; + changedPaths: readonly string[]; +} + +export type { AspDogfoodHostFixtureEvidence }; + +interface AspDogfoodCommandRunReceipt { + id: string; + command: readonly string[]; + status: "passed" | "failed" | "retained-not-run"; + exitCode: number | null; + stdoutSha256: string; + stderrSha256: string; + output?: unknown; + assertion: string; +} + +export type { AspDogfoodCommandRunReceipt }; + +interface AspDogfoodProviderManifestEvidence { + manifestPath: string; + manifestSha256: string; + manifest: unknown; +} + +export type { AspDogfoodProviderManifestEvidence }; + +interface AspDogfoodProviderEvidence { + providerId: "opcore"; + packageName: "opcore"; + binPath: string; + indexPath: string; + indexSha256: string; + command: readonly ["opcore-asp-provider", "--stdio"]; + entrypoint: { + transport: "stdio"; + bin: string; + args: readonly ["--stdio"]; + }; + manifest: AspDogfoodProviderManifestEvidence; +} + +export type { AspDogfoodProviderEvidence }; + +interface AspDogfoodRepoEnrollmentEvidence { + repo: string; + mode: "advisory" | "shadow"; + repoAdd: AspDogfoodCommandRunReceipt; + repoEnable: AspDogfoodCommandRunReceipt; + repoStatus: AspDogfoodCommandRunReceipt; +} + +export type { AspDogfoodRepoEnrollmentEvidence }; + +interface AspDogfoodManagerStateEvidence { + status: AspDogfoodCommandRunReceipt; + serverAdd: AspDogfoodCommandRunReceipt; + serverStatus: AspDogfoodCommandRunReceipt; +} + +export type { AspDogfoodManagerStateEvidence }; + +interface AspDogfoodHostCheckEvidence extends AspDogfoodCommandRunReceipt { + hostDecision: unknown; + receipt: unknown; + assurance: { + mode: string; + transactionGuarantee: string; + }; +} + +export type { AspDogfoodHostCheckEvidence }; + +interface AspDogfoodHostEvaluationEvidence { + check: AspDogfoodHostCheckEvidence; + ciVerify?: AspDogfoodCommandRunReceipt; +} + +export type { AspDogfoodHostEvaluationEvidence }; + +interface AspDogfoodProviderProbeEvidence extends AspDogfoodCommandRunReceipt { + assessment: unknown; + validAsOf: unknown; + coverage: unknown; + diagnosticsCount: number; + hostOwnedFieldLeak: false; +} + +export type { AspDogfoodProviderProbeEvidence }; + +interface AspDogfoodUnsupportedSurfaceEvidence { + surface: AspDogfoodUnsupportedSurfaceId; + status: "degraded" | "parity-blocker"; + cleanCoverage: false; + blocker: string; +} + +export type { AspDogfoodUnsupportedSurfaceEvidence }; + +interface AspDogfoodParityBlocker { + source: string; + detail: string; +} + +export type { AspDogfoodParityBlocker }; + +interface AspDogfoodAuthorityEvidence { + hostOwnsDecisions: true; + providerOutputIsHostDecision: false; + localAuthorityOverride: { + present: false; + sharedAuthorityWeakened: false; + }; +} + +export type { AspDogfoodAuthorityEvidence }; + +interface AspDogfoodForbiddenMarkerScan { + scannedTextCount: number; + findingCount: 0; + markersBlocked: readonly AspDogfoodForbiddenProviderMarker[]; +} + +export type { AspDogfoodForbiddenMarkerScan }; + +interface AspDogfoodReceiptIdentity { + schemaVersion: 1; + issue: "#120"; + origin: "covibes-authored-asp-dogfood-proof"; + generatedAt: string; + commitSha: string; + privateRepo: true; + bootstrapSource: "local-sibling"; + packageNames: readonly ReleaseReceiptPackageName[]; + installedPackages: readonly ReleaseCutoverInstalledPackageEvidence[]; +} + +export type { AspDogfoodReceiptIdentity }; + +interface AspDogfoodReceiptEvidence { + manager: AspDogfoodManagerEvidence; + aspHome: AspDogfoodAspHomeEvidence; + hostFixture: AspDogfoodHostFixtureEvidence; + provider: AspDogfoodProviderEvidence; + managerState: AspDogfoodManagerStateEvidence; + repoEnrollment: AspDogfoodRepoEnrollmentEvidence; + hostEvaluation: AspDogfoodHostEvaluationEvidence; + providerProbe: AspDogfoodProviderProbeEvidence; + selfValidation: OpcoreSelfValidationReceipt; + unsupportedSurfaces: readonly AspDogfoodUnsupportedSurfaceEvidence[]; + parityBlockers: readonly AspDogfoodParityBlocker[]; + authority: AspDogfoodAuthorityEvidence; + publicReleaseActions: readonly []; + forbiddenMarkerScan: AspDogfoodForbiddenMarkerScan; +} + +export type { AspDogfoodReceiptEvidence }; + +interface AspDogfoodReceipt extends AspDogfoodReceiptIdentity, AspDogfoodReceiptEvidence {} + +export type { AspDogfoodReceipt }; diff --git a/packages/contracts/src/release/asp-validators-01.ts b/packages/contracts/src/release/asp-validators-01.ts new file mode 100644 index 0000000..efef708 --- /dev/null +++ b/packages/contracts/src/release/asp-validators-01.ts @@ -0,0 +1,218 @@ +import { validateRequiredObject } from "../shared/validators-02.js"; +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { + validateExactStringSequence, + validateExactStringSet, + validateNonEmptyString, + validateNonNegativeInteger, + validateSha256, + validateStringArray, +} from "../shared/validators-01.js"; +import type { + AspDogfoodAspHomeEvidence, + AspDogfoodHostEvaluationEvidence, + AspDogfoodHostFixtureEvidence, + AspDogfoodManagerEvidence, + AspDogfoodManagerStateEvidence, + AspDogfoodProviderEvidence, + AspDogfoodProviderManifestEvidence, + AspDogfoodProviderProbeEvidence, + AspDogfoodRepoEnrollmentEvidence, +} from "./asp-contracts-01.js"; +import { + assertNoAspDogfoodHostOwnedFields, + validateAspDogfoodCommandRun, + validateAspDogfoodPassedCommandRun, +} from "./asp-validators-02.js"; + +function validateAspDogfoodManager(manager: AspDogfoodManagerEvidence): void { + if (!manager || typeof manager !== "object") throw new Error("ASP dogfood manager evidence is required"); + if (manager.bootstrapSource !== "local-sibling") throw new Error("ASP dogfood bootstrapSource must be local-sibling"); + validateNonEmptyString(manager.aspRepoPath, "ASP dogfood manager aspRepoPath"); + validateNonEmptyString(manager.aspBinPath, "ASP dogfood manager aspBinPath"); + validateNonEmptyString(manager.cliPath, "ASP dogfood manager cliPath"); + validateNonEmptyString(manager.commitSha, "ASP dogfood manager commitSha"); +} + +export { validateAspDogfoodManager }; + +function validateAspDogfoodAspHome(aspHome: AspDogfoodAspHomeEvidence): void { + if (!aspHome || typeof aspHome !== "object") throw new Error("ASP dogfood ASP_HOME evidence is required"); + validateNonEmptyString(aspHome.path, "ASP dogfood ASP_HOME path"); + if (aspHome.temp !== true) throw new Error("ASP dogfood ASP_HOME must be temporary"); + if (aspHome.isolated !== true) throw new Error("ASP dogfood ASP_HOME must be isolated"); + if (aspHome.sharedStateMutated !== false) throw new Error("ASP dogfood shared ASP state must not be mutated"); + if (aspHome.pathSanitized !== true) throw new Error("ASP dogfood PATH must be sanitized for manager execution"); +} + +export { validateAspDogfoodAspHome }; + +function validateAspDogfoodHostFixture(fixture: AspDogfoodHostFixtureEvidence): void { + if (!fixture || typeof fixture !== "object") throw new Error("ASP dogfood host fixture evidence is required"); + validateNonEmptyString(fixture.repo, "ASP dogfood host fixture repo"); + if (fixture.temp !== true) throw new Error("ASP dogfood host fixture repo must be temporary"); + if (fixture.sourceRepoMutated !== false) throw new Error("ASP dogfood host fixture must not mutate the source repo"); + if (fixture.baselineCommitted !== true) throw new Error("ASP dogfood host fixture must commit a baseline"); + validateStringArray(fixture.changedPaths, "ASP dogfood host fixture changedPaths", { allowEmpty: false }); + for (const path of fixture.changedPaths) validateRepoRelativePath(path); +} + +export { validateAspDogfoodHostFixture }; + +function validateAspDogfoodProvider(provider: AspDogfoodProviderEvidence): void { + if (!provider || typeof provider !== "object") throw new Error("ASP dogfood provider evidence is required"); + if (provider.providerId !== "opcore") throw new Error("ASP dogfood providerId must be opcore"); + if (provider.packageName !== "opcore") { + throw new Error("ASP dogfood provider package must be opcore"); + } + validateNonEmptyString(provider.binPath, "ASP dogfood provider binPath"); + if (!provider.binPath.endsWith("node_modules/.bin/opcore-asp-provider")) { + throw new Error("ASP dogfood provider binPath must use installed node_modules/.bin/opcore-asp-provider"); + } + validateNonEmptyString(provider.indexPath, "ASP dogfood provider indexPath"); + if ( + !provider.indexPath.endsWith("node_modules/opcore/node_modules/@the-open-engine/opcore-asp-provider/dist/index.js") + ) { + throw new Error("ASP dogfood provider indexPath must be bundled opcore-asp-provider dist/index.js"); + } + validateSha256(provider.indexSha256, "ASP dogfood provider indexSha256"); + validateExactStringSequence(provider.command, ["opcore-asp-provider", "--stdio"], "ASP dogfood provider command"); + if (!provider.entrypoint || typeof provider.entrypoint !== "object") + throw new Error("ASP dogfood provider entrypoint is required"); + if (provider.entrypoint.transport !== "stdio") + throw new Error("ASP dogfood provider entrypoint transport must be stdio"); + validateAspDogfoodProviderBinPath(provider.entrypoint.bin, "ASP dogfood provider entrypoint bin"); + validateExactStringSequence(provider.entrypoint.args, ["--stdio"], "ASP dogfood provider entrypoint args"); + validateAspDogfoodProviderManifest(provider.manifest); +} + +export { validateAspDogfoodProvider }; + +function validateAspDogfoodProviderManifest(manifestEvidence: AspDogfoodProviderManifestEvidence): void { + if (!manifestEvidence || typeof manifestEvidence !== "object") + throw new Error("ASP dogfood provider manifest evidence is required"); + validateNonEmptyString(manifestEvidence.manifestPath, "ASP dogfood provider manifestPath"); + validateSha256(manifestEvidence.manifestSha256, "ASP dogfood provider manifestSha256"); + validateRequiredObject(manifestEvidence.manifest, "ASP dogfood provider manifest must be structured metadata"); + const manifest = manifestEvidence.manifest as Record; + if (manifest.manifestVersion !== "asp-server/0.1") + throw new Error("ASP dogfood provider manifestVersion must be asp-server/0.1"); + const server = manifest.server as Record | undefined; + if (!server || server.id !== "opcore") throw new Error("ASP dogfood provider manifest server.id must be opcore"); + const entrypoint = manifest.entrypoint as Record | undefined; + if (!entrypoint || entrypoint.transport !== "stdio" || typeof entrypoint.bin !== "string") { + throw new Error("ASP dogfood provider manifest entrypoint must be opcore-asp-provider --stdio"); + } + validateAspDogfoodProviderBinPath(entrypoint.bin, "ASP dogfood provider manifest entrypoint bin"); + validateExactStringSequence( + entrypoint.args as readonly string[], + ["--stdio"], + "ASP dogfood provider manifest entrypoint args", + ); + const capabilities = manifest.capabilities; + if (!Array.isArray(capabilities)) { + throw new Error("ASP dogfood provider manifest capabilities must be an array"); + } + validateExactStringSet( + capabilities, + ["check"], + "ASP dogfood provider manifest capabilities", + ); +} + +export { validateAspDogfoodProviderManifest }; + +function validateAspDogfoodProviderBinPath(value: unknown, label: string): void { + validateNonEmptyString(value, label); + const normalized = String(value).replaceAll("\\", "/"); + if (!/node_modules\/\.bin\/opcore-asp-provider(?:\.cmd)?$/.test(normalized)) { + throw new Error(`${label} must use installed node_modules/.bin/opcore-asp-provider`); + } +} + +export { validateAspDogfoodProviderBinPath }; + +function validateAspDogfoodManagerState(state: AspDogfoodManagerStateEvidence): void { + if (!state || typeof state !== "object") throw new Error("ASP dogfood managerState is required"); + validateAspDogfoodPassedCommandRun(state.status, "asp-status", "ASP dogfood manager status"); + validateAspDogfoodPassedCommandRun(state.serverAdd, "asp-server-add", "ASP dogfood manager server add"); + validateAspDogfoodPassedCommandRun(state.serverStatus, "asp-server-status", "ASP dogfood manager server status"); + validateRequiredObject(state.serverStatus.output, "ASP dogfood server status output is required"); +} + +export { validateAspDogfoodManagerState }; + +function validateAspDogfoodRepoEnrollment(enrollment: AspDogfoodRepoEnrollmentEvidence): void { + if (!enrollment || typeof enrollment !== "object") throw new Error("ASP dogfood repo enrollment is required"); + validateNonEmptyString(enrollment.repo, "ASP dogfood repo enrollment repo"); + if (enrollment.mode !== "advisory" && enrollment.mode !== "shadow") { + throw new Error("ASP dogfood repo enrollment mode must be advisory or shadow"); + } + validateAspDogfoodPassedCommandRun(enrollment.repoAdd, "asp-repo-add", "ASP dogfood repo add"); + validateAspDogfoodPassedCommandRun(enrollment.repoEnable, "asp-repo-enable", "ASP dogfood repo enable"); + validateAspDogfoodPassedCommandRun(enrollment.repoStatus, "asp-repo-status", "ASP dogfood repo status"); +} + +export { validateAspDogfoodRepoEnrollment }; + +function validateAspDogfoodHostEvaluation(evaluation: AspDogfoodHostEvaluationEvidence): void { + if (!evaluation || typeof evaluation !== "object") throw new Error("ASP dogfood host evaluation is required"); + validateAspDogfoodPassedCommandRun(evaluation.check, "asp-check-changed", "ASP dogfood host check"); + if (!evaluation.check.command.includes("check")) throw new Error("ASP dogfood host check must run asp check"); + validateRequiredObject(evaluation.check.hostDecision, "ASP dogfood host decision is required"); + validateRequiredObject(evaluation.check.receipt, "ASP dogfood host receipt is required"); + validateAspDogfoodHostAuthorityEvidence(evaluation.check.hostDecision, "ASP dogfood host decision", { + requireProviderProvenance: false, + }); + validateAspDogfoodHostAuthorityEvidence(evaluation.check.receipt, "ASP dogfood host receipt", { + requireProviderProvenance: true, + }); + validateRequiredObject(evaluation.check.assurance, "ASP dogfood host assurance is required"); + validateNonEmptyString(evaluation.check.assurance.mode, "ASP dogfood host assurance mode"); + validateNonEmptyString(evaluation.check.assurance.transactionGuarantee, "ASP dogfood host transactionGuarantee"); + if (evaluation.ciVerify !== undefined) { + validateAspDogfoodCommandRun(evaluation.ciVerify, "asp-ci-verify", "ASP dogfood CI verify"); + if (!evaluation.ciVerify.command.includes("ci") || !evaluation.ciVerify.command.includes("verify")) { + throw new Error("ASP dogfood CI verifier must run asp ci verify"); + } + } +} + +export { validateAspDogfoodHostEvaluation }; + +function validateAspDogfoodHostAuthorityEvidence( + value: unknown, + label: string, + options: { requireProviderProvenance: boolean }, +): void { + if (!value || typeof value !== "object") throw new Error(`${label} is required`); + const record = value as Record; + const authorityEvidence = record.authorityEvidence; + if (!Array.isArray(authorityEvidence) || authorityEvidence.length === 0) { + throw new Error(`${label} must include host authorityEvidence`); + } + const providerProvenance = record.providerProvenance; + if (options.requireProviderProvenance && (!Array.isArray(providerProvenance) || providerProvenance.length === 0)) { + throw new Error(`${label} must include providerProvenance`); + } +} + +export { validateAspDogfoodHostAuthorityEvidence }; + +function validateAspDogfoodProviderProbe(probe: AspDogfoodProviderProbeEvidence): void { + if (!probe || typeof probe !== "object") throw new Error("ASP dogfood provider probe is required"); + validateAspDogfoodPassedCommandRun(probe, "provider-probe", "ASP dogfood provider probe"); + validateExactStringSequence(probe.command, ["opcore-asp-provider", "--stdio"], "ASP dogfood provider probe command"); + if (!probe.assessment || typeof probe.assessment !== "object") + throw new Error("ASP dogfood provider probe assessment is required"); + if (!probe.validAsOf || typeof probe.validAsOf !== "object") + throw new Error("ASP dogfood provider probe validAsOf is required"); + if (!probe.coverage || typeof probe.coverage !== "object") + throw new Error("ASP dogfood provider probe coverage is required"); + validateNonNegativeInteger(probe.diagnosticsCount, "ASP dogfood provider probe diagnosticsCount"); + if (probe.hostOwnedFieldLeak !== false) + throw new Error("ASP dogfood provider output must not contain host-owned decision fields"); + assertNoAspDogfoodHostOwnedFields(probe.assessment); +} + +export { validateAspDogfoodProviderProbe }; diff --git a/packages/contracts/src/release/asp-validators-02.ts b/packages/contracts/src/release/asp-validators-02.ts new file mode 100644 index 0000000..18fefb4 --- /dev/null +++ b/packages/contracts/src/release/asp-validators-02.ts @@ -0,0 +1,158 @@ +import { validateRequiredObject } from "../shared/validators-02.js"; +import { includesString } from "../shared/primitives.js"; +import { + validateExactStringSet, + validateNonEmptyArray, + validateNonEmptyString, + validateNonNegativeInteger, + validatePositiveInteger, + validateSha256, + validateStringArray, +} from "../shared/validators-01.js"; +import { collectStrings } from "../shared/validators-02.js"; +import type { + AspDogfoodAuthorityEvidence, + AspDogfoodCommandRunReceipt, + AspDogfoodForbiddenMarkerScan, + AspDogfoodParityBlocker, + AspDogfoodReceipt, + AspDogfoodUnsupportedSurfaceEvidence, +} from "./asp-contracts-01.js"; +import { aspDogfoodForbiddenProviderMarkers, aspDogfoodUnsupportedSurfaceIds } from "./vocabulary-02.js"; + +function validateAspDogfoodUnsupportedSurfaces(surfaces: readonly AspDogfoodUnsupportedSurfaceEvidence[]): void { + validateNonEmptyArray(surfaces, "ASP dogfood unsupported surfaces"); + validateExactStringSet( + surfaces.map((entry) => entry.surface), + aspDogfoodUnsupportedSurfaceIds, + "ASP dogfood unsupported surfaces", + ); + for (const entry of surfaces) { + if (!entry || typeof entry !== "object") throw new Error("ASP dogfood unsupported surface entry is required"); + if (!includesString(aspDogfoodUnsupportedSurfaceIds, entry.surface)) { + throw new Error(`Unknown ASP dogfood unsupported surface: ${String(entry.surface)}`); + } + if (!includesString(["degraded", "parity-blocker"] as const, entry.status)) { + throw new Error("ASP dogfood unsupported surface status must be degraded or parity-blocker"); + } + if (entry.cleanCoverage !== false) + throw new Error("ASP dogfood unsupported inspect/edit surfaces must not be represented as clean coverage"); + validateNonEmptyString(entry.blocker, "ASP dogfood unsupported surface blocker"); + } +} + +export { validateAspDogfoodUnsupportedSurfaces }; + +function validateAspDogfoodParityBlockers(blockers: readonly AspDogfoodParityBlocker[]): void { + if (!Array.isArray(blockers)) throw new Error("ASP dogfood parity blockers must be an array"); + for (const blocker of blockers) { + if (!blocker || typeof blocker !== "object") throw new Error("ASP dogfood parity blocker is required"); + validateNonEmptyString(blocker.source, "ASP dogfood parity blocker source"); + validateNonEmptyString(blocker.detail, "ASP dogfood parity blocker detail"); + } +} + +export { validateAspDogfoodParityBlockers }; + +function validateAspDogfoodAuthority(authority: AspDogfoodAuthorityEvidence): void { + if (!authority || typeof authority !== "object") throw new Error("ASP dogfood authority evidence is required"); + if (authority.hostOwnsDecisions !== true) throw new Error("ASP dogfood host must own decisions"); + if (authority.providerOutputIsHostDecision !== false) + throw new Error("ASP dogfood provider output must not be treated as host decision"); + validateRequiredObject(authority.localAuthorityOverride, "ASP dogfood local authority override evidence is required"); + if ( + authority.localAuthorityOverride.present !== false || + authority.localAuthorityOverride.sharedAuthorityWeakened !== false + ) { + throw new Error("ASP dogfood must not silently weaken shared authority through local override"); + } +} + +export { validateAspDogfoodAuthority }; + +function validateAspDogfoodForbiddenMarkerScan(scan: AspDogfoodForbiddenMarkerScan): void { + if (!scan || typeof scan !== "object") throw new Error("ASP dogfood forbidden marker scan is required"); + validatePositiveInteger(scan.scannedTextCount, "ASP dogfood forbidden marker scannedTextCount"); + if (scan.findingCount !== 0) throw new Error("ASP dogfood forbidden marker findingCount must be 0"); + validateExactStringSet( + scan.markersBlocked, + aspDogfoodForbiddenProviderMarkers, + "ASP dogfood forbidden provider markers", + ); +} + +export { validateAspDogfoodForbiddenMarkerScan }; + +function validateAspDogfoodCommandRun(receipt: AspDogfoodCommandRunReceipt, expectedId: string, label: string): void { + if (!receipt || typeof receipt !== "object") throw new Error(`${label} receipt is required`); + if (receipt.id !== expectedId) throw new Error(`${label} id must be ${expectedId}`); + validateStringArray(receipt.command, `${label} command`, { + allowEmpty: false, + }); + if (!includesString(["passed", "failed", "retained-not-run"] as const, receipt.status)) { + throw new Error(`${label} status must be passed, failed, or retained-not-run`); + } + if (receipt.status === "passed" && receipt.exitCode !== 0) + throw new Error(`${label} passed status must use exitCode 0`); + if (receipt.status === "retained-not-run" && receipt.exitCode !== null) { + throw new Error(`${label} retained-not-run status must use null exitCode`); + } + if (receipt.status === "failed") validateNonNegativeInteger(receipt.exitCode, `${label} exitCode`); + validateSha256(receipt.stdoutSha256, `${label} stdoutSha256`); + validateSha256(receipt.stderrSha256, `${label} stderrSha256`); + validateNonEmptyString(receipt.assertion, `${label} assertion`); +} + +export { validateAspDogfoodCommandRun }; + +function validateAspDogfoodPassedCommandRun( + receipt: AspDogfoodCommandRunReceipt, + expectedId: string, + label: string, +): void { + validateAspDogfoodCommandRun(receipt, expectedId, label); + if (receipt.status !== "passed") throw new Error(`${label} status must be passed`); + if (receipt.exitCode !== 0) throw new Error(`${label} passed status must use exitCode 0`); +} + +export { validateAspDogfoodPassedCommandRun }; + +function validateAspDogfoodForbiddenProviderEntrypoint(receipt: AspDogfoodReceipt): void { + const providerTexts = collectStrings(receipt.provider); + const findings: string[] = []; + for (const text of providerTexts) { + const normalized = text.replaceAll("\\", "/").toLowerCase(); + for (const marker of aspDogfoodForbiddenProviderMarkers) { + if (normalized.includes(marker.toLowerCase())) findings.push(marker); + } + } + if (findings.length > 0) { + throw new Error(`ASP dogfood provider entrypoint contains forbidden marker: ${[...new Set(findings)].join(", ")}`); + } +} + +export { validateAspDogfoodForbiddenProviderEntrypoint }; + +function assertNoAspDogfoodHostOwnedFields(value: unknown, path = "$"): void { + if (!value || typeof value !== "object") return; + if (Array.isArray(value)) { + value.forEach((entry, index) => assertNoAspDogfoodHostOwnedFields(entry, `${path}[${index}]`)); + return; + } + const forbidden = new Set([ + "decision", + "verdict", + "pass", + "authority", + "authorityEvidence", + "assurance", + "transactionGuarantee", + "applyReceipt", + ]); + for (const [key, child] of Object.entries(value)) { + if (forbidden.has(key)) throw new Error(`ASP dogfood provider output contains host-owned field ${path}.${key}`); + assertNoAspDogfoodHostOwnedFields(child, `${path}.${key}`); + } +} + +export { assertNoAspDogfoodHostOwnedFields }; diff --git a/packages/contracts/src/release/cutover-contracts.ts b/packages/contracts/src/release/cutover-contracts.ts new file mode 100644 index 0000000..6e6a20c --- /dev/null +++ b/packages/contracts/src/release/cutover-contracts.ts @@ -0,0 +1,170 @@ +import type { CommandOwner, CommandRouteStatus } from "../command/vocabulary.js"; +import type { ManagedToolDescriptor } from "../managed/contracts.js"; +import type { + ReleaseReceiptResolvedArtifactEvidence, + ReleaseReceiptResolvedChecksumEvidence, +} from "./receipt-contracts-01.js"; +import type { + ReleaseCutoverCommandId, + ReleaseCutoverPythonCommandId, + ReleaseCutoverRustCommandId, + ReleaseReceiptPackageName, +} from "./vocabulary-01.js"; +import type { ReleaseCutoverInputIssue, ReleaseCutoverNegativeCheckId } from "./vocabulary-02.js"; + +interface ReleaseCutoverTarballEvidence { + filename: string; + sha256: string; +} + +export type { ReleaseCutoverTarballEvidence }; + +interface ReleaseCutoverInstalledManifestEvidence { + path: string; + sha256: string; + bins: Readonly>; +} + +export type { ReleaseCutoverInstalledManifestEvidence }; + +interface ReleaseCutoverInstalledFileEvidence { + path: string; + sha256: string; +} + +export type { ReleaseCutoverInstalledFileEvidence }; + +interface ReleaseCutoverInstalledPackageEvidence { + packageName: ReleaseReceiptPackageName; + version: string; + tarball: ReleaseCutoverTarballEvidence; + installedManifest: ReleaseCutoverInstalledManifestEvidence; + installedFiles: readonly ReleaseCutoverInstalledFileEvidence[]; +} + +export type { ReleaseCutoverInstalledPackageEvidence }; + +interface ReleaseCutoverDescriptorEvidence { + path: string; + packageName: "opcore"; + checksumSha256: string; + descriptor: ManagedToolDescriptor; + resolvedArtifacts: readonly ReleaseReceiptResolvedArtifactEvidence[]; + resolvedChecksums: readonly ReleaseReceiptResolvedChecksumEvidence[]; +} + +export type { ReleaseCutoverDescriptorEvidence }; + +interface ReleaseCutoverEnvironmentIsolationEvidence { + pathSanitized: true; + siblingRepositoriesExcluded: true; + opcoreBinsVerified: true; +} + +export type { ReleaseCutoverEnvironmentIsolationEvidence }; + +interface ReleaseCutoverCommandReceipt { + id: ReleaseCutoverCommandId; + command: readonly string[]; + canonicalCommand: readonly string[]; + owner: CommandOwner; + status: CommandRouteStatus; + exitCode: number; + binPath: string; + stdoutSha256: string; + stderrSha256: string; + assertion: string; +} + +export type { ReleaseCutoverCommandReceipt }; + +interface ReleaseCutoverRustCommandReceipt { + id: ReleaseCutoverRustCommandId; + command: readonly string[]; + canonicalCommand: readonly string[]; + owner: "graph"; + status: "ok"; + exitCode: 0; + binPath: string; + stdoutSha256: string; + stderrSha256: string; + assertion: string; +} + +export type { ReleaseCutoverRustCommandReceipt }; + +interface ReleaseCutoverPythonCommandReceipt { + id: ReleaseCutoverPythonCommandId; + command: readonly string[]; + canonicalCommand: readonly string[]; + evidence: readonly string[]; + owner: CommandOwner; + status: "ok"; + exitCode: 0; + binPath: string; + stdoutSha256: string; + stderrSha256: string; + assertion: string; +} + +export type { ReleaseCutoverPythonCommandReceipt }; + +interface ReleaseCutoverNegativeCheck { + id: ReleaseCutoverNegativeCheckId; + command: readonly string[]; + status: "passed"; + exitCode: 0; + assertion: string; +} + +export type { ReleaseCutoverNegativeCheck }; + +interface OpcoreSelfValidationReceipt { + id: "opcore-self-check"; + command: readonly ["npm", "run", "opcore:self-check"]; + status: "passed"; + exitCode: 0; + stdoutSha256: string; + stderrSha256: string; + assertion: string; +} + +export type { OpcoreSelfValidationReceipt }; + +interface ReleaseCutoverForbiddenMarkerScan { + scannedTextCount: number; + findingCount: 0; + markersBlocked: readonly string[]; +} + +export type { ReleaseCutoverForbiddenMarkerScan }; + +interface ReleaseCutoverInputEvidence { + issue: ReleaseCutoverInputIssue; + path: string; + checksumSha256: string; +} + +export type { ReleaseCutoverInputEvidence }; + +interface ReleaseCutoverReceipt { + schemaVersion: 1; + issue: "#30"; + origin: "covibes-authored-cutover-proof"; + generatedAt: string; + commitSha: string; + privateRepo: true; + packageNames: readonly ReleaseReceiptPackageName[]; + installedPackages: readonly ReleaseCutoverInstalledPackageEvidence[]; + descriptor: ReleaseCutoverDescriptorEvidence; + environmentIsolation: ReleaseCutoverEnvironmentIsolationEvidence; + commandReceipts: readonly ReleaseCutoverCommandReceipt[]; + rustCommandReceipts: readonly ReleaseCutoverRustCommandReceipt[]; + pythonCommandReceipts: readonly ReleaseCutoverPythonCommandReceipt[]; + negativeChecks: readonly ReleaseCutoverNegativeCheck[]; + selfValidation: OpcoreSelfValidationReceipt; + forbiddenMarkerScan: ReleaseCutoverForbiddenMarkerScan; + inputEvidence: readonly ReleaseCutoverInputEvidence[]; +} + +export type { ReleaseCutoverReceipt }; diff --git a/packages/contracts/src/release/cutover-validators-01.ts b/packages/contracts/src/release/cutover-validators-01.ts new file mode 100644 index 0000000..b4b8cd4 --- /dev/null +++ b/packages/contracts/src/release/cutover-validators-01.ts @@ -0,0 +1,232 @@ +import { + validateExactValue, + validateRequiredObject, +} from "../shared/validators-02.js"; +import { validateCommandOwner, validateCommandRouteStatus } from "../command/helper-validators.js"; +import { includesString } from "../shared/primitives.js"; +import { managedToolDescriptorArtifactTypes } from "../managed/contracts.js"; +import { bundledGraphCoreNativePath, validateReleaseReceiptPackageName } from "../managed/helper-validators.js"; +import { validateManagedToolDescriptor } from "../managed/validators-01.js"; +import { + validateExactStringSequence, + validateExactStringSet, + validateExitCodeForStatus, + validateNonEmptyArray, + validateNonEmptyString, + validateSha256, + validateStringArray, +} from "../shared/validators-01.js"; +import type { + ReleaseCutoverCommandReceipt, + ReleaseCutoverDescriptorEvidence, + ReleaseCutoverEnvironmentIsolationEvidence, + ReleaseCutoverInstalledPackageEvidence, +} from "./cutover-contracts.js"; +import { validateReleaseCutoverExpectedCommand } from "./cutover-validators-02.js"; +import { graphCoreNativePackageNameForTarget, graphCoreNativeSupportedTargets } from "./graph-vocabulary-02.js"; +import { validateReleaseReceiptBins } from "./receipt-validators-03.js"; +import { releaseCutoverRequiredCommandIds, releaseReceiptPackageNames } from "./vocabulary-01.js"; +import { releaseCutoverCommandExpectations } from "./vocabulary-03.js"; + +function validateReleaseCutoverInstalledPackages(packages: readonly ReleaseCutoverInstalledPackageEvidence[]): void { + validateNonEmptyArray(packages, "Release cutover installed package evidence"); + validateReleaseCutoverInstalledPackageSet(packages.map((entry) => entry.packageName)); + for (const entry of packages) { + if (!entry || typeof entry !== "object") + throw new Error("Release cutover installed package evidence entry is required"); + validateReleaseReceiptPackageName(entry.packageName, "Release cutover installed package packageName"); + validateNonEmptyString(entry.version, "Release cutover installed package version"); + if (!entry.tarball || typeof entry.tarball !== "object") + throw new Error("Release cutover tarball evidence is required"); + validateNonEmptyString(entry.tarball.filename, "Release cutover tarball filename"); + validateSha256(entry.tarball.sha256, "Release cutover tarball sha256"); + validateRequiredObject(entry.installedManifest, "Release cutover installed manifest evidence is required"); + validateNonEmptyString(entry.installedManifest.path, "Release cutover installed manifest path"); + if ( + !entry.installedManifest.path.includes("node_modules/") || + !entry.installedManifest.path.endsWith("package.json") + ) { + throw new Error("Release cutover installed manifest path must be inside node_modules and end with package.json"); + } + validateSha256(entry.installedManifest.sha256, "Release cutover installed manifest sha256"); + validateReleaseReceiptBins(entry.installedManifest.bins, entry.packageName); + validateReleaseCutoverInstalledFiles(entry); + } +} + +export { validateReleaseCutoverInstalledPackages }; + +function validateReleaseCutoverInstalledFiles(entry: ReleaseCutoverInstalledPackageEvidence): void { + validateNonEmptyArray(entry.installedFiles, "Release cutover installed files"); + const prefix = `node_modules/${entry.packageName}/`; + const paths = []; + for (const file of entry.installedFiles) { + if (!file || typeof file !== "object") throw new Error("Release cutover installed file evidence entry is required"); + validateNonEmptyString(file.path, "Release cutover installed file path"); + if (!file.path.startsWith(prefix)) { + throw new Error(`Release cutover installed file path must be inside ${prefix}`); + } + validateSha256(file.sha256, "Release cutover installed file sha256"); + paths.push(file.path); + } + if (new Set(paths).size !== paths.length) { + throw new Error("Release cutover installed file paths must be unique"); + } + if (!paths.includes(entry.installedManifest.path)) { + throw new Error("Release cutover installed files must include package.json"); + } + const binPaths = Object.values(entry.installedManifest.bins).map((path) => `${prefix}${path}`); + for (const binPath of binPaths) { + if (!paths.includes(binPath)) throw new Error(`Release cutover installed files must include bin target ${binPath}`); + } + if (entry.packageName === "opcore") validateReleaseCutoverOpcoreFiles(paths); +} + +export { validateReleaseCutoverInstalledFiles }; + +function validateReleaseCutoverOpcoreFiles(paths: readonly string[]): void { + const aspManifest = + "node_modules/opcore/node_modules/@the-open-engine/opcore-asp-provider/dist/manifests/asp-server.json"; + if (!paths.includes(aspManifest)) { + throw new Error("Release cutover Opcore installed files must include bundled canonical asp-server.json"); + } + for (const target of graphCoreNativeSupportedTargets) { + const bundledPackageName = graphCoreNativePackageNameForTarget(target); + const binary = `node_modules/opcore/${bundledGraphCoreNativePath(bundledPackageName, "opcore-graph-core")}`; + if (!paths.includes(binary)) { + throw new Error(`Release cutover Opcore installed files must include bundled native binary for ${target}`); + } + } +} + +function validateReleaseCutoverInstalledPackageSet(packageNames: readonly string[]): void { + validateExactStringSet(packageNames, releaseReceiptPackageNames, "Release cutover installed package evidence"); + for (const packageName of packageNames) { + validateReleaseReceiptPackageName(packageName, "Release cutover installed package packageName"); + } +} + +export { validateReleaseCutoverInstalledPackageSet }; + +function validateReleaseCutoverDescriptor(descriptorEvidence: ReleaseCutoverDescriptorEvidence): void { + if (!descriptorEvidence || typeof descriptorEvidence !== "object") + throw new Error("Release cutover descriptor evidence is required"); + validateNonEmptyString(descriptorEvidence.path, "Release cutover descriptor path"); + if (descriptorEvidence.packageName !== "opcore") { + throw new Error("Release cutover descriptor packageName must be opcore"); + } + validateSha256(descriptorEvidence.checksumSha256, "Release cutover descriptor checksumSha256"); + const descriptor = validateManagedToolDescriptor(descriptorEvidence.descriptor); + validateNonEmptyArray(descriptorEvidence.resolvedArtifacts, "Release cutover descriptor resolvedArtifacts"); + validateExactStringSet( + descriptorEvidence.resolvedArtifacts.map((entry) => entry.id), + descriptor.artifacts.map((entry) => entry.id), + "Release cutover descriptor resolved artifact ids", + ); + for (const artifact of descriptorEvidence.resolvedArtifacts) { + validateReleaseReceiptPackageName(artifact.packageName, "Release cutover descriptor resolved artifact packageName"); + validateNonEmptyString(artifact.path, "Release cutover descriptor resolved artifact path"); + validateNonEmptyString(artifact.id, "Release cutover descriptor resolved artifact id"); + if (!includesString(managedToolDescriptorArtifactTypes, artifact.type)) { + throw new Error(`Unknown release cutover descriptor resolved artifact type: ${String(artifact.type)}`); + } + if (artifact.packageFile !== true) + throw new Error("Release cutover descriptor resolved artifacts must be package files"); + } + validateNonEmptyArray(descriptorEvidence.resolvedChecksums, "Release cutover descriptor resolvedChecksums"); + validateExactStringSet( + descriptorEvidence.resolvedChecksums.map((entry) => entry.id), + descriptor.checksums.map((entry) => entry.id), + "Release cutover descriptor resolved checksum ids", + ); + for (const checksum of descriptorEvidence.resolvedChecksums) { + validateReleaseReceiptPackageName(checksum.packageName, "Release cutover descriptor resolved checksum packageName"); + validateNonEmptyString(checksum.path, "Release cutover descriptor resolved checksum path"); + validateNonEmptyString(checksum.id, "Release cutover descriptor resolved checksum id"); + if (checksum.algorithm !== "sha256") + throw new Error("Release cutover descriptor checksum algorithm must be sha256"); + validateSha256(checksum.value, "Release cutover descriptor resolved checksum value"); + if (checksum.packageFile !== true) + throw new Error("Release cutover descriptor resolved checksums must be package files"); + } +} + +export { validateReleaseCutoverDescriptor }; + +function validateReleaseCutoverEnvironmentIsolation(environment: ReleaseCutoverEnvironmentIsolationEvidence): void { + if (!environment || typeof environment !== "object") + throw new Error("Release cutover environmentIsolation is required"); + if (environment.pathSanitized !== true) throw new Error("Release cutover PATH must be sanitized"); + if (environment.siblingRepositoriesExcluded !== true) { + throw new Error("Release cutover sibling repository paths must be excluded"); + } + if (environment.opcoreBinsVerified !== true) { + throw new Error("Release cutover installed project bins must be verified"); + } +} + +export { validateReleaseCutoverEnvironmentIsolation }; + +function validateReleaseCutoverCommandReceipts(receipts: readonly ReleaseCutoverCommandReceipt[]): void { + validateNonEmptyArray(receipts, "Release cutover command receipts"); + validateExactStringSet( + receipts.map((entry) => entry.id), + releaseCutoverRequiredCommandIds, + "Release cutover command receipts", + ); + for (const receipt of receipts) validateReleaseCutoverCommandReceipt(receipt); +} + +export { validateReleaseCutoverCommandReceipts }; + +function validateReleaseCutoverCommandReceipt(receipt: ReleaseCutoverCommandReceipt): void { + validateRequiredObject(receipt, "Release cutover command receipt is required"); + if (!includesString(releaseCutoverRequiredCommandIds, receipt.id)) { + throw new Error(`Unknown release cutover command receipt id: ${String(receipt.id)}`); + } + validateStringArray(receipt.command, "Release cutover command receipt command", { allowEmpty: false }); + validateStringArray(receipt.canonicalCommand, "Release cutover command receipt canonicalCommand", { + allowEmpty: false, + }); + validateExactStringSequence(receipt.command, receipt.canonicalCommand, `Release cutover ${receipt.id} command`); + const expected = releaseCutoverCommandExpectations[receipt.id]; + validateExactValue( + receipt.command[0], + expected.bin, + `Release cutover command ${receipt.id} command must use canonical ${expected.bin} bin`, + ); + validateExactValue( + receipt.canonicalCommand[0], + expected.bin, + `Release cutover command ${receipt.id} canonicalCommand must use canonical ${expected.bin} bin`, + ); + validateCommandOwner(receipt.owner); + validateReleaseCutoverExpectedCommand(receipt.canonicalCommand, expected, receipt.id); + validateExactValue( + receipt.owner, + expected.owner, + `Release cutover command ${receipt.id} owner must match expected ${expected.owner}`, + ); + const status = validateCommandRouteStatus(receipt.status); + if (receipt.status === "not_implemented") { + throw new Error("Release cutover command receipts must not be not_implemented"); + } + validateExactValue( + status, + expected.status, + `Release cutover command ${receipt.id} status must match expected ${expected.status}`, + ); + validateExitCodeForStatus(receipt.exitCode, status); + validateExactValue( + receipt.exitCode, + expected.exitCode, + `Release cutover command ${receipt.id} exitCode must match expected ${expected.exitCode}`, + ); + validateNonEmptyString(receipt.binPath, "Release cutover command receipt binPath"); + if (!receipt.binPath.endsWith(`node_modules/.bin/${expected.bin}`)) { + throw new Error(`Release cutover command receipt binPath must use installed node_modules/.bin/${expected.bin}`); + } + validateSha256(receipt.stdoutSha256, "Release cutover command receipt stdoutSha256"); + validateSha256(receipt.stderrSha256, "Release cutover command receipt stderrSha256"); + validateNonEmptyString(receipt.assertion, "Release cutover command receipt assertion"); +} diff --git a/packages/contracts/src/release/cutover-validators-02.ts b/packages/contracts/src/release/cutover-validators-02.ts new file mode 100644 index 0000000..ac46529 --- /dev/null +++ b/packages/contracts/src/release/cutover-validators-02.ts @@ -0,0 +1,244 @@ +import { validateCommandOwner } from "../command/helper-validators.js"; +import { includesString } from "../shared/primitives.js"; +import { + validateExactValue, + validateRequiredObject, +} from "../shared/validators-02.js"; +import { + validateExactStringSequence, + validateExactStringSet, + validateNonEmptyArray, + validateNonEmptyString, + validateNonNegativeInteger, + validateSha256, + validateStringArray, +} from "../shared/validators-01.js"; +import type { + ReleaseCutoverForbiddenMarkerScan, + ReleaseCutoverInputEvidence, + ReleaseCutoverNegativeCheck, + ReleaseCutoverPythonCommandReceipt, + ReleaseCutoverRustCommandReceipt, +} from "./cutover-contracts.js"; +import type { + ReleaseCutoverCommandId, + ReleaseCutoverPythonCommandId, + ReleaseCutoverRustCommandId} from "./vocabulary-01.js"; +import { + releaseCutoverNegativeCheckIds, + releaseCutoverPythonCommandIds, + releaseCutoverRustCommandIds, +} from "./vocabulary-01.js"; +import type { + ReleaseCutoverCommandExpectation} from "./vocabulary-02.js"; +import { + releaseCutoverInputIssues, + releaseCutoverRequestFilePlaceholder, +} from "./vocabulary-02.js"; +import { + releaseCutoverNegativeCheckExpectations, + releaseCutoverPythonCommandExpectations, + releaseCutoverPythonEvidenceExpectations, + releaseCutoverRustCommandExpectations, +} from "./vocabulary-04.js"; + +function validateReleaseCutoverRustCommandReceipts(receipts: readonly ReleaseCutoverRustCommandReceipt[]): void { + validateNonEmptyArray(receipts, "Release cutover Rust command receipts"); + validateExactStringSet( + receipts.map((entry) => entry.id), + releaseCutoverRustCommandIds, + "Release cutover Rust command receipts", + ); + for (const receipt of receipts) { + if (!receipt || typeof receipt !== "object") throw new Error("Release cutover Rust command receipt is required"); + if (!includesString(releaseCutoverRustCommandIds, receipt.id)) { + throw new Error(`Unknown release cutover Rust command receipt id: ${String(receipt.id)}`); + } + validateStringArray(receipt.command, "Release cutover Rust command receipt command", { allowEmpty: false }); + validateStringArray(receipt.canonicalCommand, "Release cutover Rust command receipt canonicalCommand", { + allowEmpty: false, + }); + validateExactStringSequence( + receipt.command, + receipt.canonicalCommand, + `Release cutover Rust ${receipt.id} command`, + ); + const expected = releaseCutoverRustCommandExpectations[receipt.id]; + validateReleaseCutoverExpectedCommand(receipt.canonicalCommand, expected, receipt.id); + if (receipt.owner !== "graph") throw new Error(`Release cutover Rust command ${receipt.id} owner must be graph`); + if (receipt.status !== "ok") throw new Error(`Release cutover Rust command ${receipt.id} status must be ok`); + if (receipt.exitCode !== 0) throw new Error(`Release cutover Rust command ${receipt.id} exitCode must be 0`); + validateNonEmptyString(receipt.binPath, "Release cutover Rust command receipt binPath"); + if (!receipt.binPath.endsWith("node_modules/.bin/opcore")) { + throw new Error("Release cutover Rust command receipt binPath must use installed node_modules/.bin/opcore"); + } + validateSha256(receipt.stdoutSha256, "Release cutover Rust command receipt stdoutSha256"); + validateSha256(receipt.stderrSha256, "Release cutover Rust command receipt stderrSha256"); + validateNonEmptyString(receipt.assertion, "Release cutover Rust command receipt assertion"); + } +} + +export { validateReleaseCutoverRustCommandReceipts }; + +function validateReleaseCutoverPythonCommandReceipts(receipts: readonly ReleaseCutoverPythonCommandReceipt[]): void { + validateNonEmptyArray(receipts, "Release cutover Python command receipts"); + validateExactStringSet( + receipts.map((entry) => entry.id), + releaseCutoverPythonCommandIds, + "Release cutover Python command receipts", + ); + for (const receipt of receipts) validateReleaseCutoverPythonCommandReceipt(receipt); +} + +export { validateReleaseCutoverPythonCommandReceipts }; + +function validateReleaseCutoverPythonCommandReceipt(receipt: ReleaseCutoverPythonCommandReceipt): void { + validateRequiredObject(receipt, "Release cutover Python command receipt is required"); + if (!includesString(releaseCutoverPythonCommandIds, receipt.id)) { + throw new Error(`Unknown release cutover Python command receipt id: ${String(receipt.id)}`); + } + validateStringArray(receipt.command, "Release cutover Python command receipt command", { allowEmpty: false }); + validateStringArray(receipt.canonicalCommand, "Release cutover Python command receipt canonicalCommand", { + allowEmpty: false, + }); + validateStringArray(receipt.evidence, "Release cutover Python command receipt evidence", { allowEmpty: false }); + validateExactStringSequence( + receipt.command, + receipt.canonicalCommand, + `Release cutover Python ${receipt.id} command`, + ); + const expected = releaseCutoverPythonCommandExpectations[receipt.id]; + validateExactStringSet( + receipt.evidence, + releaseCutoverPythonEvidenceExpectations[receipt.id], + `Release cutover Python command ${receipt.id} evidence`, + ); + validateExactValue( + receipt.command[0], + expected.bin, + `Release cutover Python command ${receipt.id} command must use canonical ${expected.bin} bin`, + ); + validateExactValue( + receipt.canonicalCommand[0], + expected.bin, + `Release cutover Python command ${receipt.id} canonicalCommand must use canonical ${expected.bin} bin`, + ); + validateCommandOwner(receipt.owner); + validateReleaseCutoverExpectedCommand(receipt.canonicalCommand, expected, receipt.id); + validateExactValue( + receipt.owner, + expected.owner, + `Release cutover Python command ${receipt.id} owner must match expected ${expected.owner}`, + ); + validateExactValue(receipt.status, "ok", `Release cutover Python command ${receipt.id} status must be ok`); + validateExactValue(receipt.exitCode, 0, `Release cutover Python command ${receipt.id} exitCode must be 0`); + validateNonEmptyString(receipt.binPath, "Release cutover Python command receipt binPath"); + if (!receipt.binPath.endsWith(`node_modules/.bin/${expected.bin}`)) { + throw new Error( + `Release cutover Python command receipt binPath must use installed node_modules/.bin/${expected.bin}`, + ); + } + validateSha256(receipt.stdoutSha256, "Release cutover Python command receipt stdoutSha256"); + validateSha256(receipt.stderrSha256, "Release cutover Python command receipt stderrSha256"); + validateNonEmptyString(receipt.assertion, "Release cutover Python command receipt assertion"); +} + +function validateReleaseCutoverExpectedCommand( + command: readonly string[], + expectation: ReleaseCutoverCommandExpectation, + id: ReleaseCutoverCommandId | ReleaseCutoverRustCommandId | ReleaseCutoverPythonCommandId, +): void { + if (!releaseCutoverCommandMatchesExpectation(command, expectation)) { + throw new Error( + `Release cutover command ${id} canonicalCommand must match expected ${formatReleaseCutoverCommand(expectation)}`, + ); + } +} + +export { validateReleaseCutoverExpectedCommand }; + +function releaseCutoverCommandMatchesExpectation( + command: readonly string[], + expectation: ReleaseCutoverCommandExpectation, +): boolean { + if (command.length !== expectation.canonicalCommand.length) return false; + return expectation.canonicalCommand.every((expected, index) => { + const actual = command[index]; + if (expected !== releaseCutoverRequestFilePlaceholder) return actual === expected; + return releaseCutoverPathBasename(actual) === expectation.requestFileBasename; + }); +} + +export { releaseCutoverCommandMatchesExpectation }; + +function releaseCutoverPathBasename(path: string): string { + const normalized = path.replaceAll("\\", "/"); + const parts = normalized.split("/"); + return parts[parts.length - 1] ?? normalized; +} + +export { releaseCutoverPathBasename }; + +function formatReleaseCutoverCommand(expectation: ReleaseCutoverCommandExpectation): string { + return expectation.canonicalCommand + .map((part) => (part === releaseCutoverRequestFilePlaceholder ? `<${expectation.requestFileBasename}>` : part)) + .join(" "); +} + +export { formatReleaseCutoverCommand }; + +function validateReleaseCutoverNegativeChecks(checks: readonly ReleaseCutoverNegativeCheck[]): void { + validateNonEmptyArray(checks, "Release cutover negative checks"); + validateExactStringSet( + checks.map((entry) => entry.id), + releaseCutoverNegativeCheckIds, + "Release cutover negative checks", + ); + for (const check of checks) { + if (!check || typeof check !== "object") throw new Error("Release cutover negative check is required"); + validateNonEmptyString(check.id, "Release cutover negative check id"); + if (!includesString(releaseCutoverNegativeCheckIds, check.id)) { + throw new Error(`Unknown release cutover negative check id: ${String(check.id)}`); + } + validateStringArray(check.command, "Release cutover negative check command", { allowEmpty: false }); + validateExactStringSequence( + check.command, + releaseCutoverNegativeCheckExpectations[check.id], + `Release cutover negative check ${check.id} command`, + ); + if (check.status !== "passed") throw new Error("Release cutover negative check status must be passed"); + if (check.exitCode !== 0) throw new Error("Release cutover negative check exitCode must be 0"); + validateNonEmptyString(check.assertion, "Release cutover negative check assertion"); + } +} + +export { validateReleaseCutoverNegativeChecks }; + +function validateReleaseCutoverForbiddenMarkerScan(scan: ReleaseCutoverForbiddenMarkerScan): void { + if (!scan || typeof scan !== "object") throw new Error("Release cutover forbiddenMarkerScan is required"); + validateNonNegativeInteger(scan.scannedTextCount, "Release cutover forbidden marker scannedTextCount"); + if (scan.scannedTextCount === 0) throw new Error("Release cutover forbidden marker scan must scan at least one text"); + if (scan.findingCount !== 0) throw new Error("Release cutover forbidden marker findingCount must be 0"); + validateStringArray(scan.markersBlocked, "Release cutover forbidden marker labels", { allowEmpty: false }); +} + +export { validateReleaseCutoverForbiddenMarkerScan }; + +function validateReleaseCutoverInputEvidence(evidence: readonly ReleaseCutoverInputEvidence[]): void { + validateNonEmptyArray(evidence, "Release cutover input evidence"); + validateExactStringSet( + evidence.map((entry) => entry.issue), + releaseCutoverInputIssues, + "Release cutover input evidence issues", + ); + for (const entry of evidence) { + if (!entry || typeof entry !== "object") throw new Error("Release cutover input evidence entry is required"); + if (!includesString(releaseCutoverInputIssues, entry.issue)) { + throw new Error(`Unknown release cutover input evidence issue: ${String(entry.issue)}`); + } + validateNonEmptyString(entry.path, "Release cutover input evidence path"); + validateSha256(entry.checksumSha256, "Release cutover input evidence checksumSha256"); + } +} + +export { validateReleaseCutoverInputEvidence }; diff --git a/packages/contracts/src/release/graph-contracts.ts b/packages/contracts/src/release/graph-contracts.ts new file mode 100644 index 0000000..56af1d8 --- /dev/null +++ b/packages/contracts/src/release/graph-contracts.ts @@ -0,0 +1,177 @@ +import type { GraphProviderArtifactMetadata } from "../graph/provider-contracts-01.js"; +import type { + GraphReleaseBenchmarkMetric, + GraphReleaseCoreCommandId, + GraphReleaseDeferredChild, + GraphReleaseHandoffIssue, + GraphReleaseOptionalAnalysisSurface, + GraphReleaseRustCommandId, + GraphReleaseSurfaceClassification, +} from "./graph-vocabulary-01.js"; +import type { + GraphCoreNativePackageName, + GraphCoreNativeSupportedTarget, + GraphReleaseDirectSqliteQueryId, + GraphReleaseReportReceiptId, + GraphReleaseServeTransportId, +} from "./graph-vocabulary-02.js"; + +interface GraphReleaseCommandCoverage { + id: GraphReleaseCoreCommandId; + bin: "opcore"; + command: readonly string[]; + canonicalCommand: readonly string[]; + status: "passed"; + exitCode: 0; + fixture: string; + durationMs: number; +} + +export type { GraphReleaseCommandCoverage }; + +interface GraphReleaseRustCommandCoverage { + id: GraphReleaseRustCommandId; + bin: "opcore"; + command: readonly string[]; + canonicalCommand: readonly string[]; + status: "passed"; + exitCode: 0; + fixture: string; + durationMs: number; +} + +export type { GraphReleaseRustCommandCoverage }; + +interface GraphReleaseDirectSqliteQueryReceipt { + id: GraphReleaseDirectSqliteQueryId; + query: string; + status: "passed"; + rowCount: number; + fixture: string; +} + +export type { GraphReleaseDirectSqliteQueryReceipt }; + +interface GraphReleaseServeTransportReceipt { + id: GraphReleaseServeTransportId; + protocol: "opcore.graph.daemon" | "jsonrpc-2.0" | (string & {}); + operation: "ping" | "status" | "query" | "search" | "shutdown" | (string & {}); + status: "passed"; + exitCode: 0; +} + +export type { GraphReleaseServeTransportReceipt }; + +interface GraphReleaseBenchmarkReceipt { + metric: GraphReleaseBenchmarkMetric; + value: number; + unit: "ms" | "bytes"; + baselineIssue: "#19"; + baselineReceipt: string; + comparison: "recorded" | "within_baseline" | "above_baseline" | "below_baseline"; +} + +export type { GraphReleaseBenchmarkReceipt }; + +interface GraphReleasePackageInspection { + packageName: "@the-open-engine/opcore-graph"; + tarballName: string; + fileCount: number; + files: readonly string[]; + forbiddenMarkersAbsent: true; + generatedBuildMetadataAbsent: true; + privatePathsAbsent: true; + sourceProvenanceAbsent: true; + packageMetadataAbsent: true; + gitHistoryAbsent: true; + foreignImplementationNamesAbsent: true; + inspections: readonly string[]; +} + +export type { GraphReleasePackageInspection }; + +interface GraphReleaseNativeArtifactEvidence { + packageName: GraphCoreNativePackageName; + targetPlatform: GraphCoreNativeSupportedTarget; + metadata: GraphProviderArtifactMetadata; + binaryPath: "opcore-graph-core"; + checksumPath: "opcore-graph-core.sha256"; + metadataPath: "metadata.json"; + binarySha256: string; + checksumFileSha256: string; + metadataSha256: string; + packageFiles: readonly string[]; +} + +export type { GraphReleaseNativeArtifactEvidence }; + +interface GraphReleaseReportReceipt { + id: GraphReleaseReportReceiptId; + command: readonly string[]; + status: "passed"; + exitCode: 0; + path: string; + checksumSha256?: string; +} + +export type { GraphReleaseReportReceipt }; + +interface GraphReleaseOptionalSurfaceReceipt { + issue: GraphReleaseDeferredChild; + id: GraphReleaseOptionalAnalysisSurface["id"] | (string & {}); + classification: GraphReleaseSurfaceClassification; + status: "unsupported" | "deferred"; +} + +export type { GraphReleaseOptionalSurfaceReceipt }; + +interface GraphReleaseHandoffReceipt { + issue: GraphReleaseHandoffIssue; + receiptPath: string; + checksumSha256: string; + rollbackNote: string; +} + +export type { GraphReleaseHandoffReceipt }; + +interface GraphReleasePackageVersion { + packageName: string; + version: string; +} + +export type { GraphReleasePackageVersion }; + +interface GraphReleaseReceiptIdentity { + schemaVersion: 1; + issue: "#17"; + origin: "covibes-authored-synthetic"; + generatedAt: string; + commitSha: string; + graphPackageVersions: readonly GraphReleasePackageVersion[]; + graphProviderSchemaVersion: 1; + requiredChildren: readonly string[]; + deferredChildren: readonly string[]; +} + +export type { GraphReleaseReceiptIdentity }; + +interface GraphReleaseReceiptEvidence { + commandCoverage: readonly GraphReleaseCommandCoverage[]; + rustCommandCoverage: readonly GraphReleaseRustCommandCoverage[]; + directSqliteQueries: readonly GraphReleaseDirectSqliteQueryReceipt[]; + serveTransport: readonly GraphReleaseServeTransportReceipt[]; + benchmarks: readonly GraphReleaseBenchmarkReceipt[]; + packageInspection: GraphReleasePackageInspection; + supportedNativeTargets: readonly GraphCoreNativeSupportedTarget[]; + nativeArtifacts: readonly GraphReleaseNativeArtifactEvidence[]; + reportReceipts: readonly GraphReleaseReportReceipt[]; + graphArtifact: GraphProviderArtifactMetadata; + optionalSurfaces: readonly GraphReleaseOptionalSurfaceReceipt[]; + handoff: readonly GraphReleaseHandoffReceipt[]; +} + +export type { GraphReleaseReceiptEvidence }; + +interface GraphReleaseReceipt extends GraphReleaseReceiptIdentity, GraphReleaseReceiptEvidence {} + +export type { GraphReleaseReceipt }; diff --git a/packages/contracts/src/release/graph-optional-validators.ts b/packages/contracts/src/release/graph-optional-validators.ts new file mode 100644 index 0000000..e2f2d78 --- /dev/null +++ b/packages/contracts/src/release/graph-optional-validators.ts @@ -0,0 +1,62 @@ +import { validateGraphReleaseSurfaceClassification } from "../command/helper-validators.js"; +import { includesString } from "../shared/primitives.js"; +import { validateNonEmptyArray, validateNonEmptyString } from "../shared/validators-01.js"; +import type { GraphReleaseOptionalSurfaceReceipt } from "./graph-contracts.js"; +import type { + GraphReleaseDeferredChild} from "./graph-vocabulary-01.js"; +import { + graphReleaseDeferredChildren, + graphReleaseOptionalAnalysisSurfaces, +} from "./graph-vocabulary-01.js"; + +function validateGraphReleaseOptionalSurfaces(surfaces: readonly GraphReleaseOptionalSurfaceReceipt[]): void { + validateNonEmptyArray(surfaces, "Graph release optionalSurfaces"); + for (const surface of surfaces) { + if (!surface || typeof surface !== "object") throw new Error("Graph release optional surface is required"); + validateGraphReleaseDeferredChild(surface.issue, "Graph release optional surface issue"); + validateNonEmptyString(surface.id, "Graph release optional surface id"); + validateGraphReleaseSurfaceClassification(surface.classification); + if (surface.status !== "unsupported" && surface.status !== "deferred") { + throw new Error("Graph release optional surface status must be unsupported or deferred"); + } + if (surface.classification === "required") { + throw new Error("Graph release optional surfaces must not mark staged graph release surfaces as required"); + } + } + validateGraphReleaseOptionalAnalysisSurfaceSet(surfaces, "Graph release optional surfaces"); +} + +export { validateGraphReleaseOptionalSurfaces }; + +function validateGraphReleaseOptionalAnalysisSurfaceSet( + surfaces: readonly Pick[], + label: string, +): void { + const actual = surfaces.map(graphReleaseOptionalSurfaceKey).sort(); + const expected = graphReleaseOptionalAnalysisSurfaces.map(graphReleaseOptionalSurfaceKey).sort(); + if (actual.length !== expected.length || actual.some((value, index) => value !== expected[index])) { + throw new Error(`${label} must match staged graph release surfaces`); + } +} + +export { validateGraphReleaseOptionalAnalysisSurfaceSet }; + +function graphReleaseOptionalSurfaceKey( + surface: Pick, +): string { + return `${surface.issue}:${surface.id}:${surface.classification}:${surface.status}`; +} + +export { graphReleaseOptionalSurfaceKey }; + +function validateGraphReleaseDeferredChild( + issue: unknown, + label = "Graph release deferred child", +): GraphReleaseDeferredChild { + if (!includesString(graphReleaseDeferredChildren, issue)) { + throw new Error(`${label} must be one of ${graphReleaseDeferredChildren.join(", ")}`); + } + return issue; +} + +export { validateGraphReleaseDeferredChild }; diff --git a/packages/contracts/src/release/graph-validators-01.ts b/packages/contracts/src/release/graph-validators-01.ts new file mode 100644 index 0000000..34dd960 --- /dev/null +++ b/packages/contracts/src/release/graph-validators-01.ts @@ -0,0 +1,214 @@ +import { includesString } from "../shared/primitives.js"; +import { + validateExactStringSet, + validateNonEmptyArray, + validateNonEmptyString, + validateStringArray, +} from "../shared/validators-01.js"; +import { + validateExactValue, + validateRequiredObject, +} from "../shared/validators-02.js"; +import type { + GraphReleaseBenchmarkReceipt, + GraphReleaseCommandCoverage, + GraphReleaseDirectSqliteQueryReceipt, + GraphReleasePackageVersion, + GraphReleaseRustCommandCoverage, + GraphReleaseServeTransportReceipt, +} from "./graph-contracts.js"; +import { + validateGraphReleaseBenchmarkMetric, + validateGraphReleaseCoreCommandId, + validateGraphReleaseRustCommandId, +} from "./graph-validators-02.js"; +import { + graphReleaseOperationForServeTransportId, + graphReleaseRouteForCommandId, + graphReleaseRouteForRustCommandId, + validateGraphReleaseServeTransportId, +} from "./graph-validators-03.js"; +import { + graphReleaseBenchmarkMetrics, + graphReleaseCoreCommandIds, + graphReleaseRustCommandIds, +} from "./graph-vocabulary-01.js"; +import { graphReleaseDirectSqliteQueryIds, graphReleaseServeTransportIds } from "./graph-vocabulary-02.js"; + +function validateGraphReleasePackageVersions(versions: readonly GraphReleasePackageVersion[]): void { + validateNonEmptyArray(versions, "Graph release graphPackageVersions"); + for (const version of versions) { + if (!version || typeof version !== "object") throw new Error("Graph release package version is required"); + validateNonEmptyString(version.packageName, "Graph release package version packageName"); + validateNonEmptyString(version.version, "Graph release package version version"); + } + if (!versions.some((version) => version.packageName === "@the-open-engine/opcore-graph")) { + throw new Error("Graph release package versions must include @the-open-engine/opcore-graph"); + } +} + +export { validateGraphReleasePackageVersions }; + +function validateGraphReleaseCommandCoverage(coverage: readonly GraphReleaseCommandCoverage[]): void { + validateNonEmptyArray(coverage, "Graph release commandCoverage"); + validateExactStringSet( + coverage.map((entry) => entry.id), + graphReleaseCoreCommandIds, + "Graph release command coverage ids", + ); + for (const entry of coverage) validateGraphReleaseCommandCoverageEntry(entry); +} + +export { validateGraphReleaseCommandCoverage }; + +function validateGraphReleaseCommandCoverageEntry(entry: GraphReleaseCommandCoverage): void { + validateRequiredObject(entry, "Graph release command coverage entry is required"); + validateGraphReleaseCoreCommandId(entry.id); + validateExactValue(entry.bin, "opcore", `Unknown graph release command bin: ${String(entry.bin)}`); + validateStringArray(entry.command, "Graph release command coverage command", { allowEmpty: false }); + validateStringArray(entry.canonicalCommand, "Graph release command coverage canonicalCommand", { + allowEmpty: false, + }); + validateExactValue(entry.status, "passed", "Graph release command coverage status must be passed"); + validateExactValue(entry.exitCode, 0, "Graph release command coverage exitCode must be 0"); + validateNonEmptyString(entry.fixture, "Graph release command coverage fixture"); + if (typeof entry.durationMs !== "number" || entry.durationMs <= 0) { + throw new Error("Graph release command coverage durationMs must be positive"); + } + const route = graphReleaseRouteForCommandId(entry.id); + validateExactValue(entry.bin, route.bin, `Graph release command ${entry.id} must use ${route.bin}`); + validateExactValue( + entry.command.join("\0"), + route.command.join("\0"), + `Graph release command ${entry.id} command must be ${route.command.join(" ")}`, + ); + validateExactValue( + entry.canonicalCommand.join("\0"), + route.canonicalCommand.join("\0"), + `Graph release command ${entry.id} canonicalCommand must be ${route.canonicalCommand.join(" ")}`, + ); +} + +function validateGraphReleaseRustCommandCoverage(coverage: readonly GraphReleaseRustCommandCoverage[]): void { + validateNonEmptyArray(coverage, "Graph release rustCommandCoverage"); + validateExactStringSet( + coverage.map((entry) => entry.id), + graphReleaseRustCommandIds, + "Graph release Rust command coverage ids", + ); + for (const entry of coverage) validateGraphReleaseRustCommandCoverageEntry(entry); +} + +export { validateGraphReleaseRustCommandCoverage }; + +function validateGraphReleaseRustCommandCoverageEntry(entry: GraphReleaseRustCommandCoverage): void { + validateRequiredObject(entry, "Graph release Rust command coverage entry is required"); + validateGraphReleaseRustCommandId(entry.id); + validateExactValue(entry.bin, "opcore", `Unknown graph release Rust command bin: ${String(entry.bin)}`); + validateStringArray(entry.command, "Graph release Rust command coverage command", { allowEmpty: false }); + validateStringArray(entry.canonicalCommand, "Graph release Rust command coverage canonicalCommand", { + allowEmpty: false, + }); + validateExactValue(entry.status, "passed", "Graph release Rust command coverage status must be passed"); + validateExactValue(entry.exitCode, 0, "Graph release Rust command coverage exitCode must be 0"); + validateNonEmptyString(entry.fixture, "Graph release Rust command coverage fixture"); + if (typeof entry.durationMs !== "number" || entry.durationMs <= 0) { + throw new Error("Graph release Rust command coverage durationMs must be positive"); + } + const route = graphReleaseRouteForRustCommandId(entry.id); + validateExactValue(entry.bin, route.bin, `Graph release Rust command ${entry.id} must use ${route.bin}`); + validateExactValue( + entry.command.join("\0"), + route.command.join("\0"), + `Graph release Rust command ${entry.id} route must match ${route.command.join(" ")}`, + ); + validateExactValue( + entry.canonicalCommand.join("\0"), + route.canonicalCommand.join("\0"), + `Graph release Rust command ${entry.id} route must match ${route.canonicalCommand.join(" ")}`, + ); +} + +function validateGraphReleaseDirectSqliteQueries(queries: readonly GraphReleaseDirectSqliteQueryReceipt[]): void { + validateNonEmptyArray(queries, "Graph release directSqliteQueries"); + validateExactStringSet( + queries.map((entry) => entry.id), + graphReleaseDirectSqliteQueryIds, + "Graph release direct SQLite query ids", + ); + for (const query of queries) { + if (!query || typeof query !== "object") throw new Error("Graph release direct SQLite query receipt is required"); + if (!includesString(graphReleaseDirectSqliteQueryIds, query.id)) { + throw new Error(`Unknown graph release direct SQLite query id: ${String(query.id)}`); + } + validateNonEmptyString(query.query, "Graph release direct SQLite query query"); + if (query.status !== "passed") throw new Error("Graph release direct SQLite query status must be passed"); + if (typeof query.rowCount !== "number" || query.rowCount < 0) { + throw new Error("Graph release direct SQLite query rowCount must be non-negative"); + } + validateNonEmptyString(query.fixture, "Graph release direct SQLite query fixture"); + } +} + +export { validateGraphReleaseDirectSqliteQueries }; + +function validateGraphReleaseServeTransport(receipts: readonly GraphReleaseServeTransportReceipt[]): void { + validateNonEmptyArray(receipts, "Graph release serveTransport"); + validateExactStringSet( + receipts.map((entry) => entry.id), + graphReleaseServeTransportIds, + "Graph release serve transport ids", + ); + for (const receipt of receipts) { + if (!receipt || typeof receipt !== "object") throw new Error("Graph release serve transport receipt is required"); + validateGraphReleaseServeTransportId(receipt.id); + if (receipt.protocol !== "opcore.graph.daemon") { + throw new Error("Graph release serve transport protocol must be opcore.graph.daemon"); + } + validateNonEmptyString(receipt.operation, "Graph release serve transport operation"); + if (receipt.operation !== graphReleaseOperationForServeTransportId(receipt.id)) { + throw new Error( + `Graph release serve transport ${receipt.id} operation must be ` + + graphReleaseOperationForServeTransportId(receipt.id), + ); + } + if (receipt.status !== "passed") throw new Error("Graph release serve transport status must be passed"); + if (receipt.exitCode !== 0) throw new Error("Graph release serve transport exitCode must be 0"); + } +} + +export { validateGraphReleaseServeTransport }; + +function validateGraphReleaseBenchmarks(benchmarks: readonly GraphReleaseBenchmarkReceipt[]): void { + validateNonEmptyArray(benchmarks, "Graph release benchmarks"); + validateExactStringSet( + benchmarks.map((entry) => entry.metric), + graphReleaseBenchmarkMetrics, + "Graph release benchmark metrics", + ); + for (const benchmark of benchmarks) validateGraphReleaseBenchmark(benchmark); +} + +export { validateGraphReleaseBenchmarks }; + +function validateGraphReleaseBenchmark(benchmark: GraphReleaseBenchmarkReceipt): void { + validateRequiredObject(benchmark, "Graph release benchmark receipt is required"); + validateGraphReleaseBenchmarkMetric(benchmark.metric); + if (typeof benchmark.value !== "number" || benchmark.value <= 0) { + throw new Error("Graph release benchmark value must be positive"); + } + if (benchmark.unit !== "ms" && benchmark.unit !== "bytes") { + throw new Error("Graph release benchmark unit must be ms or bytes"); + } + const expectedUnit = benchmark.metric.endsWith("_bytes") ? "bytes" : "ms"; + validateExactValue( + benchmark.unit, + expectedUnit, + `Graph release benchmark ${benchmark.metric} must use ${expectedUnit}`, + ); + validateExactValue(benchmark.baselineIssue, "#19", "Graph release benchmark baselineIssue must be #19"); + validateNonEmptyString(benchmark.baselineReceipt, "Graph release benchmark baselineReceipt"); + if (!["recorded", "within_baseline", "above_baseline", "below_baseline"].includes(benchmark.comparison)) { + throw new Error(`Unknown graph release benchmark comparison: ${String(benchmark.comparison)}`); + } +} diff --git a/packages/contracts/src/release/graph-validators-02.ts b/packages/contracts/src/release/graph-validators-02.ts new file mode 100644 index 0000000..65ce31a --- /dev/null +++ b/packages/contracts/src/release/graph-validators-02.ts @@ -0,0 +1,198 @@ +import { includesString } from "../shared/primitives.js"; +import { validateGraphProviderArtifactMetadata } from "../graph/protocol-validators.js"; +import { + validateExactStringSet, + validateNonEmptyArray, + validateNonEmptyString, + validateSha256, + validateStringArray, +} from "../shared/validators-01.js"; +import { + validateExactValue, + validateRequiredObject, +} from "../shared/validators-02.js"; +import type { + GraphReleaseHandoffReceipt, + GraphReleaseNativeArtifactEvidence, + GraphReleasePackageInspection, + GraphReleaseReportReceipt, +} from "./graph-contracts.js"; +import { validateGraphReleaseHandoffIssue } from "./graph-validators-03.js"; +import type { + GraphReleaseBenchmarkMetric, + GraphReleaseCoreCommandId, + GraphReleaseRustCommandId} from "./graph-vocabulary-01.js"; +import { + graphReleaseBenchmarkMetrics, + graphReleaseCoreCommandIds, + graphReleaseHandoffIssues, + graphReleaseRustCommandIds, +} from "./graph-vocabulary-01.js"; +import { + graphCoreNativePackageNameForTarget, + graphCoreNativeSupportedTargets, + graphReleaseReportReceiptIds, +} from "./graph-vocabulary-02.js"; + +function validateGraphReleasePackageInspection(inspection: GraphReleasePackageInspection): void { + if (!inspection || typeof inspection !== "object") throw new Error("Graph release packageInspection is required"); + if (inspection.packageName !== "@the-open-engine/opcore-graph") { + throw new Error("Graph release packageInspection packageName must be @the-open-engine/opcore-graph"); + } + validateNonEmptyString(inspection.tarballName, "Graph release packageInspection tarballName"); + if (typeof inspection.fileCount !== "number" || inspection.fileCount <= 0) { + throw new Error("Graph release packageInspection fileCount must be positive"); + } + validateStringArray(inspection.files, "Graph release packageInspection files", { allowEmpty: false }); + if (inspection.fileCount !== inspection.files.length) { + throw new Error("Graph release packageInspection fileCount must equal files length"); + } + validateStringArray(inspection.inspections, "Graph release packageInspection inspections", { allowEmpty: false }); + for (const key of [ + "forbiddenMarkersAbsent", + "generatedBuildMetadataAbsent", + "privatePathsAbsent", + "sourceProvenanceAbsent", + "packageMetadataAbsent", + "gitHistoryAbsent", + "foreignImplementationNamesAbsent", + ] as const) { + if (inspection[key] !== true) throw new Error(`Graph release packageInspection ${key} must be true`); + } +} + +export { validateGraphReleasePackageInspection }; + +function validateGraphReleaseNativeArtifacts(nativeArtifacts: readonly GraphReleaseNativeArtifactEvidence[]): void { + validateNonEmptyArray(nativeArtifacts, "Graph release nativeArtifacts"); + validateExactStringSet( + nativeArtifacts.map((artifact) => artifact.targetPlatform), + graphCoreNativeSupportedTargets, + "Graph release native artifact targets", + ); + for (const nativeArtifact of nativeArtifacts) validateGraphReleaseNativeArtifact(nativeArtifact); +} + +export { validateGraphReleaseNativeArtifacts }; + +function validateGraphReleaseNativeArtifact(nativeArtifact: GraphReleaseNativeArtifactEvidence): void { + validateRequiredObject(nativeArtifact, "Graph release native artifact evidence is required"); + const expectedPackageName = graphCoreNativePackageNameForTarget(nativeArtifact.targetPlatform); + validateExactValue( + nativeArtifact.packageName, + expectedPackageName, + `Graph release native artifact packageName for ${nativeArtifact.targetPlatform} must be ${expectedPackageName}`, + ); + validateGraphProviderArtifactMetadata(nativeArtifact.metadata); + validateExactValue( + nativeArtifact.metadata.targetPlatform, + nativeArtifact.targetPlatform, + "Graph release native artifact targetPlatform must match metadata", + ); + validateExactValue( + nativeArtifact.binaryPath, + "opcore-graph-core", + "Graph release native binaryPath must be opcore-graph-core", + ); + validateExactValue( + nativeArtifact.checksumPath, + "opcore-graph-core.sha256", + "Graph release native checksumPath must be opcore-graph-core.sha256", + ); + validateExactValue( + nativeArtifact.metadataPath, + "metadata.json", + "Graph release native metadataPath must be metadata.json", + ); + validateExactValue( + nativeArtifact.metadata.binaryPath, + nativeArtifact.binaryPath, + "Graph release native artifact binaryPath must match metadata", + ); + validateExactValue( + nativeArtifact.metadata.checksumPath, + nativeArtifact.checksumPath, + "Graph release native artifact checksumPath must match metadata", + ); + validateExactValue( + nativeArtifact.metadata.checksumSha256, + nativeArtifact.binarySha256, + "Graph release native artifact metadata checksum must match binary sha256", + ); + validateSha256(nativeArtifact.binarySha256, "Graph release native artifact binarySha256"); + validateSha256(nativeArtifact.checksumFileSha256, "Graph release native artifact checksumFileSha256"); + validateSha256(nativeArtifact.metadataSha256, "Graph release native artifact metadataSha256"); + validateExactStringSet( + nativeArtifact.packageFiles, + ["package.json", "README.md", "opcore-graph-core", "opcore-graph-core.sha256", "metadata.json"], + `Graph release native package files ${nativeArtifact.targetPlatform}`, + ); +} + +function validateGraphReleaseReportReceipts(receipts: readonly GraphReleaseReportReceipt[]): void { + validateNonEmptyArray(receipts, "Graph release reportReceipts"); + validateExactStringSet( + receipts.map((entry) => entry.id), + graphReleaseReportReceiptIds, + "Graph release report receipt ids", + ); + for (const receipt of receipts) { + if (!receipt || typeof receipt !== "object") throw new Error("Graph release report receipt is required"); + if (!includesString(graphReleaseReportReceiptIds, receipt.id)) { + throw new Error(`Unknown graph release report receipt id: ${String(receipt.id)}`); + } + validateStringArray(receipt.command, "Graph release report receipt command", { allowEmpty: false }); + if (receipt.status !== "passed") throw new Error("Graph release report receipt status must be passed"); + if (receipt.exitCode !== 0) throw new Error("Graph release report receipt exitCode must be 0"); + validateNonEmptyString(receipt.path, "Graph release report receipt path"); + if (receipt.checksumSha256 !== undefined) + validateNonEmptyString(receipt.checksumSha256, "Graph release report receipt checksumSha256"); + } +} + +export { validateGraphReleaseReportReceipts }; + +function validateGraphReleaseHandoff(handoff: readonly GraphReleaseHandoffReceipt[]): void { + validateNonEmptyArray(handoff, "Graph release handoff"); + validateExactStringSet( + handoff.map((entry) => entry.issue), + graphReleaseHandoffIssues, + "Graph release handoff issues", + ); + for (const entry of handoff) { + if (!entry || typeof entry !== "object") throw new Error("Graph release handoff entry is required"); + validateGraphReleaseHandoffIssue(entry.issue); + validateNonEmptyString(entry.receiptPath, "Graph release handoff receiptPath"); + validateNonEmptyString(entry.checksumSha256, "Graph release handoff checksumSha256"); + validateNonEmptyString(entry.rollbackNote, "Graph release handoff rollbackNote"); + } +} + +export { validateGraphReleaseHandoff }; + +function validateGraphReleaseCoreCommandId(id: unknown): GraphReleaseCoreCommandId { + if (!includesString(graphReleaseCoreCommandIds, id)) { + throw new Error(`Unknown graph release command id: ${String(id)}`); + } + return id; +} + +export { validateGraphReleaseCoreCommandId }; + +function validateGraphReleaseRustCommandId(id: unknown): GraphReleaseRustCommandId { + if (!includesString(graphReleaseRustCommandIds, id)) { + throw new Error(`Unknown graph release Rust command id: ${String(id)}`); + } + return id; +} + +export { validateGraphReleaseRustCommandId }; + +function validateGraphReleaseBenchmarkMetric(metric: unknown): GraphReleaseBenchmarkMetric { + if (!includesString(graphReleaseBenchmarkMetrics, metric)) { + throw new Error(`Unknown graph release benchmark metric: ${String(metric)}`); + } + return metric; +} + +export { validateGraphReleaseBenchmarkMetric }; diff --git a/packages/contracts/src/release/graph-validators-03.ts b/packages/contracts/src/release/graph-validators-03.ts new file mode 100644 index 0000000..9f0d537 --- /dev/null +++ b/packages/contracts/src/release/graph-validators-03.ts @@ -0,0 +1,80 @@ +import { includesString } from "../shared/primitives.js"; +import { validateExactStringSet, validateNonEmptyArray } from "../shared/validators-01.js"; +import type { + GraphReleaseCoreCommandId, + GraphReleaseHandoffIssue, + GraphReleaseRustCommandId} from "./graph-vocabulary-01.js"; +import { + graphReleaseHandoffIssues, +} from "./graph-vocabulary-01.js"; +import type { GraphReleaseServeTransportId} from "./graph-vocabulary-02.js"; +import { graphReleaseServeTransportIds } from "./graph-vocabulary-02.js"; +import type { ReleaseReceiptPackageEvidence } from "./receipt-contracts-01.js"; +import { validateReleaseReceiptPackage } from "./receipt-validators-01.js"; +import { releaseReceiptPackageNames } from "./vocabulary-01.js"; + +function validateGraphReleaseHandoffIssue(issue: unknown): GraphReleaseHandoffIssue { + if (!includesString(graphReleaseHandoffIssues, issue)) { + throw new Error(`Unknown graph release handoff issue: ${String(issue)}`); + } + return issue; +} + +export { validateGraphReleaseHandoffIssue }; + +function validateGraphReleaseServeTransportId(id: unknown): GraphReleaseServeTransportId { + if (!includesString(graphReleaseServeTransportIds, id)) { + throw new Error(`Unknown graph release serve transport id: ${String(id)}`); + } + return id; +} + +export { validateGraphReleaseServeTransportId }; + +function graphReleaseOperationForServeTransportId(id: GraphReleaseServeTransportId): string { + return id.replace("serve-jsonl-", ""); +} + +export { graphReleaseOperationForServeTransportId }; + +function graphReleaseRouteForCommandId(id: GraphReleaseCoreCommandId): { + bin: "opcore"; + command: readonly string[]; + canonicalCommand: readonly string[]; +} { + const command = id.replace("opcore-graph-", ""); + return { + bin: "opcore", + command: ["graph", command], + canonicalCommand: ["opcore", "graph", command], + }; +} + +export { graphReleaseRouteForCommandId }; + +function graphReleaseRouteForRustCommandId(id: GraphReleaseRustCommandId): { + bin: "opcore"; + command: readonly string[]; + canonicalCommand: readonly string[]; +} { + const command = id.replace("opcore-graph-rust-", ""); + return { + bin: "opcore", + command: ["graph", command], + canonicalCommand: ["opcore", "graph", command], + }; +} + +export { graphReleaseRouteForRustCommandId }; + +function validateReleaseReceiptPackages(packages: readonly ReleaseReceiptPackageEvidence[]): void { + validateNonEmptyArray(packages, "Release receipt package evidence"); + validateExactStringSet( + packages.map((entry) => entry.packageName), + releaseReceiptPackageNames, + "Release receipt package evidence", + ); + for (const packageEvidence of packages) validateReleaseReceiptPackage(packageEvidence); +} + +export { validateReleaseReceiptPackages }; diff --git a/packages/contracts/src/release/graph-vocabulary-01.ts b/packages/contracts/src/release/graph-vocabulary-01.ts new file mode 100644 index 0000000..27ac4ec --- /dev/null +++ b/packages/contracts/src/release/graph-vocabulary-01.ts @@ -0,0 +1,117 @@ +const graphReleaseSurfaceClassifications = ["required", "supporting", "optional", "deferred"] as const; + +export { graphReleaseSurfaceClassifications }; + +type GraphReleaseSurfaceClassification = (typeof graphReleaseSurfaceClassifications)[number]; + +export type { GraphReleaseSurfaceClassification }; + +const graphReleaseCoreCommandIds = [ + "opcore-graph-build", + "opcore-graph-update", + "opcore-graph-watch", + "opcore-graph-status", + "opcore-graph-query", + "opcore-graph-impact", + "opcore-graph-search", + "opcore-graph-serve", +] as const; + +export { graphReleaseCoreCommandIds }; + +type GraphReleaseCoreCommandId = (typeof graphReleaseCoreCommandIds)[number]; + +export type { GraphReleaseCoreCommandId }; + +const graphReleaseRustCommandIds = [ + "opcore-graph-rust-build", + "opcore-graph-rust-update", + "opcore-graph-rust-watch", + "opcore-graph-rust-status", + "opcore-graph-rust-query", + "opcore-graph-rust-impact", + "opcore-graph-rust-search", + "opcore-graph-rust-serve", +] as const; + +export { graphReleaseRustCommandIds }; + +type GraphReleaseRustCommandId = (typeof graphReleaseRustCommandIds)[number]; + +export type { GraphReleaseRustCommandId }; + +const graphReleaseBenchmarkMetrics = [ + "install_setup_ms", + "cold_build_ms", + "incremental_update_ms", + "impact_cold_ms", + "impact_hot_ms", + "search_ms", + "daemon_startup_ms", + "daemon_query_ms", + "db_size_bytes", + "wal_size_bytes", +] as const; + +export { graphReleaseBenchmarkMetrics }; + +type GraphReleaseBenchmarkMetric = (typeof graphReleaseBenchmarkMetrics)[number]; + +export type { GraphReleaseBenchmarkMetric }; + +const graphReleaseRequiredChildren = ["#35", "#8", "#9", "#10", "#11", "#12", "#19", "#47"] as const; + +export { graphReleaseRequiredChildren }; + +type GraphReleaseRequiredChild = (typeof graphReleaseRequiredChildren)[number]; + +export type { GraphReleaseRequiredChild }; + +const graphReleaseDeferredChildren = ["#13", "#14", "#15", "#16"] as const; + +export { graphReleaseDeferredChildren }; + +type GraphReleaseDeferredChild = (typeof graphReleaseDeferredChildren)[number]; + +export type { GraphReleaseDeferredChild }; + +const graphReleaseOptionalAnalysisSurfaces = [ + { + issue: "#13", + id: "coverage", + classification: "deferred", + status: "deferred", + }, + { + issue: "#14", + id: "flows", + classification: "optional", + status: "deferred", + }, + { + issue: "#15", + id: "communities", + classification: "optional", + status: "deferred", + }, + { + issue: "#16", + id: "read_only_suggestions", + classification: "supporting", + status: "deferred", + }, +] as const; + +export { graphReleaseOptionalAnalysisSurfaces }; + +type GraphReleaseOptionalAnalysisSurface = (typeof graphReleaseOptionalAnalysisSurfaces)[number]; + +export type { GraphReleaseOptionalAnalysisSurface }; + +const graphReleaseHandoffIssues = ["#7", "#28", "#29"] as const; + +export { graphReleaseHandoffIssues }; + +type GraphReleaseHandoffIssue = (typeof graphReleaseHandoffIssues)[number]; + +export type { GraphReleaseHandoffIssue }; diff --git a/packages/contracts/src/release/graph-vocabulary-02.ts b/packages/contracts/src/release/graph-vocabulary-02.ts new file mode 100644 index 0000000..76a9c6c --- /dev/null +++ b/packages/contracts/src/release/graph-vocabulary-02.ts @@ -0,0 +1,69 @@ +const graphReleaseDirectSqliteQueryIds = [ + "status-counts", + "status-edge-counts", + "impact-edges-from-file", + "search-by-name", + "freshness-metadata", +] as const; + +export { graphReleaseDirectSqliteQueryIds }; + +type GraphReleaseDirectSqliteQueryId = (typeof graphReleaseDirectSqliteQueryIds)[number]; + +export type { GraphReleaseDirectSqliteQueryId }; + +const graphReleaseServeTransportIds = [ + "serve-jsonl-ping", + "serve-jsonl-status", + "serve-jsonl-query", + "serve-jsonl-search", + "serve-jsonl-shutdown", +] as const; + +export { graphReleaseServeTransportIds }; + +type GraphReleaseServeTransportId = (typeof graphReleaseServeTransportIds)[number]; + +export type { GraphReleaseServeTransportId }; + +const graphReleaseReportReceiptIds = ["conformance", "pack", "license", "provenance"] as const; + +export { graphReleaseReportReceiptIds }; + +type GraphReleaseReportReceiptId = (typeof graphReleaseReportReceiptIds)[number]; + +export type { GraphReleaseReportReceiptId }; + +const graphCoreNativeSupportedTargets = ["darwin-arm64", "darwin-x64", "linux-x64"] as const; + +export { graphCoreNativeSupportedTargets }; + +type GraphCoreNativeSupportedTarget = (typeof graphCoreNativeSupportedTargets)[number]; + +export type { GraphCoreNativeSupportedTarget }; + +const graphCoreNativePackageNames = [ + "@the-open-engine/opcore-graph-core-darwin-arm64", + "@the-open-engine/opcore-graph-core-darwin-x64", + "@the-open-engine/opcore-graph-core-linux-x64", +] as const; + +export { graphCoreNativePackageNames }; + +type GraphCoreNativePackageName = (typeof graphCoreNativePackageNames)[number]; + +export type { GraphCoreNativePackageName }; + +const graphCoreNativePackageNamesByTarget = { + "darwin-arm64": "@the-open-engine/opcore-graph-core-darwin-arm64", + "darwin-x64": "@the-open-engine/opcore-graph-core-darwin-x64", + "linux-x64": "@the-open-engine/opcore-graph-core-linux-x64", +} as const satisfies Record; + +export { graphCoreNativePackageNamesByTarget }; + +function graphCoreNativePackageNameForTarget(target: GraphCoreNativeSupportedTarget): GraphCoreNativePackageName { + return graphCoreNativePackageNamesByTarget[target]; +} + +export { graphCoreNativePackageNameForTarget }; diff --git a/packages/contracts/src/release/public-validators.ts b/packages/contracts/src/release/public-validators.ts new file mode 100644 index 0000000..0d0b679 --- /dev/null +++ b/packages/contracts/src/release/public-validators.ts @@ -0,0 +1,212 @@ +import { validateRequiredObject } from "../shared/validators-02.js"; +import { validateGraphProviderArtifactMetadata } from "../graph/protocol-validators.js"; +import { + validateExactStringSequence, + validateExactStringSet, + validateGraphReleaseSourceFreeStrings, + validateNonEmptyString, + validateSha256, +} from "../shared/validators-01.js"; +import type { AspDogfoodReceipt } from "./asp-contracts-01.js"; +import { + validateAspDogfoodAspHome, + validateAspDogfoodHostEvaluation, + validateAspDogfoodHostFixture, + validateAspDogfoodManager, + validateAspDogfoodManagerState, + validateAspDogfoodProvider, + validateAspDogfoodProviderProbe, + validateAspDogfoodRepoEnrollment, +} from "./asp-validators-01.js"; +import { + validateAspDogfoodAuthority, + validateAspDogfoodForbiddenMarkerScan, + validateAspDogfoodForbiddenProviderEntrypoint, + validateAspDogfoodParityBlockers, + validateAspDogfoodUnsupportedSurfaces, +} from "./asp-validators-02.js"; +import type { OpcoreSelfValidationReceipt, ReleaseCutoverReceipt } from "./cutover-contracts.js"; +import { + validateReleaseCutoverCommandReceipts, + validateReleaseCutoverDescriptor, + validateReleaseCutoverEnvironmentIsolation, + validateReleaseCutoverInstalledPackages, +} from "./cutover-validators-01.js"; +import { + validateReleaseCutoverForbiddenMarkerScan, + validateReleaseCutoverInputEvidence, + validateReleaseCutoverNegativeChecks, + validateReleaseCutoverPythonCommandReceipts, + validateReleaseCutoverRustCommandReceipts, +} from "./cutover-validators-02.js"; +import type { GraphReleaseReceipt } from "./graph-contracts.js"; +import { + validateGraphReleaseBenchmarks, + validateGraphReleaseCommandCoverage, + validateGraphReleaseDirectSqliteQueries, + validateGraphReleasePackageVersions, + validateGraphReleaseRustCommandCoverage, + validateGraphReleaseServeTransport, +} from "./graph-validators-01.js"; +import { + validateGraphReleaseHandoff, + validateGraphReleaseNativeArtifacts, + validateGraphReleasePackageInspection, + validateGraphReleaseReportReceipts, +} from "./graph-validators-02.js"; +import { validateGraphReleaseOptionalSurfaces } from "./graph-optional-validators.js"; +import { validateReleaseReceiptPackages } from "./graph-validators-03.js"; +import { graphReleaseDeferredChildren, graphReleaseRequiredChildren } from "./graph-vocabulary-01.js"; +import { graphCoreNativeSupportedTargets } from "./graph-vocabulary-02.js"; +import type { ReleaseReceipt } from "./receipt-contracts-02.js"; +import { validateReleaseReceiptDescriptor } from "./receipt-validators-01.js"; +import { + validateReleaseReceiptLicense, + validateReleaseReceiptNativeArtifacts, + validateReleaseReceiptProvenance, + validateReleaseReceiptSecretHistory, +} from "./receipt-validators-02.js"; +import { validateReleaseReceiptGraphReleaseEvidence, validateReleaseReceiptReports } from "./receipt-validators-03.js"; +import { releaseReceiptCommandGroups, releaseReceiptPackageNames } from "./vocabulary-01.js"; + +function validateGraphReleaseReceipt(receipt: GraphReleaseReceipt): GraphReleaseReceipt { + validateRequiredObject(receipt, "Graph release receipt is required"); + if (receipt.schemaVersion !== 1) { + throw new Error("Graph release receipt schemaVersion must be 1"); + } + if (receipt.issue !== "#17") { + throw new Error("Graph release receipt issue must be #17"); + } + if (receipt.origin !== "covibes-authored-synthetic") { + throw new Error("Graph release receipt origin must be covibes-authored-synthetic"); + } + + validateNonEmptyString(receipt.generatedAt, "Graph release receipt generatedAt"); + validateNonEmptyString(receipt.commitSha, "Graph release receipt commitSha"); + if (receipt.graphProviderSchemaVersion !== 1) { + throw new Error("Graph release receipt graphProviderSchemaVersion must be 1"); + } + validateGraphReleasePackageVersions(receipt.graphPackageVersions); + validateExactStringSet(receipt.requiredChildren, graphReleaseRequiredChildren, "Graph release required children"); + validateExactStringSet(receipt.deferredChildren, graphReleaseDeferredChildren, "Graph release deferred children"); + validateGraphReleaseCommandCoverage(receipt.commandCoverage); + validateGraphReleaseRustCommandCoverage(receipt.rustCommandCoverage); + validateGraphReleaseDirectSqliteQueries(receipt.directSqliteQueries); + validateGraphReleaseServeTransport(receipt.serveTransport); + validateGraphReleaseBenchmarks(receipt.benchmarks); + validateGraphReleasePackageInspection(receipt.packageInspection); + validateExactStringSet( + receipt.supportedNativeTargets, + graphCoreNativeSupportedTargets, + "Graph release supported native targets", + ); + validateGraphReleaseNativeArtifacts(receipt.nativeArtifacts); + validateGraphReleaseReportReceipts(receipt.reportReceipts); + validateGraphProviderArtifactMetadata(receipt.graphArtifact); + validateGraphReleaseOptionalSurfaces(receipt.optionalSurfaces); + validateGraphReleaseHandoff(receipt.handoff); + validateGraphReleaseSourceFreeStrings(receipt); + + return receipt; +} + +export { validateGraphReleaseReceipt }; + +function validateReleaseReceipt(receipt: ReleaseReceipt): ReleaseReceipt { + if (!receipt || typeof receipt !== "object") throw new Error("Release receipt is required"); + if (receipt.schemaVersion !== 1) throw new Error("Release receipt schemaVersion must be 1"); + if (receipt.issue !== "#29") throw new Error("Release receipt issue must be #29"); + if (receipt.origin !== "covibes-authored-release-proof") { + throw new Error("Release receipt origin must be covibes-authored-release-proof"); + } + validateNonEmptyString(receipt.generatedAt, "Release receipt generatedAt"); + validateNonEmptyString(receipt.commitSha, "Release receipt commitSha"); + if (receipt.privateRepo !== true) throw new Error("Release receipt maintainer evidence marker must be true"); + validateExactStringSet(receipt.packageNames, releaseReceiptPackageNames, "Release receipt package names"); + validateExactStringSet(receipt.commandGroups, releaseReceiptCommandGroups, "Release receipt command groups"); + validateReleaseReceiptPackages(receipt.packages); + validateReleaseReceiptDescriptor(receipt.descriptor, receipt.packages); + validateReleaseReceiptNativeArtifacts(receipt.nativeArtifacts, receipt.packages, receipt.descriptor); + validateReleaseReceiptLicense(receipt.license); + validateReleaseReceiptProvenance(receipt.provenance); + validateReleaseReceiptSecretHistory(receipt.secretHistory); + validateReleaseReceiptReports(receipt.reports); + validateReleaseReceiptGraphReleaseEvidence(receipt.graphReleaseReceipt); + return receipt; +} + +export { validateReleaseReceipt }; + +const validateOpcoreSelfValidation = (receipt: OpcoreSelfValidationReceipt, label: string): void => { + if (!receipt || typeof receipt !== "object") throw new Error(`${label} receipt is required`); + if (receipt.id !== "opcore-self-check") throw new Error(`${label} id must be opcore-self-check`); + validateExactStringSequence(receipt.command, ["npm", "run", "opcore:self-check"], `${label} command`); + if (receipt.status !== "passed") throw new Error(`${label} status must be passed`); + if (receipt.exitCode !== 0) throw new Error(`${label} exitCode must be 0`); + validateSha256(receipt.stdoutSha256, `${label} stdoutSha256`); + validateSha256(receipt.stderrSha256, `${label} stderrSha256`); + validateNonEmptyString(receipt.assertion, `${label} assertion`); +}; + +export { validateOpcoreSelfValidation }; + +function validateReleaseCutoverReceipt(receipt: ReleaseCutoverReceipt): ReleaseCutoverReceipt { + if (!receipt || typeof receipt !== "object") throw new Error("Release cutover receipt is required"); + if (receipt.schemaVersion !== 1) throw new Error("Release cutover receipt schemaVersion must be 1"); + if (receipt.issue !== "#30") throw new Error("Release cutover receipt issue must be #30"); + if (receipt.origin !== "covibes-authored-cutover-proof") { + throw new Error("Release cutover receipt origin must be covibes-authored-cutover-proof"); + } + validateNonEmptyString(receipt.generatedAt, "Release cutover receipt generatedAt"); + validateNonEmptyString(receipt.commitSha, "Release cutover receipt commitSha"); + if (receipt.privateRepo !== true) throw new Error("Release cutover receipt maintainer evidence marker must be true"); + validateExactStringSet(receipt.packageNames, releaseReceiptPackageNames, "Release cutover receipt package names"); + validateReleaseCutoverInstalledPackages(receipt.installedPackages); + validateReleaseCutoverDescriptor(receipt.descriptor); + validateReleaseCutoverEnvironmentIsolation(receipt.environmentIsolation); + validateReleaseCutoverCommandReceipts(receipt.commandReceipts); + validateReleaseCutoverRustCommandReceipts(receipt.rustCommandReceipts); + validateReleaseCutoverPythonCommandReceipts(receipt.pythonCommandReceipts); + validateReleaseCutoverNegativeChecks(receipt.negativeChecks); + validateOpcoreSelfValidation(receipt.selfValidation, "Release cutover self-validation"); + validateReleaseCutoverForbiddenMarkerScan(receipt.forbiddenMarkerScan); + validateReleaseCutoverInputEvidence(receipt.inputEvidence); + return receipt; +} + +export { validateReleaseCutoverReceipt }; + +function validateAspDogfoodReceipt(receipt: AspDogfoodReceipt): AspDogfoodReceipt { + if (!receipt || typeof receipt !== "object") throw new Error("ASP dogfood receipt is required"); + if (receipt.schemaVersion !== 1) throw new Error("ASP dogfood receipt schemaVersion must be 1"); + if (receipt.issue !== "#120") throw new Error("ASP dogfood receipt issue must be #120"); + if (receipt.origin !== "covibes-authored-asp-dogfood-proof") { + throw new Error("ASP dogfood receipt origin must be covibes-authored-asp-dogfood-proof"); + } + validateNonEmptyString(receipt.generatedAt, "ASP dogfood receipt generatedAt"); + validateNonEmptyString(receipt.commitSha, "ASP dogfood receipt commitSha"); + if (receipt.privateRepo !== true) throw new Error("ASP dogfood receipt privateRepo must be true"); + if (receipt.bootstrapSource !== "local-sibling") throw new Error("ASP dogfood bootstrapSource must be local-sibling"); + validateExactStringSet(receipt.packageNames, releaseReceiptPackageNames, "ASP dogfood receipt package names"); + validateReleaseCutoverInstalledPackages(receipt.installedPackages); + validateAspDogfoodManager(receipt.manager); + validateAspDogfoodAspHome(receipt.aspHome); + validateAspDogfoodHostFixture(receipt.hostFixture); + validateAspDogfoodProvider(receipt.provider); + validateAspDogfoodManagerState(receipt.managerState); + validateAspDogfoodRepoEnrollment(receipt.repoEnrollment); + validateAspDogfoodHostEvaluation(receipt.hostEvaluation); + validateAspDogfoodProviderProbe(receipt.providerProbe); + validateOpcoreSelfValidation(receipt.selfValidation, "ASP dogfood self-validation"); + validateAspDogfoodUnsupportedSurfaces(receipt.unsupportedSurfaces); + validateAspDogfoodParityBlockers(receipt.parityBlockers); + validateAspDogfoodAuthority(receipt.authority); + if (!Array.isArray(receipt.publicReleaseActions) || receipt.publicReleaseActions.length !== 0) { + throw new Error("ASP dogfood receipt must not record public publish, registry, or standard-readiness actions"); + } + validateAspDogfoodForbiddenMarkerScan(receipt.forbiddenMarkerScan); + validateAspDogfoodForbiddenProviderEntrypoint(receipt); + return receipt; +} + +export { validateAspDogfoodReceipt }; diff --git a/packages/contracts/src/release/receipt-contracts-01.ts b/packages/contracts/src/release/receipt-contracts-01.ts new file mode 100644 index 0000000..c5ab099 --- /dev/null +++ b/packages/contracts/src/release/receipt-contracts-01.ts @@ -0,0 +1,205 @@ +import type { GraphProviderArtifactMetadata } from "../graph/provider-contracts-01.js"; +import type { + ManagedToolDescriptor, + ManagedToolDescriptorArtifactReference, + ManagedToolDescriptorArtifactType, +} from "../managed/contracts.js"; +import type { GraphCoreNativePackageName, GraphCoreNativeSupportedTarget } from "./graph-vocabulary-02.js"; +import type { + ReleaseReceiptCommandGroupName, + ReleaseReceiptPackageName, + ReleaseReceiptReportId, + ReleaseReceiptSecretFindingScope, +} from "./vocabulary-01.js"; + +interface ReleaseReceiptTarballEvidence { + filename: string; + path: string; + sha256: string; + integrity?: string; + shasum?: string; +} + +export type { ReleaseReceiptTarballEvidence }; + +interface ReleaseReceiptPackageManifestMetadata { + name: ReleaseReceiptPackageName; + version: string; + license: string; + main?: string; + types?: string; + files: readonly string[]; + bins: Readonly>; + dependencies: Readonly>; + optionalDependencies?: Readonly>; + bundledDependencies: readonly string[]; + os?: readonly string[]; + cpu?: readonly string[]; +} + +export type { ReleaseReceiptPackageManifestMetadata }; + +interface ReleaseReceiptNativeArtifactEvidence { + packageName: "opcore"; + bundledPackageName: GraphCoreNativePackageName; + targetPlatform: GraphCoreNativeSupportedTarget; + metadata: GraphProviderArtifactMetadata; + binaryPath: string; + checksumPath: string; + metadataPath: string; + binarySha256: string; + checksumFileSha256: string; + metadataSha256: string; + descriptorArtifactId: string; + descriptorChecksumId: string; +} + +export type { ReleaseReceiptNativeArtifactEvidence }; + +interface ReleaseReceiptPackageEvidence { + packageName: ReleaseReceiptPackageName; + packageRoot: string; + version: string; + manifest: ReleaseReceiptPackageManifestMetadata; + tarball: ReleaseReceiptTarballEvidence; + files: readonly string[]; + fileCount: number; + expectedFiles: readonly string[]; + expectedFileCount: number; + bins: Readonly>; + descriptorReferences: readonly ManagedToolDescriptorArtifactReference[]; + nativeArtifacts: readonly ReleaseReceiptNativeArtifactEvidence[]; +} + +export type { ReleaseReceiptPackageEvidence }; + +interface ReleaseReceiptDescriptorCommandGroupEvidence { + name: ReleaseReceiptCommandGroupName; + canonicalCommand: readonly string[]; + packageName: string; +} + +export type { ReleaseReceiptDescriptorCommandGroupEvidence }; + +interface ReleaseReceiptResolvedArtifactEvidence { + id: string; + packageName: ReleaseReceiptPackageName; + path: string; + type: ManagedToolDescriptorArtifactType; + required: boolean; + packageFile: true; + checksumRef?: string; +} + +export type { ReleaseReceiptResolvedArtifactEvidence }; + +interface ReleaseReceiptResolvedChecksumEvidence { + id: string; + packageName: ReleaseReceiptPackageName; + path: string; + algorithm: "sha256"; + artifactRef: string; + required: boolean; + packageFile: true; + value: string; +} + +export type { ReleaseReceiptResolvedChecksumEvidence }; + +interface ReleaseReceiptDescriptorEvidence { + path: string; + packageName: "opcore"; + checksumSha256: string; + descriptor: ManagedToolDescriptor; + commandGroups: readonly ReleaseReceiptDescriptorCommandGroupEvidence[]; + resolvedArtifacts: readonly ReleaseReceiptResolvedArtifactEvidence[]; + resolvedChecksums: readonly ReleaseReceiptResolvedChecksumEvidence[]; +} + +export type { ReleaseReceiptDescriptorEvidence }; + +interface ReleaseReceiptLicensePackageEvidence { + name: string; + version: string; + license: string; + source: string; + bundled: boolean; +} + +export type { ReleaseReceiptLicensePackageEvidence }; + +interface ReleaseReceiptLicenseEvidence { + reportPath: string; + reportSha256: string; + productionDependencyCount: number; + bundledDependencyCount: number; + workspacePackageCount: number; + unresolvedLicenseCount: 0; + packages: readonly ReleaseReceiptLicensePackageEvidence[]; +} + +export type { ReleaseReceiptLicenseEvidence }; + +interface ReleaseReceiptProvenanceFinding { + scope: "current-tree" | "git-history"; + marker: string; + path?: string; + commit?: string; + line?: number; +} + +export type { ReleaseReceiptProvenanceFinding }; + +interface ReleaseReceiptProvenanceEvidence { + reportPath: string; + reportSha256: string; + scannedFileCount: number; + historyCommitCount: number; + findingCount: 0; + findings: readonly ReleaseReceiptProvenanceFinding[]; +} + +export type { ReleaseReceiptProvenanceEvidence }; + +interface ReleaseReceiptSecretFinding { + scope: ReleaseReceiptSecretFindingScope; + kind: string; + path?: string; + commit?: string; + line?: number; + fingerprint: string; + allowlisted: boolean; +} + +export type { ReleaseReceiptSecretFinding }; + +interface ReleaseReceiptSecretHistoryEvidence { + allowlistPath: string; + allowlistSha256: string; + currentTreeScannedFileCount: number; + gitHistoryScannedCommitCount: number; + findingCount: 0; + findings: readonly ReleaseReceiptSecretFinding[]; +} + +export type { ReleaseReceiptSecretHistoryEvidence }; + +interface ReleaseReceiptReport { + id: ReleaseReceiptReportId; + command: readonly string[]; + status: "passed"; + exitCode: 0; + path?: string; + checksumSha256?: string; + summary: string; +} + +export type { ReleaseReceiptReport }; + +interface ReleaseReceiptGraphReleaseEvidence { + path: string; + issue: "#17"; + checksumSha256: string; +} + +export type { ReleaseReceiptGraphReleaseEvidence }; diff --git a/packages/contracts/src/release/receipt-contracts-02.ts b/packages/contracts/src/release/receipt-contracts-02.ts new file mode 100644 index 0000000..283d899 --- /dev/null +++ b/packages/contracts/src/release/receipt-contracts-02.ts @@ -0,0 +1,32 @@ +import type { + ReleaseReceiptDescriptorEvidence, + ReleaseReceiptGraphReleaseEvidence, + ReleaseReceiptLicenseEvidence, + ReleaseReceiptNativeArtifactEvidence, + ReleaseReceiptPackageEvidence, + ReleaseReceiptProvenanceEvidence, + ReleaseReceiptReport, + ReleaseReceiptSecretHistoryEvidence, +} from "./receipt-contracts-01.js"; +import type { ReleaseReceiptCommandGroupName, ReleaseReceiptPackageName } from "./vocabulary-01.js"; + +interface ReleaseReceipt { + schemaVersion: 1; + issue: "#29"; + origin: "covibes-authored-release-proof"; + generatedAt: string; + commitSha: string; + privateRepo: true; + packageNames: readonly ReleaseReceiptPackageName[]; + commandGroups: readonly ReleaseReceiptCommandGroupName[]; + packages: readonly ReleaseReceiptPackageEvidence[]; + descriptor: ReleaseReceiptDescriptorEvidence; + nativeArtifacts: readonly ReleaseReceiptNativeArtifactEvidence[]; + license: ReleaseReceiptLicenseEvidence; + provenance: ReleaseReceiptProvenanceEvidence; + secretHistory: ReleaseReceiptSecretHistoryEvidence; + reports: readonly ReleaseReceiptReport[]; + graphReleaseReceipt: ReleaseReceiptGraphReleaseEvidence; +} + +export type { ReleaseReceipt }; diff --git a/packages/contracts/src/release/receipt-validators-01.ts b/packages/contracts/src/release/receipt-validators-01.ts new file mode 100644 index 0000000..372ade4 --- /dev/null +++ b/packages/contracts/src/release/receipt-validators-01.ts @@ -0,0 +1,239 @@ +import { includesString } from "../shared/primitives.js"; +import type { ManagedToolDescriptorArtifactReference} from "../managed/contracts.js"; +import { managedToolDescriptorArtifactTypes } from "../managed/contracts.js"; +import { + isGraphCoreNativePackageName, + validateManagedToolDescriptorPackageReference, + validateReleaseReceiptCommandGroupName, + validateReleaseReceiptPackageName, + validateStringRecord, +} from "../managed/helper-validators.js"; +import { validateManagedToolDescriptor } from "../managed/validators-01.js"; +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { + packageEvidenceIncludesFile, + validateExactStringSequence, + validateExactStringSet, + validateNonEmptyArray, + validateNonEmptyString, + validateSha256, + validateStringArray, +} from "../shared/validators-01.js"; +import { + validateExactValue, + validateRequiredObject, +} from "../shared/validators-02.js"; +import { graphCoreNativeSupportedTargets } from "./graph-vocabulary-02.js"; +import type { + ReleaseReceiptDescriptorEvidence, + ReleaseReceiptPackageEvidence, + ReleaseReceiptPackageManifestMetadata, + ReleaseReceiptResolvedArtifactEvidence, + ReleaseReceiptTarballEvidence, +} from "./receipt-contracts-01.js"; +import { validateReleaseReceiptNativeArtifact, validateReleaseResolvedChecksums } from "./receipt-validators-02.js"; +import { validateReleaseReceiptBins } from "./receipt-validators-03.js"; +import type { ReleaseReceiptPackageName} from "./vocabulary-01.js"; +import { releaseReceiptCommandGroups } from "./vocabulary-01.js"; + +function validateReleaseReceiptPackage(packageEvidence: ReleaseReceiptPackageEvidence): void { + validateRequiredObject(packageEvidence, "Release receipt package evidence entry is required"); + validateReleaseReceiptPackageName(packageEvidence.packageName, "Release receipt package evidence packageName"); + validateRepoRelativePath(packageEvidence.packageRoot); + validateNonEmptyString(packageEvidence.version, "Release receipt package evidence version"); + validateReleaseReceiptPackageManifest(packageEvidence.manifest, packageEvidence.packageName); + validateReleaseReceiptTarball(packageEvidence.tarball); + validateStringArray(packageEvidence.files, "Release receipt package evidence files", { allowEmpty: false }); + validateStringArray(packageEvidence.expectedFiles, "Release receipt package evidence expectedFiles", { + allowEmpty: false, + }); + validateReleaseReceiptPackageCounts(packageEvidence); + validateExactStringSet( + packageEvidence.files, + packageEvidence.expectedFiles, + `${packageEvidence.packageName} packed files`, + ); + validateReleaseReceiptBins(packageEvidence.bins, packageEvidence.packageName); + validateReleaseReceiptDescriptorReferences(packageEvidence); + validateReleaseReceiptPackageNativeArtifacts(packageEvidence); + for (const nativeArtifact of packageEvidence.nativeArtifacts) validateReleaseReceiptNativeArtifact(nativeArtifact); +} + +export { validateReleaseReceiptPackage }; + +function validateReleaseReceiptPackageCounts(packageEvidence: ReleaseReceiptPackageEvidence): void { + if (!Number.isInteger(packageEvidence.fileCount) || packageEvidence.fileCount !== packageEvidence.files.length) { + throw new Error("Release receipt package evidence fileCount must equal files length"); + } + if ( + !Number.isInteger(packageEvidence.expectedFileCount) || + packageEvidence.expectedFileCount !== packageEvidence.expectedFiles.length + ) { + throw new Error("Release receipt package evidence expectedFileCount must equal expectedFiles length"); + } +} + +function validateReleaseReceiptDescriptorReferences(packageEvidence: ReleaseReceiptPackageEvidence): void { + for (const descriptorReference of packageEvidence.descriptorReferences) { + validateManagedToolDescriptorPackageReference(descriptorReference, packageEvidence.packageName); + if (!packageEvidence.files.includes(descriptorReference.path)) { + throw new Error( + `Release receipt descriptor reference ${descriptorReference.id} is not in ` + + `${packageEvidence.packageName} packed files`, + ); + } + } +} + +function validateReleaseReceiptPackageNativeArtifacts(packageEvidence: ReleaseReceiptPackageEvidence): void { + if (packageEvidence.packageName === "opcore") { + validateNonEmptyArray(packageEvidence.nativeArtifacts, "Release receipt native package artifacts"); + validateExactStringSet( + packageEvidence.nativeArtifacts.map((entry) => entry.targetPlatform), + graphCoreNativeSupportedTargets, + "Release receipt Opcore bundled native artifact targets", + ); + } else if (isGraphCoreNativePackageName(packageEvidence.packageName)) { + throw new Error("Release receipt must not publish native graph-core packages separately"); + } else if (packageEvidence.nativeArtifacts.length > 0) { + throw new Error(`${packageEvidence.packageName} must not report native graph artifacts`); + } +} + +function validateReleaseReceiptPackageManifest( + manifest: ReleaseReceiptPackageManifestMetadata, + packageName: ReleaseReceiptPackageName, +): void { + if (!manifest || typeof manifest !== "object") throw new Error("Release receipt package manifest is required"); + if (manifest.name !== packageName) throw new Error(`Release receipt package manifest name must match ${packageName}`); + validateNonEmptyString(manifest.version, "Release receipt package manifest version"); + validateNonEmptyString(manifest.license, "Release receipt package manifest license"); + if (isGraphCoreNativePackageName(packageName)) { + if (manifest.main !== undefined || manifest.types !== undefined) { + throw new Error("Release receipt native package manifest must not declare main or types"); + } + } else { + if (manifest.main === undefined || manifest.types === undefined) { + throw new Error("Release receipt package manifest must declare main and types"); + } + validateRepoRelativePath(manifest.main); + validateRepoRelativePath(manifest.types); + } + validateStringArray(manifest.files, "Release receipt package manifest files", { allowEmpty: false }); + validateReleaseReceiptBins(manifest.bins, packageName); + validateStringRecord(manifest.dependencies, "Release receipt package manifest dependencies"); + if (manifest.optionalDependencies !== undefined) { + validateStringRecord(manifest.optionalDependencies, "Release receipt package manifest optionalDependencies"); + } + validateStringArray(manifest.bundledDependencies, "Release receipt package manifest bundledDependencies", { + allowEmpty: true, + }); +} + +export { validateReleaseReceiptPackageManifest }; + +function validateReleaseReceiptTarball(tarball: ReleaseReceiptTarballEvidence): void { + if (!tarball || typeof tarball !== "object") throw new Error("Release receipt tarball evidence is required"); + validateNonEmptyString(tarball.filename, "Release receipt tarball filename"); + validateRepoRelativePath(tarball.path); + validateSha256(tarball.sha256, "Release receipt tarball sha256"); + if (tarball.integrity !== undefined) validateNonEmptyString(tarball.integrity, "Release receipt tarball integrity"); + if (tarball.shasum !== undefined) validateNonEmptyString(tarball.shasum, "Release receipt tarball shasum"); +} + +export { validateReleaseReceiptTarball }; + +function validateReleaseReceiptDescriptor( + descriptorEvidence: ReleaseReceiptDescriptorEvidence, + packages: readonly ReleaseReceiptPackageEvidence[], +): void { + if (!descriptorEvidence || typeof descriptorEvidence !== "object") + throw new Error("Release receipt descriptor evidence is required"); + validateRepoRelativePath(descriptorEvidence.path); + if (descriptorEvidence.packageName !== "opcore") { + throw new Error("Release receipt descriptor packageName must be opcore"); + } + validateSha256(descriptorEvidence.checksumSha256, "Release receipt descriptor checksumSha256"); + const descriptor = validateManagedToolDescriptor(descriptorEvidence.descriptor); + validateExactStringSet( + descriptorEvidence.commandGroups.map((entry) => entry.name), + releaseReceiptCommandGroups, + "Release receipt descriptor command groups", + ); + for (const group of descriptorEvidence.commandGroups) { + validateReleaseReceiptCommandGroupName(group.name, "Release receipt descriptor command group name"); + validateExactStringSequence( + group.canonicalCommand, + ["opcore", group.name], + `Release receipt descriptor ${group.name} canonicalCommand`, + ); + const descriptorGroup = descriptor.commandGroups.find((entry) => entry.name === group.name); + if (!descriptorGroup) + throw new Error(`Release receipt descriptor command group missing from descriptor: ${group.name}`); + if (group.packageName !== descriptorGroup.packageName) { + throw new Error(`Release receipt descriptor command group ${group.name} packageName must match descriptor`); + } + } + validateReleaseResolvedArtifacts(descriptorEvidence.resolvedArtifacts, descriptor.artifacts, packages); + validateReleaseResolvedChecksums(descriptorEvidence.resolvedChecksums, descriptor.checksums, packages); +} + +export { validateReleaseReceiptDescriptor }; + +function validateReleaseResolvedArtifacts( + resolvedArtifacts: readonly ReleaseReceiptResolvedArtifactEvidence[], + descriptorArtifacts: readonly ManagedToolDescriptorArtifactReference[], + packages: readonly ReleaseReceiptPackageEvidence[], +): void { + validateNonEmptyArray(resolvedArtifacts, "Release receipt descriptor resolvedArtifacts"); + validateExactStringSet( + resolvedArtifacts.map((entry) => entry.id), + descriptorArtifacts.map((entry) => entry.id), + "Release receipt descriptor resolved artifact ids", + ); + for (const resolved of resolvedArtifacts) { + validateReleaseResolvedArtifact(resolved, descriptorArtifacts, packages); + } +} + +export { validateReleaseResolvedArtifacts }; + +function validateReleaseResolvedArtifact( + resolved: ReleaseReceiptResolvedArtifactEvidence, + descriptorArtifacts: readonly ManagedToolDescriptorArtifactReference[], + packages: readonly ReleaseReceiptPackageEvidence[], +): void { + validateReleaseReceiptPackageName(resolved.packageName, "Release receipt descriptor resolved artifact packageName"); + validateRepoRelativePath(resolved.path); + if (!includesString(managedToolDescriptorArtifactTypes, resolved.type)) { + throw new Error(`Unknown release receipt descriptor resolved artifact type: ${String(resolved.type)}`); + } + validateExactValue( + resolved.packageFile, + true, + `Release receipt resolved artifact ${resolved.id} must resolve to a package file`, + ); + const descriptorArtifact = descriptorArtifacts.find((entry) => entry.id === resolved.id); + if (!descriptorArtifact) { + throw new Error(`Release receipt resolved artifact is not declared by descriptor: ${resolved.id}`); + } + validateReleaseResolvedArtifactMirror(resolved, descriptorArtifact); + if (!packageEvidenceIncludesFile(packages, resolved.packageName, resolved.path)) { + throw new Error(`Release receipt resolved artifact ${resolved.id} is not present in packed package files`); + } +} + +function validateReleaseResolvedArtifactMirror( + resolved: ReleaseReceiptResolvedArtifactEvidence, + descriptorArtifact: ManagedToolDescriptorArtifactReference, +): void { + if ( + descriptorArtifact.packageName !== resolved.packageName || + descriptorArtifact.path !== resolved.path || + descriptorArtifact.type !== resolved.type || + descriptorArtifact.required !== resolved.required || + descriptorArtifact.checksumRef !== resolved.checksumRef + ) { + throw new Error(`Release receipt resolved artifact must mirror descriptor: ${resolved.id}`); + } +} diff --git a/packages/contracts/src/release/receipt-validators-02.ts b/packages/contracts/src/release/receipt-validators-02.ts new file mode 100644 index 0000000..4982064 --- /dev/null +++ b/packages/contracts/src/release/receipt-validators-02.ts @@ -0,0 +1,287 @@ +import { validateGraphProviderArtifactMetadata } from "../graph/protocol-validators.js"; +import type { ManagedToolDescriptorChecksumReference } from "../managed/contracts.js"; +import { + bundledGraphCoreNativePath, + graphCoreNativeTargetForPackageName, + isGraphCoreNativePackageName, + validateReleaseReceiptPackageName, +} from "../managed/helper-validators.js"; +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { + packageEvidenceIncludesFile, + validateExactStringSet, + validateNonEmptyArray, + validateNonEmptyString, + validateNonNegativeInteger, + validateSha256, +} from "../shared/validators-01.js"; +import { + validateExactValue, + validateRequiredObject, +} from "../shared/validators-02.js"; +import { graphCoreNativeSupportedTargets } from "./graph-vocabulary-02.js"; +import type { + ReleaseReceiptDescriptorEvidence, + ReleaseReceiptLicenseEvidence, + ReleaseReceiptNativeArtifactEvidence, + ReleaseReceiptPackageEvidence, + ReleaseReceiptProvenanceEvidence, + ReleaseReceiptResolvedChecksumEvidence, + ReleaseReceiptSecretHistoryEvidence, +} from "./receipt-contracts-01.js"; +import { releaseReceiptPackageNames } from "./vocabulary-01.js"; + +function validateReleaseResolvedChecksums( + resolvedChecksums: readonly ReleaseReceiptResolvedChecksumEvidence[], + descriptorChecksums: readonly ManagedToolDescriptorChecksumReference[], + packages: readonly ReleaseReceiptPackageEvidence[], +): void { + validateNonEmptyArray(resolvedChecksums, "Release receipt descriptor resolvedChecksums"); + validateExactStringSet( + resolvedChecksums.map((entry) => entry.id), + descriptorChecksums.map((entry) => entry.id), + "Release receipt descriptor resolved checksum ids", + ); + for (const resolved of resolvedChecksums) { + validateReleaseResolvedChecksum(resolved, descriptorChecksums, packages); + } +} + +export { validateReleaseResolvedChecksums }; + +function validateReleaseResolvedChecksum( + resolved: ReleaseReceiptResolvedChecksumEvidence, + descriptorChecksums: readonly ManagedToolDescriptorChecksumReference[], + packages: readonly ReleaseReceiptPackageEvidence[], +): void { + validateReleaseReceiptPackageName(resolved.packageName, "Release receipt descriptor resolved checksum packageName"); + validateRepoRelativePath(resolved.path); + validateExactValue( + resolved.algorithm, + "sha256", + "Release receipt descriptor checksum algorithm must be sha256", + ); + validateExactValue( + resolved.packageFile, + true, + `Release receipt resolved checksum ${resolved.id} must resolve to a package file`, + ); + validateSha256(resolved.value, "Release receipt descriptor checksum value"); + const descriptorChecksum = descriptorChecksums.find((entry) => entry.id === resolved.id); + if (!descriptorChecksum) { + throw new Error(`Release receipt resolved checksum is not declared by descriptor: ${resolved.id}`); + } + validateReleaseResolvedChecksumMirror(resolved, descriptorChecksum); + if (!packageEvidenceIncludesFile(packages, resolved.packageName, resolved.path)) { + throw new Error(`Release receipt resolved checksum ${resolved.id} is not present in packed package files`); + } +} + +function validateReleaseResolvedChecksumMirror( + resolved: ReleaseReceiptResolvedChecksumEvidence, + descriptorChecksum: ManagedToolDescriptorChecksumReference, +): void { + if ( + descriptorChecksum.packageName !== resolved.packageName || + descriptorChecksum.path !== resolved.path || + descriptorChecksum.algorithm !== resolved.algorithm || + descriptorChecksum.artifactRef !== resolved.artifactRef || + descriptorChecksum.required !== resolved.required + ) { + throw new Error(`Release receipt resolved checksum must mirror descriptor: ${resolved.id}`); + } + if (descriptorChecksum.value !== undefined && descriptorChecksum.value !== resolved.value) { + throw new Error(`Release receipt resolved checksum value must match descriptor: ${resolved.id}`); + } +} + +function validateReleaseReceiptNativeArtifacts( + nativeArtifacts: readonly ReleaseReceiptNativeArtifactEvidence[], + packages: readonly ReleaseReceiptPackageEvidence[], + descriptorEvidence: ReleaseReceiptDescriptorEvidence, +): void { + validateNonEmptyArray(nativeArtifacts, "Release receipt native artifacts"); + validateExactStringSet( + nativeArtifacts.map((artifact) => artifact.targetPlatform), + graphCoreNativeSupportedTargets, + "Release receipt native artifact targets", + ); + for (const nativeArtifact of nativeArtifacts) { + validateReleaseReceiptNativeArtifactBinding(nativeArtifact, packages, descriptorEvidence); + } +} + +export { validateReleaseReceiptNativeArtifacts }; + +function validateReleaseReceiptNativeArtifactBinding( + nativeArtifact: ReleaseReceiptNativeArtifactEvidence, + packages: readonly ReleaseReceiptPackageEvidence[], + descriptorEvidence: ReleaseReceiptDescriptorEvidence, +): void { + validateReleaseReceiptNativeArtifact(nativeArtifact); + validateReleaseReceiptNativeArtifactFiles(nativeArtifact, packages); + const binaryArtifact = descriptorEvidence.resolvedArtifacts.find( + (artifact) => artifact.id === nativeArtifact.descriptorArtifactId, + ); + if ( + !binaryArtifact || + binaryArtifact.packageName !== nativeArtifact.packageName || + binaryArtifact.path !== nativeArtifact.binaryPath + ) { + throw new Error("Release receipt native artifact binary must resolve from descriptor artifacts"); + } + const checksum = descriptorEvidence.resolvedChecksums.find( + (entry) => entry.id === nativeArtifact.descriptorChecksumId, + ); + if ( + !checksum || + checksum.packageName !== nativeArtifact.packageName || + checksum.path !== nativeArtifact.checksumPath || + checksum.value !== nativeArtifact.binarySha256 + ) { + throw new Error("Release receipt native artifact checksum must resolve from descriptor checksum evidence"); + } +} + +function validateReleaseReceiptNativeArtifactFiles( + nativeArtifact: ReleaseReceiptNativeArtifactEvidence, + packages: readonly ReleaseReceiptPackageEvidence[], +): void { + const files = [ + [nativeArtifact.binaryPath, "binary"], + [nativeArtifact.checksumPath, "checksum"], + [nativeArtifact.metadataPath, "metadata"], + ] as const; + for (const [path, label] of files) { + if (!packageEvidenceIncludesFile(packages, nativeArtifact.packageName, path)) { + throw new Error(`Release receipt native artifact ${label} must be present in native package files`); + } + } +} + +function validateReleaseReceiptNativeArtifact(nativeArtifact: ReleaseReceiptNativeArtifactEvidence): void { + validateRequiredObject(nativeArtifact, "Release receipt native artifact evidence is required"); + validateExactValue( + nativeArtifact.packageName, + "opcore", + "Release receipt native artifact packageName must be opcore", + ); + if (!isGraphCoreNativePackageName(nativeArtifact.bundledPackageName)) { + throw new Error("Release receipt native artifact bundledPackageName must be an Opcore graph-core native package"); + } + const expectedTarget = graphCoreNativeTargetForPackageName(nativeArtifact.bundledPackageName); + validateExactValue( + nativeArtifact.targetPlatform, + expectedTarget, + `Release receipt native artifact targetPlatform must be ${expectedTarget}`, + ); + validateExactValue( + nativeArtifact.binaryPath, + bundledGraphCoreNativePath(nativeArtifact.bundledPackageName, "opcore-graph-core"), + "Release receipt native artifact binaryPath must point at the bundled native binary", + ); + validateExactValue( + nativeArtifact.checksumPath, + bundledGraphCoreNativePath(nativeArtifact.bundledPackageName, "opcore-graph-core.sha256"), + "Release receipt native artifact checksumPath must point at the bundled native checksum", + ); + validateExactValue( + nativeArtifact.metadataPath, + bundledGraphCoreNativePath(nativeArtifact.bundledPackageName, "metadata.json"), + "Release receipt native artifact metadataPath must point at the bundled native metadata", + ); + validateGraphProviderArtifactMetadata(nativeArtifact.metadata); + validateRepoRelativePath(nativeArtifact.binaryPath); + validateRepoRelativePath(nativeArtifact.checksumPath); + validateRepoRelativePath(nativeArtifact.metadataPath); + validateSha256(nativeArtifact.binarySha256, "Release receipt native artifact binarySha256"); + validateSha256(nativeArtifact.checksumFileSha256, "Release receipt native artifact checksumFileSha256"); + validateSha256(nativeArtifact.metadataSha256, "Release receipt native artifact metadataSha256"); + validateNonEmptyString(nativeArtifact.descriptorArtifactId, "Release receipt native artifact descriptorArtifactId"); + validateNonEmptyString(nativeArtifact.descriptorChecksumId, "Release receipt native artifact descriptorChecksumId"); + validateReleaseReceiptNativeArtifactMetadata(nativeArtifact); +} + +export { validateReleaseReceiptNativeArtifact }; + +function validateReleaseReceiptNativeArtifactMetadata(nativeArtifact: ReleaseReceiptNativeArtifactEvidence): void { + validateExactValue( + nativeArtifact.metadata.targetPlatform, + nativeArtifact.targetPlatform, + "Release receipt native artifact targetPlatform must match metadata", + ); + validateExactValue( + nativeArtifact.metadata.binaryPath, + "opcore-graph-core", + "Release receipt native artifact binaryPath must match metadata", + ); + validateExactValue( + nativeArtifact.metadata.checksumPath, + "opcore-graph-core.sha256", + "Release receipt native artifact checksumPath must match metadata", + ); + validateExactValue( + nativeArtifact.metadata.checksumSha256, + nativeArtifact.binarySha256, + "Release receipt native artifact binary sha256 must match metadata checksum", + ); +} + +function validateReleaseReceiptLicense(license: ReleaseReceiptLicenseEvidence): void { + if (!license || typeof license !== "object") throw new Error("Release receipt license evidence is required"); + validateRepoRelativePath(license.reportPath); + validateSha256(license.reportSha256, "Release receipt license reportSha256"); + validateNonNegativeInteger(license.productionDependencyCount, "Release receipt license productionDependencyCount"); + validateNonNegativeInteger(license.bundledDependencyCount, "Release receipt license bundledDependencyCount"); + validateNonNegativeInteger(license.workspacePackageCount, "Release receipt license workspacePackageCount"); + if (license.workspacePackageCount < releaseReceiptPackageNames.length) { + throw new Error( + `Release receipt license workspacePackageCount must be at least ${releaseReceiptPackageNames.length}`, + ); + } + if (license.unresolvedLicenseCount !== 0) throw new Error("Release receipt license unresolvedLicenseCount must be 0"); + if (!Array.isArray(license.packages)) throw new Error("Release receipt license packages must be an array"); + for (const packageEvidence of license.packages) { + validateNonEmptyString(packageEvidence.name, "Release receipt license package name"); + validateNonEmptyString(packageEvidence.version, "Release receipt license package version"); + validateNonEmptyString(packageEvidence.license, "Release receipt license package license"); + validateNonEmptyString(packageEvidence.source, "Release receipt license package source"); + if (typeof packageEvidence.bundled !== "boolean") + throw new Error("Release receipt license package bundled must be boolean"); + } +} + +export { validateReleaseReceiptLicense }; + +function validateReleaseReceiptProvenance(provenance: ReleaseReceiptProvenanceEvidence): void { + if (!provenance || typeof provenance !== "object") throw new Error("Release receipt provenance evidence is required"); + validateRepoRelativePath(provenance.reportPath); + validateSha256(provenance.reportSha256, "Release receipt provenance reportSha256"); + validateNonNegativeInteger(provenance.scannedFileCount, "Release receipt provenance scannedFileCount"); + validateNonNegativeInteger(provenance.historyCommitCount, "Release receipt provenance historyCommitCount"); + if (provenance.findingCount !== 0 || provenance.findings.length !== 0) { + throw new Error("Release receipt provenance findings must be empty"); + } +} + +export { validateReleaseReceiptProvenance }; + +function validateReleaseReceiptSecretHistory(secretHistory: ReleaseReceiptSecretHistoryEvidence): void { + if (!secretHistory || typeof secretHistory !== "object") + throw new Error("Release receipt secret history evidence is required"); + validateRepoRelativePath(secretHistory.allowlistPath); + validateSha256(secretHistory.allowlistSha256, "Release receipt secret history allowlistSha256"); + validateNonNegativeInteger( + secretHistory.currentTreeScannedFileCount, + "Release receipt secret history currentTreeScannedFileCount", + ); + validateNonNegativeInteger( + secretHistory.gitHistoryScannedCommitCount, + "Release receipt secret history gitHistoryScannedCommitCount", + ); + if (secretHistory.findingCount !== 0 || secretHistory.findings.length !== 0) { + throw new Error("Release receipt secret findings must be empty"); + } +} + +export { validateReleaseReceiptSecretHistory }; diff --git a/packages/contracts/src/release/receipt-validators-03.ts b/packages/contracts/src/release/receipt-validators-03.ts new file mode 100644 index 0000000..e1acb0f --- /dev/null +++ b/packages/contracts/src/release/receipt-validators-03.ts @@ -0,0 +1,64 @@ +import { validateReleaseReceiptReportId } from "../managed/helper-validators.js"; +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { + validateExactStringSet, + validateNonEmptyArray, + validateNonEmptyString, + validateSha256, + validateStringArray, +} from "../shared/validators-01.js"; +import type { ReleaseReceiptGraphReleaseEvidence, ReleaseReceiptReport } from "./receipt-contracts-01.js"; +import type { ReleaseReceiptPackageName} from "./vocabulary-01.js"; +import { releaseReceiptReportIds } from "./vocabulary-01.js"; + +function validateReleaseReceiptReports(reports: readonly ReleaseReceiptReport[]): void { + validateNonEmptyArray(reports, "Release receipt reports"); + validateExactStringSet( + reports.map((entry) => entry.id), + releaseReceiptReportIds, + "Release receipt reports", + ); + for (const report of reports) { + validateReleaseReceiptReportId(report.id, "Release receipt report id"); + validateStringArray(report.command, "Release receipt report command", { + allowEmpty: false, + }); + if (report.status !== "passed") throw new Error("Release receipt report status must be passed"); + if (report.exitCode !== 0) throw new Error("Release receipt report exitCode must be 0"); + if (report.path !== undefined) validateRepoRelativePath(report.path); + if (report.checksumSha256 !== undefined) + validateSha256(report.checksumSha256, "Release receipt report checksumSha256"); + validateNonEmptyString(report.summary, "Release receipt report summary"); + } +} + +export { validateReleaseReceiptReports }; + +function validateReleaseReceiptGraphReleaseEvidence(evidence: ReleaseReceiptGraphReleaseEvidence): void { + if (!evidence || typeof evidence !== "object") throw new Error("Release receipt graph release evidence is required"); + validateRepoRelativePath(evidence.path); + if (evidence.issue !== "#17") throw new Error("Release receipt graph release evidence issue must be #17"); + validateSha256(evidence.checksumSha256, "Release receipt graph release checksumSha256"); +} + +export { validateReleaseReceiptGraphReleaseEvidence }; + +function validateReleaseReceiptBins( + bins: Readonly>, + packageName: ReleaseReceiptPackageName, +): void { + if (!bins || typeof bins !== "object" || Array.isArray(bins)) + throw new Error("Release receipt bins must be an object"); + const binNames = Object.keys(bins); + for (const bin of binNames) { + validateNonEmptyString(bin, "Release receipt bin name"); + validateRepoRelativePath(bins[bin]); + } + if (packageName === "opcore") { + validateExactStringSet(binNames, ["opcore", "opcore-asp-provider"], "Release receipt Opcore package bins"); + } else if (binNames.length > 0) { + throw new Error(`${packageName} must not expose CLI bins`); + } +} + +export { validateReleaseReceiptBins }; diff --git a/packages/contracts/src/release/vocabulary-01.ts b/packages/contracts/src/release/vocabulary-01.ts new file mode 100644 index 0000000..f96bc57 --- /dev/null +++ b/packages/contracts/src/release/vocabulary-01.ts @@ -0,0 +1,137 @@ +import { graphCoreNativePackageNames } from "./graph-vocabulary-02.js"; + +const releaseReceiptPackageNames = ["opcore"] as const; + +export { releaseReceiptPackageNames }; + +type ReleaseReceiptPackageName = (typeof releaseReceiptPackageNames)[number]; + +export type { ReleaseReceiptPackageName }; + +const releaseReceiptBundledPackageNames = [ + "@the-open-engine/opcore-asp-provider", + "@the-open-engine/opcore-contracts", + "@the-open-engine/opcore-edit", + "@the-open-engine/opcore-graph", + "@the-open-engine/opcore-validation", + "@the-open-engine/opcore-validation-clone", + "@the-open-engine/opcore-validation-docs", + "@the-open-engine/opcore-validation-python", + "@the-open-engine/opcore-validation-rust", + "@the-open-engine/opcore-validation-typescript", + ...graphCoreNativePackageNames, +] as const; + +export { releaseReceiptBundledPackageNames }; + +const releaseReceiptCommandGroups = ["graph", "inspect", "edit", "check", "validate", "status", "doctor"] as const; + +export { releaseReceiptCommandGroups }; + +type ReleaseReceiptCommandGroupName = (typeof releaseReceiptCommandGroups)[number]; + +export type { ReleaseReceiptCommandGroupName }; + +const releaseReceiptReportIds = [ + "package-inspection", + "license", + "provenance", + "release-hygiene", + "graph-release", + "secret-history", +] as const; + +export { releaseReceiptReportIds }; + +type ReleaseReceiptReportId = (typeof releaseReceiptReportIds)[number]; + +export type { ReleaseReceiptReportId }; + +const releaseReceiptSecretFindingScopes = ["current-tree", "git-history"] as const; + +export { releaseReceiptSecretFindingScopes }; + +type ReleaseReceiptSecretFindingScope = (typeof releaseReceiptSecretFindingScopes)[number]; + +export type { ReleaseReceiptSecretFindingScope }; + +const releaseCutoverRequiredCommandIds = [ + "opcore-scan", + "opcore-status", + "opcore-check-changed", + "opcore-measure", + "opcore-try", + "status", + "doctor", + "graph-build", + "graph-status", + "graph-query", + "graph-impact", + "graph-review-context", + "graph-detect-changes", + "graph-search", + "graph-serve", + "inspect-symbols", + "inspect-definition", + "inspect-references", + "inspect-signature", + "inspect-implementations", + "inspect-search", + "edit-preview", + "edit-apply", + "edit-refused", + "check-files", + "validate-request", + "validate-pre-write-pass", + "validate-pre-write-fail", +] as const; + +export { releaseCutoverRequiredCommandIds }; + +type ReleaseCutoverCommandId = (typeof releaseCutoverRequiredCommandIds)[number]; + +export type { ReleaseCutoverCommandId }; + +const releaseCutoverRustCommandIds = [ + "graph-rust-build", + "graph-rust-status", + "graph-rust-query", + "graph-rust-impact", + "graph-rust-review-context", + "graph-rust-detect-changes", + "graph-rust-search", +] as const; + +export { releaseCutoverRustCommandIds }; + +type ReleaseCutoverRustCommandId = (typeof releaseCutoverRustCommandIds)[number]; + +export type { ReleaseCutoverRustCommandId }; + +const releaseCutoverPythonCommandIds = [ + "opcore-python-scan", + "opcore-python-status", + "opcore-python-check-changed", + "opcore-python-measure", + "graph-python-build", + "graph-python-status", + "graph-python-query", + "graph-python-search", +] as const; + +export { releaseCutoverPythonCommandIds }; + +type ReleaseCutoverPythonCommandId = (typeof releaseCutoverPythonCommandIds)[number]; + +export type { ReleaseCutoverPythonCommandId }; + +const releaseCutoverNegativeCheckIds = [ + "missing-required-graph-check", + "missing-required-graph-validate", + "python-types-degraded-no-tools", + "python-source-hygiene-no-ruff", + "python-relevant-tests-no-pytest", + "python-toolchain-degraded-no-tools", +] as const; + +export { releaseCutoverNegativeCheckIds }; diff --git a/packages/contracts/src/release/vocabulary-02.ts b/packages/contracts/src/release/vocabulary-02.ts new file mode 100644 index 0000000..1a658a8 --- /dev/null +++ b/packages/contracts/src/release/vocabulary-02.ts @@ -0,0 +1,57 @@ +import type { CommandOwner, CommandRouteStatus } from "../command/vocabulary.js"; +import type { releaseCutoverNegativeCheckIds } from "./vocabulary-01.js"; + +type ReleaseCutoverNegativeCheckId = (typeof releaseCutoverNegativeCheckIds)[number]; + +export type { ReleaseCutoverNegativeCheckId }; + +const releaseCutoverInputIssues = ["#17", "#29", "#58"] as const; + +export { releaseCutoverInputIssues }; + +type ReleaseCutoverInputIssue = (typeof releaseCutoverInputIssues)[number]; + +export type { ReleaseCutoverInputIssue }; + +const aspDogfoodUnsupportedSurfaceIds = ["inspect", "edit"] as const; + +export { aspDogfoodUnsupportedSurfaceIds }; + +type AspDogfoodUnsupportedSurfaceId = (typeof aspDogfoodUnsupportedSurfaceIds)[number]; + +export type { AspDogfoodUnsupportedSurfaceId }; + +const aspDogfoodForbiddenProviderMarkers = ["opcore asp serve", "opcore asp"] as const; + +export { aspDogfoodForbiddenProviderMarkers }; + +type AspDogfoodForbiddenProviderMarker = (typeof aspDogfoodForbiddenProviderMarkers)[number]; + +export type { AspDogfoodForbiddenProviderMarker }; + +const releaseCutoverRequestFilePlaceholder = ""; + +export { releaseCutoverRequestFilePlaceholder }; + +const releaseCutoverMissingGraphRepoPlaceholder = ""; + +export { releaseCutoverMissingGraphRepoPlaceholder }; + +const releaseCutoverRequiredGraphRequestPlaceholder = ""; + +export { releaseCutoverRequiredGraphRequestPlaceholder }; + +type ReleaseCutoverExpectedCommandStatus = CommandRouteStatus; + +export type { ReleaseCutoverExpectedCommandStatus }; + +interface ReleaseCutoverCommandExpectation { + readonly canonicalCommand: readonly string[]; + readonly requestFileBasename?: string; + readonly owner: CommandOwner; + readonly status: ReleaseCutoverExpectedCommandStatus; + readonly exitCode: 0 | 1 | 2 | 64; + readonly bin: "opcore"; +} + +export type { ReleaseCutoverCommandExpectation }; diff --git a/packages/contracts/src/release/vocabulary-03.ts b/packages/contracts/src/release/vocabulary-03.ts new file mode 100644 index 0000000..8f90457 --- /dev/null +++ b/packages/contracts/src/release/vocabulary-03.ts @@ -0,0 +1,272 @@ +import type { ReleaseCutoverCommandId } from "./vocabulary-01.js"; +import type { ReleaseCutoverCommandExpectation} from "./vocabulary-02.js"; +import { releaseCutoverRequestFilePlaceholder } from "./vocabulary-02.js"; + +const releaseCutoverCommandExpectations = { + "opcore-scan": { + canonicalCommand: ["opcore", "scan"], + owner: "runtime", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "opcore-status": { + canonicalCommand: ["opcore", "status"], + owner: "runtime", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "opcore-check-changed": { + canonicalCommand: [ + "opcore", + "check", + "changed", + "--report-mode", + "introduced", + "--base", + "HEAD", + "--checks", + "typescript.syntax", + ], + owner: "validation", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "opcore-measure": { + canonicalCommand: ["opcore", "measure"], + owner: "runtime", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "opcore-try": { + canonicalCommand: ["opcore", "try"], + owner: "runtime", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + status: { + canonicalCommand: ["opcore", "status"], + owner: "runtime", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + doctor: { + canonicalCommand: ["opcore", "doctor"], + owner: "runtime", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "graph-build": { + canonicalCommand: ["opcore", "graph", "build"], + owner: "graph", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "graph-status": { + canonicalCommand: ["opcore", "graph", "status"], + owner: "graph", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "graph-query": { + canonicalCommand: ["opcore", "graph", "query"], + owner: "graph", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "graph-impact": { + canonicalCommand: ["opcore", "graph", "impact", "--files", "src/components/GreetingCard.tsx"], + owner: "graph", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "graph-review-context": { + canonicalCommand: ["opcore", "graph", "review-context", "--files", "src/components/GreetingCard.tsx"], + owner: "graph", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "graph-detect-changes": { + canonicalCommand: ["opcore", "graph", "detect-changes", "--files", "src/components/GreetingCard.tsx"], + owner: "graph", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "graph-search": { + canonicalCommand: ["opcore", "graph", "search", "Greeting", "--limit", "5"], + owner: "graph", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "graph-serve": { + canonicalCommand: ["opcore", "graph", "serve"], + owner: "graph", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "inspect-symbols": { + canonicalCommand: ["opcore", "inspect", "symbols", "Greeting", "--limit", "5"], + owner: "inspect", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "inspect-definition": { + canonicalCommand: ["opcore", "inspect", "definition", "GreetingCard"], + owner: "inspect", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "inspect-references": { + canonicalCommand: [ + "opcore", + "inspect", + "references", + "function:src/components/GreetingCard.tsx#GreetingCard", + "--limit", + "5", + ], + owner: "inspect", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "inspect-signature": { + canonicalCommand: ["opcore", "inspect", "signature", "function:src/components/GreetingCard.tsx#GreetingCard"], + owner: "inspect", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "inspect-implementations": { + canonicalCommand: ["opcore", "inspect", "implementations", "class:src/models.ts#GreetingModel"], + owner: "inspect", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "inspect-search": { + canonicalCommand: ["opcore", "inspect", "search", "Greeting", "--limit", "5"], + owner: "inspect", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "edit-preview": { + canonicalCommand: [ + "opcore", + "edit", + "exact", + "--path", + "src/cutover.ts", + "--expected", + "export const cutoverValue: number = 1;", + "--replacement", + "export const cutoverValue: number = 2;", + ], + owner: "edit", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "edit-apply": { + canonicalCommand: [ + "opcore", + "edit", + "exact", + "--path", + "src/cutover.ts", + "--expected", + "export const cutoverValue: number = 1;", + "--replacement", + "export const cutoverValue: number = 2;", + "--apply", + ], + owner: "edit", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "edit-refused": { + canonicalCommand: [ + "opcore", + "edit", + "exact", + "--path", + "src/cutover.ts", + "--expected", + "export const cutoverValue: number = 2;", + "--replacement", + "export const cutoverValue: number = missingCutoverSymbol;", + "--apply", + ], + owner: "edit", + status: "error", + exitCode: 1, + bin: "opcore", + }, + "check-files": { + canonicalCommand: ["opcore", "check", "files", "src/cutover.ts", "--checks", "typescript.syntax,typescript.types"], + owner: "validation", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "validate-request": { + canonicalCommand: ["opcore", "validate", "request", "--request-file", releaseCutoverRequestFilePlaceholder], + requestFileBasename: "validate-request.json", + owner: "validation", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "validate-pre-write-pass": { + canonicalCommand: [ + "opcore", + "validate", + "pre-write", + "--request-file", + releaseCutoverRequestFilePlaceholder, + "--timeout-ms", + "30000", + ], + requestFileBasename: "pre-write-pass.json", + owner: "validation", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "validate-pre-write-fail": { + canonicalCommand: [ + "opcore", + "validate", + "pre-write", + "--request-file", + releaseCutoverRequestFilePlaceholder, + "--timeout-ms", + "30000", + ], + requestFileBasename: "pre-write-fail.json", + owner: "validation", + status: "error", + exitCode: 1, + bin: "opcore", + }, +} as const satisfies Record; + +export { releaseCutoverCommandExpectations }; diff --git a/packages/contracts/src/release/vocabulary-04.ts b/packages/contracts/src/release/vocabulary-04.ts new file mode 100644 index 0000000..e38e3ac --- /dev/null +++ b/packages/contracts/src/release/vocabulary-04.ts @@ -0,0 +1,181 @@ +import type { ReleaseCutoverPythonCommandId, ReleaseCutoverRustCommandId } from "./vocabulary-01.js"; +import type { + ReleaseCutoverCommandExpectation, + ReleaseCutoverNegativeCheckId} from "./vocabulary-02.js"; +import { + releaseCutoverMissingGraphRepoPlaceholder, + releaseCutoverRequiredGraphRequestPlaceholder, +} from "./vocabulary-02.js"; + +const releaseCutoverRustCommandExpectations = { + "graph-rust-build": { + canonicalCommand: ["opcore", "graph", "build"], + owner: "graph", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "graph-rust-status": { + canonicalCommand: ["opcore", "graph", "status"], + owner: "graph", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "graph-rust-query": { + canonicalCommand: ["opcore", "graph", "query"], + owner: "graph", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "graph-rust-impact": { + canonicalCommand: ["opcore", "graph", "impact", "--files", "src/helpers.rs"], + owner: "graph", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "graph-rust-review-context": { + canonicalCommand: ["opcore", "graph", "review-context", "--files", "src/helpers.rs"], + owner: "graph", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "graph-rust-detect-changes": { + canonicalCommand: ["opcore", "graph", "detect-changes", "--files", "src/helpers.rs"], + owner: "graph", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "graph-rust-search": { + canonicalCommand: ["opcore", "graph", "search", "Widget", "--limit", "5"], + owner: "graph", + status: "ok", + exitCode: 0, + bin: "opcore", + }, +} as const satisfies Record; + +export { releaseCutoverRustCommandExpectations }; + +const releaseCutoverPythonCommandExpectations = { + "opcore-python-scan": { + canonicalCommand: ["opcore", "scan"], + owner: "runtime", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "opcore-python-status": { + canonicalCommand: ["opcore", "status"], + owner: "runtime", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "opcore-python-check-changed": { + canonicalCommand: [ + "opcore", + "check", + "changed", + "--report-mode", + "introduced", + "--base", + "HEAD", + "--checks", + "python.syntax,python.source-hygiene", + ], + owner: "validation", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "opcore-python-measure": { + canonicalCommand: ["opcore", "measure"], + owner: "runtime", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "graph-python-build": { + canonicalCommand: ["opcore", "graph", "build"], + owner: "graph", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "graph-python-status": { + canonicalCommand: ["opcore", "graph", "status"], + owner: "graph", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "graph-python-query": { + canonicalCommand: ["opcore", "graph", "query"], + owner: "graph", + status: "ok", + exitCode: 0, + bin: "opcore", + }, + "graph-python-search": { + canonicalCommand: ["opcore", "graph", "search", "Greeter", "--limit", "5"], + owner: "graph", + status: "ok", + exitCode: 0, + bin: "opcore", + }, +} as const satisfies Record; + +export { releaseCutoverPythonCommandExpectations }; + +const releaseCutoverPythonEvidenceExpectations = { + "opcore-python-scan": ["python-coverage", "python-validation", "python-types-degraded"], + "opcore-python-status": ["python-coverage", "python-validation"], + "opcore-python-check-changed": ["python-syntax", "python-source-hygiene"], + "opcore-python-measure": ["python-measure-delta"], + "graph-python-build": ["python-graph-provider"], + "graph-python-status": ["python-graph-provider"], + "graph-python-query": ["src/acme/app.py", "Greeter", "build_name"], + "graph-python-search": ["src/acme/app.py", "Greeter"], +} as const satisfies Record; + +export { releaseCutoverPythonEvidenceExpectations }; + +const releaseCutoverNegativeCheckExpectations = { + "missing-required-graph-check": [ + "opcore", + "check", + "files", + "src/index.ts", + "--repo", + releaseCutoverMissingGraphRepoPlaceholder, + "--graph-mode", + "required", + "--checks", + "typescript.import-graph", + ], + "missing-required-graph-validate": [ + "opcore", + "validate", + "request", + "--request-file", + releaseCutoverRequiredGraphRequestPlaceholder, + ], + "python-types-degraded-no-tools": ["opcore", "check", "files", "src/acme/app.py", "--checks", "python.types"], + "python-source-hygiene-no-ruff": ["opcore", "check", "files", "src/acme/app.py", "--checks", "python.source-hygiene"], + "python-relevant-tests-no-pytest": [ + "opcore", + "check", + "files", + "src/acme/app.py", + "--checks", + "python.relevant-tests", + ], + "python-toolchain-degraded-no-tools": ["opcore", "status"], +} as const satisfies Record; + +export { releaseCutoverNegativeCheckExpectations }; diff --git a/packages/contracts/src/shared/json.ts b/packages/contracts/src/shared/json.ts new file mode 100644 index 0000000..23bd33a --- /dev/null +++ b/packages/contracts/src/shared/json.ts @@ -0,0 +1,7 @@ +type JsonPrimitive = string | number | boolean | null; + +export type { JsonPrimitive }; + +type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue }; + +export type { JsonValue }; diff --git a/packages/contracts/src/shared/path-validators.ts b/packages/contracts/src/shared/path-validators.ts new file mode 100644 index 0000000..ca816df --- /dev/null +++ b/packages/contracts/src/shared/path-validators.ts @@ -0,0 +1,80 @@ +import { validateRequiredObject } from "./primitives.js"; +import { validateStringArray } from "./validators-01.js"; +import type { RepoIdentity } from "../graph/provider-contracts-01.js"; + +function validateRepoRelativePath(path: string): string { + if (typeof path !== "string" || path.length === 0) { + throw new Error("Repo-relative path must be a non-empty string"); + } + if (path.includes("\0")) { + throw new Error(`Repo-relative path contains a null byte: ${path}`); + } + if (/^[\\/]/.test(path) || /^[A-Za-z]:[\\/]/.test(path)) { + throw new Error(`Repo-relative path must not be absolute: ${path}`); + } + const normalized = path.replaceAll("\\", "/"); + if (repoPathEscapesRoot(normalized)) { + throw new Error(`Repo-relative path must not escape the repository: ${path}`); + } + return normalized; +} + +export { validateRepoRelativePath }; + +function validateRepoRelativePaths(paths: unknown, label: string): readonly string[] { + validateStringArray(paths as readonly string[] | undefined, label, { + allowEmpty: true, + }); + for (const path of paths as readonly string[]) validateRepoRelativePath(path); + return paths as readonly string[]; +} + +export { validateRepoRelativePaths }; + +function repoPathEscapesRoot(path: string): boolean { + return ( + path === "." || + path === ".." || + path.startsWith("../") || + path.includes("/../") || + path.endsWith("/..") + ); +} + +function validateHomeRelativePath(path: string): string { + if (typeof path !== "string" || path.length === 0) { + throw new Error("Home-relative path must be a non-empty string"); + } + if (path.includes("\0")) { + throw new Error(`Home-relative path contains a null byte: ${path}`); + } + const normalized = path.replaceAll("\\", "/"); + if (!normalized.startsWith("~/")) { + throw new Error(`Home-relative path must start with ~/: ${path}`); + } + if ( + normalized === "~/" || + normalized === "~/." || + normalized.includes("/../") || + normalized.endsWith("/..") || + normalized.includes("//") + ) { + throw new Error(`Home-relative path must not escape the home directory: ${path}`); + } + return normalized; +} + +export { validateHomeRelativePath }; + +function validateRepoIdentity(repo: RepoIdentity): RepoIdentity { + validateRequiredObject(repo, "Repo identity is required"); + if (repo.repoId && repo.repoRoot) { + throw new Error("Repo identity is ambiguous: use repoId or repoRoot, not both"); + } + if (!repo.repoId && !repo.repoRoot && !repo.remoteUrl) { + throw new Error("Repo identity must include repoId, repoRoot, or remoteUrl"); + } + return repo; +} + +export { validateRepoIdentity }; diff --git a/packages/contracts/src/shared/primitives.ts b/packages/contracts/src/shared/primitives.ts new file mode 100644 index 0000000..4b6015f --- /dev/null +++ b/packages/contracts/src/shared/primitives.ts @@ -0,0 +1,62 @@ +function collectStrings(value: unknown): string[] { + if (typeof value === "string") return [value]; + if (Array.isArray(value)) return value.flatMap((entry) => collectStrings(entry)); + if (!value || typeof value !== "object") return []; + return Object.values(value).flatMap((entry) => collectStrings(entry)); +} + +export { collectStrings }; + +function includesString(values: T, value: unknown): value is T[number] { + return typeof value === "string" && values.includes(value); +} + +export { includesString }; + +function sameStringArray(actual: readonly string[], expected: readonly string[]): boolean { + return actual.length === expected.length && actual.every((value, index) => value === expected[index]); +} + +export { sameStringArray }; + +function withoutUndefinedProperties>(value: T): Partial { + return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined)) as Partial; +} + +export { withoutUndefinedProperties }; + +function validateOptional(value: T | undefined, validator: (value: T) => void): void { + if (value !== undefined) validator(value); +} + +export { validateOptional }; + +function validateBoolean(value: unknown, label: string): asserts value is boolean { + if (typeof value !== "boolean") throw new Error(`${label} must be boolean`); +} + +export { validateBoolean }; + +function validateObject(value: unknown, label: string): asserts value is object { + if (!value || typeof value !== "object") throw new Error(`${label} is required`); +} + +export { validateObject }; + +function validateArray(value: unknown, label: string): asserts value is readonly unknown[] { + if (!Array.isArray(value)) throw new Error(`${label} must be an array`); +} + +export { validateArray }; + +function validateRequiredObject(value: T, message: string): asserts value is T & object { + if (!value || typeof value !== "object") throw new Error(message); +} + +export { validateRequiredObject }; + +function validateExactValue(value: T, expected: T, message: string): void { + if (value !== expected) throw new Error(message); +} + +export { validateExactValue }; diff --git a/packages/contracts/src/shared/validators-01.ts b/packages/contracts/src/shared/validators-01.ts new file mode 100644 index 0000000..a539628 --- /dev/null +++ b/packages/contracts/src/shared/validators-01.ts @@ -0,0 +1,202 @@ +import { + collectStrings, + sameStringArray, + validateRequiredObject, +} from "./primitives.js"; +import type { CommandExitSemantics } from "../command/contracts.js"; +import type { CommandRouteStatus } from "../command/vocabulary.js"; +import type { GraphFreshness } from "../graph/provider-contracts-01.js"; +import type { ReleaseReceiptPackageEvidence } from "../release/receipt-contracts-01.js"; +import type { ReleaseReceiptPackageName } from "../release/vocabulary-01.js"; +import { validationCheckIdRegex } from "../validation/vocabulary-02.js"; + +function packageEvidenceIncludesFile( + packages: readonly ReleaseReceiptPackageEvidence[], + packageName: ReleaseReceiptPackageName, + path: string, +): boolean { + return packages.find((entry) => entry.packageName === packageName)?.files.includes(path) ?? false; +} + +export { packageEvidenceIncludesFile }; + +function validateSha256(value: unknown, label: string): string { + const text = validateNonEmptyString(value, label); + if (!/^[a-f0-9]{64}$/i.test(text)) throw new Error(`${label} must be a sha256 hex digest`); + return text; +} + +export { validateSha256 }; + +function validateExactStringSet(actual: readonly string[], expected: readonly string[], label: string): void { + validateStringArray(actual, label, { allowEmpty: false }); + const actualSorted = [...actual].sort(); + const expectedSorted = [...expected].sort(); + if ( + actualSorted.length !== expectedSorted.length || + actualSorted.some((value, index) => value !== expectedSorted[index]) + ) { + throw new Error(`${label} must exactly match ${expected.join(", ")}`); + } +} + +export { validateExactStringSet }; + +function validateExactStringSequence(actual: readonly string[], expected: readonly string[], label: string): void { + validateStringArray(actual, label, { allowEmpty: false }); + if (!sameStringArray(actual, expected)) { + throw new Error(`${label} must exactly match ${expected.join(" ")}`); + } +} + +export { validateExactStringSequence }; + +function validateGraphReleaseSourceFreeStrings(value: unknown): void { + const forbidden = [/tirth8205/i, /pyproject\.toml/i, /setup\.py/i, /setup\.cfg/i, /Pipfile/i, /git clone/i]; + for (const text of collectStrings(value)) { + const pattern = forbidden.find((entry) => entry.test(text)); + if (pattern) throw new Error(`Graph release receipt contains forbidden source provenance: ${text}`); + } +} + +export { validateGraphReleaseSourceFreeStrings }; + +function validateCommandExitSemantics(exitSemantics: CommandExitSemantics): CommandExitSemantics { + validateRequiredObject(exitSemantics, "Command router manifest must include exitSemantics"); + if (exitSemantics.ok !== 0) throw new Error("Command exit semantics ok must be 0"); + if (exitSemantics.error !== 1) throw new Error("Command exit semantics error must be 1"); + if (exitSemantics.notImplemented !== 2) throw new Error("Command exit semantics notImplemented must be 2"); + if (exitSemantics.unsupported !== 64) throw new Error("Command exit semantics unsupported must be 64"); + if (typeof exitSemantics.jsonStable !== "boolean") { + throw new Error("Command exit semantics jsonStable must be boolean"); + } + return exitSemantics; +} + +export { validateCommandExitSemantics }; + +function validateExitCodeForStatus(exitCode: unknown, status: CommandRouteStatus): number { + if (typeof exitCode !== "number" || !Number.isInteger(exitCode) || exitCode < 0) { + throw new Error("Command route exitCode must be a non-negative integer"); + } + const expected = { + ok: { code: 0, message: "Command route ok status must use exitCode 0" }, + error: { code: 1, message: "Command route error status must use exitCode 1" }, + not_implemented: { + code: 2, + message: "Command route not_implemented status must use exitCode 2", + }, + unsupported: { code: 64, message: "Command route unsupported status must use exitCode 64" }, + }[status]; + if (exitCode !== expected.code) throw new Error(expected.message); + return exitCode; +} + +export { validateExitCodeForStatus }; + +function validateStringArray( + values: readonly string[] | undefined, + label: string, + options: { allowEmpty: boolean; allowEmptyValues?: boolean }, +): readonly string[] { + if (!Array.isArray(values)) { + throw new Error(`${label} must be an array`); + } + if (!options.allowEmpty && values.length === 0) { + throw new Error(`${label} must not be empty`); + } + for (const value of values) { + if (options.allowEmptyValues === true) { + if (typeof value !== "string") throw new Error(`${label} must contain only strings`); + } else { + validateNonEmptyString(value, label); + } + } + return values; +} + +export { validateStringArray }; + +function validateValidationChecks(checks: readonly string[], label: string): readonly string[] { + validateStringArray(checks, label, { allowEmpty: true }); + for (const check of checks) { + if (check.trim().length === 0) { + throw new Error(`${label} entries must include non-whitespace content`); + } + validateValidationCheckId(check, `${label} entry`); + } + return checks; +} + +export { validateValidationChecks }; + +function validateValidationCheckId(checkId: unknown, label: string): string { + const value = validateNonEmptyString(checkId, label); + if (!validationCheckIdRegex.test(value)) { + throw new Error(`${label} must be a stable validation check id`); + } + return value; +} + +export { validateValidationCheckId }; + +function validateNonNegativeNumber(value: unknown, label: string): number { + if (typeof value !== "number" || !Number.isFinite(value) || value < 0) { + throw new Error(`${label} must be a non-negative number`); + } + return value; +} + +export { validateNonNegativeNumber }; + +function validateNonNegativeInteger(value: unknown, label: string): number { + if (!Number.isInteger(value) || (value as number) < 0) { + throw new Error(`${label} must be a non-negative integer`); + } + return value as number; +} + +export { validateNonNegativeInteger }; + +function validatePositiveInteger(value: unknown, label: string): number { + if (!Number.isInteger(value) || (value as number) < 1) { + throw new Error(`${label} must be a positive integer`); + } + return value as number; +} + +export { validatePositiveInteger }; + +function validateNonEmptyArray(values: readonly unknown[] | undefined, label: string): readonly unknown[] { + if (!Array.isArray(values) || values.length === 0) { + throw new Error(`${label} must be a non-empty array`); + } + return values; +} + +export { validateNonEmptyArray }; + +function validateNonEmptyString(value: unknown, label: string): string { + if (typeof value !== "string" || value.length === 0) { + throw new Error(`${label} must be a non-empty string`); + } + return value; +} + +export { validateNonEmptyString }; + +function validateGraphFreshness(freshness: GraphFreshness | undefined, label: string): GraphFreshness { + validateRequiredObject(freshness, `${label} graph provider status must include freshness`); + if (typeof freshness.generatedAt !== "string" || freshness.generatedAt.length === 0) { + throw new Error(`${label} graph provider freshness must include generatedAt`); + } + if (typeof freshness.ageMs !== "number") { + throw new Error(`${label} graph provider freshness must include numeric ageMs`); + } + if (typeof freshness.stale !== "boolean") { + throw new Error(`${label} graph provider freshness must include stale`); + } + return freshness; +} + +export { validateGraphFreshness }; diff --git a/packages/contracts/src/shared/validators-02.ts b/packages/contracts/src/shared/validators-02.ts new file mode 100644 index 0000000..090ecbc --- /dev/null +++ b/packages/contracts/src/shared/validators-02.ts @@ -0,0 +1,33 @@ +import type { GraphSnapshotMetadata } from "../graph/provider-contracts-02.js"; +import { validateRepoIdentity } from "./path-validators.js"; +import { validateGraphFreshness } from "./validators-01.js"; +export { + collectStrings, + validateArray, + validateBoolean, + validateExactValue, + validateObject, + validateOptional, + validateRequiredObject, +} from "./primitives.js"; +import { + validateRequiredObject, +} from "./primitives.js"; + +function validateGraphSnapshotMetadata(metadata: GraphSnapshotMetadata): GraphSnapshotMetadata { + validateRequiredObject(metadata, "Graph snapshot metadata is required"); + if (typeof metadata.schemaVersion !== "number") { + throw new Error("Graph snapshot metadata must include numeric schemaVersion"); + } + if (typeof metadata.provider !== "string" || metadata.provider.length === 0) { + throw new Error("Graph snapshot metadata must include provider"); + } + validateRepoIdentity(metadata.repo); + validateGraphFreshness(metadata.freshness, "Graph snapshot"); + if (!Array.isArray(metadata.nodeKinds) || !Array.isArray(metadata.edgeKinds)) { + throw new Error("Graph snapshot metadata must include nodeKinds and edgeKinds"); + } + return metadata; +} + +export { validateGraphSnapshotMetadata }; diff --git a/packages/contracts/src/validation/capability-contracts.ts b/packages/contracts/src/validation/capability-contracts.ts new file mode 100644 index 0000000..62ac0f1 --- /dev/null +++ b/packages/contracts/src/validation/capability-contracts.ts @@ -0,0 +1,111 @@ +import type { EditRefusal } from "../edit/contracts.js"; +import type { GraphProviderStatus } from "../graph/provider-contracts-02.js"; +import type { + PythonValidationCapabilityRun, + ValidationDiagnostic, + ValidationResultManifest, +} from "./diagnostic-contracts.js"; +import type { PythonProjectContext } from "./python-project-contracts-02.js"; +import type { ValidationFailure } from "./request-contracts.js"; +import type { ValidationResultStatus } from "./vocabulary-01.js"; + +const pythonCapabilityActivations = ["enabled", "disabled", "not_applicable"] as const; + +export { pythonCapabilityActivations }; + +type PythonCapabilityActivation = (typeof pythonCapabilityActivations)[number]; + +export type { PythonCapabilityActivation }; + +const pythonPytestSelectionModes = ["none", "direct_argv", "manifest"] as const; + +export { pythonPytestSelectionModes }; + +type PythonPytestSelectionMode = (typeof pythonPytestSelectionModes)[number]; + +export type { PythonPytestSelectionMode }; + +const pythonCapabilityProcessTerminations = ["exited", "timeout", "signal", "spawn_error", "overflow"] as const; + +export { pythonCapabilityProcessTerminations }; + +type PythonCapabilityProcessTermination = (typeof pythonCapabilityProcessTerminations)[number]; + +export type { PythonCapabilityProcessTermination }; + +interface PythonCapabilityCounts { + candidateCount: number; + collectedCount: number; + executedCount: number; + passedCount: number; + failedCount: number; + skippedCount: number; + xfailedCount: number; + xpassedCount: number; + errorCount: number; +} + +export type { PythonCapabilityCounts }; + +interface PythonCapabilityCleanupEvidence { + attempted: boolean; + ok: boolean; + failureMessage?: string; +} + +export type { PythonCapabilityCleanupEvidence }; + +interface PythonCapabilityInvocation { + stage: "collection" | "execution"; + command: string; + argsDigest: string; + argCount: number; + selectionMode: PythonPytestSelectionMode; + selectionDigest?: string; + durationMs: number; + termination: PythonCapabilityProcessTermination; + exitCode?: number; + signal?: string; + outputBytes: number; + stdoutDigest?: string; + stderrDigest?: string; +} + +export type { PythonCapabilityInvocation }; + +interface PythonPytestValidationCapabilityRun { + capability: "pytest"; + checkId: string; + activation: PythonCapabilityActivation; + outcome: string; + message: string; + projectKey?: string; + projectRoot?: string; + configFile?: string; + targetCount?: number; + candidatePaths?: readonly string[]; + collectedNodeIds?: readonly string[]; + afterStateFingerprint?: string; + selectionMode?: PythonPytestSelectionMode; + selectionDigest?: string; + counts?: PythonCapabilityCounts; + collection?: PythonCapabilityInvocation; + execution?: PythonCapabilityInvocation; + cleanup?: PythonCapabilityCleanupEvidence; +} + +export type { PythonPytestValidationCapabilityRun }; + +interface ValidationResult { + ok: boolean; + status: ValidationResultStatus; + diagnostics: readonly ValidationDiagnostic[]; + graphStatus?: GraphProviderStatus; + failure?: ValidationFailure; + refusal?: EditRefusal; + manifest?: ValidationResultManifest; + pythonProjectContexts?: readonly PythonProjectContext[]; + pythonCapabilityRuns?: readonly PythonValidationCapabilityRun[]; +} + +export type { ValidationResult }; diff --git a/packages/contracts/src/validation/diagnostic-contracts.ts b/packages/contracts/src/validation/diagnostic-contracts.ts new file mode 100644 index 0000000..e7ca5e9 --- /dev/null +++ b/packages/contracts/src/validation/diagnostic-contracts.ts @@ -0,0 +1,140 @@ +import type { PythonPytestValidationCapabilityRun } from "./capability-contracts.js"; +import type { + PYTHON_VALIDATION_CAPABILITY_RUN_SCHEMA_ID, + PythonProjectExecutableSource, +} from "./python-project-contracts-01.js"; +import type { PythonTypesValidationCapabilityRun } from "./python-project-contracts-02.js"; +import type { ValidationScopeKind } from "./request-contracts.js"; +import type { + ValidationCheckOutcome, + ValidationCheckRunStatus, + ValidationDiagnosticCategory, +} from "./vocabulary-01.js"; +import type { + PythonValidationCapabilityState, + PythonValidationCapabilityTermination, + ValidationSkippedCheckReason, +} from "./vocabulary-02.js"; + +interface ValidationDiagnostic { + category: ValidationDiagnosticCategory; + message: string; + path?: string; + severity: "info" | "warning" | "error"; + code?: string; + line?: number; + column?: number; + endLine?: number; + endColumn?: number; + tool?: ValidationDiagnosticToolProvenance; +} + +export type { ValidationDiagnostic }; + +interface ValidationDiagnosticToolProvenance { + name: string; + command: string; + version?: string; + source?: string; + cwd?: string; +} + +export type { ValidationDiagnosticToolProvenance }; + +interface ValidationCheckManifestEntry { + checkId: string; + owner: string; + adapter: string; + defaultSeverity: ValidationDiagnostic["severity"]; + supportedScopes: readonly ValidationScopeKind[]; + requiresGraph: boolean; +} + +export type { ValidationCheckManifestEntry }; + +interface PythonRuffCapabilityIdentity { + schemaId: typeof PYTHON_VALIDATION_CAPABILITY_RUN_SCHEMA_ID; + schemaVersion: 1; + checkId: "python.ruff-lint" | "python.ruff-format"; + capability: "ruff_lint" | "ruff_format"; + state: PythonValidationCapabilityState; + projectKey?: string; + contextFingerprint?: string; + afterStateManifestFingerprint?: string; + sourcePaths?: readonly string[]; + configPaths?: readonly string[]; + executable?: string; + command?: string; +} + +export type { PythonRuffCapabilityIdentity }; + +interface PythonRuffCapabilityExecution { + argv?: readonly string[]; + cwd?: string; + configPath?: string; + toolVersion?: string; + toolSource?: PythonProjectExecutableSource; + termination?: PythonValidationCapabilityTermination; + exitCode?: number; + signal?: string; + invocations?: readonly PythonValidationCapabilityInvocation[]; + durationMs: number; + diagnosticCount: number; + failureMessage?: string; +} + +export type { PythonRuffCapabilityExecution }; + +interface PythonRuffValidationCapabilityRun extends PythonRuffCapabilityIdentity, PythonRuffCapabilityExecution {} + +export type { PythonRuffValidationCapabilityRun }; + +type PythonValidationCapabilityRun = + | PythonTypesValidationCapabilityRun + | PythonRuffValidationCapabilityRun + | PythonPytestValidationCapabilityRun; + +export type { PythonValidationCapabilityRun }; + +interface PythonValidationCapabilityInvocation { + argv: readonly string[]; + termination: PythonValidationCapabilityTermination; + exitCode?: number; + signal?: string; + durationMs: number; +} + +export type { PythonValidationCapabilityInvocation }; + +interface ValidationCheckRunSummary { + checkId: string; + status: ValidationCheckRunStatus; + outcome?: ValidationCheckOutcome; + durationMs?: number; + diagnosticCount?: number; + failureMessage?: string; + pythonCapabilityRuns?: readonly PythonValidationCapabilityRun[]; +} + +export type { ValidationCheckRunSummary }; + +interface ValidationSkippedCheck { + checkId: string; + reason: ValidationSkippedCheckReason; + message: string; +} + +export type { ValidationSkippedCheck }; + +interface ValidationResultManifest { + schemaVersion: number; + checks: readonly string[]; + generatedAt: string; + entries?: readonly ValidationCheckManifestEntry[]; + durationMs?: number; + runs?: readonly ValidationCheckRunSummary[]; + skippedChecks?: readonly ValidationSkippedCheck[]; +} + +export type { ValidationResultManifest }; diff --git a/packages/contracts/src/validation/prewrite-status-validators-01.ts b/packages/contracts/src/validation/prewrite-status-validators-01.ts new file mode 100644 index 0000000..b0c5a23 --- /dev/null +++ b/packages/contracts/src/validation/prewrite-status-validators-01.ts @@ -0,0 +1,192 @@ +import { + validateBoolean, + validateExactValue, + validateOptional, + validateRequiredObject, +} from "../shared/validators-02.js"; +import { includesString } from "../shared/primitives.js"; +import { validateProviderStatus } from "../graph/provider-validators.js"; +import { graphProviderModes } from "../graph/vocabulary-01.js"; +import { validateRepoIdentity } from "../shared/path-validators.js"; +import { + validateExactStringSet, + validateNonEmptyString, + validateNonNegativeNumber, + validateStringArray, + validateValidationChecks, +} from "../shared/validators-01.js"; +import { + validateValidationAdapterDegradedCheckStatus, + validateValidationAdapterToolchainStatus, +} from "./prewrite-status-validators-02.js"; +import { validateValidationScope } from "./request-validators-01.js"; +import { + validatePreWriteValidationFailureSummary, + validatePreWriteValidationGraph, + validatePreWriteValidationOverlaySummary, + validateValidationCheckManifestEntry, +} from "./request-validators-02.js"; +import type { + PreWriteValidationReceipt, + ValidationAdapterRuntimeStatus, + ValidationStatusPayload} from "./status-contracts.js"; +import { + validationAdapterRuntimeStates, + validationDaemonReadinessStates, +} from "./status-contracts.js"; +import { validationResultStatuses } from "./vocabulary-01.js"; + +function validatePreWriteValidationReceipt(receipt: PreWriteValidationReceipt): PreWriteValidationReceipt { + validateRequiredObject(receipt, "Pre-write validation receipt is required"); + validateExactValue(receipt.schemaVersion, 1, "Pre-write validation receipt schemaVersion must be 1"); + validateExactValue( + receipt.kind, + "pre_write_validation", + "Pre-write validation receipt kind must be pre_write_validation", + ); + validateExactValue( + receipt.route, + "validate.pre-write", + "Pre-write validation receipt route must be validate.pre-write", + ); + validateStringArray(receipt.canonicalCommand, "Pre-write validation receipt canonicalCommand", { allowEmpty: false }); + validateNonEmptyString(receipt.generatedAt, "Pre-write validation receipt generatedAt"); + validateNonNegativeNumber(receipt.durationMs, "Pre-write validation receipt durationMs"); + if (!Number.isInteger(receipt.timeoutMs) || receipt.timeoutMs < 1) { + throw new Error("Pre-write validation receipt timeoutMs must be a positive integer"); + } + validateBoolean(receipt.ok, "Pre-write validation receipt ok"); + validatePreWriteValidationReceiptOptionals(receipt); + if (!includesString(validationResultStatuses, receipt.validationStatus)) { + throw new Error(`Unknown pre-write validation receipt status: ${String(receipt.validationStatus)}`); + } + if (!Number.isInteger(receipt.diagnosticCount) || receipt.diagnosticCount < 0) { + throw new Error("Pre-write validation receipt diagnosticCount must be a non-negative integer"); + } + validatePreWriteValidationReceiptOutcome(receipt); + return receipt; +} + +export { validatePreWriteValidationReceipt }; + +function validatePreWriteValidationReceiptOptionals(receipt: PreWriteValidationReceipt): void { + validateOptional(receipt.requestId, (value) => + validateNonEmptyString(value, "Pre-write validation receipt requestId"), + ); + validateOptional(receipt.repo, validateRepoIdentity); + validateOptional(receipt.scope, validateValidationScope); + validateOptional(receipt.checks, (value) => + validateValidationChecks(value, "Pre-write validation receipt checks"), + ); + validateOptional(receipt.graph, validatePreWriteValidationGraph); + validateOptional(receipt.overlays, validatePreWriteValidationOverlaySummary); + validateOptional(receipt.failureSummary, validatePreWriteValidationFailureSummary); +} + +function validatePreWriteValidationReceiptOutcome(receipt: PreWriteValidationReceipt): void { + if (!receipt.ok) { + if (receipt.validationStatus === "passed") { + throw new Error("Pre-write validation failure receipt must not use passed validationStatus"); + } + if (receipt.failureSummary === undefined) { + throw new Error("Pre-write validation failure receipt must include failureSummary"); + } + return; + } + if (receipt.validationStatus !== "passed") { + throw new Error("Pre-write validation pass receipt must use passed validationStatus"); + } + const requiredEvidence = [receipt.repo, receipt.scope, receipt.checks, receipt.graph, receipt.overlays]; + if (requiredEvidence.some((value) => value === undefined)) { + throw new Error("Pre-write validation pass receipt must include repo, scope, checks, graph, and overlays"); + } + if (receipt.failureSummary !== undefined) { + throw new Error("Pre-write validation pass receipt must not include failureSummary"); + } +} + +function validateValidationStatusPayload(payload: ValidationStatusPayload): ValidationStatusPayload { + validateRequiredObject(payload, "Validation status payload is required"); + validateExactValue(payload.schemaVersion, 1, "Validation status payload schemaVersion must be 1"); + validateBoolean(payload.ready, "Validation status payload ready"); + validateNonEmptyString(payload.generatedAt, "Validation status payload generatedAt"); + validateValidationStatusAdapterRegistry(payload); + validateValidationStatusGraph(payload); + validateOptional(payload.daemon, validateValidationDaemonStatus); + return payload; +} + +export { validateValidationStatusPayload }; + +function validateValidationStatusAdapterRegistry(payload: ValidationStatusPayload): void { + validateRequiredObject(payload.adapterRegistry, "Validation status payload adapterRegistry is required"); + validateExactStringSet( + payload.adapterRegistry.checkRoutes, + ["files", "staged", "changed", "tree", "all", "manifest"], + "Validation status payload checkRoutes", + ); + validateExactStringSet( + payload.adapterRegistry.validateRoutes, + ["request", "hypothetical", "pre-write", "manifest"], + "Validation status payload validateRoutes", + ); + validateValidationChecks(payload.adapterRegistry.checkIds, "Validation status payload checkIds"); + if (!Array.isArray(payload.adapterRegistry.entries)) { + throw new Error("Validation status payload entries must be an array"); + } + for (const entry of payload.adapterRegistry.entries) validateValidationCheckManifestEntry(entry); + validateOptional(payload.adapterRegistry.adapters, (adapters) => { + if (!Array.isArray(payload.adapterRegistry.adapters)) { + throw new Error("Validation status payload adapters must be an array"); + } + for (const adapter of adapters) validateValidationAdapterRuntimeStatus(adapter); + }); +} + +function validateValidationStatusGraph(payload: ValidationStatusPayload): void { + validateRequiredObject(payload.graph, "Validation status payload graph is required"); + if (!includesString(graphProviderModes, payload.graph.mode)) { + throw new Error(`Unknown validation status graph mode: ${String(payload.graph.mode)}`); + } + const graphStatus = validateProviderStatus(payload.graph.status); + if (graphStatus.mode !== payload.graph.mode) { + throw new Error("Validation status graph status mode must match graph mode"); + } +} + +function validateValidationDaemonStatus(daemon: NonNullable): void { + validateRequiredObject(daemon, "Validation status daemon must be an object"); + if (!includesString(validationDaemonReadinessStates, daemon.state)) { + throw new Error(`Unknown validation daemon readiness state: ${String(daemon.state)}`); + } + validateOptional(daemon.message, (value) => validateNonEmptyString(value, "Validation status daemon message")); +} + +function validateValidationAdapterRuntimeStatus( + status: ValidationAdapterRuntimeStatus, +): ValidationAdapterRuntimeStatus { + validateRequiredObject(status, "Validation adapter runtime status is required"); + validateNonEmptyString(status.adapter, "Validation adapter runtime status adapter"); + if (!includesString(validationAdapterRuntimeStates, status.status)) { + throw new Error(`Unknown validation adapter runtime status: ${String(status.status)}`); + } + validateValidationChecks(status.checkIds, "Validation adapter runtime status checkIds"); + if (status.toolchain !== undefined) { + if (!Array.isArray(status.toolchain)) { + throw new Error("Validation adapter runtime status toolchain must be an array"); + } + for (const tool of status.toolchain) validateValidationAdapterToolchainStatus(tool); + } + if (status.degradedChecks !== undefined) { + if (!Array.isArray(status.degradedChecks)) { + throw new Error("Validation adapter runtime status degradedChecks must be an array"); + } + for (const degradedCheck of status.degradedChecks) validateValidationAdapterDegradedCheckStatus(degradedCheck); + } + if (status.tempWorkspaceRequired !== undefined && typeof status.tempWorkspaceRequired !== "boolean") { + throw new Error("Validation adapter runtime status tempWorkspaceRequired must be boolean"); + } + return status; +} + +export { validateValidationAdapterRuntimeStatus }; diff --git a/packages/contracts/src/validation/prewrite-status-validators-02.ts b/packages/contracts/src/validation/prewrite-status-validators-02.ts new file mode 100644 index 0000000..8205489 --- /dev/null +++ b/packages/contracts/src/validation/prewrite-status-validators-02.ts @@ -0,0 +1,74 @@ +import { validateRequiredObject } from "../shared/validators-02.js"; +import { includesString } from "../shared/primitives.js"; +import { validateNonEmptyString, validateValidationCheckId } from "../shared/validators-01.js"; +import type { ValidationAdapterDegradedCheckStatus, ValidationAdapterToolchainStatus } from "./status-contracts.js"; +import { validationCheckRunStatuses } from "./vocabulary-01.js"; + +function validateValidationAdapterToolchainStatus( + status: ValidationAdapterToolchainStatus, +): ValidationAdapterToolchainStatus { + validateRequiredObject(status, "Validation adapter toolchain status is required"); + validateNonEmptyString(status.tool, "Validation adapter toolchain status tool"); + if (typeof status.available !== "boolean") { + throw new Error("Validation adapter toolchain status available must be boolean"); + } + if (status.command !== undefined) + validateNonEmptyString(status.command, "Validation adapter toolchain status command"); + if (status.version !== undefined) + validateNonEmptyString(status.version, "Validation adapter toolchain status version"); + if (status.failureMessage !== undefined) { + validateNonEmptyString(status.failureMessage, "Validation adapter toolchain status failureMessage"); + } + if (status.cwd !== undefined) validateNonEmptyString(status.cwd, "Validation adapter toolchain status cwd"); + if (status.configFile !== undefined) { + validateNonEmptyString(status.configFile, "Validation adapter toolchain status configFile"); + } + if (status.source !== undefined) validateNonEmptyString(status.source, "Validation adapter toolchain status source"); + return status; +} + +export { validateValidationAdapterToolchainStatus }; + +function validateValidationAdapterDegradedCheckStatus( + status: ValidationAdapterDegradedCheckStatus, +): ValidationAdapterDegradedCheckStatus { + validateRequiredObject(status, "Validation adapter degraded check status is required"); + validateValidationCheckId(status.checkId, "Validation adapter degraded check status checkId"); + if (!includesString(validationCheckRunStatuses, status.status)) { + throw new Error(`Unknown validation adapter degraded check status: ${String(status.status)}`); + } + validateNonEmptyString(status.reason, "Validation adapter degraded check status reason"); + validateNonEmptyString(status.message, "Validation adapter degraded check status message"); + if (status.requiredTool !== undefined) { + validateNonEmptyString(status.requiredTool, "Validation adapter degraded check status requiredTool"); + } + if (status.retainedCompatibility !== undefined && typeof status.retainedCompatibility !== "boolean") { + throw new Error("Validation adapter degraded check status retainedCompatibility must be boolean"); + } + if (status.followUpIssue !== undefined) { + validateNonEmptyString(status.followUpIssue, "Validation adapter degraded check status followUpIssue"); + } + if (status.currentUsage !== undefined) { + validateValidationAdapterCurrentUsage(status.currentUsage); + } + return status; +} + +export { validateValidationAdapterDegradedCheckStatus }; + +function validateValidationAdapterCurrentUsage( + currentUsage: ValidationAdapterDegradedCheckStatus["currentUsage"], +): NonNullable { + validateRequiredObject( + currentUsage, + "Validation adapter degraded check status currentUsage is required when present", + ); + for (const key of ["opcore", "orchestra", "covibes", "gateway"] as const) { + if (typeof currentUsage[key] !== "boolean") { + throw new Error(`Validation adapter degraded check status currentUsage.${key} must be boolean`); + } + } + return currentUsage; +} + +export { validateValidationAdapterCurrentUsage }; diff --git a/packages/contracts/src/validation/python-project-contracts-01.ts b/packages/contracts/src/validation/python-project-contracts-01.ts new file mode 100644 index 0000000..8b4f0eb --- /dev/null +++ b/packages/contracts/src/validation/python-project-contracts-01.ts @@ -0,0 +1,95 @@ +const PYTHON_PROJECT_CONTEXT_SCHEMA_ID = "opcore.python.project-context.v1" as const; + +export { PYTHON_PROJECT_CONTEXT_SCHEMA_ID }; + +const PYTHON_VALIDATION_CAPABILITY_RUN_SCHEMA_ID = "opcore.python.validation-capability-run" as const; + +export { PYTHON_VALIDATION_CAPABILITY_RUN_SCHEMA_ID }; + +const pythonProjectContextOutcomes = ["resolved", "degraded", "unsupported", "ambiguous"] as const; + +export { pythonProjectContextOutcomes }; + +type PythonProjectContextOutcome = (typeof pythonProjectContextOutcomes)[number]; + +export type { PythonProjectContextOutcome }; + +const pythonProjectContextReasonCodes = [ + "missing_config", + "invalid_config", + "conflicting_managers", + "conflicting_targets", + "interpreter_unavailable", + "tool_unavailable", + "probe_timeout", + "probe_signal", + "probe_spawn_failure", + "probe_exit_failure", + "malformed_probe_output", + "unsupported_target", + "unsupported_platform", + "path_refused", + "symlink_refused", + "incompatible_interpreter", + "ambiguous_path", +] as const; + +export { pythonProjectContextReasonCodes }; + +type PythonProjectContextReasonCode = (typeof pythonProjectContextReasonCodes)[number]; + +export type { PythonProjectContextReasonCode }; + +const pythonProjectManagerKinds = ["pip", "uv", "poetry", "pdm", "pipenv"] as const; + +export { pythonProjectManagerKinds }; + +type PythonProjectManagerKind = (typeof pythonProjectManagerKinds)[number]; + +export type { PythonProjectManagerKind }; + +const pythonProjectLayoutKinds = ["flat", "src", "namespace", "stub", "package"] as const; + +export { pythonProjectLayoutKinds }; + +type PythonProjectLayoutKind = (typeof pythonProjectLayoutKinds)[number]; + +export type { PythonProjectLayoutKind }; + +const pythonProjectExecutableSources = [ + "explicit_override", + "active_environment", + "project_local_environment", + "manager_environment", + "path", +] as const; + +export { pythonProjectExecutableSources }; + +type PythonProjectExecutableSource = (typeof pythonProjectExecutableSources)[number]; + +export type { PythonProjectExecutableSource }; + +const pythonProjectToolKinds = ["mypy", "pyright", "ruff", "pytest", "build"] as const; + +export { pythonProjectToolKinds }; + +type PythonProjectToolKind = (typeof pythonProjectToolKinds)[number]; + +export type { PythonProjectToolKind }; + +interface PythonProjectContextReason { + code: PythonProjectContextReasonCode; + message: string; + path?: string; + tool?: string; +} + +export type { PythonProjectContextReason }; + +interface PythonProjectFileEvidence { + path: string; + role: "boundary" | "config" | "lock" | "requirements" | "build" | "layout"; +} + +export type { PythonProjectFileEvidence }; diff --git a/packages/contracts/src/validation/python-project-contracts-02.ts b/packages/contracts/src/validation/python-project-contracts-02.ts new file mode 100644 index 0000000..4e31801 --- /dev/null +++ b/packages/contracts/src/validation/python-project-contracts-02.ts @@ -0,0 +1,156 @@ +import type { + PYTHON_PROJECT_CONTEXT_SCHEMA_ID, + PYTHON_VALIDATION_CAPABILITY_RUN_SCHEMA_ID, + PythonProjectContextOutcome, + PythonProjectContextReason, + PythonProjectExecutableSource, + PythonProjectFileEvidence, + PythonProjectLayoutKind, + PythonProjectManagerKind, + PythonProjectToolKind, +} from "./python-project-contracts-01.js"; +import type { PythonValidationAuthority, PythonValidationCapabilityRunStatus } from "./vocabulary-01.js"; +import type { PythonValidationAuthoritySource, PythonValidationCapabilityTerminationKind } from "./vocabulary-02.js"; + +interface PythonProjectManagerEvidence { + kind: PythonProjectManagerKind; + configFiles: readonly string[]; + lockFiles: readonly string[]; +} + +export type { PythonProjectManagerEvidence }; + +interface PythonProjectExecutableProvenance { + executable: string; + argv: readonly string[]; + cwd: string; + source: PythonProjectExecutableSource; + version?: string; + configFile?: string; +} + +export type { PythonProjectExecutableProvenance }; + +interface PythonInterpreterProvenance extends PythonProjectExecutableProvenance { + version: string; + implementation: string; + platform: string; + architecture: string; + abi: string; + soabi: string; +} + +export type { PythonInterpreterProvenance }; + +interface PythonProjectToolProvenance extends PythonProjectExecutableProvenance { + tool: PythonProjectToolKind; + available: boolean; +} + +export type { PythonProjectToolProvenance }; + +interface PythonProjectTarget { + requiresPython?: string; + version?: string; + platform?: string; + implementation?: string; + conflicts: readonly string[]; +} + +export type { PythonProjectTarget }; + +interface PythonProjectLayoutEvidence { + kinds: readonly PythonProjectLayoutKind[]; + paths: readonly string[]; +} + +export type { PythonProjectLayoutEvidence }; + +interface PythonProjectBuildSystem { + configFile: string; + backend?: string; + requires: readonly string[]; +} + +export type { PythonProjectBuildSystem }; + +interface PythonProjectContext { + schemaId: typeof PYTHON_PROJECT_CONTEXT_SCHEMA_ID; + schemaVersion: 1; + target: string; + repositoryRoot: string; + projectRoot: string; + projectBoundary: string; + sourceRoots: readonly string[]; + layout: PythonProjectLayoutEvidence; + evidence: readonly PythonProjectFileEvidence[]; + targetRuntime: PythonProjectTarget; + managers: readonly PythonProjectManagerEvidence[]; + buildSystem?: PythonProjectBuildSystem; + interpreter?: PythonInterpreterProvenance; + tools: readonly PythonProjectToolProvenance[]; + projectKey: string; + contextFingerprint: string; + outcome: PythonProjectContextOutcome; + reasons: readonly PythonProjectContextReason[]; +} + +export type { PythonProjectContext }; + +interface PythonValidationCapabilityToolProvenance { + name: PythonValidationAuthority; + /** Portable executable locator: repo:, project:, path:, or external:. */ + executable: string; + argv: readonly string[]; + cwd: string; + source: PythonProjectExecutableSource; + version?: string; + configFile?: string; +} + +export type { PythonValidationCapabilityToolProvenance }; + +interface PythonValidationCapabilityExecution { + termination: PythonValidationCapabilityTerminationKind; + exitCode?: number; + signal?: string; + failureSummary?: string; +} + +export type { PythonValidationCapabilityExecution }; + +interface PythonTypesCapabilityIdentity { + schemaId: typeof PYTHON_VALIDATION_CAPABILITY_RUN_SCHEMA_ID; + schemaVersion: 1; + capability: "types"; + checkId: "python.types"; + projectKey: string; + contextFingerprint: string; + projectRoot: string; + targets: readonly string[]; + selectedSourcePaths: readonly string[]; + selectedConfigPaths: readonly string[]; + afterStateManifestFingerprint: string; + authority?: PythonValidationAuthority; + authoritySource?: PythonValidationAuthoritySource; +} + +export type { PythonTypesCapabilityIdentity }; + +interface PythonTypesCapabilityOutcome { + status: PythonValidationCapabilityRunStatus; + tool?: PythonValidationCapabilityToolProvenance; + execution?: PythonValidationCapabilityExecution; + durationMs: number; + diagnosticCount: number; + errorCount: number; + warningCount: number; + noteCount: number; +} + +export type { PythonTypesCapabilityOutcome }; + +/** Portable, source-free evidence for one attempted Python capability in one canonical project. */ +interface PythonTypesValidationCapabilityRun extends PythonTypesCapabilityIdentity, PythonTypesCapabilityOutcome {} + +export type { PythonTypesValidationCapabilityRun }; diff --git a/packages/contracts/src/validation/python-project-validators-01.ts b/packages/contracts/src/validation/python-project-validators-01.ts new file mode 100644 index 0000000..b27a1a0 --- /dev/null +++ b/packages/contracts/src/validation/python-project-validators-01.ts @@ -0,0 +1,295 @@ +import { includesString } from "../shared/primitives.js"; +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { validateNonEmptyString, validateNonNegativeInteger, validateStringArray } from "../shared/validators-01.js"; +import { validateArray, validateObject, validateOptional } from "../shared/validators-02.js"; +import type { + PythonCapabilityCleanupEvidence, + PythonCapabilityCounts, + PythonCapabilityInvocation} from "./capability-contracts.js"; +import { + pythonCapabilityProcessTerminations, + pythonPytestSelectionModes, +} from "./capability-contracts.js"; +import { + PYTHON_PROJECT_CONTEXT_SCHEMA_ID, + pythonProjectContextOutcomes, + pythonProjectContextReasonCodes, + pythonProjectLayoutKinds, + pythonProjectManagerKinds, + pythonProjectToolKinds, +} from "./python-project-contracts-01.js"; +import type { PythonProjectContext } from "./python-project-contracts-02.js"; +import { validateExactObjectKeys } from "./python-validator-primitives.js"; +import { + validateExactEnumArray, + validatePythonExecutableProvenance, + validatePythonInterpreterProvenance, + validatePythonProjectRoot, + validatePythonProjectRoots, + validatePythonProjectTarget, + validateRepoPathArray, + validateSha256Identity, +} from "./python-project-validators-02.js"; + +function validatePythonProjectContext(context: PythonProjectContext): PythonProjectContext { + validateObject(context, "Python project context"); + validatePythonProjectContextIdentity(context); + validatePythonProjectLayout(context); + validatePythonProjectEvidence(context); + validatePythonProjectTarget(context.targetRuntime); + validatePythonProjectManagers(context.managers); + validateOptional(context.buildSystem, validatePythonProjectBuildSystem); + validateOptional(context.interpreter, validatePythonInterpreterProvenance); + validatePythonProjectTools(context.tools); + validatePythonProjectOutcome(context); + return context; +} + +export { validatePythonProjectContext }; + +function validatePythonProjectContextIdentity(context: PythonProjectContext): void { + validateExactObjectKeys( + context, + [ + "schemaId", + "schemaVersion", + "target", + "repositoryRoot", + "projectRoot", + "projectBoundary", + "sourceRoots", + "layout", + "evidence", + "targetRuntime", + "managers", + "buildSystem", + "interpreter", + "tools", + "projectKey", + "contextFingerprint", + "outcome", + "reasons", + ], + "Python project context", + ); + if (context.schemaId !== PYTHON_PROJECT_CONTEXT_SCHEMA_ID) { + throw new Error(`Python project context schemaId must be ${PYTHON_PROJECT_CONTEXT_SCHEMA_ID}`); + } + if (context.schemaVersion !== 1) throw new Error("Python project context schemaVersion must be 1"); + validateRepoRelativePath(context.target); + if (!/\.pyi?$/u.test(context.target)) throw new Error("Python project context target must be a .py or .pyi path"); + validateNonEmptyString(context.repositoryRoot, "Python project context repositoryRoot"); + validatePythonProjectRoot(context.projectRoot, "Python project context projectRoot"); + validatePythonProjectRoot(context.projectBoundary, "Python project context projectBoundary"); + validatePythonProjectRoots(context.sourceRoots, "Python project context sourceRoots"); +} + +export { validatePythonProjectContextIdentity }; + +function validatePythonProjectLayout(context: PythonProjectContext): void { + validateObject(context.layout, "Python project context layout"); + validateExactObjectKeys(context.layout, ["kinds", "paths"], "Python project context layout"); + validateExactEnumArray(context.layout.kinds, pythonProjectLayoutKinds, "Python project context layout kinds", false); + validatePythonProjectRoots(context.layout.paths, "Python project context layout paths"); +} + +export { validatePythonProjectLayout }; + +function validatePythonProjectEvidence(context: PythonProjectContext): void { + validateArray(context.evidence, "Python project context evidence"); + for (const entry of context.evidence) { + validateExactObjectKeys(entry, ["path", "role"], "Python project context evidence"); + validateRepoRelativePath(entry.path); + if (!includesString(["boundary", "config", "lock", "requirements", "build", "layout"] as const, entry.role)) { + throw new Error(`Unknown Python project evidence role: ${String(entry.role)}`); + } + } +} + +export { validatePythonProjectEvidence }; + +function validatePythonProjectManagers(managers: PythonProjectContext["managers"]): void { + validateArray(managers, "Python project context managers"); + for (const manager of managers) { + validateExactObjectKeys(manager, ["kind", "configFiles", "lockFiles"], "Python project manager evidence"); + if (!includesString(pythonProjectManagerKinds, manager.kind)) { + throw new Error(`Unknown Python project manager kind: ${String(manager.kind)}`); + } + validateRepoPathArray(manager.configFiles, "Python project manager configFiles"); + validateRepoPathArray(manager.lockFiles, "Python project manager lockFiles"); + } +} + +export { validatePythonProjectManagers }; + +function validatePythonProjectBuildSystem(buildSystem: NonNullable): void { + validateExactObjectKeys(buildSystem, ["configFile", "backend", "requires"], "Python project buildSystem"); + validateRepoRelativePath(buildSystem.configFile); + validateOptional(buildSystem.backend, (backend) => + validateNonEmptyString(backend, "Python project buildSystem backend"), + ); + validateStringArray(buildSystem.requires, "Python project buildSystem requires", { allowEmpty: true }); +} + +export { validatePythonProjectBuildSystem }; + +function validatePythonProjectTools(tools: PythonProjectContext["tools"]): void { + validateArray(tools, "Python project context tools"); + for (const tool of tools) { + validateExactObjectKeys( + tool, + ["tool", "available", "executable", "argv", "cwd", "source", "version", "configFile"], + "Python project tool provenance", + ); + if (!includesString(pythonProjectToolKinds, tool.tool)) + throw new Error(`Unknown Python project tool: ${String(tool.tool)}`); + if (typeof tool.available !== "boolean") throw new Error("Python project tool available must be boolean"); + validatePythonExecutableProvenance(tool, `Python project tool ${tool.tool}`); + if (tool.available && tool.version === undefined) { + throw new Error(`Available Python project tool ${tool.tool} must include version provenance`); + } + } +} + +export { validatePythonProjectTools }; + +function validatePythonProjectOutcome(context: PythonProjectContext): void { + validateSha256Identity(context.projectKey, "Python project context projectKey"); + validateSha256Identity(context.contextFingerprint, "Python project context contextFingerprint"); + if (!includesString(pythonProjectContextOutcomes, context.outcome)) { + throw new Error(`Unknown Python project context outcome: ${String(context.outcome)}`); + } + validateArray(context.reasons, "Python project context reasons"); + for (const reason of context.reasons) { + validateExactObjectKeys(reason, ["code", "message", "path", "tool"], "Python project context reason"); + if (!includesString(pythonProjectContextReasonCodes, reason.code)) { + throw new Error(`Unknown Python project context reason: ${String(reason.code)}`); + } + validateNonEmptyString(reason.message, "Python project context reason message"); + validateOptional(reason.path, validateRepoRelativePath); + validateOptional(reason.tool, (tool) => validateNonEmptyString(tool, "Python project context reason tool")); + } + if (context.outcome === "resolved" && context.reasons.length > 0) { + throw new Error("Resolved Python project context must not include reasons"); + } + if (context.outcome !== "resolved" && context.reasons.length === 0) { + throw new Error("Non-resolved Python project context must include reasons"); + } +} + +export { validatePythonProjectOutcome }; + +function validatePythonProjectContexts(contexts: readonly PythonProjectContext[]): readonly PythonProjectContext[] { + if (!Array.isArray(contexts)) throw new Error("Python project contexts must be an array"); + const targets = new Set(); + for (const context of contexts) { + validatePythonProjectContext(context); + if (targets.has(context.target)) throw new Error(`Duplicate Python project context target: ${context.target}`); + targets.add(context.target); + } + return contexts; +} + +export { validatePythonProjectContexts }; + +function validatePythonCapabilityCounts(counts: PythonCapabilityCounts): void { + if (!counts || typeof counts !== "object") throw new Error("Python capability counts are required"); + validateExactObjectKeys( + counts, + [ + "candidateCount", + "collectedCount", + "executedCount", + "passedCount", + "failedCount", + "skippedCount", + "xfailedCount", + "xpassedCount", + "errorCount", + ], + "Python capability counts", + ); + for (const key of Object.keys(counts) as (keyof PythonCapabilityCounts)[]) { + validateNonNegativeInteger(counts[key], `Python capability counts ${key}`); + } +} + +export { validatePythonCapabilityCounts }; + +function validatePythonCapabilityCleanupEvidence(cleanup: PythonCapabilityCleanupEvidence): void { + if (!cleanup || typeof cleanup !== "object") throw new Error("Python capability cleanup evidence is required"); + validateExactObjectKeys(cleanup, ["attempted", "ok", "failureMessage"], "Python capability cleanup evidence"); + if (typeof cleanup.attempted !== "boolean") throw new Error("Python capability cleanup attempted must be boolean"); + if (typeof cleanup.ok !== "boolean") throw new Error("Python capability cleanup ok must be boolean"); + if (cleanup.failureMessage !== undefined) { + validateNonEmptyString(cleanup.failureMessage, "Python capability cleanup failureMessage"); + } +} + +export { validatePythonCapabilityCleanupEvidence }; + +function validatePythonCapabilityInvocation(invocation: PythonCapabilityInvocation, label: string): void { + validatePythonCapabilityInvocationWithDuration( + invocation, + label, + (value, durationLabel) => validateNonNegativeInteger(value, durationLabel), + ); +} + +export { validatePythonCapabilityInvocation }; + +type PythonCapabilityDurationValidator = (value: unknown, label: string) => number; + +function validatePythonCapabilityInvocationWithDuration( + invocation: PythonCapabilityInvocation, + label: string, + validateDuration: PythonCapabilityDurationValidator, +): void { + if (!invocation || typeof invocation !== "object") throw new Error(`${label} is required`); + validateExactObjectKeys( + invocation, + [ + "stage", + "command", + "argsDigest", + "argCount", + "selectionMode", + "selectionDigest", + "durationMs", + "termination", + "exitCode", + "signal", + "outputBytes", + "stdoutDigest", + "stderrDigest", + ], + label, + ); + if (!includesString(["collection", "execution"] as const, invocation.stage)) { + throw new Error(`${label} stage must be collection or execution`); + } + validateNonEmptyString(invocation.command, `${label} command`); + validateSha256Identity(invocation.argsDigest, `${label} argsDigest`); + validateNonNegativeInteger(invocation.argCount, `${label} argCount`); + if (!includesString(pythonPytestSelectionModes, invocation.selectionMode)) { + throw new Error(`Unknown ${label} selectionMode: ${String(invocation.selectionMode)}`); + } + validateOptional(invocation.selectionDigest, (value) => + validateSha256Identity(value, `${label} selectionDigest`), + ); + validateDuration(invocation.durationMs, `${label} durationMs`); + if (!includesString(pythonCapabilityProcessTerminations, invocation.termination)) { + throw new Error(`Unknown ${label} termination: ${String(invocation.termination)}`); + } + validateOptional(invocation.exitCode, (value) => validateNonNegativeInteger(value, `${label} exitCode`)); + validateOptional(invocation.signal, (value) => validateNonEmptyString(value, `${label} signal`)); + validateNonNegativeInteger(invocation.outputBytes, `${label} outputBytes`); + validateOptional(invocation.stdoutDigest, (value) => + validateSha256Identity(value, `${label} stdoutDigest`), + ); + validateOptional(invocation.stderrDigest, (value) => + validateSha256Identity(value, `${label} stderrDigest`), + ); +} + +export { validatePythonCapabilityInvocationWithDuration }; diff --git a/packages/contracts/src/validation/python-project-validators-02.ts b/packages/contracts/src/validation/python-project-validators-02.ts new file mode 100644 index 0000000..47bd184 --- /dev/null +++ b/packages/contracts/src/validation/python-project-validators-02.ts @@ -0,0 +1,153 @@ +import { validateRequiredObject } from "../shared/validators-02.js"; +import { includesString } from "../shared/primitives.js"; +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { validateNonEmptyString, validatePositiveInteger, validateStringArray } from "../shared/validators-01.js"; +import { pythonProjectExecutableSources } from "./python-project-contracts-01.js"; +import type { + PythonInterpreterProvenance, + PythonProjectExecutableProvenance, + PythonProjectTarget, +} from "./python-project-contracts-02.js"; +import { + validateContextDocFilename, + validateContextDocRequiredPath, + validateExactObjectKeys, +} from "./python-validator-primitives.js"; +import type { RequiredContextDocPolicy } from "./status-contracts.js"; + +function validatePythonProjectTarget(target: PythonProjectTarget): void { + if (!target || typeof target !== "object") throw new Error("Python project targetRuntime is required"); + validateExactObjectKeys( + target, + ["requiresPython", "version", "platform", "implementation", "conflicts"], + "Python project targetRuntime", + ); + for (const [key, value] of Object.entries(target)) { + if (key === "conflicts") continue; + if (value !== undefined) validateNonEmptyString(value, `Python project targetRuntime ${key}`); + } + validateStringArray(target.conflicts, "Python project targetRuntime conflicts", { allowEmpty: true }); +} + +export { validatePythonProjectTarget }; + +function validatePythonExecutableProvenance(value: PythonProjectExecutableProvenance, label: string): void { + if (!value || typeof value !== "object") throw new Error(`${label} provenance is required`); + validateNonEmptyString(value.executable, `${label} executable`); + validateStringArray(value.argv, `${label} argv`, { allowEmpty: false }); + if (value.argv[0] !== value.executable) throw new Error(`${label} argv must start with executable`); + validateNonEmptyString(value.cwd, `${label} cwd`); + if (!includesString(pythonProjectExecutableSources, value.source)) + throw new Error(`Unknown ${label} source: ${String(value.source)}`); + if (value.version !== undefined) { + validateNonEmptyString(value.version, `${label} version`); + if (!/^[0-9]+\.[0-9][-+._A-Za-z0-9]*$/u.test(value.version)) { + throw new Error(`${label} version must be exact version provenance`); + } + } + if (value.configFile !== undefined) validateRepoRelativePath(value.configFile); +} + +export { validatePythonExecutableProvenance }; + +function validatePythonInterpreterProvenance(value: PythonInterpreterProvenance): void { + validateExactObjectKeys( + value, + [ + "executable", + "argv", + "cwd", + "source", + "version", + "configFile", + "implementation", + "platform", + "architecture", + "abi", + "soabi", + ], + "Python interpreter provenance", + ); + validatePythonExecutableProvenance(value, "Python interpreter"); + if (!/^\d+\.\d+\.\d+(?:(?:a|b|rc)\d+)?(?:\+[A-Za-z0-9]+(?:[._-][A-Za-z0-9]+)*)?$/u.test(value.version)) { + throw new Error("Python interpreter version must be an exact Python version"); + } + for (const [key, field] of [ + ["implementation", value.implementation], + ["platform", value.platform], + ["architecture", value.architecture], + ["abi", value.abi], + ["soabi", value.soabi], + ] as const) { + validateNonEmptyString(field, `Python interpreter ${key}`); + } +} + +export { validatePythonInterpreterProvenance }; + +function validatePythonProjectRoot(value: string, label: string): void { + validateNonEmptyString(label, "Python project root label"); + if (value === ".") return; + validateRepoRelativePath(value); +} + +export { validatePythonProjectRoot }; + +function validatePythonProjectRoots(values: readonly string[], label: string): void { + if (!Array.isArray(values) || values.length === 0) throw new Error(`${label} must be a non-empty array`); + for (const value of values) validatePythonProjectRoot(value, label); +} + +export { validatePythonProjectRoots }; + +function validateRepoPathArray(values: readonly string[], label: string): void { + if (!Array.isArray(values)) throw new Error(`${label} must be an array`); + for (const value of values) validateRepoRelativePath(value); +} + +export { validateRepoPathArray }; + +function validateExactEnumArray( + values: readonly T[], + allowed: readonly T[], + label: string, + requireAll: boolean, +): void { + if (!Array.isArray(values) || values.length === 0) throw new Error(`${label} must be a non-empty array`); + const seen = new Set(); + for (const value of values) { + if (!includesString(allowed, value)) throw new Error(`Unknown ${label} value: ${String(value)}`); + if (seen.has(value)) throw new Error(`${label} must not contain duplicates`); + seen.add(value); + } + if (requireAll && seen.size !== allowed.length) throw new Error(`${label} must contain every supported value`); +} + +export { validateExactEnumArray }; + +function validateSha256Identity(value: string, label: string): void { + if (!/^sha256:[a-f0-9]{64}$/u.test(value)) throw new Error(`${label} must be a sha256 identity`); +} + +export { validateSha256Identity }; + +function validateRequiredContextDocPolicy(policy: RequiredContextDocPolicy): RequiredContextDocPolicy { + validateRequiredObject(policy, "Required context doc policy is required"); + validateStringArray(policy.filenames, "Required context doc policy filenames", { allowEmpty: false }); + for (const filename of policy.filenames) validateContextDocFilename(filename); + validateStringArray(policy.requiredPaths, "Required context doc policy requiredPaths", { allowEmpty: false }); + for (const path of policy.requiredPaths) validateContextDocRequiredPath(path); + if (policy.requireRoot !== undefined && typeof policy.requireRoot !== "boolean") { + throw new Error("Required context doc policy requireRoot must be boolean"); + } + if (!Number.isInteger(policy.minimumContentLength) || policy.minimumContentLength < 1) { + throw new Error("Required context doc policy minimumContentLength must be a positive integer"); + } + if (policy.maxLines !== undefined) validatePositiveInteger(policy.maxLines, "Required context doc policy maxLines"); + if (policy.maxSectionLines !== undefined) { + validatePositiveInteger(policy.maxSectionLines, "Required context doc policy maxSectionLines"); + } + return policy; +} + +export { validateRequiredContextDocPolicy }; diff --git a/packages/contracts/src/validation/python-pytest-validators-01.ts b/packages/contracts/src/validation/python-pytest-validators-01.ts new file mode 100644 index 0000000..bc50cb7 --- /dev/null +++ b/packages/contracts/src/validation/python-pytest-validators-01.ts @@ -0,0 +1,210 @@ +import { includesString } from "../shared/primitives.js"; +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { + validateNonEmptyString, + validateNonNegativeInteger, + validateNonNegativeNumber, + validateStringArray, +} from "../shared/validators-01.js"; +import { + validateExactValue, + validateOptional, + validateRequiredObject, +} from "../shared/validators-02.js"; +import type { + PythonCapabilityCleanupEvidence, + PythonCapabilityCounts, + PythonCapabilityInvocation, + PythonPytestValidationCapabilityRun} from "./capability-contracts.js"; +import { + pythonCapabilityActivations, + pythonPytestSelectionModes, +} from "./capability-contracts.js"; +import { pythonProjectExecutableSources } from "./python-project-contracts-01.js"; +import type { + PythonTypesValidationCapabilityRun, + PythonValidationCapabilityToolProvenance, +} from "./python-project-contracts-02.js"; +import { validateExactObjectKeys } from "./python-validator-primitives.js"; +import { + validatePythonProjectRoot, + validateRepoPathArray, + validateSha256Identity, +} from "./python-project-validators-02.js"; +import { validatePythonCapabilityInvocationWithDuration } from "./python-project-validators-01.js"; +import { containsHostAbsolutePath } from "./python-validator-primitives.js"; +import { validatePortablePythonCapabilityExecutable } from "./python-pytest-validators-02.js"; + +function validatePythonPytestValidationCapabilityRun( + run: PythonPytestValidationCapabilityRun, +): PythonPytestValidationCapabilityRun { + validateRequiredObject(run, "Python pytest capability run is required"); + validateExactObjectKeys( + run, + [ + "capability", + "checkId", + "activation", + "outcome", + "message", + "projectKey", + "projectRoot", + "configFile", + "targetCount", + "candidatePaths", + "collectedNodeIds", + "afterStateFingerprint", + "selectionMode", + "selectionDigest", + "counts", + "collection", + "execution", + "cleanup", + ], + "Python pytest capability run", + ); + if (run.capability !== "pytest" || run.checkId !== "python.pytest") { + throw new Error("Python pytest capability run must describe python.pytest"); + } + if (!includesString(pythonCapabilityActivations, run.activation)) { + throw new Error(`Unknown Python pytest capability activation: ${String(run.activation)}`); + } + validateNonEmptyString(run.outcome, "Python pytest capability run outcome"); + validateNonEmptyString(run.message, "Python pytest capability run message"); + validatePythonPytestCapabilityIdentity(run); + validatePythonPytestCapabilityExecutionEvidence(run); + return run; +} + +export { validatePythonPytestValidationCapabilityRun }; + +function validatePythonPytestCapabilityIdentity(run: PythonPytestValidationCapabilityRun): void { + validateOptional(run.projectKey, (value) => + validateSha256Identity(value, "Python pytest capability run projectKey"), + ); + validateOptional(run.projectRoot, (value) => + validatePythonProjectRoot(value, "Python pytest capability run projectRoot"), + ); + validateOptional(run.configFile, validateRepoRelativePath); + validateOptional(run.targetCount, (value) => + validateNonNegativeInteger(value, "Python pytest capability run targetCount"), + ); + validateOptional(run.candidatePaths, (value) => + validateRepoPathArray(value, "Python pytest capability run candidatePaths"), + ); + validateOptional(run.collectedNodeIds, (value) => { + validateStringArray(value, "Python pytest capability run collectedNodeIds", { allowEmpty: true }); + }); + validateOptional(run.afterStateFingerprint, (value) => + validateSha256Identity(value, "Python pytest capability run afterStateFingerprint"), + ); + if (run.selectionMode !== undefined && !includesString(pythonPytestSelectionModes, run.selectionMode)) { + throw new Error(`Unknown Python pytest capability selection mode: ${String(run.selectionMode)}`); + } + validateOptional(run.selectionDigest, (value) => + validateSha256Identity(value, "Python pytest capability run selectionDigest"), + ); +} + +function validatePythonPytestCapabilityExecutionEvidence(run: PythonPytestValidationCapabilityRun): void { + validateOptional(run.counts, validatePythonPytestCapabilityCounts); + validateOptional(run.collection, (value) => + validatePythonPytestCapabilityInvocation(value, "Python pytest capability collection"), + ); + validateOptional(run.execution, (value) => + validatePythonPytestCapabilityInvocation(value, "Python pytest capability execution"), + ); + validateOptional(run.cleanup, validatePythonPytestCapabilityCleanupEvidence); +} + +function validatePythonPytestCapabilityCounts(counts: PythonCapabilityCounts): void { + if (!counts || typeof counts !== "object") throw new Error("Python pytest capability counts are required"); + validateExactObjectKeys( + counts, + [ + "candidateCount", + "collectedCount", + "executedCount", + "passedCount", + "failedCount", + "skippedCount", + "xfailedCount", + "xpassedCount", + "errorCount", + ], + "Python pytest capability counts", + ); + for (const key of Object.keys(counts) as (keyof PythonCapabilityCounts)[]) { + validateNonNegativeInteger(counts[key], `Python pytest capability counts ${key}`); + } +} + +export { validatePythonPytestCapabilityCounts }; + +function validatePythonPytestCapabilityInvocation(invocation: PythonCapabilityInvocation, label: string): void { + validatePythonCapabilityInvocationWithDuration( + invocation, + label, + (value, durationLabel) => validateNonNegativeNumber(value, durationLabel), + ); +} + +export { validatePythonPytestCapabilityInvocation }; + +function validatePythonPytestCapabilityCleanupEvidence(cleanup: PythonCapabilityCleanupEvidence): void { + if (!cleanup || typeof cleanup !== "object") throw new Error("Python pytest capability cleanup evidence is required"); + validateExactObjectKeys(cleanup, ["attempted", "ok", "failureMessage"], "Python pytest capability cleanup evidence"); + if (typeof cleanup.attempted !== "boolean") + throw new Error("Python pytest capability cleanup attempted must be boolean"); + if (typeof cleanup.ok !== "boolean") throw new Error("Python pytest capability cleanup ok must be boolean"); + if (cleanup.failureMessage !== undefined) { + validateNonEmptyString(cleanup.failureMessage, "Python pytest capability cleanup failureMessage"); + } +} + +export { validatePythonPytestCapabilityCleanupEvidence }; + +function validatePythonValidationCapabilityTool( + tool: PythonValidationCapabilityToolProvenance, + run: PythonTypesValidationCapabilityRun, +): void { + validateExactObjectKeys( + tool, + ["name", "executable", "argv", "cwd", "source", "version", "configFile"], + "Python validation capability tool", + ); + validateExactValue(tool.name, run.authority, "Python validation capability tool must match authority"); + validatePortablePythonCapabilityExecutable(tool.executable); + validateStringArray(tool.argv, "Python validation capability tool argv", { + allowEmpty: false, + }); + validateExactValue( + tool.argv[0], + tool.executable, + "Python validation capability tool argv must start with executable", + ); + for (const argument of tool.argv) { + if (containsHostAbsolutePath(argument)) { + throw new Error("Python validation capability tool requires portable argv without host-absolute paths"); + } + } + validatePythonProjectRoot(tool.cwd, "Python validation capability tool cwd"); + validateExactValue(tool.cwd, run.projectRoot, "Python validation capability tool cwd must equal projectRoot"); + if (!includesString(pythonProjectExecutableSources, tool.source)) { + throw new Error(`Unknown Python validation capability tool source: ${String(tool.source)}`); + } + validateOptional(tool.version, (version) => { + validateNonEmptyString(tool.version, "Python validation capability tool version"); + if (!/^[0-9]+\.[0-9][-+._A-Za-z0-9]*$/u.test(version)) { + throw new Error("Python validation capability tool version must be exact version provenance"); + } + }); + validateOptional(tool.configFile, (configFile) => { + validateRepoRelativePath(configFile); + if (!run.selectedConfigPaths.includes(configFile)) { + throw new Error("Python validation capability tool configFile must be a selected config path"); + } + }); +} + +export { validatePythonValidationCapabilityTool }; diff --git a/packages/contracts/src/validation/python-pytest-validators-02.ts b/packages/contracts/src/validation/python-pytest-validators-02.ts new file mode 100644 index 0000000..0e15a5e --- /dev/null +++ b/packages/contracts/src/validation/python-pytest-validators-02.ts @@ -0,0 +1,100 @@ +import { includesString } from "../shared/primitives.js"; +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { validateNonEmptyString, validateNonNegativeInteger } from "../shared/validators-01.js"; +import type { PythonValidationCapabilityExecution } from "./python-project-contracts-02.js"; +import { + containsHostAbsolutePath, + validateExactObjectKeys, +} from "./python-validator-primitives.js"; +import { pythonValidationCapabilityTerminationKinds } from "./vocabulary-02.js"; + +function validatePortablePythonCapabilityExecutable(executable: string): void { + validateNonEmptyString(executable, "Python validation capability tool executable"); + const match = /^(repo|project|path|external):(.+)$/u.exec(executable); + if (match === null) throw new Error("Python validation capability tool requires a portable executable locator"); + const [, kind, value] = match; + if (kind === "repo" || kind === "project") { + try { + validateRepoRelativePath(value); + } catch { + throw new Error("Python validation capability tool requires a portable executable locator"); + } + return; + } + if (!/^[A-Za-z0-9_.+-]+$/u.test(value)) { + throw new Error("Python validation capability tool requires a portable executable locator"); + } +} + +export { validatePortablePythonCapabilityExecutable }; + +function validatePythonValidationCapabilityExecution(execution: PythonValidationCapabilityExecution): void { + validateExactObjectKeys( + execution, + ["termination", "exitCode", "signal", "failureSummary"], + "Python validation capability execution", + ); + if (!includesString(pythonValidationCapabilityTerminationKinds, execution.termination)) { + throw new Error(`Unknown Python validation capability termination: ${String(execution.termination)}`); + } + validatePythonCapabilityExit(execution); + validatePythonCapabilitySignal(execution); + validatePythonCapabilityFailureSummary(execution); +} + +export { validatePythonValidationCapabilityExecution }; + +function validatePythonCapabilityExit(execution: PythonValidationCapabilityExecution): void { + if (execution.exitCode !== undefined) + validateNonNegativeInteger(execution.exitCode, "Python validation capability execution exitCode"); + if (execution.termination === "exited" && execution.exitCode === undefined) { + throw new Error("Exited Python validation capability execution requires exitCode"); + } + if (execution.termination !== "exited" && execution.exitCode !== undefined) { + throw new Error("Non-exited Python validation capability execution must not include exitCode"); + } +} + +export { validatePythonCapabilityExit }; + +function validatePythonCapabilitySignal(execution: PythonValidationCapabilityExecution): void { + if (execution.termination === "signal" && execution.signal === undefined) { + throw new Error("Signaled Python validation capability execution requires signal"); + } + if (execution.termination !== "signal" && execution.signal !== undefined) { + throw new Error("Non-signaled Python validation capability execution must not include signal"); + } + if (execution.signal !== undefined) + validateNonEmptyString(execution.signal, "Python validation capability execution signal"); +} + +export { validatePythonCapabilitySignal }; + +function validatePythonCapabilityFailureSummary(execution: PythonValidationCapabilityExecution): void { + if (execution.failureSummary !== undefined) { + validateNonEmptyString(execution.failureSummary, "Python validation capability execution failureSummary"); + if (execution.failureSummary.length > 1024) + throw new Error("Python validation capability execution failureSummary is too long"); + if (containsHostAbsolutePath(execution.failureSummary)) { + throw new Error("Python validation capability execution failureSummary must not contain host-absolute paths"); + } + } + if (execution.termination !== "exited" && execution.failureSummary === undefined) { + throw new Error("Non-exited Python validation capability execution requires failureSummary"); + } +} + +export { validatePythonCapabilityFailureSummary }; + +function validateSortedUniqueRepoPaths(values: readonly string[], label: string, allowEmpty: boolean): void { + if (!Array.isArray(values) || (!allowEmpty && values.length === 0)) { + throw new Error(`${label} must be ${allowEmpty ? "an" : "a non-empty"} array`); + } + for (const value of values) validateRepoRelativePath(value); + const sorted = [...new Set(values)].sort(); + if (sorted.length !== values.length || sorted.some((value, index) => value !== values[index])) { + throw new Error(`${label} must be sorted and unique`); + } +} + +export { validateSortedUniqueRepoPaths }; diff --git a/packages/contracts/src/validation/python-ruff-validators-01.ts b/packages/contracts/src/validation/python-ruff-validators-01.ts new file mode 100644 index 0000000..42f8830 --- /dev/null +++ b/packages/contracts/src/validation/python-ruff-validators-01.ts @@ -0,0 +1,230 @@ +import { + includesString, + validateArray, + validateObject, + validateOptional, + validateRequiredObject, +} from "../shared/primitives.js"; +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { + validateNonEmptyString, + validateNonNegativeInteger, + validateNonNegativeNumber, + validateStringArray, + validateValidationCheckId, +} from "../shared/validators-01.js"; +import type { + PythonRuffValidationCapabilityRun, + PythonValidationCapabilityInvocation, +} from "./diagnostic-contracts.js"; +import { pythonProjectExecutableSources } from "./python-project-contracts-01.js"; +import { validateSha256Identity } from "./python-project-validators-02.js"; +import { validatePortablePythonCapabilityExecutable } from "./python-pytest-validators-02.js"; +import { + containsHostAbsolutePath, + validateExactObjectKeys, + validatePythonCapabilityRunSchema, +} from "./python-validator-primitives.js"; +import { validateRuffCapabilityRun } from "./python-ruff-validators-02.js"; +import { validatePortablePythonCapabilityArgv, validateRuffTerminationEvidence } from "./python-ruff-validators-03.js"; +import { pythonValidationCapabilityStates, pythonValidationCapabilityTerminations } from "./vocabulary-02.js"; + +function validatePythonRuffValidationCapabilityRun( + run: PythonRuffValidationCapabilityRun, +): PythonRuffValidationCapabilityRun { + validateObject(run, "Python validation capability run"); + validatePythonRuffRunHeader(run); + validatePythonRuffRunIdentity(run); + validatePythonRuffRunProcess(run); + validatePythonRuffRunInvocations(run); + validatePythonRuffRunOutcome(run); + validateRuffCapabilityRun(run); + return run; +} + +export { validatePythonRuffValidationCapabilityRun }; + +function validatePythonRuffRunHeader(run: PythonRuffValidationCapabilityRun): void { + validateExactObjectKeys( + run, + [ + "schemaId", + "schemaVersion", + "checkId", + "capability", + "state", + "projectKey", + "contextFingerprint", + "afterStateManifestFingerprint", + "sourcePaths", + "configPaths", + "executable", + "command", + "argv", + "cwd", + "configPath", + "toolVersion", + "toolSource", + "termination", + "exitCode", + "signal", + "invocations", + "durationMs", + "diagnosticCount", + "failureMessage", + ], + "Python Ruff validation capability run", + ); + validatePythonCapabilityRunSchema(run); + validateValidationCheckId(run.checkId, "Python validation capability run checkId"); + if (!includesString(["ruff_lint", "ruff_format"] as const, run.capability)) { + throw new Error(`Unknown Python Ruff validation capability: ${String(run.capability)}`); + } + if (!includesString(pythonValidationCapabilityStates, run.state)) { + throw new Error(`Unknown Python validation capability state: ${String(run.state)}`); + } +} + +export { validatePythonRuffRunHeader }; + +function validatePythonRuffRunIdentity(run: PythonRuffValidationCapabilityRun): void { + validateOptional(run.projectKey, (value) => + validateSha256Identity(value, "Python validation capability run projectKey"), + ); + validateOptional(run.contextFingerprint, (value) => + validateSha256Identity(value, "Python validation capability run contextFingerprint"), + ); + validateOptional(run.afterStateManifestFingerprint, (value) => + validateSha256Identity(value, "Python validation capability run afterStateManifestFingerprint"), + ); + validateOptional(run.sourcePaths, (paths) => validatePythonRuffPaths(paths, "sourcePaths")); + validateOptional(run.configPaths, (paths) => validatePythonRuffPaths(paths, "configPaths")); +} + +export { validatePythonRuffRunIdentity }; + +function validatePythonRuffPaths(paths: readonly string[], label: string): void { + validateStringArray(paths, `Python validation capability run ${label}`, { allowEmpty: true }); + for (const path of paths) validateRepoRelativePath(path); +} + +export { validatePythonRuffPaths }; + +function validatePythonRuffRunProcess(run: PythonRuffValidationCapabilityRun): void { + validateOptional(run.executable, validatePortablePythonCapabilityExecutable); + validateOptional(run.command, (command) => + validateNonEmptyString(command, "Python validation capability run command"), + ); + validateOptional(run.argv, (argv) => { + validateStringArray(run.argv, "Python validation capability run argv", { + allowEmpty: false, + }); + validatePortablePythonCapabilityArgv(argv); + }); + validateOptional(run.cwd, (cwd) => validateNonEmptyString(cwd, "Python validation capability run cwd")); + validateOptional(run.configPath, validateRepoRelativePath); + validatePythonRuffTool(run); + validateOptional(run.termination, (termination) => { + if (!includesString(pythonValidationCapabilityTerminations, termination)) { + throw new Error(`Unknown Python validation capability termination: ${String(termination)}`); + } + }); + validateOptional(run.exitCode, (exitCode) => + validateNonNegativeInteger(exitCode, "Python validation capability run exitCode"), + ); + validateOptional(run.signal, (signal) => validateNonEmptyString(signal, "Python validation capability run signal")); +} + +export { validatePythonRuffRunProcess }; + +function validatePythonRuffTool(run: PythonRuffValidationCapabilityRun): void { + if (run.toolVersion !== undefined) { + validateNonEmptyString(run.toolVersion, "Python validation capability run toolVersion"); + if (!/^[0-9]+\.[0-9][-+._A-Za-z0-9]*$/u.test(run.toolVersion)) { + throw new Error("Python validation capability run toolVersion must be exact version provenance"); + } + } + if (run.toolSource !== undefined && !includesString(pythonProjectExecutableSources, run.toolSource)) { + throw new Error(`Unknown Python validation capability run toolSource: ${String(run.toolSource)}`); + } +} + +export { validatePythonRuffTool }; + +function validatePythonRuffRunInvocations(run: PythonRuffValidationCapabilityRun): void { + validateOptional(run.invocations, (invocations) => { + validateArray(invocations, "Python validation capability run invocations"); + if (invocations.length === 0) { + throw new Error("Python validation capability run invocations must be a non-empty array"); + } + for (const invocation of invocations) { + validatePythonValidationCapabilityInvocation(invocation); + if (run.executable !== undefined && invocation.argv[0] !== run.executable) { + throw new Error("Python validation capability invocation argv must start with executable"); + } + } + }); +} + +export { validatePythonRuffRunInvocations }; + +function validatePythonRuffRunOutcome(run: PythonRuffValidationCapabilityRun): void { + validateNonNegativeNumber(run.durationMs, "Python validation capability run durationMs"); + validateNonNegativeInteger(run.diagnosticCount, "Python validation capability run diagnosticCount"); + validateOptional(run.failureMessage, (failureMessage) => { + validateNonEmptyString(failureMessage, "Python validation capability run failureMessage"); + if (containsHostAbsolutePath(failureMessage)) { + throw new Error("Python validation capability run failureMessage must not contain host-absolute paths"); + } + }); + validateInactivePythonRuffRun(run); + validatePythonRuffRunTerminationFields(run); +} + +export { validatePythonRuffRunOutcome }; + +function validateInactivePythonRuffRun(run: PythonRuffValidationCapabilityRun): void { + if (run.state !== "not_applicable" && run.state !== "disabled") return; + const processEvidence = [run.termination, run.exitCode, run.signal, run.command, run.argv, run.invocations]; + if (processEvidence.some((value) => value !== undefined)) { + throw new Error(`Python validation capability state ${run.state} must not record a process invocation`); + } +} + +export { validateInactivePythonRuffRun }; + +function validatePythonRuffRunTerminationFields(run: PythonRuffValidationCapabilityRun): void { + if (run.signal !== undefined && run.termination !== "signal") { + throw new Error("Python validation capability run signal requires signal termination"); + } + if (run.exitCode !== undefined && run.termination !== "exited") { + throw new Error("Python validation capability run exitCode requires exited termination"); + } +} + +export { validatePythonRuffRunTerminationFields }; + +function validatePythonValidationCapabilityInvocation(invocation: PythonValidationCapabilityInvocation): void { + validateRequiredObject(invocation, "Python validation capability invocation is required"); + validateStringArray(invocation.argv, "Python validation capability invocation argv", { allowEmpty: false }); + validatePortablePythonCapabilityArgv(invocation.argv); + if (!includesString(pythonValidationCapabilityTerminations, invocation.termination)) { + throw new Error(`Unknown Python validation capability invocation termination: ${String(invocation.termination)}`); + } + if (invocation.exitCode !== undefined) { + validateNonNegativeInteger(invocation.exitCode, "Python validation capability invocation exitCode"); + if (invocation.termination !== "exited") { + throw new Error("Python validation capability invocation exitCode requires exited termination"); + } + } + if (invocation.signal !== undefined) { + validateNonEmptyString(invocation.signal, "Python validation capability invocation signal"); + if (invocation.termination !== "signal") { + throw new Error("Python validation capability invocation signal requires signal termination"); + } + } + validateRuffTerminationEvidence(invocation, "Python validation capability invocation"); + validateNonNegativeNumber(invocation.durationMs, "Python validation capability invocation durationMs"); +} + +export { validatePythonValidationCapabilityInvocation }; diff --git a/packages/contracts/src/validation/python-ruff-validators-02.ts b/packages/contracts/src/validation/python-ruff-validators-02.ts new file mode 100644 index 0000000..6bd01b4 --- /dev/null +++ b/packages/contracts/src/validation/python-ruff-validators-02.ts @@ -0,0 +1,244 @@ +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import type { PythonRuffValidationCapabilityRun } from "./diagnostic-contracts.js"; +import { validateRuffTerminationEvidence } from "./python-ruff-validators-03.js"; + +function validateRuffCapabilityRun(run: PythonRuffValidationCapabilityRun): void { + if (!validateRuffCapabilityIdentity(run)) return; + if (run.state === "not_applicable" || run.state === "disabled") return; + validateActivatedRuffExactState(run); + validateRuffCapabilityState(run); +} + +export { validateRuffCapabilityRun }; + +function validateRuffCapabilityIdentity(run: PythonRuffValidationCapabilityRun): boolean { + const expectedCheckId = expectedRuffCheckId(run.capability); + if (expectedCheckId === undefined) return false; + if (run.checkId !== expectedCheckId) { + throw new Error(`Python validation capability ${run.capability} requires checkId ${expectedCheckId}`); + } + return true; +} + +export { validateRuffCapabilityIdentity }; + +function validateRuffCapabilityState(run: PythonRuffValidationCapabilityRun): void { + switch (run.state) { + case "tool_unavailable": + case "unsupported_target": + return validateUnavailableRuffCapability(run); + case "timeout": + return validateTimeoutRuffCapability(run); + case "invalid_config": + return validateInvalidConfigRuffCapability(run); + case "tool_failure": + return validateFailedRuffCapability(run); + case "passed": + case "findings": + return validateCompletedRuffCapability(run); + } +} + +export { validateRuffCapabilityState }; + +function expectedRuffCheckId( + capability: PythonRuffValidationCapabilityRun["capability"], +): "python.ruff-lint" | "python.ruff-format" | undefined { + if (capability === "ruff_lint") return "python.ruff-lint"; + if (capability === "ruff_format") return "python.ruff-format"; + return undefined; +} + +export { expectedRuffCheckId }; + +function validateActivatedRuffExactState(run: PythonRuffValidationCapabilityRun): void { + const exactStateFields: readonly (keyof PythonRuffValidationCapabilityRun)[] = [ + "projectKey", + "contextFingerprint", + "afterStateManifestFingerprint", + "sourcePaths", + "configPaths", + "cwd", + ]; + for (const field of exactStateFields) { + if (run[field] === undefined) { + throw new Error(`Activated Ruff capability run requires ${field}`); + } + } + if ((run.sourcePaths?.length ?? 0) === 0) { + throw new Error("Activated Ruff capability run requires at least one source path"); + } + if (run.cwd !== "." && run.cwd !== undefined) validateRepoRelativePath(run.cwd); +} + +export { validateActivatedRuffExactState }; + +function validateUnavailableRuffCapability(run: PythonRuffValidationCapabilityRun): void { + requireRuffFailureMessage(run); + rejectRuffProcessEvidence(run); +} + +export { validateUnavailableRuffCapability }; + +function validateTimeoutRuffCapability(run: PythonRuffValidationCapabilityRun): void { + requireRuffFailureMessage(run); + requireExecutedRuffCapability(run); + if (run.termination !== "timeout") { + throw new Error("Ruff capability timeout requires timeout termination"); + } + validateExecutedRuffCapabilityCoherence(run); +} + +export { validateTimeoutRuffCapability }; + +function validateInvalidConfigRuffCapability(run: PythonRuffValidationCapabilityRun): void { + requireRuffFailureMessage(run); + if (!hasRuffProcessEvidence(run)) return; + requireExecutedRuffCapability(run); + if (run.termination !== "exited" || run.exitCode !== 2) { + throw new Error("Executed Ruff capability invalid_config requires exited configuration-rejection evidence"); + } + validateExecutedRuffCapabilityCoherence(run); +} + +export { validateInvalidConfigRuffCapability }; + +function validateFailedRuffCapability(run: PythonRuffValidationCapabilityRun): void { + requireRuffFailureMessage(run); + if (!hasRuffProcessEvidence(run)) return; + requireExecutedRuffCapability(run); + if (run.termination === "timeout") { + throw new Error("Executed Ruff capability tool_failure must not use timeout termination"); + } + validateExecutedRuffCapabilityCoherence(run); +} + +export { validateFailedRuffCapability }; + +function validateCompletedRuffCapability(run: PythonRuffValidationCapabilityRun): void { + requireExecutedRuffCapability(run); + if (run.termination !== "exited") { + throw new Error("Executed Ruff capability run requires exited termination"); + } + validateCompletedRuffInvocations(run); + validateExecutedRuffCapabilityCoherence(run); + validateCompletedRuffResult(run); +} + +export { validateCompletedRuffCapability }; + +function validateCompletedRuffInvocations(run: PythonRuffValidationCapabilityRun): void { + for (const invocation of requireRuffInvocations(run)) { + if (invocation.termination !== "exited" || (invocation.exitCode !== 0 && invocation.exitCode !== 1)) { + throw new Error("Executed Ruff capability invocation requires exited Ruff result code 0 or 1"); + } + } +} + +export { validateCompletedRuffInvocations }; + +function validateCompletedRuffResult(run: PythonRuffValidationCapabilityRun): void { + const expectedExitCode = run.state === "passed" ? 0 : 1; + if (run.exitCode !== expectedExitCode) { + throw new Error(`Executed Ruff capability state ${run.state} requires exitCode ${expectedExitCode}`); + } + if (run.state === "passed" && run.diagnosticCount !== 0) { + throw new Error("Passed Ruff capability run requires zero diagnostics"); + } + if (run.state === "findings" && run.diagnosticCount <= 0) { + throw new Error("Ruff findings capability run requires positive diagnosticCount"); + } +} + +export { validateCompletedRuffResult }; + +function requireRuffFailureMessage(run: PythonRuffValidationCapabilityRun): void { + if (run.failureMessage === undefined) { + throw new Error(`Ruff capability state ${run.state} requires failureMessage`); + } +} + +export { requireRuffFailureMessage }; + +function hasRuffProcessEvidence(run: PythonRuffValidationCapabilityRun): boolean { + return ( + run.command !== undefined || + run.argv !== undefined || + run.termination !== undefined || + run.exitCode !== undefined || + run.signal !== undefined || + run.invocations !== undefined + ); +} + +export { hasRuffProcessEvidence }; + +function rejectRuffProcessEvidence(run: PythonRuffValidationCapabilityRun): void { + if (hasRuffProcessEvidence(run)) { + throw new Error(`Ruff capability state ${run.state} must not record process evidence`); + } +} + +export { rejectRuffProcessEvidence }; + +function requireExecutedRuffCapability(run: PythonRuffValidationCapabilityRun): void { + const executionFields: readonly (keyof PythonRuffValidationCapabilityRun)[] = [ + "executable", + "command", + "argv", + "toolVersion", + "toolSource", + "termination", + "invocations", + ]; + for (const field of executionFields) { + if (run[field] === undefined) { + throw new Error(`Executed Ruff capability run requires ${field}`); + } + } + if (run.durationMs <= 0) { + throw new Error("Executed Ruff capability run requires positive durationMs"); + } + if (run.argv?.[0] !== run.executable) { + throw new Error("Executed Ruff capability run argv must start with executable"); + } + if (run.command !== run.argv?.join(" ")) { + throw new Error("Executed Ruff capability run command must match argv"); + } + validateRuffTerminationEvidence(run, "Executed Ruff capability run"); + for (const invocation of requireRuffInvocations(run)) { + if (invocation.argv[0] !== run.executable) { + throw new Error("Executed Ruff capability invocation argv must start with executable"); + } + if (invocation.durationMs <= 0) { + throw new Error("Executed Ruff capability invocation requires positive durationMs"); + } + } +} + +export { requireExecutedRuffCapability }; + +function requireRuffInvocations( + run: PythonRuffValidationCapabilityRun, +): NonNullable { + if (run.invocations === undefined) { + throw new Error("Executed Ruff capability run requires invocations"); + } + return run.invocations; +} + +function validateExecutedRuffCapabilityCoherence(run: PythonRuffValidationCapabilityRun): void { + const matchingInvocation = run.invocations?.some( + (invocation) => + invocation.termination === run.termination && + invocation.exitCode === run.exitCode && + invocation.signal === run.signal && + invocation.argv.length === run.argv?.length && + invocation.argv.every((argument, index) => argument === run.argv?.[index]), + ); + if (matchingInvocation !== true) { + throw new Error("Executed Ruff capability run requires an invocation matching its argv and termination evidence"); + } +} + +export { validateExecutedRuffCapabilityCoherence }; diff --git a/packages/contracts/src/validation/python-ruff-validators-03.ts b/packages/contracts/src/validation/python-ruff-validators-03.ts new file mode 100644 index 0000000..d64fc07 --- /dev/null +++ b/packages/contracts/src/validation/python-ruff-validators-03.ts @@ -0,0 +1,75 @@ +import { validateRequiredObject } from "../shared/validators-02.js"; +import { includesString } from "../shared/primitives.js"; +import { validateNonEmptyString, validateValidationCheckId } from "../shared/validators-01.js"; +import type { + PythonRuffValidationCapabilityRun, + PythonValidationCapabilityInvocation, + ValidationSkippedCheck, +} from "./diagnostic-contracts.js"; +import { containsHostAbsolutePath } from "./python-validator-primitives.js"; +import type { ValidationFailure } from "./request-contracts.js"; +import { validationFailureCategories } from "./vocabulary-01.js"; +import { validationSkippedCheckReasons } from "./vocabulary-02.js"; + +function validateRuffTerminationEvidence( + evidence: Pick< + PythonRuffValidationCapabilityRun | PythonValidationCapabilityInvocation, + "termination" | "exitCode" | "signal" + >, + label: string, +): void { + if (evidence.termination === "exited") { + if (evidence.exitCode === undefined || evidence.signal !== undefined) { + throw new Error(`${label} exited termination requires exitCode without signal`); + } + return; + } + if (evidence.termination === "signal") { + if (evidence.signal === undefined || evidence.exitCode !== undefined) { + throw new Error(`${label} signal termination requires signal without exitCode`); + } + return; + } + if (evidence.exitCode !== undefined || evidence.signal !== undefined) { + throw new Error(`${label} ${String(evidence.termination)} termination must not record exitCode or signal`); + } +} + +export { validateRuffTerminationEvidence }; + +function validatePortablePythonCapabilityArgv(argv: readonly string[]): void { + for (const argument of argv) { + if (containsHostAbsolutePath(argument)) { + throw new Error("Python validation capability run requires portable argv without host-absolute paths"); + } + } +} + +export { validatePortablePythonCapabilityArgv }; + +function validateValidationSkippedCheck(skippedCheck: ValidationSkippedCheck): ValidationSkippedCheck { + validateRequiredObject(skippedCheck, "Validation skipped check is required"); + validateValidationCheckId(skippedCheck.checkId, "Validation skipped check checkId"); + if (!includesString(validationSkippedCheckReasons, skippedCheck.reason)) { + throw new Error(`Unknown validation skipped reason: ${String(skippedCheck.reason)}`); + } + validateNonEmptyString(skippedCheck.message, "Validation skipped check message"); + return skippedCheck; +} + +export { validateValidationSkippedCheck }; + +function validateValidationFailure(failure: ValidationFailure): ValidationFailure { + validateRequiredObject(failure, "Validation failure is required"); + if (!includesString(validationFailureCategories, failure.category)) { + throw new Error(`Unknown validation failure category: ${String(failure.category)}`); + } + validateNonEmptyString(failure.message, "Validation failure message"); + if (failure.retryable !== undefined && typeof failure.retryable !== "boolean") { + throw new Error("Validation failure retryable must be boolean"); + } + if (failure.cause !== undefined) validateNonEmptyString(failure.cause, "Validation failure cause"); + return failure; +} + +export { validateValidationFailure }; diff --git a/packages/contracts/src/validation/python-types-validators.ts b/packages/contracts/src/validation/python-types-validators.ts new file mode 100644 index 0000000..2f74151 --- /dev/null +++ b/packages/contracts/src/validation/python-types-validators.ts @@ -0,0 +1,247 @@ +import { includesString } from "../shared/primitives.js"; +import { validateNonNegativeInteger } from "../shared/validators-01.js"; +import type { PythonValidationCapabilityRun } from "./diagnostic-contracts.js"; +import type { PythonTypesValidationCapabilityRun } from "./python-project-contracts-02.js"; +import { + validatePythonProjectRoot, + validateSha256Identity, +} from "./python-project-validators-02.js"; +import { + validatePythonPytestValidationCapabilityRun, + validatePythonValidationCapabilityTool, +} from "./python-pytest-validators-01.js"; +import { + validatePythonValidationCapabilityExecution, + validateSortedUniqueRepoPaths, +} from "./python-pytest-validators-02.js"; +import { validatePythonRuffValidationCapabilityRun } from "./python-ruff-validators-01.js"; +import { + validateExactObjectKeys, + validatePythonCapabilityRunSchema, +} from "./python-validator-primitives.js"; +import { pythonValidationAuthorities, pythonValidationCapabilityRunStatuses } from "./vocabulary-01.js"; +import { pythonValidationAuthoritySources } from "./vocabulary-02.js"; + +function validatePythonValidationCapabilityRun(run: PythonValidationCapabilityRun): PythonValidationCapabilityRun { + if (run.capability === "types") return validatePythonTypesValidationCapabilityRun(run); + if (run.capability === "pytest") return validatePythonPytestValidationCapabilityRun(run); + return validatePythonRuffValidationCapabilityRun(run); +} + +export { validatePythonValidationCapabilityRun }; + +function validatePythonTypesValidationCapabilityRun( + run: PythonTypesValidationCapabilityRun, +): PythonTypesValidationCapabilityRun { + if (!run || typeof run !== "object") throw new Error("Python validation capability run is required"); + validatePythonCapabilityRunShape(run); + validatePythonCapabilityRunIdentity(run); + validatePythonCapabilityRunCounts(run); + if (run.tool !== undefined) validatePythonValidationCapabilityTool(run.tool, run); + if (run.execution !== undefined) validatePythonValidationCapabilityExecution(run.execution); + validatePythonCapabilityRunStatus(run); + return run; +} + +export { validatePythonTypesValidationCapabilityRun }; + +function validatePythonCapabilityRunShape(run: PythonTypesValidationCapabilityRun): void { + validateExactObjectKeys( + run, + [ + "schemaId", + "schemaVersion", + "capability", + "checkId", + "projectKey", + "contextFingerprint", + "projectRoot", + "targets", + "selectedSourcePaths", + "selectedConfigPaths", + "afterStateManifestFingerprint", + "authority", + "authoritySource", + "status", + "tool", + "execution", + "durationMs", + "diagnosticCount", + "errorCount", + "warningCount", + "noteCount", + ], + "Python validation capability run", + ); + validatePythonCapabilityRunSchema(run); + if (run.capability !== "types" || run.checkId !== "python.types") { + throw new Error("Python validation capability run must describe python.types"); + } +} + +export { validatePythonCapabilityRunShape }; + +function validatePythonCapabilityRunIdentity(run: PythonTypesValidationCapabilityRun): void { + validateSha256Identity(run.projectKey, "Python validation capability run projectKey"); + validateSha256Identity(run.contextFingerprint, "Python validation capability run contextFingerprint"); + validatePythonProjectRoot(run.projectRoot, "Python validation capability run projectRoot"); + validateSortedUniqueRepoPaths(run.targets, "Python validation capability run targets", false); + validateSortedUniqueRepoPaths(run.selectedSourcePaths, "Python validation capability run selectedSourcePaths", false); + validateSortedUniqueRepoPaths(run.selectedConfigPaths, "Python validation capability run selectedConfigPaths", true); + validatePythonCapabilityRunTargets(run); + validateSha256Identity( + run.afterStateManifestFingerprint, + "Python validation capability run afterStateManifestFingerprint", + ); + validatePythonCapabilityRunAuthority(run); + if (!includesString(pythonValidationCapabilityRunStatuses, run.status)) { + throw new Error(`Unknown Python validation capability run status: ${String(run.status)}`); + } +} + +export { validatePythonCapabilityRunIdentity }; + +function validatePythonCapabilityRunTargets(run: PythonTypesValidationCapabilityRun): void { + for (const target of run.targets) { + if (!run.selectedSourcePaths.includes(target)) { + throw new Error("Python validation capability run targets must be selected source paths"); + } + } +} + +function validatePythonCapabilityRunAuthority(run: PythonTypesValidationCapabilityRun): void { + if (run.authority !== undefined && !includesString(pythonValidationAuthorities, run.authority)) { + throw new Error(`Unknown Python validation authority: ${String(run.authority)}`); + } + if (run.authoritySource !== undefined && !includesString(pythonValidationAuthoritySources, run.authoritySource)) { + throw new Error(`Unknown Python validation authority source: ${String(run.authoritySource)}`); + } + if ((run.authority === undefined) !== (run.authoritySource === undefined)) { + throw new Error("Python validation capability authority and authoritySource must be present together"); + } + const permitsMissingAuthority = run.status === "invalid_config" || run.status === "unsupported_target"; + if (run.authority === undefined && !permitsMissingAuthority) { + throw new Error(`Python validation capability run ${run.status} requires selected authority evidence`); + } +} + +function validatePythonCapabilityRunCounts(run: PythonTypesValidationCapabilityRun): void { + for (const [key, value] of [ + ["durationMs", run.durationMs], + ["diagnosticCount", run.diagnosticCount], + ["errorCount", run.errorCount], + ["warningCount", run.warningCount], + ["noteCount", run.noteCount], + ] as const) + validateNonNegativeInteger(value, `Python validation capability run ${key}`); + if (run.diagnosticCount !== run.errorCount + run.warningCount + run.noteCount) { + throw new Error("Python validation capability run diagnosticCount must equal severity counts"); + } +} + +export { validatePythonCapabilityRunCounts }; + +function validatePythonCapabilityRunStatus(run: PythonTypesValidationCapabilityRun): void { + if (run.status === "passed") validatePassedPythonCapability(run); + if (run.status === "findings") validateFindingsPythonCapability(run); + if (run.status === "timeout") validateTimeoutPythonCapability(run); + if (run.status === "invalid_config") validateInvalidPythonCapability(run); + if (run.status === "unsupported_target") validateUnexecutedPythonCapability(run); + if (run.status === "tool_unavailable") validateUnavailablePythonCapability(run); + if (run.status === "tool_failure") validateFailedPythonCapability(run); +} + +export { validatePythonCapabilityRunStatus }; + +function validatePassedPythonCapability(run: PythonTypesValidationCapabilityRun): void { + requireExitedPythonCapability(run); + if (run.execution?.exitCode !== 0 || run.errorCount !== 0) { + throw new Error("Passed Python validation capability run requires exit 0 and zero errors"); + } +} + +export { validatePassedPythonCapability }; + +function validateFindingsPythonCapability(run: PythonTypesValidationCapabilityRun): void { + requireExitedPythonCapability(run); + if (run.execution?.exitCode !== 1 || run.diagnosticCount === 0) { + throw new Error("Findings Python validation capability run requires exit 1 and diagnostics"); + } + if (run.errorCount + run.warningCount === 0) { + throw new Error("Findings Python validation capability run requires an error or warning"); + } +} + +export { validateFindingsPythonCapability }; + +function requireExitedPythonCapability(run: PythonTypesValidationCapabilityRun): void { + if (run.tool === undefined || run.execution?.termination !== "exited") { + throw new Error(`Python validation capability run ${run.status} requires exited tool evidence`); + } +} + +export { requireExitedPythonCapability }; + +function validateTimeoutPythonCapability(run: PythonTypesValidationCapabilityRun): void { + if ( + run.tool === undefined || + run.execution?.termination !== "timeout" || + run.execution.failureSummary === undefined + ) { + throw new Error("Timeout Python validation capability run requires tool and timeout failure evidence"); + } +} + +export { validateTimeoutPythonCapability }; + +function validateInvalidPythonCapability(run: PythonTypesValidationCapabilityRun): void { + if (run.authority === undefined && (run.tool !== undefined || run.execution !== undefined)) { + throw new Error( + "Unselected invalid-config Python validation capability run must not include tool or execution evidence", + ); + } + if (run.execution === undefined) return; + if (run.tool === undefined || run.execution.termination !== "exited" || run.execution.failureSummary === undefined) { + throw new Error("Executed invalid-config Python validation capability run requires exited tool failure evidence"); + } +} + +export { validateInvalidPythonCapability }; + +function validateUnexecutedPythonCapability(run: PythonTypesValidationCapabilityRun): void { + if (run.execution !== undefined) + throw new Error(`${run.status} Python validation capability run must not include execution evidence`); +} + +export { validateUnexecutedPythonCapability }; + +function validateUnavailablePythonCapability(run: PythonTypesValidationCapabilityRun): void { + if (run.tool === undefined || run.execution !== undefined) { + throw new Error("Tool-unavailable Python validation capability run requires tool provenance without execution"); + } +} + +export { validateUnavailablePythonCapability }; + +function validateFailedPythonCapability(run: PythonTypesValidationCapabilityRun): void { + if ( + run.tool === undefined || + run.execution === undefined || + run.execution.termination === "timeout" || + run.execution.failureSummary === undefined + ) { + throw new Error("Tool-failure Python validation capability run requires non-timeout tool failure evidence"); + } +} + +export { validateFailedPythonCapability }; + +function validatePythonValidationCapabilityRuns( + runs: readonly PythonValidationCapabilityRun[], +): readonly PythonValidationCapabilityRun[] { + if (!Array.isArray(runs)) throw new Error("Python validation capability runs must be an array"); + for (const run of runs) validatePythonValidationCapabilityRun(run); + return runs; +} + +export { validatePythonValidationCapabilityRuns }; diff --git a/packages/contracts/src/validation/python-validator-primitives.ts b/packages/contracts/src/validation/python-validator-primitives.ts new file mode 100644 index 0000000..3c51cd5 --- /dev/null +++ b/packages/contracts/src/validation/python-validator-primitives.ts @@ -0,0 +1,48 @@ +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { PYTHON_VALIDATION_CAPABILITY_RUN_SCHEMA_ID } from "./python-project-contracts-01.js"; + +function validatePythonCapabilityRunSchema(run: { schemaId: string; schemaVersion: number }): void { + if (run.schemaId !== PYTHON_VALIDATION_CAPABILITY_RUN_SCHEMA_ID) { + throw new Error(`Python validation capability run schemaId must be ${PYTHON_VALIDATION_CAPABILITY_RUN_SCHEMA_ID}`); + } + if (run.schemaVersion !== 1) throw new Error("Python validation capability run schemaVersion must be 1"); +} + +export { validatePythonCapabilityRunSchema }; + +function validateExactObjectKeys(value: object, allowedKeys: readonly string[], label: string): void { + const allowed = new Set(allowedKeys); + const unexpected = Object.keys(value).filter((key) => !allowed.has(key)); + if (unexpected.length > 0) { + throw new Error(`${label} has unexpected properties: ${unexpected.sort().join(", ")}`); + } +} + +export { validateExactObjectKeys }; + +function containsHostAbsolutePath(value: string): boolean { + return ( + /^(?:\/|\\\\|[A-Za-z]:[\\/])/u.test(value) || + /[\s("'=](?:\/|\\\\|[A-Za-z]:[\\/])/u.test(value) || + /file:\/\//iu.test(value) + ); +} + +export { containsHostAbsolutePath }; + +function validateContextDocFilename(filename: string): string { + validateRepoRelativePath(filename); + if (filename.includes("/")) { + throw new Error(`Required context doc policy filename must be a basename: ${filename}`); + } + return filename; +} + +export { validateContextDocFilename }; + +function validateContextDocRequiredPath(path: string): string { + if (path === ".") return path; + return validateRepoRelativePath(path); +} + +export { validateContextDocRequiredPath }; diff --git a/packages/contracts/src/validation/request-contracts.ts b/packages/contracts/src/validation/request-contracts.ts new file mode 100644 index 0000000..5978723 --- /dev/null +++ b/packages/contracts/src/validation/request-contracts.ts @@ -0,0 +1,163 @@ +import type { RepoIdentity } from "../graph/provider-contracts-01.js"; +import type { GraphProviderStatus } from "../graph/provider-contracts-02.js"; +import type { CLONE_PROTOCOL, GraphProviderMode } from "../graph/vocabulary-01.js"; +import type { ValidationFailureCategory, ValidationReportMode } from "./vocabulary-01.js"; + +const validationScopeKinds = ["files", "changed", "staged", "tree", "all", "repo", "package"] as const; + +export { validationScopeKinds }; + +type ValidationScopeKind = (typeof validationScopeKinds)[number]; + +export type { ValidationScopeKind }; + +type ValidationScope = + | { + kind: "files"; + files: readonly string[]; + } + | { + kind: "changed"; + baseRef: string; + } + | { + kind: "staged"; + } + | { + kind: "tree"; + treeRef: string; + changedFrom: string; + } + | { + kind: "all"; + } + | { + kind: "repo"; + } + | { + kind: "package"; + packageName: string; + packageRoot: string; + }; + +export type { ValidationScope }; + +type HypotheticalOverlay = + | { + path: string; + action: "write"; + content: string; + checksumBefore?: string; + } + | { + path: string; + action: "delete"; + checksumBefore?: string; + }; + +export type { HypotheticalOverlay }; + +const cloneReportModes = ["all", "introduced"] as const; + +export { cloneReportModes }; + +type CloneReportMode = (typeof cloneReportModes)[number]; + +export type { CloneReportMode }; + +const cloneSourceReadModes = ["disk", "gitIndex", "gitTree"] as const; + +export { cloneSourceReadModes }; + +type CloneSourceReadMode = (typeof cloneSourceReadModes)[number]; + +export type { CloneSourceReadMode }; + +interface CloneAnalysisRequest { + protocol: typeof CLONE_PROTOCOL; + requestId?: string; + schemaVersion: 1; + repo: RepoIdentity; + reportMode: CloneReportMode; + paths?: readonly string[]; + sourcePaths?: readonly string[]; + sourceReadMode?: CloneSourceReadMode; + sourceTreeRef?: string; + overlays: readonly HypotheticalOverlay[]; + windowSize?: number; + minLines?: number; + minTokens?: number; + threshold?: number; + partitions?: readonly (readonly string[])[]; + exclude?: readonly string[]; + modes?: readonly string[]; +} + +export type { CloneAnalysisRequest }; + +interface CloneFinding { + cloneClassId: string; + contentHash: string; + path: string; + peerPath: string; + paths: readonly string[]; + lineCount: number; + tokenCount: number; + introduced: boolean; +} + +export type { CloneFinding }; + +interface CloneAnalysisSummary { + analyzedFiles: number; + cloneClassCount: number; + findingCount: number; + overlayCount: number; +} + +export type { CloneAnalysisSummary }; + +interface CloneAnalysisResult { + protocol: typeof CLONE_PROTOCOL; + requestId?: string; + schemaVersion: 1; + repo: RepoIdentity; + reportMode: CloneReportMode; + status: "passed"; + persisted: boolean; + dbPath?: string; + findings: readonly CloneFinding[]; + summary: CloneAnalysisSummary; +} + +export type { CloneAnalysisResult }; + +interface ValidationFailure { + category: ValidationFailureCategory; + message: string; + retryable?: boolean; + cause?: string; +} + +export type { ValidationFailure }; + +interface ValidationGraphConfig { + mode: GraphProviderMode; + provider?: string; + maxAgeMs?: number; + status?: GraphProviderStatus; +} + +export type { ValidationGraphConfig }; + +interface ValidationRequest { + requestId?: string; + repo: RepoIdentity; + scope: ValidationScope; + graph: ValidationGraphConfig; + overlays: readonly HypotheticalOverlay[]; + checks?: readonly string[]; + reportMode?: ValidationReportMode; +} + +export type { ValidationRequest }; diff --git a/packages/contracts/src/validation/request-validators-01.ts b/packages/contracts/src/validation/request-validators-01.ts new file mode 100644 index 0000000..8e800e7 --- /dev/null +++ b/packages/contracts/src/validation/request-validators-01.ts @@ -0,0 +1,179 @@ +import { validateOptional, validateRequiredObject } from "../shared/validators-02.js"; +import { includesString } from "../shared/primitives.js"; +import { validateProviderStatus } from "../graph/provider-validators.js"; +import { graphProviderModes } from "../graph/vocabulary-01.js"; +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { validateNonEmptyString, validatePositiveInteger, validateStringArray } from "../shared/validators-01.js"; +import type { ValidationDiagnostic, ValidationDiagnosticToolProvenance } from "./diagnostic-contracts.js"; +import type { + HypotheticalOverlay, + ValidationGraphConfig, + ValidationScope} from "./request-contracts.js"; +import { + validationScopeKinds, +} from "./request-contracts.js"; +import { validationDiagnosticCategories } from "./vocabulary-01.js"; + +function validateValidationScope(scope: ValidationScope): ValidationScope { + validateRequiredObject(scope, "Validation scope is required"); + if (!includesString(validationScopeKinds, scope.kind)) { + throw new Error(`Unknown validation scope kind: ${String((scope as { kind?: unknown }).kind)}`); + } + if (scope.kind === "files") { + validateStringArray(scope.files, "Validation scope files", { + allowEmpty: false, + }); + for (const file of scope.files) validateRepoRelativePath(file); + } + if (scope.kind === "changed") { + validateNonEmptyString(scope.baseRef, "Validation changed scope baseRef"); + } + if (scope.kind === "tree") { + validateNonEmptyString(scope.treeRef, "Validation tree scope treeRef"); + validateNonEmptyString(scope.changedFrom, "Validation tree scope changedFrom"); + } + if (scope.kind === "package") { + validateNonEmptyString(scope.packageName, "Validation package scope packageName"); + validateRepoRelativePath(scope.packageRoot); + } + return scope; +} + +export { validateValidationScope }; + +function validateValidationGraphConfig(graph: ValidationGraphConfig): ValidationGraphConfig { + validateRequiredObject(graph, "Validation graph config is required"); + if (!includesString(graphProviderModes, graph.mode)) { + throw new Error(`Unknown validation graph mode: ${String(graph.mode)}`); + } + if (graph.provider !== undefined) validateNonEmptyString(graph.provider, "Validation graph provider"); + if (graph.maxAgeMs !== undefined && (typeof graph.maxAgeMs !== "number" || graph.maxAgeMs < 0)) { + throw new Error("Validation graph maxAgeMs must be non-negative"); + } + if (graph.status !== undefined) { + validateProviderStatus(graph.status); + if (graph.status.mode !== graph.mode) { + throw new Error("Validation graph status mode must match graph mode"); + } + if (graph.provider !== undefined && graph.status.provider !== graph.provider) { + throw new Error("Validation graph status provider must match graph provider"); + } + } + return graph; +} + +export { validateValidationGraphConfig }; + +function validateHypotheticalOverlays(overlays: readonly HypotheticalOverlay[]): readonly HypotheticalOverlay[] { + if (!Array.isArray(overlays)) { + throw new Error("Validation request overlays must be an array"); + } + const normalizedPaths = new Set(); + for (const overlay of overlays) { + validateHypotheticalOverlay(overlay); + const normalizedPath = validateRepoRelativePath(overlay.path); + if (normalizedPaths.has(normalizedPath)) { + throw new Error(`Validation request overlays include duplicate path: ${normalizedPath}`); + } + normalizedPaths.add(normalizedPath); + } + return overlays; +} + +export { validateHypotheticalOverlays }; + +function validateHypotheticalOverlay(overlay: HypotheticalOverlay): HypotheticalOverlay { + validateRequiredObject(overlay, "Validation request overlay is required"); + validateRepoRelativePath(overlay.path); + if (!includesString(["write", "delete"] as const, overlay.action)) { + throw new Error(`Unknown validation overlay action: ${String((overlay as { action?: unknown }).action)}`); + } + if (overlay.action === "write") { + if (typeof overlay.content !== "string") { + throw new Error("Validation write overlay must include content"); + } + } + if (overlay.action === "delete" && Object.hasOwn(overlay, "content")) { + throw new Error("Validation delete overlay must not include content"); + } + if (overlay.checksumBefore !== undefined) { + validateNonEmptyString(overlay.checksumBefore, "Validation overlay checksumBefore"); + } + return overlay; +} + +export { validateHypotheticalOverlay }; + +function validateValidationDiagnostics(diagnostics: readonly ValidationDiagnostic[]): readonly ValidationDiagnostic[] { + if (!Array.isArray(diagnostics)) { + throw new Error("Validation result diagnostics must be an array"); + } + for (const diagnostic of diagnostics) validateValidationDiagnostic(diagnostic); + return diagnostics; +} + +export { validateValidationDiagnostics }; + +function validateValidationDiagnostic(diagnostic: ValidationDiagnostic): ValidationDiagnostic { + validateRequiredObject(diagnostic, "Validation diagnostic is required"); + if (!includesString(validationDiagnosticCategories, diagnostic.category)) { + throw new Error(`Unknown validation diagnostic category: ${String(diagnostic.category)}`); + } + validateNonEmptyString(diagnostic.message, "Validation diagnostic message"); + if (diagnostic.path !== undefined) validateRepoRelativePath(diagnostic.path); + if (!includesString(["info", "warning", "error"] as const, diagnostic.severity)) { + throw new Error(`Unknown validation diagnostic severity: ${String(diagnostic.severity)}`); + } + if (diagnostic.code !== undefined) validateNonEmptyString(diagnostic.code, "Validation diagnostic code"); + validateValidationDiagnosticLocation(diagnostic); + if (diagnostic.tool !== undefined) validateValidationDiagnosticTool(diagnostic.tool); + return diagnostic; +} + +export { validateValidationDiagnostic }; + +function validateValidationDiagnosticLocation(diagnostic: ValidationDiagnostic): void { + for (const field of ["line", "column", "endLine", "endColumn"] as const) { + if (diagnostic[field] !== undefined) validatePositiveInteger(diagnostic[field], `Validation diagnostic ${field}`); + } + validateValidationDiagnosticLocationPresence(diagnostic); + validateValidationDiagnosticLocationOrder(diagnostic); +} + +export { validateValidationDiagnosticLocation }; + +function validateValidationDiagnosticLocationPresence(diagnostic: ValidationDiagnostic): void { + if (diagnostic.column !== undefined && diagnostic.line === undefined) { + throw new Error("Validation diagnostic column requires line"); + } + const hasEndLocation = diagnostic.endLine !== undefined || diagnostic.endColumn !== undefined; + if (hasEndLocation && diagnostic.line === undefined) { + throw new Error("Validation diagnostic end location requires line"); + } + if (diagnostic.endColumn !== undefined && diagnostic.endLine === undefined) { + throw new Error("Validation diagnostic endColumn requires endLine"); + } +} + +function validateValidationDiagnosticLocationOrder(diagnostic: ValidationDiagnostic): void { + if (diagnostic.line !== undefined && diagnostic.endLine !== undefined) { + const startsAfterEnd = + diagnostic.endLine < diagnostic.line || + (diagnostic.endLine === diagnostic.line && + diagnostic.column !== undefined && + diagnostic.endColumn !== undefined && + diagnostic.endColumn < diagnostic.column); + if (startsAfterEnd) throw new Error("Validation diagnostic end location must not precede start location"); + } +} + +function validateValidationDiagnosticTool(tool: ValidationDiagnosticToolProvenance): void { + if (!tool || typeof tool !== "object") throw new Error("Validation diagnostic tool provenance is required"); + validateNonEmptyString(tool.name, "Validation diagnostic tool name"); + validateNonEmptyString(tool.command, "Validation diagnostic tool command"); + validateOptional(tool.version, (value) => validateNonEmptyString(value, "Validation diagnostic tool version")); + validateOptional(tool.source, (value) => validateNonEmptyString(value, "Validation diagnostic tool source")); + validateOptional(tool.cwd, (value) => validateNonEmptyString(value, "Validation diagnostic tool cwd")); +} + +export { validateValidationDiagnosticTool }; diff --git a/packages/contracts/src/validation/request-validators-02.ts b/packages/contracts/src/validation/request-validators-02.ts new file mode 100644 index 0000000..163ef55 --- /dev/null +++ b/packages/contracts/src/validation/request-validators-02.ts @@ -0,0 +1,204 @@ +import { validateOptional, validateRequiredObject } from "../shared/validators-02.js"; +import { includesString } from "../shared/primitives.js"; +import { validateProviderStatus } from "../graph/provider-validators.js"; +import { GRAPH_SCHEMA_VERSION, graphProviderModes } from "../graph/vocabulary-01.js"; +import { validateRepoRelativePath } from "../shared/path-validators.js"; +import { + validateNonEmptyString, + validateNonNegativeInteger, + validateNonNegativeNumber, + validateStringArray, + validateValidationCheckId, + validateValidationChecks, +} from "../shared/validators-01.js"; +import type { + ValidationCheckManifestEntry, + ValidationCheckRunSummary, + ValidationResultManifest, +} from "./diagnostic-contracts.js"; +import { validateValidationSkippedCheck } from "./python-ruff-validators-03.js"; +import { validatePythonValidationCapabilityRun } from "./python-types-validators.js"; +import { validationScopeKinds } from "./request-contracts.js"; +import type { + PreWriteValidationFailureSummary, + PreWriteValidationOverlaySummary, + PreWriteValidationReceipt, +} from "./status-contracts.js"; +import type { + ValidationCheckOutcome, + ValidationCheckRunStatus} from "./vocabulary-01.js"; +import { + validationCheckOutcomes, + validationCheckRunStatuses, + validationResultStatuses, +} from "./vocabulary-01.js"; + +function validateValidationResultManifest(manifest: ValidationResultManifest): ValidationResultManifest { + validateRequiredObject(manifest, "Validation result manifest is required"); + if (manifest.schemaVersion !== GRAPH_SCHEMA_VERSION) { + throw new Error(`Validation result manifest schemaVersion must be ${GRAPH_SCHEMA_VERSION}`); + } + validateValidationChecks(manifest.checks, "Validation result manifest checks"); + validateNonEmptyString(manifest.generatedAt, "Validation result manifest generatedAt"); + validateOptional(manifest.durationMs, (value) => + validateNonNegativeNumber(value, "Validation result manifest durationMs"), + ); + validateOptional(manifest.entries, validateValidationManifestEntries); + validateOptional(manifest.runs, validateValidationManifestRuns); + validateOptional(manifest.skippedChecks, validateValidationManifestSkippedChecks); + return manifest; +} + +export { validateValidationResultManifest }; + +function validateValidationManifestEntries(entries: readonly ValidationCheckManifestEntry[]): void { + if (!Array.isArray(entries)) throw new Error("Validation result manifest entries must be an array"); + for (const entry of entries) validateValidationCheckManifestEntry(entry); +} + +function validateValidationManifestRuns(runs: readonly ValidationCheckRunSummary[]): void { + if (!Array.isArray(runs)) throw new Error("Validation result manifest runs must be an array"); + for (const run of runs) validateValidationCheckRunSummary(run); +} + +function validateValidationManifestSkippedChecks( + skippedChecks: NonNullable, +): void { + if (!Array.isArray(skippedChecks)) throw new Error("Validation result manifest skippedChecks must be an array"); + for (const skippedCheck of skippedChecks) validateValidationSkippedCheck(skippedCheck); +} + +function validatePreWriteValidationGraph(graph: PreWriteValidationReceipt["graph"]): void { + validateRequiredObject(graph, "Pre-write validation receipt graph is required"); + if (!includesString(graphProviderModes, graph.mode)) { + throw new Error(`Unknown pre-write validation receipt graph mode: ${String(graph.mode)}`); + } + if (graph.provider !== undefined) + validateNonEmptyString(graph.provider, "Pre-write validation receipt graph provider"); + if (graph.status !== undefined) { + validateProviderStatus(graph.status); + if (graph.status.mode !== graph.mode) { + throw new Error("Pre-write validation receipt graph status mode must match graph mode"); + } + if (graph.provider !== undefined && graph.status.provider !== graph.provider) { + throw new Error("Pre-write validation receipt graph status provider must match graph provider"); + } + } +} + +export { validatePreWriteValidationGraph }; + +function validatePreWriteValidationOverlaySummary(summary: PreWriteValidationOverlaySummary): void { + validateRequiredObject(summary, "Pre-write validation receipt overlays are required"); + for (const key of ["count", "writeCount", "deleteCount"] as const) { + if (!Number.isInteger(summary[key]) || summary[key] < 0) { + throw new Error(`Pre-write validation receipt overlays ${key} must be a non-negative integer`); + } + } + validateStringArray(summary.paths, "Pre-write validation receipt overlay paths", { allowEmpty: true }); + for (const path of summary.paths) validateRepoRelativePath(path); + if (summary.count !== summary.writeCount + summary.deleteCount) { + throw new Error("Pre-write validation receipt overlay count must equal writeCount plus deleteCount"); + } + if (summary.count !== summary.paths.length) { + throw new Error("Pre-write validation receipt overlay count must equal paths length"); + } +} + +export { validatePreWriteValidationOverlaySummary }; + +function validatePreWriteValidationFailureSummary(summary: PreWriteValidationFailureSummary): void { + validateRequiredObject(summary, "Pre-write validation receipt failureSummary is required"); + if (!includesString(validationResultStatuses, summary.category)) { + throw new Error(`Unknown pre-write validation receipt failure category: ${String(summary.category)}`); + } + if (summary.category === "passed") { + throw new Error("Pre-write validation receipt failure category must not be passed"); + } + validateNonEmptyString(summary.message, "Pre-write validation receipt failureSummary message"); + if (summary.cause !== undefined) + validateNonEmptyString(summary.cause, "Pre-write validation receipt failureSummary cause"); + if (summary.retryable !== undefined && typeof summary.retryable !== "boolean") { + throw new Error("Pre-write validation receipt failureSummary retryable must be boolean"); + } +} + +export { validatePreWriteValidationFailureSummary }; + +function validateValidationCheckManifestEntry(entry: ValidationCheckManifestEntry): ValidationCheckManifestEntry { + validateRequiredObject(entry, "Validation check manifest entry is required"); + validateValidationCheckId(entry.checkId, "Validation check manifest entry checkId"); + validateNonEmptyString(entry.owner, "Validation check manifest entry owner"); + validateNonEmptyString(entry.adapter, "Validation check manifest entry adapter"); + if (!includesString(["info", "warning", "error"] as const, entry.defaultSeverity)) { + throw new Error(`Unknown validation check manifest entry defaultSeverity: ${String(entry.defaultSeverity)}`); + } + if (!Array.isArray(entry.supportedScopes) || entry.supportedScopes.length === 0) { + throw new Error("Validation check manifest entry supportedScopes must be a non-empty array"); + } + for (const scopeKind of entry.supportedScopes) { + if (!includesString(validationScopeKinds, scopeKind)) { + throw new Error(`Unknown validation check manifest entry supported scope: ${String(scopeKind)}`); + } + } + if (typeof entry.requiresGraph !== "boolean") { + throw new Error("Validation check manifest entry requiresGraph must be boolean"); + } + return entry; +} + +export { validateValidationCheckManifestEntry }; + +function validateValidationCheckRunSummary(run: ValidationCheckRunSummary): ValidationCheckRunSummary { + validateRequiredObject(run, "Validation check run summary is required"); + validateValidationCheckId(run.checkId, "Validation check run summary checkId"); + if (!includesString(validationCheckRunStatuses, run.status)) { + throw new Error(`Unknown validation check run status: ${String(run.status)}`); + } + if (run.outcome !== undefined && !includesString(validationCheckOutcomes, run.outcome)) { + throw new Error(`Unknown validation check outcome: ${String(run.outcome)}`); + } + validateValidationCheckOutcomeStatus(run); + validateOptional(run.durationMs, (value) => + validateNonNegativeNumber(value, "Validation check run summary durationMs"), + ); + validateOptional(run.diagnosticCount, (value) => + validateNonNegativeInteger(value, "Validation check run summary diagnosticCount"), + ); + validateOptional(run.failureMessage, (value) => + validateNonEmptyString(value, "Validation check run summary failureMessage"), + ); + if ( + includesString(["infrastructure_failure", "provider_failure", "unsupported_request"] as const, run.status) && + run.failureMessage === undefined + ) { + throw new Error("Validation check run summary failureMessage is required for failure statuses"); + } + validateOptional(run.pythonCapabilityRuns, (runs) => { + if (!Array.isArray(runs)) { + throw new Error("Validation check run summary pythonCapabilityRuns must be an array"); + } + for (const capabilityRun of runs) validatePythonValidationCapabilityRun(capabilityRun); + }); + return run; +} + +export { validateValidationCheckRunSummary }; + +function validateValidationCheckOutcomeStatus(run: ValidationCheckRunSummary): void { + if (run.outcome === undefined) return; + const expectedStatus: Record = { + passed: "passed", + findings: "policy_failure", + tool_unavailable: "unsupported_request", + invalid_config: "unsupported_request", + timeout: "infrastructure_failure", + unsupported_target: "unsupported_request", + tool_failure: "infrastructure_failure", + }; + if (run.status !== expectedStatus[run.outcome]) { + throw new Error(`Validation check outcome ${run.outcome} requires status ${expectedStatus[run.outcome]}`); + } +} + +export { validateValidationCheckOutcomeStatus }; diff --git a/packages/contracts/src/validation/result-validator.ts b/packages/contracts/src/validation/result-validator.ts new file mode 100644 index 0000000..b538e14 --- /dev/null +++ b/packages/contracts/src/validation/result-validator.ts @@ -0,0 +1,95 @@ +import { + validateBoolean, + validateOptional, + validateRequiredObject, +} from "../shared/validators-02.js"; +import { validateEditRefusal } from "../edit/refusal-validator.js"; +import { validateProviderStatus } from "../graph/provider-validators.js"; +import { validateRepoIdentity } from "../shared/path-validators.js"; +import { includesString } from "../shared/primitives.js"; +import { validateNonEmptyString, validateValidationChecks } from "../shared/validators-01.js"; +import type { ValidationResult } from "./capability-contracts.js"; +import { validatePythonProjectContexts } from "./python-project-validators-01.js"; +import { validateValidationFailure } from "./python-ruff-validators-03.js"; +import { validatePythonValidationCapabilityRuns } from "./python-types-validators.js"; +import type { ValidationRequest } from "./request-contracts.js"; +import { + validateHypotheticalOverlays, + validateValidationDiagnostics, + validateValidationGraphConfig, + validateValidationScope, +} from "./request-validators-01.js"; +import { validateValidationResultManifest } from "./request-validators-02.js"; +import { validationFailureCategories, validationReportModes, validationResultStatuses } from "./vocabulary-01.js"; + +function validateValidationRequestPayload(request: ValidationRequest): ValidationRequest { + validateRequiredObject(request, "Validation request is required"); + if (request.requestId !== undefined) validateNonEmptyString(request.requestId, "Validation request requestId"); + validateRepoIdentity(request.repo); + validateValidationScope(request.scope); + validateValidationGraphConfig(request.graph); + validateHypotheticalOverlays(request.overlays); + if (request.checks !== undefined) validateValidationChecks(request.checks, "Validation request checks"); + if (request.reportMode !== undefined && !includesString(validationReportModes, request.reportMode)) { + throw new Error(`Unknown validation request reportMode: ${String(request.reportMode)}`); + } + return request; +} + +export { validateValidationRequestPayload }; + +function validateValidationResultPayload(result: ValidationResult): ValidationResult { + validateRequiredObject(result, "Validation result is required"); + validateBoolean(result.ok, "Validation result ok"); + if (!includesString(validationResultStatuses, result.status)) { + throw new Error(`Unknown validation result status: ${String(result.status)}`); + } + if (result.status === "passed" && !result.ok) { + throw new Error("Validation passed result must use ok=true"); + } + if (result.ok && result.status !== "passed") { + throw new Error("Validation result ok=true must use passed status"); + } + validateValidationDiagnostics(result.diagnostics); + validateOptional(result.graphStatus, validateProviderStatus); + validateOptional(result.failure, validateValidationFailure); + validateOptional(result.refusal, validateEditRefusal); + validateValidationResultRefusal(result); + validateValidationResultFailure(result); + validateValidationResultSuccess(result); + validateOptional(result.manifest, validateValidationResultManifest); + validateOptional(result.pythonProjectContexts, validatePythonProjectContexts); + validateOptional(result.pythonCapabilityRuns, validatePythonValidationCapabilityRuns); + return result; +} + +export { validateValidationResultPayload }; + +function validateValidationResultRefusal(result: ValidationResult): void { + if (result.status === "refused" && result.refusal === undefined) { + throw new Error("Validation refused result must include refusal"); + } + if (result.status === "refused" && result.failure !== undefined) { + throw new Error("Validation refused result must not include failure"); + } +} + +function validateValidationResultFailure(result: ValidationResult): void { + if (includesString(validationFailureCategories, result.status) && result.failure === undefined) { + throw new Error(`Validation ${result.status} result must include failure`); + } + if (includesString(validationFailureCategories, result.status)) { + if (result.failure?.category !== result.status) { + throw new Error("Validation failure category must match result status"); + } + if (result.refusal !== undefined) { + throw new Error("Validation failure result must not include refusal"); + } + } +} + +function validateValidationResultSuccess(result: ValidationResult): void { + if (result.status === "passed" && (result.failure !== undefined || result.refusal !== undefined)) { + throw new Error("Validation passed result must not include failure or refusal"); + } +} diff --git a/packages/contracts/src/validation/status-contracts.ts b/packages/contracts/src/validation/status-contracts.ts new file mode 100644 index 0000000..a03970a --- /dev/null +++ b/packages/contracts/src/validation/status-contracts.ts @@ -0,0 +1,151 @@ +import type { RepoIdentity } from "../graph/provider-contracts-01.js"; +import type { GraphProviderStatus } from "../graph/provider-contracts-02.js"; +import type { GraphProviderMode } from "../graph/vocabulary-01.js"; +import type { ValidationCheckManifestEntry } from "./diagnostic-contracts.js"; +import type { ValidationScope } from "./request-contracts.js"; +import type { ValidationCheckRunStatus, ValidationResultStatus } from "./vocabulary-01.js"; + +interface RequiredContextDocPolicy { + filenames: readonly string[]; + requiredPaths: readonly string[]; + requireRoot?: boolean; + minimumContentLength: number; + maxLines?: number; + maxSectionLines?: number; +} + +export type { RequiredContextDocPolicy }; + +const requiredContextDocPolicy = { + filenames: ["AGENTS.md", "CLAUDE.md"], + requiredPaths: ["."], + requireRoot: true, + minimumContentLength: 120, +} as const satisfies RequiredContextDocPolicy; + +export { requiredContextDocPolicy }; + +interface PreWriteValidationOverlaySummary { + count: number; + writeCount: number; + deleteCount: number; + paths: readonly string[]; +} + +export type { PreWriteValidationOverlaySummary }; + +interface PreWriteValidationFailureSummary { + category: ValidationResultStatus; + message: string; + cause?: string; + retryable?: boolean; +} + +export type { PreWriteValidationFailureSummary }; + +interface PreWriteValidationReceipt { + schemaVersion: 1; + kind: "pre_write_validation"; + route: "validate.pre-write"; + canonicalCommand: readonly string[]; + generatedAt: string; + durationMs: number; + timeoutMs: number; + ok: boolean; + requestId?: string; + repo?: RepoIdentity; + scope?: ValidationScope; + checks?: readonly string[]; + graph?: { + mode: GraphProviderMode; + provider?: string; + status?: GraphProviderStatus; + }; + overlays?: PreWriteValidationOverlaySummary; + validationStatus: ValidationResultStatus; + diagnosticCount: number; + failureSummary?: PreWriteValidationFailureSummary; +} + +export type { PreWriteValidationReceipt }; + +const validationDaemonReadinessStates = ["not_configured", "ready", "unavailable", "error"] as const; + +export { validationDaemonReadinessStates }; + +type ValidationDaemonReadinessState = (typeof validationDaemonReadinessStates)[number]; + +export type { ValidationDaemonReadinessState }; + +const validationAdapterRuntimeStates = ["available", "degraded", "unavailable"] as const; + +export { validationAdapterRuntimeStates }; + +type ValidationAdapterRuntimeState = (typeof validationAdapterRuntimeStates)[number]; + +export type { ValidationAdapterRuntimeState }; + +interface ValidationAdapterToolchainStatus { + tool: string; + available: boolean; + command?: string; + version?: string; + failureMessage?: string; + cwd?: string; + configFile?: string; + source?: string; +} + +export type { ValidationAdapterToolchainStatus }; + +interface ValidationAdapterDegradedCheckStatus { + checkId: string; + status: ValidationCheckRunStatus; + reason: string; + message: string; + requiredTool?: string; + retainedCompatibility?: boolean; + followUpIssue?: string; + currentUsage?: { + opcore: boolean; + orchestra: boolean; + covibes: boolean; + gateway: boolean; + }; +} + +export type { ValidationAdapterDegradedCheckStatus }; + +interface ValidationAdapterRuntimeStatus { + adapter: string; + status: ValidationAdapterRuntimeState; + checkIds: readonly string[]; + toolchain?: readonly ValidationAdapterToolchainStatus[]; + degradedChecks?: readonly ValidationAdapterDegradedCheckStatus[]; + tempWorkspaceRequired?: boolean; +} + +export type { ValidationAdapterRuntimeStatus }; + +interface ValidationStatusPayload { + schemaVersion: 1; + ready: boolean; + generatedAt: string; + adapterRegistry: { + checkRoutes: readonly string[]; + validateRoutes: readonly string[]; + checkIds: readonly string[]; + entries: readonly ValidationCheckManifestEntry[]; + adapters?: readonly ValidationAdapterRuntimeStatus[]; + }; + graph: { + mode: GraphProviderMode; + status: GraphProviderStatus; + }; + daemon?: { + state: ValidationDaemonReadinessState; + message?: string; + }; +} + +export type { ValidationStatusPayload }; diff --git a/packages/contracts/src/validation/vocabulary-01.ts b/packages/contracts/src/validation/vocabulary-01.ts new file mode 100644 index 0000000..7be2eb6 --- /dev/null +++ b/packages/contracts/src/validation/vocabulary-01.ts @@ -0,0 +1,104 @@ +const validationDiagnosticCategories = [ + "syntax", + "types", + "lint", + "test", + "graph", + "policy", + "provider", + "infrastructure", + "edit_safety", +] as const; + +export { validationDiagnosticCategories }; + +type ValidationDiagnosticCategory = (typeof validationDiagnosticCategories)[number]; + +export type { ValidationDiagnosticCategory }; + +const validationResultStatuses = [ + "passed", + "policy_failure", + "infrastructure_failure", + "provider_failure", + "unsupported_request", + "invalid_payload", + "skipped", + "refused", +] as const; + +export { validationResultStatuses }; + +type ValidationResultStatus = (typeof validationResultStatuses)[number]; + +export type { ValidationResultStatus }; + +const validationFailureCategories = [ + "policy_failure", + "infrastructure_failure", + "provider_failure", + "unsupported_request", + "invalid_payload", + "skipped", +] as const; + +export { validationFailureCategories }; + +type ValidationFailureCategory = (typeof validationFailureCategories)[number]; + +export type { ValidationFailureCategory }; + +const validationReportModes = ["all", "introduced"] as const; + +export { validationReportModes }; + +type ValidationReportMode = (typeof validationReportModes)[number]; + +export type { ValidationReportMode }; + +const validationCheckRunStatuses = [ + "passed", + "policy_failure", + "infrastructure_failure", + "provider_failure", + "unsupported_request", + "skipped", +] as const; + +export { validationCheckRunStatuses }; + +type ValidationCheckRunStatus = (typeof validationCheckRunStatuses)[number]; + +export type { ValidationCheckRunStatus }; + +const validationCheckOutcomes = [ + "passed", + "findings", + "tool_unavailable", + "invalid_config", + "timeout", + "unsupported_target", + "tool_failure", +] as const; + +export { validationCheckOutcomes }; + +type ValidationCheckOutcome = (typeof validationCheckOutcomes)[number]; + +export type { ValidationCheckOutcome }; + +const pythonValidationCapabilityRunStatuses = [...validationCheckOutcomes] as const; + +export { pythonValidationCapabilityRunStatuses }; + +type PythonValidationCapabilityRunStatus = (typeof pythonValidationCapabilityRunStatuses)[number]; + +export type { PythonValidationCapabilityRunStatus }; + +const pythonValidationAuthorities = ["mypy", "pyright"] as const; + +export { pythonValidationAuthorities }; + +type PythonValidationAuthority = (typeof pythonValidationAuthorities)[number]; + +export type { PythonValidationAuthority }; diff --git a/packages/contracts/src/validation/vocabulary-02.ts b/packages/contracts/src/validation/vocabulary-02.ts new file mode 100644 index 0000000..574c28b --- /dev/null +++ b/packages/contracts/src/validation/vocabulary-02.ts @@ -0,0 +1,71 @@ +import { validationCheckOutcomes } from "./vocabulary-01.js"; + +const pythonValidationAuthoritySources = ["explicit", "project_config"] as const; + +export { pythonValidationAuthoritySources }; + +type PythonValidationAuthoritySource = (typeof pythonValidationAuthoritySources)[number]; + +export type { PythonValidationAuthoritySource }; + +const pythonValidationCapabilityTerminationKinds = ["exited", "timeout", "signal", "spawn_error"] as const; + +export { pythonValidationCapabilityTerminationKinds }; + +type PythonValidationCapabilityTerminationKind = (typeof pythonValidationCapabilityTerminationKinds)[number]; + +export type { PythonValidationCapabilityTerminationKind }; + +const pythonValidationCapabilities = ["types", "ruff_lint", "ruff_format", "pytest"] as const; + +export { pythonValidationCapabilities }; + +type PythonValidationCapability = (typeof pythonValidationCapabilities)[number]; + +export type { PythonValidationCapability }; + +const pythonValidationCapabilityStates = [...validationCheckOutcomes, "not_applicable", "disabled"] as const; + +export { pythonValidationCapabilityStates }; + +type PythonValidationCapabilityState = (typeof pythonValidationCapabilityStates)[number]; + +export type { PythonValidationCapabilityState }; + +const pythonValidationCapabilityTerminations = ["exited", "timeout", "signal", "spawn_error", "overflow"] as const; + +export { pythonValidationCapabilityTerminations }; + +type PythonValidationCapabilityTermination = (typeof pythonValidationCapabilityTerminations)[number]; + +export type { PythonValidationCapabilityTermination }; + +const validationSkippedCheckReasons = [ + "graph_unavailable", + "unsupported_scope", + "not_requested", + "no_files", + "provider_failure", +] as const; + +export { validationSkippedCheckReasons }; + +type ValidationSkippedCheckReason = (typeof validationSkippedCheckReasons)[number]; + +export type { ValidationSkippedCheckReason }; + +const validationCheckIdPattern = "^[a-z][a-z0-9]*(?:[._:-][a-z0-9]+)*$" as const; + +export { validationCheckIdPattern }; + +const validationCheckIdRegex = new RegExp(validationCheckIdPattern); + +export { validationCheckIdRegex }; + +const latencyStableIdRegex = /^[a-z][a-z0-9_-]*$/; + +export { latencyStableIdRegex }; + +const latencyTelemetryCommandTokenRegex = /^(?=.*[A-Za-z0-9])[-@A-Za-z0-9._,:=]+$/; + +export { latencyTelemetryCommandTokenRegex }; diff --git a/packages/contracts/src/validation/vocabulary-03.ts b/packages/contracts/src/validation/vocabulary-03.ts new file mode 100644 index 0000000..0fde4d7 --- /dev/null +++ b/packages/contracts/src/validation/vocabulary-03.ts @@ -0,0 +1,7 @@ +const latencyTelemetrySourceFileExtensionRegex = new RegExp( + String.raw`\.(?:[cm]?[tj]sx?|mjs|cjs|jsonl?|rs|pyi?|mdx?|toml|lock|ya?ml|txt|inc|css|s[ac]ss|` + + String.raw`html?|vue|svelte|go|java|rb|php|swift|kts?|scala|lua|cs|c|cc|cpp|h|hpp)(?:$|[,=:])`, + "i", +); + +export { latencyTelemetrySourceFileExtensionRegex }; diff --git a/packages/edit/src/index.ts b/packages/edit/src/index.ts index 100bf1b..cd4b3c1 100644 --- a/packages/edit/src/index.ts +++ b/packages/edit/src/index.ts @@ -10,6 +10,7 @@ export * from "./patch-tree-command.js"; export * from "./path-policy.js"; export * from "./planner.js"; export * from "./language-service.js"; +export * from "./typescript-project/index.js"; export * from "./symbol-command.js"; export * from "./symbol-graph.js"; export * from "./symbol-preview.js"; diff --git a/packages/edit/src/language-service.ts b/packages/edit/src/language-service.ts index 125707a..f6139f9 100644 --- a/packages/edit/src/language-service.ts +++ b/packages/edit/src/language-service.ts @@ -1,11 +1,21 @@ import { existsSync, readFileSync, realpathSync, readdirSync, statSync } from "node:fs"; -import { dirname, extname, isAbsolute, join, relative, resolve } from "node:path"; +import { dirname, extname, join, relative, resolve } from "node:path"; import type { EditRefusal, RepoRelativeChange } from "@the-open-engine/opcore-contracts"; import { decodeTextContent } from "./content-policy.js"; -import { Node, Project, SyntaxKind, ts, type SourceFile, type Symbol as MorphSymbol } from "ts-morph"; +import { Node, SyntaxKind, type Project, type SourceFile, type Symbol as MorphSymbol } from "ts-morph"; import { calculateEditChecksum } from "./hash.js"; import { normalizeEditRepoRelativePath } from "./path-policy.js"; import type { MoveSymbolEditRequest, RenameSymbolEditRequest, SignatureParameterChange, SignatureSymbolEditRequest, SymbolEditTarget } from "./symbol-requests.js"; +import { + TypeScriptProjectService, + defaultTypeScriptProjectExcludedDirectories, + isPathInside as isInside, + isSafeExistingFileInsideRepo, + normalizeModulePath, + type TypeScriptProjectContext, + type TypeScriptProjectOptions, + type TypeScriptProjectScope +} from "./typescript-project/index.js"; export type SymbolMaterializationResult = | { @@ -22,24 +32,36 @@ export interface AffectedChecksum { checksumAfter?: string; } -export type SymbolEditLanguageServiceProjectScope = "import_closure" | "whole_repo"; +export type SymbolEditLanguageServiceProjectScope = TypeScriptProjectScope; -export interface SymbolEditLanguageServiceOptions { - project?: Project; - projectScope?: SymbolEditLanguageServiceProjectScope; - projectTsconfigPath?: string; - snapshotProject?: (project: Project) => unknown; - revertProject?: (project: Project, snapshot: unknown) => void; -} - -const symbolEditProjectScopes = new WeakMap(); +export interface SymbolEditLanguageServiceOptions extends TypeScriptProjectOptions {} const sourceFileExtensions = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]); -const extensionlessImportCandidates = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".d.ts"] as const; -const excludedDirectories = new Set(["node_modules", "dist", "build", ".git", ".lattice"]); +const symbolEditProjectService = new TypeScriptProjectService({ + sourceExtensions: [...sourceFileExtensions], + extensionlessImportCandidates: [ + ".ts", + ".tsx", + ".js", + ".jsx", + ".mjs", + ".cjs", + ".d.ts" + ], + excludedDirectories: defaultTypeScriptProjectExcludedDirectories, + allowDirectoryRoots: true, + configMode: "project_references", + discoverAllConfigs: true, + tolerateMalformedImportConfig: false, + defaultIncludeDependents: true +}); export function isSupportedSymbolSourcePath(path: string): boolean { - return sourceFileExtensions.has(extname(path).toLowerCase()); + return symbolEditProjectService.isSupportedSourcePath(path); +} + +export function listSymbolEditLanguageServiceSourceFiles(repoRoot: string): string[] { + return symbolEditProjectService.listSourceFiles(repoRoot); } export function materializeRenameSymbolEdit( @@ -160,34 +182,7 @@ export function materializeSignatureSymbolEdit( return mergedChangesResult(changeSets); } -function createProject(repoRoot: string, preferredRepoPath: string | undefined, options: SymbolEditLanguageServiceOptions): ProjectContext { - const rawTsconfigPath = join(repoRoot, "tsconfig.json"); - const preferredAbsolutePath = preferredRepoPath === undefined ? undefined : resolve(repoRoot, preferredRepoPath); - const tsconfigPath = resolveSymbolEditTsconfigPath(repoRoot, options.projectTsconfigPath) ?? resolveTsconfigPath(repoRoot, rawTsconfigPath, preferredAbsolutePath); - const projectScope = options.projectScope ?? "import_closure"; - if (options.project !== undefined && canUseInjectedProject(options.project, projectScope)) { - if (projectScope === "import_closure" && preferredRepoPath !== undefined) { - addScopedSourceFilesToProject(repoRoot, tsconfigPath, options.project, [preferredRepoPath]); - } - return { - project: options.project, - ...(options.projectTsconfigPath ? { tsconfigPath: options.projectTsconfigPath } : {}), - ...(options.snapshotProject ? { snapshotProject: options.snapshotProject } : {}), - ...(options.revertProject ? { revertProject: options.revertProject } : {}) - }; - } - return { - project: createProjectForTsconfig(repoRoot, tsconfigPath, preferredRepoPath, projectScope), - ...(tsconfigPath ? { tsconfigPath } : {}) - }; -} - -type ProjectContext = { - project: Project; - tsconfigPath?: string; - snapshotProject?: (project: Project) => unknown; - revertProject?: (project: Project, snapshot: unknown) => void; -}; +type ProjectContext = TypeScriptProjectContext; function createProjectResult( repoRoot: string, @@ -195,7 +190,15 @@ function createProjectResult( options: SymbolEditLanguageServiceOptions ): { ok: true; value: ProjectContext } | { ok: false; refusal: EditRefusal } { try { - return { ok: true, value: createProject(repoRoot, preferredRepoPath, options) }; + const context = symbolEditProjectService.createContexts( + repoRoot, + preferredRepoPath, + options + )[0]; + if (context === undefined) { + throw new Error("No TypeScript project context was available"); + } + return { ok: true, value: context }; } catch (error) { return projectConfigurationRefusal(error, preferredRepoPath); } @@ -207,36 +210,28 @@ function createProjectContextsResult( options: SymbolEditLanguageServiceOptions ): { ok: true; value: ProjectContext[] } | { ok: false; refusal: EditRefusal } { try { - return { ok: true, value: createProjectContexts(repoRoot, preferredRepoPath, options) }; + return { + ok: true, + value: symbolEditProjectService.createContexts( + repoRoot, + preferredRepoPath, + options + ) + }; } catch (error) { return projectConfigurationRefusal(error, preferredRepoPath); } } -function createProjectContexts(repoRoot: string, preferredRepoPath: string | undefined, options: SymbolEditLanguageServiceOptions): ProjectContext[] { - if (options.project !== undefined) return [createProject(repoRoot, preferredRepoPath, options)]; - const rawTsconfigPath = join(repoRoot, "tsconfig.json"); - const preferredAbsolutePath = preferredRepoPath === undefined ? undefined : resolve(repoRoot, preferredRepoPath); - const preferredTsconfigPath = resolveSymbolEditTsconfigPath(repoRoot, options.projectTsconfigPath) ?? resolveTsconfigPath(repoRoot, rawTsconfigPath, preferredAbsolutePath); - const projectScope = options.projectScope ?? "import_closure"; - const orderedPaths: string[] = []; - if (preferredTsconfigPath !== undefined) orderedPaths.push(resolve(preferredTsconfigPath)); - for (const tsconfigPath of collectProjectTsconfigPaths(repoRoot)) { - if (!orderedPaths.includes(tsconfigPath)) orderedPaths.push(tsconfigPath); - } - if (orderedPaths.length === 0) return [{ project: createProjectForTsconfig(repoRoot, undefined, preferredRepoPath, projectScope) }]; - return orderedPaths.map((tsconfigPath) => ({ - project: createProjectForTsconfig(repoRoot, tsconfigPath, preferredRepoPath, projectScope), - tsconfigPath - })); -} - -function canUseInjectedProject(project: Project, requiredScope: SymbolEditLanguageServiceProjectScope): boolean { - return requiredScope !== "whole_repo" || symbolEditProjectScopes.get(project) === "whole_repo"; -} - -function projectConfigurationRefusal(error: unknown, path?: string): { ok: false; refusal: EditRefusal } { - return refused("unsafe_edit", `TypeScript project configuration cannot be loaded for symbol edit: ${errorMessage(error)}`, path); +function projectConfigurationRefusal( + error: unknown, + path?: string +): { ok: false; refusal: EditRefusal } { + return refused( + "unsafe_edit", + `TypeScript project configuration cannot be loaded for symbol edit: ${errorMessage(error)}`, + path + ); } export function createSymbolEditLanguageServiceProject( @@ -244,351 +239,15 @@ export function createSymbolEditLanguageServiceProject( preferredRepoPath?: string, options: SymbolEditLanguageServiceOptions = {} ): Project { - return createProject(repoRoot, preferredRepoPath, options).project; + return symbolEditProjectService.createProject( + repoRoot, + preferredRepoPath, + options + ); } function withProjectSnapshot(context: ProjectContext, run: () => T): T { - if (context.snapshotProject === undefined || context.revertProject === undefined) return run(); - const snapshot = context.snapshotProject(context.project); - try { - return run(); - } finally { - context.revertProject(context.project, snapshot); - } -} - -function createProjectForTsconfig( - repoRoot: string, - tsconfigPath: string | undefined, - preferredRepoPath: string | undefined, - scope: SymbolEditLanguageServiceProjectScope -): Project { - const project = new Project({ - tsConfigFilePath: tsconfigPath, - skipAddingFilesFromTsConfig: true, - skipFileDependencyResolution: true, - compilerOptions: { - allowJs: true, - checkJs: false - } - }); - const sourceFiles = scope === "whole_repo" || preferredRepoPath === undefined - ? listSourceFiles(repoRoot) - : scopedSourceFiles(repoRoot, tsconfigPath, [preferredRepoPath], { includeDependents: true }); - for (const filePath of sourceFiles) { - if (!project.getSourceFile(filePath)) project.addSourceFileAtPath(filePath); - } - symbolEditProjectScopes.set(project, scope); - return project; -} - -function addScopedSourceFilesToProject( - repoRoot: string, - tsconfigPath: string | undefined, - project: Project, - rootRepoPaths: readonly string[] -): void { - for (const filePath of scopedSourceFiles(repoRoot, tsconfigPath, rootRepoPaths, { includeDependents: true })) { - if (project.getSourceFile(filePath) === undefined) project.addSourceFileAtPath(filePath); - } -} - -function collectProjectTsconfigPaths(repoRoot: string): string[] { - const discovered = new Set(); - const queue = [join(repoRoot, "tsconfig.json"), ...listTsconfigJsonPaths(repoRoot)]; - for (let index = 0; index < queue.length; index += 1) { - const tsconfigPath = resolve(queue[index]); - if (discovered.has(tsconfigPath) || !isSafeExistingFileInsideRepo(repoRoot, tsconfigPath)) continue; - discovered.add(tsconfigPath); - try { - const config = parseTsconfig(tsconfigPath); - for (const referencePath of referencedTsconfigPaths(repoRoot, tsconfigPath, config)) queue.push(referencePath); - } catch { - // Ignore malformed auxiliary configs here; the preferred project path keeps existing behavior. - } - } - return [...discovered].sort(); -} - -function listTsconfigJsonPaths(repoRoot: string): string[] { - const paths: string[] = []; - visit(repoRoot); - return paths.sort(); - - function visit(directory: string): void { - for (const entry of readdirSync(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name))) { - const path = join(directory, entry.name); - if (entry.isDirectory()) { - if (!excludedDirectories.has(entry.name)) visit(path); - } else if (entry.isFile() && entry.name === "tsconfig.json" && isSafeExistingFileInsideRepo(repoRoot, path)) { - paths.push(resolve(path)); - } - } - } -} - -function resolveTsconfigPath(repoRoot: string, tsconfigPath: string, preferredAbsolutePath?: string): string | undefined { - return resolveTsconfigPathInternal(repoRoot, tsconfigPath, preferredAbsolutePath, new Set()); -} - -function resolveTsconfigPathInternal(repoRoot: string, tsconfigPath: string, preferredAbsolutePath: string | undefined, seen: Set): string | undefined { - if (!isSafeExistingFileInsideRepo(repoRoot, tsconfigPath)) return undefined; - const normalizedTsconfigPath = resolve(tsconfigPath); - if (seen.has(normalizedTsconfigPath)) return undefined; - seen.add(normalizedTsconfigPath); - try { - const config = parseTsconfig(tsconfigPath); - const referenceOnly = Array.isArray(config.references) && config.references.length > 0 && Array.isArray(config.files) && config.files.length === 0 && config.include === undefined; - if (!referenceOnly) return tsconfigPath; - const candidates = referencedTsconfigPaths(repoRoot, tsconfigPath, config); - if (preferredAbsolutePath !== undefined) { - const ownedCandidates = candidates - .flatMap((candidate) => { - const resolvedCandidate = resolveTsconfigPathInternal(repoRoot, candidate, preferredAbsolutePath, new Set(seen)); - return resolvedCandidate === undefined ? [] : [resolvedCandidate]; - }) - .filter((candidate, index, values) => values.indexOf(candidate) === index) - .filter((candidate) => isInside(dirname(candidate), preferredAbsolutePath)) - .sort((left, right) => dirname(right).length - dirname(left).length || left.localeCompare(right)); - if (ownedCandidates.length > 0) return ownedCandidates[0]; - } - return candidates[0] ?? tsconfigPath; - } catch { - return tsconfigPath; - } -} - -type TsconfigJson = { - references?: readonly { path?: string }[]; - files?: readonly string[]; - include?: readonly string[]; - compilerOptions?: { - baseUrl?: unknown; - paths?: unknown; - }; -}; - -function parseTsconfig(tsconfigPath: string): TsconfigJson { - const parsed = ts.parseConfigFileTextToJson(tsconfigPath, readFileSync(tsconfigPath, "utf8")); - if (parsed.error !== undefined) throw new Error(ts.flattenDiagnosticMessageText(parsed.error.messageText, "\n")); - return parsed.config as TsconfigJson; -} - -function referencedTsconfigPaths(repoRoot: string, tsconfigPath: string, config: TsconfigJson): string[] { - const candidates: string[] = []; - for (const reference of config.references ?? []) { - if (!reference.path) continue; - const candidate = reference.path.endsWith(".json") - ? resolve(dirname(tsconfigPath), reference.path) - : resolve(dirname(tsconfigPath), reference.path, "tsconfig.json"); - if (isSafeExistingFileInsideRepo(repoRoot, candidate)) candidates.push(candidate); - } - return candidates; -} - -interface ImportResolutionOptions { - baseUrl: string; - hasBaseUrl: boolean; - paths: Readonly>; -} - -function scopedSourceFiles( - repoRoot: string, - tsconfigPath: string | undefined, - rootRepoPaths: readonly string[], - options: { includeDependents: boolean } -): string[] { - const importOptions = importResolutionOptions(repoRoot, tsconfigPath); - const importTargetsByFile = new Map(); - const allSourceFiles = options.includeDependents ? listSourceFiles(repoRoot) : []; - const roots = rootSourceFiles(repoRoot, rootRepoPaths); - const reverseTargets = new Set(roots); - const selected = new Set(); - addForwardClosure(roots, selected); - - if (options.includeDependents) { - let changed = true; - while (changed) { - changed = false; - for (const filePath of allSourceFiles) { - if (selected.has(filePath)) continue; - const importsReverseTarget = importTargets(filePath).some((importedPath) => reverseTargets.has(importedPath)); - if (!importsReverseTarget) continue; - const beforeSize = selected.size; - addForwardClosure([filePath], selected); - reverseTargets.add(filePath); - if (selected.size !== beforeSize) changed = true; - } - } - } - - return [...selected].sort(); - - function addForwardClosure(rootFiles: readonly string[], selectedFiles: Set): void { - const pending = [...rootFiles].sort(); - for (let index = 0; index < pending.length; index += 1) { - const filePath = pending[index]; - if (selectedFiles.has(filePath)) continue; - selectedFiles.add(filePath); - for (const importedPath of importTargets(filePath)) { - if (!selectedFiles.has(importedPath) && !pending.includes(importedPath)) pending.push(importedPath); - } - } - } - - function importTargets(filePath: string): readonly string[] { - const cached = importTargetsByFile.get(filePath); - if (cached !== undefined) return cached; - const resolvedTargets = moduleImportSpecifiers(readFileSync(filePath, "utf8")) - .flatMap((specifier) => { - const resolvedImport = resolveImportSpecifier(repoRoot, filePath, specifier, importOptions); - return resolvedImport === undefined ? [] : [resolvedImport]; - }) - .sort(); - importTargetsByFile.set(filePath, resolvedTargets); - return resolvedTargets; - } -} - -function rootSourceFiles(repoRoot: string, rootRepoPaths: readonly string[]): string[] { - const files: string[] = []; - for (const rootRepoPath of rootRepoPaths) { - const absolutePath = resolve(repoRoot, rootRepoPath); - if (isSupportedSymbolSourcePath(absolutePath) && isSafeExistingFileInsideRepo(repoRoot, absolutePath)) { - files.push(resolve(absolutePath)); - continue; - } - if (!isSafeExistingDirectoryInsideRepo(repoRoot, absolutePath)) continue; - const directoryRoot = resolve(absolutePath); - files.push(...listSourceFiles(repoRoot).filter((filePath) => isInside(directoryRoot, filePath))); - } - return uniqueSorted(files); -} - -function moduleImportSpecifiers(text: string): readonly string[] { - const specifiers = new Set(); - for (const match of text.matchAll(/\b(?:import|export)\s+(?:type\s+)?(?:[^"'`;]*?\s+from\s+)?["']([^"']+)["']/gu)) { - if (match[1]) specifiers.add(match[1]); - } - for (const match of text.matchAll(/ `${basePath}${candidateExtension}`), - ...extensionlessImportCandidates.map((candidateExtension) => join(basePath, `index${candidateExtension}`)) - ]); -} - -function importResolutionOptions(repoRoot: string, tsconfigPath: string | undefined): ImportResolutionOptions { - const configDirectory = tsconfigPath === undefined ? repoRoot : dirname(tsconfigPath); - const config = tsconfigPath === undefined ? undefined : parseTsconfig(tsconfigPath); - const compilerOptions = config?.compilerOptions; - const baseUrl = typeof compilerOptions?.baseUrl === "string" && compilerOptions.baseUrl.length > 0 - ? resolve(configDirectory, compilerOptions.baseUrl) - : configDirectory; - return { - baseUrl, - hasBaseUrl: typeof compilerOptions?.baseUrl === "string" && compilerOptions.baseUrl.length > 0, - paths: normalizePathMappings(compilerOptions?.paths) - }; -} - -function normalizePathMappings(paths: unknown): Readonly> { - if (paths === null || typeof paths !== "object" || Array.isArray(paths)) return {}; - const normalized: Record = {}; - for (const [pattern, targets] of Object.entries(paths)) { - if (Array.isArray(targets)) normalized[pattern] = targets.filter((target): target is string => typeof target === "string"); - } - return normalized; -} - -function sortedPathMappings(paths: Readonly>): readonly [string, readonly string[]][] { - return Object.entries(paths) - .filter((entry): entry is [string, readonly string[]] => entry[1].length > 0) - .sort((left, right) => pathPatternRank(right[0]) - pathPatternRank(left[0])); -} - -function pathPatternRank(pattern: string): number { - const starIndex = pattern.indexOf("*"); - if (starIndex === -1) return pattern.length * 2 + 1; - return pattern.length - 1; -} - -function matchPathPattern(pattern: string, specifier: string): string | undefined { - const starIndex = pattern.indexOf("*"); - if (starIndex === -1) return pattern === specifier ? "" : undefined; - const prefix = pattern.slice(0, starIndex); - const suffix = pattern.slice(starIndex + 1); - if (!specifier.startsWith(prefix) || !specifier.endsWith(suffix)) return undefined; - return specifier.slice(prefix.length, specifier.length - suffix.length); -} - -function applyPathMappingTarget(target: string, wildcard: string): string { - return target.includes("*") ? target.replaceAll("*", wildcard) : target; -} - -function listSourceFiles(repoRoot: string): string[] { - const files: string[] = []; - visit(repoRoot); - return files.sort(); - - function visit(directory: string): void { - for (const entry of readdirSync(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name))) { - const path = join(directory, entry.name); - if (entry.isDirectory()) { - if (!excludedDirectories.has(entry.name)) visit(path); - } else if (entry.isFile() && isSupportedSymbolSourcePath(path)) { - const repoPath = normalizeModulePath(relative(repoRoot, path)); - if (validateExistingPathInsideRepoSync(repoRoot, path, repoPath, "Source file", "file").ok) files.push(path); - } - } - } -} - -function resolveSymbolEditTsconfigPath(repoRoot: string, tsconfigPath: string | undefined): string | undefined { - if (tsconfigPath === undefined) return undefined; - const absolutePath = resolve(repoRoot, tsconfigPath); - return isSafeExistingFileInsideRepo(repoRoot, absolutePath) ? absolutePath : undefined; + return symbolEditProjectService.withSnapshot(context, run); } function sourceFileForTarget(project: Project, repoRoot: string, repoPath: string): { ok: true; value: SourceFile } | { ok: false; refusal: EditRefusal } { @@ -1052,24 +711,6 @@ function toRepoPath(repoRoot: string, absolutePath: string): { ok: true; value: return normalized.ok ? { ok: true, value: normalized.value } : normalized; } -function isSafeExistingFileInsideRepo(repoRoot: string, absolutePath: string): boolean { - if (!isInside(repoRoot, absolutePath) || !existsSync(absolutePath)) return false; - try { - return isInside(repoRoot, realpathSync(absolutePath)) && statSync(absolutePath).isFile(); - } catch { - return false; - } -} - -function isSafeExistingDirectoryInsideRepo(repoRoot: string, absolutePath: string): boolean { - if (!isInside(repoRoot, absolutePath) || !existsSync(absolutePath)) return false; - try { - return isInside(repoRoot, realpathSync(absolutePath)) && statSync(absolutePath).isDirectory(); - } catch { - return false; - } -} - function validateExistingPathInsideRepoSync( repoRoot: string, absolutePath: string, @@ -1101,47 +742,11 @@ function requireSupportedSource(path: string): { ok: true } | { ok: false; refus return isSupportedSymbolSourcePath(path) ? { ok: true } : refused("unsupported_change", `Unsupported symbol edit language for ${path}`, path); } -function normalizeModulePath(path: string): string { - return path.replaceAll("\\", "/"); -} - -function isRepoResolvableSpecifier(specifier: string): boolean { - return specifier.length > 0 && !specifier.startsWith("/") && !specifier.includes("://"); -} - -function isRelativeSpecifier(specifier: string): boolean { - return specifier.startsWith("./") || specifier.startsWith("../"); -} - -function sourceExtension(path: string): string | undefined { - if (path.endsWith(".d.ts")) return ".d.ts"; - const match = /\.[^./]+$/u.exec(path); - return match?.[0]; -} - -function replaceImportExtension(path: string, extension: string): string { - if (path.endsWith(".d.ts")) return `${path.slice(0, -".d.ts".length)}${extension}`; - return path.replace(/\.[^./]+$/u, extension); -} - -function uniqueSorted(values: readonly string[]): string[] { - return [...new Set(values)].sort(); -} - -function unique(values: readonly string[]): readonly string[] { - return [...new Set(values)]; -} - function withExtension(filePath: string, extension: string): string { const current = extname(filePath); return current.length === 0 ? `${filePath}${extension}` : `${filePath.slice(0, -current.length)}${extension}`; } -function isInside(parentPath: string, childPath: string): boolean { - const relativePath = relative(resolve(parentPath), resolve(childPath)); - return relativePath === "" || (!relativePath.startsWith("..") && !isAbsolute(relativePath)); -} - function compareSourceMoves(left: SourceFileMove, right: SourceFileMove): number { return left.fromPath.localeCompare(right.fromPath) || left.toPath.localeCompare(right.toPath); } diff --git a/packages/edit/src/typescript-project/filesystem-discovery.ts b/packages/edit/src/typescript-project/filesystem-discovery.ts new file mode 100644 index 0000000..ab27941 --- /dev/null +++ b/packages/edit/src/typescript-project/filesystem-discovery.ts @@ -0,0 +1,30 @@ +import { readdirSync } from "node:fs"; +import { join, resolve } from "node:path"; +import { isSafeExistingFileInsideRepo } from "./path-policy.js"; + +export function discoverRepoFiles( + repoRoot: string, + excludedDirectories: ReadonlySet, + include: (path: string, name: string) => boolean +): string[] { + const files: string[] = []; + visit(repoRoot); + return files.sort(); + + function visit(directory: string): void { + const entries = readdirSync(directory, { withFileTypes: true }) + .sort((left, right) => left.name.localeCompare(right.name)); + for (const entry of entries) { + const path = join(directory, entry.name); + if (entry.isDirectory()) { + if (!excludedDirectories.has(entry.name)) visit(path); + } else if ( + entry.isFile() && + include(path, entry.name) && + isSafeExistingFileInsideRepo(repoRoot, path) + ) { + files.push(resolve(path)); + } + } + } +} diff --git a/packages/edit/src/typescript-project/import-resolution.ts b/packages/edit/src/typescript-project/import-resolution.ts new file mode 100644 index 0000000..7a51353 --- /dev/null +++ b/packages/edit/src/typescript-project/import-resolution.ts @@ -0,0 +1,232 @@ +import { readFileSync } from "node:fs"; +import { dirname, join, resolve } from "node:path"; +import { + isSafeExistingFileInsideRepo, + replaceImportExtension, + sourceExtension, + uniqueValues +} from "./path-policy.js"; +import { TypeScriptConfigService } from "./tsconfig.js"; +import type { TypeScriptProjectProfile } from "./types.js"; + +interface ImportResolverRequest { + repoRoot: string; + tsconfigPath: string | undefined; + profile: TypeScriptProjectProfile; +} + +interface ImportResolutionOptions { + baseUrl: string; + hasBaseUrl: boolean; + paths: Readonly>; +} + +export class TypeScriptImportResolver { + private readonly options: ImportResolutionOptions; + private readonly configService: TypeScriptConfigService; + + constructor(private readonly request: ImportResolverRequest) { + this.configService = new TypeScriptConfigService(request.profile); + this.options = this.readOptions(); + } + + resolve(filePath: string): readonly string[] { + return this.moduleSpecifiers(readFileSync(filePath, "utf8")) + .flatMap((specifier) => { + const resolved = this.resolveSpecifier(filePath, specifier); + return resolved === undefined ? [] : [resolved]; + }) + .sort(); + } + + private moduleSpecifiers(text: string): readonly string[] { + const specifiers = new Set(); + const staticPattern = /\b(?:import|export)\s+(?:type\s+)?(?:[^"'`;]*?\s+from\s+)?["']([^"']+)["']/gu; + const referencePattern = / + this.isRepoResolvable(specifier) + ).sort(); + } + + private collectMatches( + specifiers: Set, + text: string, + pattern: RegExp + ): void { + for (const match of text.matchAll(pattern)) { + if (match[1]) specifiers.add(match[1]); + } + } + + private resolveSpecifier( + fromPath: string, + specifier: string + ): string | undefined { + if (this.isRelative(specifier)) { + return this.resolveModulePath( + resolve(dirname(fromPath), specifier) + ); + } + const mapped = this.resolveMapped(specifier); + if (mapped !== undefined) return mapped; + return this.options.hasBaseUrl + ? this.resolveModulePath(resolve(this.options.baseUrl, specifier)) + : undefined; + } + + private resolveMapped(specifier: string): string | undefined { + for (const [pattern, targets] of this.sortedPathMappings()) { + const wildcard = this.matchPathPattern(pattern, specifier); + if (wildcard === undefined) continue; + for (const target of targets) { + const candidate = resolve( + this.options.baseUrl, + this.applyPathMappingTarget(target, wildcard) + ); + const resolved = this.resolveModulePath(candidate); + if (resolved !== undefined) return resolved; + } + } + return undefined; + } + + private resolveModulePath(basePath: string): string | undefined { + for (const candidate of this.modulePathCandidates(basePath)) { + if ( + this.request.profile.sourceExtensions.includes( + sourceExtension(candidate) ?? "" + ) && + isSafeExistingFileInsideRepo(this.request.repoRoot, candidate) + ) { + return resolve(candidate); + } + } + return undefined; + } + + private modulePathCandidates(basePath: string): readonly string[] { + const extension = sourceExtension(basePath); + if (extension === ".js" || extension === ".jsx") { + return this.javascriptImportCandidates(basePath, extension); + } + if (extension !== undefined) return [basePath]; + return uniqueValues([ + ...this.request.profile.extensionlessImportCandidates.map( + (candidate) => `${basePath}${candidate}` + ), + ...this.request.profile.extensionlessImportCandidates.map( + (candidate) => join(basePath, `index${candidate}`) + ) + ]); + } + + private javascriptImportCandidates( + basePath: string, + extension: ".js" | ".jsx" + ): readonly string[] { + const typeScriptCandidates = extension === ".jsx" + ? [replaceImportExtension(basePath, ".tsx"), replaceImportExtension(basePath, ".ts")] + : [replaceImportExtension(basePath, ".ts"), replaceImportExtension(basePath, ".tsx")]; + return uniqueValues([ + ...typeScriptCandidates, + replaceImportExtension(basePath, ".d.ts"), + basePath, + replaceImportExtension(basePath, extension === ".js" ? ".jsx" : ".js") + ]); + } + + private readOptions(): ImportResolutionOptions { + const configDirectory = this.request.tsconfigPath === undefined + ? this.request.repoRoot + : dirname(this.request.tsconfigPath); + const config = this.readConfig(); + const compilerOptions = config?.compilerOptions; + const configuredBaseUrl = compilerOptions?.baseUrl; + const hasBaseUrl = + typeof configuredBaseUrl === "string" && configuredBaseUrl.length > 0; + return { + baseUrl: hasBaseUrl + ? resolve(configDirectory, configuredBaseUrl) + : configDirectory, + hasBaseUrl, + paths: this.normalizePathMappings(compilerOptions?.paths) + }; + } + + private readConfig() { + if (this.request.tsconfigPath === undefined) return undefined; + try { + return this.configService.parse(this.request.tsconfigPath); + } catch (error) { + if (!this.request.profile.tolerateMalformedImportConfig) throw error; + return undefined; + } + } + + private normalizePathMappings( + paths: unknown + ): Readonly> { + if (paths === null || typeof paths !== "object" || Array.isArray(paths)) { + return {}; + } + const normalized: Record = {}; + for (const [pattern, targets] of Object.entries(paths)) { + if (Array.isArray(targets)) { + normalized[pattern] = targets.filter( + (target): target is string => typeof target === "string" + ); + } + } + return normalized; + } + + private sortedPathMappings(): readonly [string, readonly string[]][] { + return Object.entries(this.options.paths) + .filter( + (entry): entry is [string, readonly string[]] => entry[1].length > 0 + ) + .sort( + (left, right) => + this.pathPatternRank(right[0]) - this.pathPatternRank(left[0]) + ); + } + + private pathPatternRank(pattern: string): number { + const starIndex = pattern.indexOf("*"); + return starIndex === -1 ? pattern.length * 2 + 1 : pattern.length - 1; + } + + private matchPathPattern( + pattern: string, + specifier: string + ): string | undefined { + const starIndex = pattern.indexOf("*"); + if (starIndex === -1) return pattern === specifier ? "" : undefined; + const prefix = pattern.slice(0, starIndex); + const suffix = pattern.slice(starIndex + 1); + if (!specifier.startsWith(prefix) || !specifier.endsWith(suffix)) { + return undefined; + } + return specifier.slice(prefix.length, specifier.length - suffix.length); + } + + private applyPathMappingTarget(target: string, wildcard: string): string { + return target.includes("*") ? target.replaceAll("*", wildcard) : target; + } + + private isRepoResolvable(specifier: string): boolean { + return ( + specifier.length > 0 && + !specifier.startsWith("/") && + !specifier.includes("://") + ); + } + + private isRelative(specifier: string): boolean { + return specifier.startsWith("./") || specifier.startsWith("../"); + } +} diff --git a/packages/edit/src/typescript-project/index.ts b/packages/edit/src/typescript-project/index.ts new file mode 100644 index 0000000..94a5ec1 --- /dev/null +++ b/packages/edit/src/typescript-project/index.ts @@ -0,0 +1,4 @@ +export * from "./path-policy.js"; +export * from "./project-service.js"; +export * from "./source-discovery.js"; +export * from "./types.js"; diff --git a/packages/edit/src/typescript-project/path-policy.ts b/packages/edit/src/typescript-project/path-policy.ts new file mode 100644 index 0000000..44fb324 --- /dev/null +++ b/packages/edit/src/typescript-project/path-policy.ts @@ -0,0 +1,71 @@ +import { existsSync, realpathSync, statSync } from "node:fs"; +import { isAbsolute, relative, resolve } from "node:path"; + +export function isPathInside(parentPath: string, childPath: string): boolean { + const childRelativePath = relative(resolve(parentPath), resolve(childPath)); + return ( + childRelativePath === "" || + (!childRelativePath.startsWith("..") && !isAbsolute(childRelativePath)) + ); +} + +export function isSafeExistingFileInsideRepo( + repoRoot: string, + absolutePath: string +): boolean { + if (!isPathInside(repoRoot, absolutePath) || !existsSync(absolutePath)) { + return false; + } + try { + return ( + isPathInside(repoRoot, realpathSync(absolutePath)) && + statSync(absolutePath).isFile() + ); + } catch { + return false; + } +} + +export function isSafeExistingDirectoryInsideRepo( + repoRoot: string, + absolutePath: string +): boolean { + if (!isPathInside(repoRoot, absolutePath) || !existsSync(absolutePath)) { + return false; + } + try { + return ( + isPathInside(repoRoot, realpathSync(absolutePath)) && + statSync(absolutePath).isDirectory() + ); + } catch { + return false; + } +} + +export function normalizeModulePath(path: string): string { + return path.replaceAll("\\", "/"); +} + +export function sourceExtension(path: string): string | undefined { + if (path.endsWith(".d.ts")) return ".d.ts"; + return /\.[^./]+$/u.exec(path)?.[0]; +} + +export function replaceImportExtension( + path: string, + extension: string +): string { + if (path.endsWith(".d.ts")) { + return `${path.slice(0, -".d.ts".length)}${extension}`; + } + return path.replace(/\.[^./]+$/u, extension); +} + +export function uniqueValues(values: readonly T[]): readonly T[] { + return [...new Set(values)]; +} + +export function uniqueSortedValues(values: readonly string[]): string[] { + return [...new Set(values)].sort(); +} diff --git a/packages/edit/src/typescript-project/project-service.ts b/packages/edit/src/typescript-project/project-service.ts new file mode 100644 index 0000000..29402a2 --- /dev/null +++ b/packages/edit/src/typescript-project/project-service.ts @@ -0,0 +1,223 @@ +import { resolve } from "node:path"; +import { Project } from "ts-morph"; +import { TypeScriptSourceService } from "./source-discovery.js"; +import { TypeScriptConfigService } from "./tsconfig.js"; +import type { + TypeScriptProjectContext, + TypeScriptProjectOptions, + TypeScriptProjectProfile, + TypeScriptProjectScope +} from "./types.js"; + +const projectScopes = new WeakMap(); + +export class TypeScriptProjectService { + private readonly configService: TypeScriptConfigService; + private readonly sourceService: TypeScriptSourceService; + + constructor(private readonly profile: TypeScriptProjectProfile) { + this.configService = new TypeScriptConfigService(profile); + this.sourceService = new TypeScriptSourceService(profile); + } + + isSupportedSourcePath(path: string): boolean { + return this.sourceService.isSupported(path); + } + + listSourceFiles(repoRoot: string): string[] { + return this.sourceService.list(repoRoot); + } + + createProject( + repoRoot: string, + preferredRepoPath: string | undefined, + options: TypeScriptProjectOptions = {} + ): Project { + return this.createContext(repoRoot, preferredRepoPath, options).project; + } + + createContexts( + repoRoot: string, + preferredRepoPath: string | undefined, + options: TypeScriptProjectOptions = {} + ): TypeScriptProjectContext[] { + if (options.project !== undefined) { + return [this.createContext(repoRoot, preferredRepoPath, options)]; + } + const preferredConfig = this.preferredConfig( + repoRoot, + preferredRepoPath, + options + ); + const configPaths = this.contextConfigPaths(preferredConfig, repoRoot); + if (configPaths.length === 0) { + return [ + this.buildContext(repoRoot, preferredRepoPath, undefined, options) + ]; + } + return configPaths.map((configPath) => + this.buildContext(repoRoot, preferredRepoPath, configPath, options) + ); + } + + withSnapshot(context: TypeScriptProjectContext, run: () => T): T { + if ( + context.snapshotProject === undefined || + context.revertProject === undefined + ) { + return run(); + } + const snapshot = context.snapshotProject(context.project); + try { + return run(); + } finally { + context.revertProject(context.project, snapshot); + } + } + + private createContext( + repoRoot: string, + preferredRepoPath: string | undefined, + options: TypeScriptProjectOptions + ): TypeScriptProjectContext { + const scope = projectScope(options); + const configPath = this.preferredConfig( + repoRoot, + preferredRepoPath, + options + ); + if ( + options.project !== undefined && + this.canUseInjectedProject(options.project, scope) + ) { + this.expandInjectedProject( + repoRoot, + preferredRepoPath, + configPath, + options + ); + return { + project: options.project, + ...(configPath ? { tsconfigPath: configPath } : {}), + ...(options.snapshotProject + ? { snapshotProject: options.snapshotProject } + : {}), + ...(options.revertProject + ? { revertProject: options.revertProject } + : {}) + }; + } + return this.buildContext( + repoRoot, + preferredRepoPath, + configPath, + options + ); + } + + private buildContext( + repoRoot: string, + preferredRepoPath: string | undefined, + configPath: string | undefined, + options: TypeScriptProjectOptions + ): TypeScriptProjectContext { + const scope = projectScope(options); + const project = new Project({ + tsConfigFilePath: configPath, + skipAddingFilesFromTsConfig: true, + skipFileDependencyResolution: true, + compilerOptions: { allowJs: true, checkJs: false } + }); + const sourceFiles = + scope === "whole_repo" || preferredRepoPath === undefined + ? this.sourceService.list(repoRoot) + : this.sourceService.scoped({ + repoRoot, + tsconfigPath: configPath, + rootRepoPaths: [preferredRepoPath], + includeDependents: + options.includeDependents ?? + this.profile.defaultIncludeDependents + }); + addSourceFiles(project, sourceFiles); + projectScopes.set(project, scope); + return { + project, + ...(configPath ? { tsconfigPath: configPath } : {}) + }; + } + + private expandInjectedProject( + repoRoot: string, + preferredRepoPath: string | undefined, + configPath: string | undefined, + options: TypeScriptProjectOptions + ): void { + if ( + projectScope(options) !== "import_closure" || + preferredRepoPath === undefined || + options.project === undefined + ) { + return; + } + const sourceFiles = this.sourceService.scoped({ + repoRoot, + tsconfigPath: configPath, + rootRepoPaths: [preferredRepoPath], + includeDependents: + options.includeDependents ?? this.profile.defaultIncludeDependents + }); + addSourceFiles(options.project, sourceFiles); + } + + private preferredConfig( + repoRoot: string, + preferredRepoPath: string | undefined, + options: TypeScriptProjectOptions + ): string | undefined { + return this.configService.preferred( + repoRoot, + preferredRepoPath, + options.projectTsconfigPath + ); + } + + private contextConfigPaths( + preferredConfig: string | undefined, + repoRoot: string + ): string[] { + if (!this.profile.discoverAllConfigs) { + return preferredConfig === undefined ? [] : [preferredConfig]; + } + const ordered: string[] = []; + if (preferredConfig !== undefined) ordered.push(resolve(preferredConfig)); + for (const configPath of this.configService.collect(repoRoot)) { + if (!ordered.includes(configPath)) ordered.push(configPath); + } + return ordered; + } + + private canUseInjectedProject( + project: Project, + requiredScope: TypeScriptProjectScope + ): boolean { + return ( + requiredScope !== "whole_repo" || + projectScopes.get(project) === "whole_repo" + ); + } +} + +function addSourceFiles(project: Project, filePaths: readonly string[]): void { + for (const filePath of filePaths) { + if (project.getSourceFile(filePath) === undefined) { + project.addSourceFileAtPath(filePath); + } + } +} + +function projectScope(options: TypeScriptProjectOptions): TypeScriptProjectScope { + return options.projectScope === undefined + ? "import_closure" + : options.projectScope; +} diff --git a/packages/edit/src/typescript-project/source-discovery.ts b/packages/edit/src/typescript-project/source-discovery.ts new file mode 100644 index 0000000..a301852 --- /dev/null +++ b/packages/edit/src/typescript-project/source-discovery.ts @@ -0,0 +1,130 @@ +import { extname, resolve } from "node:path"; +import { discoverRepoFiles } from "./filesystem-discovery.js"; +import { TypeScriptImportResolver } from "./import-resolution.js"; +import { + isPathInside, + isSafeExistingDirectoryInsideRepo, + isSafeExistingFileInsideRepo, + uniqueSortedValues +} from "./path-policy.js"; +import type { TypeScriptProjectProfile } from "./types.js"; + +export interface SourceClosureRequest { + repoRoot: string; + tsconfigPath: string | undefined; + rootRepoPaths: readonly string[]; + includeDependents: boolean; +} + +export class TypeScriptSourceService { + constructor(private readonly profile: TypeScriptProjectProfile) {} + + isSupported(path: string): boolean { + return this.profile.sourceExtensions.includes(extname(path).toLowerCase()); + } + + list(repoRoot: string): string[] { + return discoverRepoFiles( + repoRoot, + this.profile.excludedDirectories, + (path) => this.isSupported(path) + ); + } + + scoped(request: SourceClosureRequest): string[] { + return new SourceClosure(this, this.profile, request).collect(); + } + + rootFiles(request: SourceClosureRequest): string[] { + const files: string[] = []; + for (const rootRepoPath of request.rootRepoPaths) { + const absolutePath = resolve(request.repoRoot, rootRepoPath); + if ( + this.isSupported(absolutePath) && + isSafeExistingFileInsideRepo(request.repoRoot, absolutePath) + ) { + files.push(resolve(absolutePath)); + } else if ( + this.profile.allowDirectoryRoots && + isSafeExistingDirectoryInsideRepo(request.repoRoot, absolutePath) + ) { + files.push( + ...this.list(request.repoRoot) + .filter((filePath) => isPathInside(absolutePath, filePath)) + ); + } + } + return uniqueSortedValues(files); + } + +} + +class SourceClosure { + private readonly resolver: TypeScriptImportResolver; + private readonly importsByFile = new Map(); + private readonly selected = new Set(); + private readonly reverseTargets: Set; + private readonly allSourceFiles: readonly string[]; + + constructor( + private readonly sourceService: TypeScriptSourceService, + profile: TypeScriptProjectProfile, + private readonly request: SourceClosureRequest + ) { + this.resolver = new TypeScriptImportResolver({ + repoRoot: request.repoRoot, + tsconfigPath: request.tsconfigPath, + profile + }); + const roots = sourceService.rootFiles(request); + this.reverseTargets = new Set(roots); + this.allSourceFiles = request.includeDependents + ? sourceService.list(request.repoRoot) + : []; + this.addForwardClosure(roots); + } + + collect(): string[] { + if (this.request.includeDependents) this.addDependents(); + return [...this.selected].sort(); + } + + private addDependents(): void { + let changed = true; + while (changed) { + changed = false; + for (const filePath of this.allSourceFiles) { + if (this.selected.has(filePath)) continue; + const importsSelected = this.imports(filePath) + .some((target) => this.reverseTargets.has(target)); + if (!importsSelected) continue; + const sizeBefore = this.selected.size; + this.addForwardClosure([filePath]); + this.reverseTargets.add(filePath); + if (this.selected.size !== sizeBefore) changed = true; + } + } + } + + private addForwardClosure(rootFiles: readonly string[]): void { + const pending = [...rootFiles].sort(); + for (let index = 0; index < pending.length; index += 1) { + const filePath = pending[index]; + if (this.selected.has(filePath)) continue; + this.selected.add(filePath); + for (const importedPath of this.imports(filePath)) { + if (!this.selected.has(importedPath) && !pending.includes(importedPath)) { + pending.push(importedPath); + } + } + } + } + + private imports(filePath: string): readonly string[] { + const cached = this.importsByFile.get(filePath); + if (cached !== undefined) return cached; + const imports = this.resolver.resolve(filePath); + this.importsByFile.set(filePath, imports); + return imports; + } +} diff --git a/packages/edit/src/typescript-project/tsconfig.ts b/packages/edit/src/typescript-project/tsconfig.ts new file mode 100644 index 0000000..02b2342 --- /dev/null +++ b/packages/edit/src/typescript-project/tsconfig.ts @@ -0,0 +1,197 @@ +import { readFileSync } from "node:fs"; +import { dirname, join, resolve } from "node:path"; +import { ts } from "ts-morph"; +import { discoverRepoFiles } from "./filesystem-discovery.js"; +import { + isPathInside, + isSafeExistingFileInsideRepo +} from "./path-policy.js"; +import type { TypeScriptProjectProfile } from "./types.js"; + +export interface TypeScriptConfigJson { + references?: readonly { path?: string }[]; + files?: readonly string[]; + include?: readonly string[]; + compilerOptions?: { + baseUrl?: unknown; + paths?: unknown; + }; +} + +export class TypeScriptConfigService { + constructor(private readonly profile: TypeScriptProjectProfile) {} + + preferred( + repoRoot: string, + preferredRepoPath: string | undefined, + explicitPath: string | undefined + ): string | undefined { + const explicit = this.resolveExplicit(repoRoot, explicitPath); + if (explicit !== undefined) return explicit; + const rootConfig = join(repoRoot, "tsconfig.json"); + if (this.profile.configMode === "root") { + return isSafeExistingFileInsideRepo(repoRoot, rootConfig) + ? resolve(rootConfig) + : undefined; + } + const preferredAbsolutePath = preferredRepoPath === undefined + ? undefined + : resolve(repoRoot, preferredRepoPath); + return this.resolveReferenceAware( + repoRoot, + rootConfig, + preferredAbsolutePath, + new Set() + ); + } + + collect(repoRoot: string): string[] { + const discovered = new Set(); + const queue = [ + join(repoRoot, "tsconfig.json"), + ...this.list(repoRoot) + ]; + for (let index = 0; index < queue.length; index += 1) { + const configPath = resolve(queue[index]); + if ( + discovered.has(configPath) || + !isSafeExistingFileInsideRepo(repoRoot, configPath) + ) { + continue; + } + discovered.add(configPath); + this.appendReferences(repoRoot, configPath, queue); + } + return [...discovered].sort(); + } + + parse(configPath: string): TypeScriptConfigJson { + const parsed = ts.parseConfigFileTextToJson( + configPath, + readFileSync(configPath, "utf8") + ); + if (parsed.error !== undefined) { + throw new Error( + ts.flattenDiagnosticMessageText(parsed.error.messageText, "\n") + ); + } + return parsed.config as TypeScriptConfigJson; + } + + private resolveExplicit( + repoRoot: string, + configPath: string | undefined + ): string | undefined { + if (configPath === undefined) return undefined; + const absolutePath = resolve(repoRoot, configPath); + return isSafeExistingFileInsideRepo(repoRoot, absolutePath) + ? absolutePath + : undefined; + } + + private resolveReferenceAware( + repoRoot: string, + configPath: string, + preferredAbsolutePath: string | undefined, + seen: Set + ): string | undefined { + if (!isSafeExistingFileInsideRepo(repoRoot, configPath)) return undefined; + const normalizedPath = resolve(configPath); + if (seen.has(normalizedPath)) return undefined; + seen.add(normalizedPath); + try { + const config = this.parse(configPath); + if (!this.isReferenceOnly(config)) return configPath; + const candidates = this.referencedPaths(repoRoot, configPath, config); + const selected = this.selectReference( + repoRoot, + candidates, + preferredAbsolutePath, + seen + ); + if (selected !== undefined) return selected; + if (candidates[0] !== undefined) return candidates[0]; + return configPath; + } catch { + return configPath; + } + } + + private selectReference( + repoRoot: string, + candidates: readonly string[], + preferredAbsolutePath: string | undefined, + seen: ReadonlySet + ): string | undefined { + if (preferredAbsolutePath === undefined) return undefined; + return candidates + .flatMap((candidate) => { + const resolved = this.resolveReferenceAware( + repoRoot, + candidate, + preferredAbsolutePath, + new Set(seen) + ); + return resolved === undefined ? [] : [resolved]; + }) + .filter((candidate, index, values) => values.indexOf(candidate) === index) + .filter((candidate) => + isPathInside(dirname(candidate), preferredAbsolutePath) + ) + .sort( + (left, right) => + dirname(right).length - dirname(left).length || + left.localeCompare(right) + )[0]; + } + + private isReferenceOnly(config: TypeScriptConfigJson): boolean { + return ( + Array.isArray(config.references) && + config.references.length > 0 && + Array.isArray(config.files) && + config.files.length === 0 && + config.include === undefined + ); + } + + private appendReferences( + repoRoot: string, + configPath: string, + queue: string[] + ): void { + try { + const config = this.parse(configPath); + queue.push(...this.referencedPaths(repoRoot, configPath, config)); + } catch { + // Malformed auxiliary configs do not replace the preferred project config. + } + } + + private referencedPaths( + repoRoot: string, + configPath: string, + config: TypeScriptConfigJson + ): string[] { + const candidates: string[] = []; + const references = config.references; + for (const reference of references === undefined ? [] : references) { + if (!reference.path) continue; + const candidate = reference.path.endsWith(".json") + ? resolve(dirname(configPath), reference.path) + : resolve(dirname(configPath), reference.path, "tsconfig.json"); + if (isSafeExistingFileInsideRepo(repoRoot, candidate)) { + candidates.push(candidate); + } + } + return candidates; + } + + private list(repoRoot: string): string[] { + return discoverRepoFiles( + repoRoot, + this.profile.excludedDirectories, + (_path, name) => name === "tsconfig.json" + ); + } +} diff --git a/packages/edit/src/typescript-project/types.ts b/packages/edit/src/typescript-project/types.ts new file mode 100644 index 0000000..b571146 --- /dev/null +++ b/packages/edit/src/typescript-project/types.ts @@ -0,0 +1,47 @@ +import type { Project } from "ts-morph"; + +export type TypeScriptProjectScope = "import_closure" | "whole_repo"; + +export interface TypeScriptProjectProfile { + sourceExtensions: readonly string[]; + extensionlessImportCandidates: readonly string[]; + excludedDirectories: ReadonlySet; + allowDirectoryRoots: boolean; + configMode: "root" | "project_references"; + discoverAllConfigs: boolean; + tolerateMalformedImportConfig: boolean; + defaultIncludeDependents: boolean; +} + +export interface TypeScriptProjectOptions { + project?: Project; + projectScope?: TypeScriptProjectScope; + projectTsconfigPath?: string; + includeDependents?: boolean; + snapshotProject?: (project: Project) => unknown; + revertProject?: (project: Project, snapshot: unknown) => void; +} + +export interface TypeScriptProjectContext { + project: Project; + tsconfigPath?: string; + snapshotProject?: (project: Project) => unknown; + revertProject?: (project: Project, snapshot: unknown) => void; +} + +export const defaultTypeScriptProjectExcludedDirectories = new Set([ + ".agents", + ".claude", + ".codex", + ".gemini", + ".git", + ".lattice", + ".opencode", + ".opcore", + ".pnpm", + "build", + "dist", + "node_modules", + "target", + "vendor" +]); diff --git a/packages/fixtures/source-extraction/wave1/wave1.expected.json b/packages/fixtures/source-extraction/wave1/wave1.expected.json index 534ff8e..9d2bf1c 100644 --- a/packages/fixtures/source-extraction/wave1/wave1.expected.json +++ b/packages/fixtures/source-extraction/wave1/wave1.expected.json @@ -362,6 +362,21 @@ "class:src/models.ts#GreetingModel", "test:src/__tests__/greeting.test.ts#renders greeting cards" ], + [ + "TESTED_BY", + "file:src/components/GreetingCard.tsx", + "file:src/__tests__/greeting.test.ts" + ], + [ + "TESTED_BY", + "file:src/math.js", + "file:src/__tests__/greeting.test.ts" + ], + [ + "TESTED_BY", + "file:src/models.ts", + "file:src/__tests__/greeting.test.ts" + ], [ "TESTED_BY", "function:src/components/GreetingCard.tsx#GreetingCard", diff --git a/packages/graph/README.md b/packages/graph/README.md index 91ceead..87b0487 100644 --- a/packages/graph/README.md +++ b/packages/graph/README.md @@ -4,4 +4,4 @@ Opcore graph provider package for repository graph extraction, query, search, an The package also owns canonical Python repo-import analysis. `analyzePythonImports` accepts only supplied `.py`/`.pyi` after-state files, materializes them in an isolated temporary repository, runs the installed graph-core build/query path, returns sorted directed `IMPORTS_FROM` file edges, and always removes temporary state. It never reads or writes the target worktree. -`createEphemeralGraphSnapshot` materializes a complete, bounded validation-visible source universe under an isolated root, builds graph-core once, binds query metadata to the logical target repository, and provides idempotent recursive disposal. Incomplete/truncated listings and file, depth, byte, build, or query failures are loud; target sources and persistent graph artifacts are never mutated. +`createEphemeralGraphSnapshot` materializes a complete, bounded validation-visible source universe plus root `tsconfig.json` under an isolated root, builds graph-core once, binds query metadata to the logical target repository, and provides idempotent recursive disposal. Root TypeScript aliases therefore resolve against the exact before/after state. Incomplete/truncated listings and file, depth, byte, build, or query failures are loud; target sources and persistent graph artifacts are never mutated. diff --git a/packages/graph/src/ephemeral-snapshot.ts b/packages/graph/src/ephemeral-snapshot.ts index 0e284f5..dcea7a5 100644 --- a/packages/graph/src/ephemeral-snapshot.ts +++ b/packages/graph/src/ephemeral-snapshot.ts @@ -76,9 +76,9 @@ export async function createEphemeralGraphSnapshotWithOperations( ): Promise { const paths = normalizeUniverse(options.sourceUniverse); const limits = normalizeLimits(options.limits); - const sourcePaths = paths.filter(isSupportedGraphSourcePath); - enforcePathLimits(sourcePaths, limits); - const materialized = await materializeSnapshotSources(sourcePaths, options.readFile, limits); + const materializationPaths = paths.filter(isGraphMaterializationPath); + enforcePathLimits(materializationPaths, limits); + const materialized = await materializeSnapshotSources(materializationPaths, options.readFile, limits); let disposed = false; try { const build = operations.build(materialized.repo); @@ -187,8 +187,8 @@ function writeSourceFile(repoRoot: string, path: string, content: string): void writeFileSync(absolutePath, content); } -function isSupportedGraphSourcePath(path: string): boolean { - return /\.(?:tsx?|[cm]ts|jsx?|pyi?|rs)$/u.test(path); +function isGraphMaterializationPath(path: string): boolean { + return path === "tsconfig.json" || /\.(?:tsx?|[cm]ts|jsx?|pyi?|rs)$/u.test(path); } function bindStatus(status: GraphProviderStatus, mode: GraphProviderMode, logicalRepo: RepoIdentity): GraphProviderStatus { diff --git a/packages/opcore-graph-core-linux-x64/metadata.json b/packages/opcore-graph-core-linux-x64/metadata.json index fb3e3eb..68ec61c 100644 --- a/packages/opcore-graph-core-linux-x64/metadata.json +++ b/packages/opcore-graph-core-linux-x64/metadata.json @@ -4,6 +4,6 @@ "targetPlatform": "linux-x64", "binaryPath": "opcore-graph-core", "checksumPath": "opcore-graph-core.sha256", - "checksumSha256": "c3a771435a7a8172a9e5f0bb20b0d111453efc70901e95308094664f77e7bdf1", + "checksumSha256": "70402bf42a09c01ea01f72199ee1985684a217da940cd1454bd4614ba63b12cf", "buildProfile": "release" } diff --git a/packages/opcore-graph-core-linux-x64/opcore-graph-core b/packages/opcore-graph-core-linux-x64/opcore-graph-core index d347eb9a8c712f325345c7c7b407e0b652aba878..76df93b02820cbc2607101067743053efe1c2f2b 100755 GIT binary patch delta 2929974 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%0DPc(m&2`ac>C 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

{PHOdbsHAUX*mj}B8DC(Z6xtA^BK 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�Wf?Ax> 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�=PaDO_>z$ItG?nvB$ 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

c;EWq@qfPVZ*M4Cn+qN7bC%dGb4PjVD_ zofUD)jSR{-EBqBBQLnS2NLiarC(eo-oNxvdyV#^Sx?wr+Br;;8}pLW8v@-ixRD>J76xQsH^*l{PM=ch8Gf z8HJzA3BMDwqj11M&9RgoDJQXmejPcG1l|I0Hc(osSmdy!>PE{iiU#%j;x(tdu{j6f z5x4C0?+vBLrK#U--iM;t8HCQZp4is#g7DNSk(;U2MG-G*XIH=xg8)_bWy`@&mK#3W z2(8tu1p#cPU7P9JMO*_N;7Y-lu&d!IYIg~{mXEwbBQD__(BDhxg-fDCW(kf8VpoAK zyL=piTyyXx8(4BoY*-}ctf(29L5j^Z>=h8j&U>~DlQnfA-=A?v;)NyD@@Mga?mB(( zvj}r00MPz^Y0)&rHzgu^!4gO3ya2W0$Urm30Q#9^FbON|^QqG>Vq#KM73g2k2L?TZ zCZg|ZBg0X`K4AS~Iraiw{RL9>s-9dfW3SL)in%PhHN3bO6YA;a#=0rk7xA0r(wjA# zffSo*KOiLkB&ScUqnECNH`BJzmzPBo&yI_#cBNo0O!-T(pQ`aW%)~5OXc67S$@g(xHD*Oe(H3}k;iy%c2hGAYLr(I( zPlv3>kY1)tw<{{tWw72A1@-0|F0c0{6s zB)Q24bXmTa#t<_f)USCGU*cm%S44Q$2#tklOoVkMXG4rOA<>d;)YM=fax7!f=MJpg zkco1*yI^Vb4jubdB-h)ALxLLp*%(LD+90yb3Sdd2pi0q0Iq(uaSt$}yYUiSC9!8?d zhS}Ru3I8k3N&jSW5F_P`ohMlkIBGfaC8&lD0Tdd31t@C2M7yqtUP`z5=COyPciuyUfdRNtmTRxU!FU5H zvN@27Dy_0CYP#fiA;|L@s{cGzK99O4QawMyhHcjG!-S%lwDFqA!^Oq-uL)Jb@kjMH zF$nxc=N`C>eh!xVkB3T-I_!bXN>7 z(AFCw&HLwu)&0u?(>4k84$EDH$egaZ2|1QiveO_SYoo>!< zQ+7@3?}OVy{}5wz&iKTRjq-=70kry05#Qo?i!=EAzy=x5&nX}8X5EQ=ySK)@Zdue1 ztb2mRuMA>i&q?C`$Z@q%-M?T(!smiB$GDwk2dq=6>tDjZC_t8|u~kc2qo3#GJGD)| zX~mH~4PRxK%HfO??(K3$z(aR5XW9_T2<8W{hI4QgE)>}6jwIao&K1sKc+IWcheyi~ zIGb9D5UQ-RehugLNmR=F!|dc^B3xis=w4L!rU;AP=8YyMqe)CZUo2urqT!H)`_7@R zH-%r(mFMkV=z}U{8OA7nvQt#EF;R?;8zc9jRW;^y_?=c)Ojy<4GD}c3rK!T?)rCG!)h^F&JeUlfo5IN|Aqy2EJai(ey%_DMA8h z450kWBCzPLI#xW1HoZe$w?#n5M2pwEay@wykSK*-m8xY^Wp%>jP;P_K6I?}6DaN2%~BS^I2v9}0qDFY|IB9kn&| zrBk;>7yTD=^65rNgb!uj5ldX4AWqgg6cODxi2lAK{FMCn$Z!{~1CBk#-o=*IQfhw} zI|Qzq=*7F(e~5|cT^tB3nMtSaVvBUcBARzksA&g$?SjrjYPjXkJNIps9dM#}rtHkB zvxSbk{j^Dz%`$*)+{35ov(X%$WI6rGUEFOZng4-n{0qKZ^^bT$Sv!^X{v&$%zCD9I z9SllUD}ak9QG@$pwCP72bgmvNq_}UJL2K`eex4ntSGg?kFXTpAC3ObH*l?n?y$g-B ziB3x9blPeYtCU8WANE&gEBN;11w|dAn@er=YK-6YXP`K~7{2UM$^BvJNY5j$&j-l+ zuRW%(ak@7OfEKK%c+`4;J7@j?cUs#_70+tisk$D(!IoTt;f)GT0~Th>^()MlR)DUK zD`}fN*G!dKuzkW~<3I&iTHGW2TYd02@qh80G@~gmPLY$d!(APdGcE3u`!dhWTDSfh zx(#{X$G|M_+c3q0s$7wUGp3;ML#oobkO8VU>0O&b%WJ72iT(c#>H$#4mVXPx7fNtT z!<#Wh278nvlgQ|TPnF%ALzyn>BxUmi`p`w~<$L>1b15Q5yTf$NvTO{6xvHa0muCD+ zMD|anH(b>op8Y3PiHO6i)wh|PPy1p#aOZ)W8l&&=j~n%OQ#eVSc8)CPVRCPHBDu`8n4!nthtGHAYs8arY&^8QO;=5czp z!2EL;65@znNW=N%uSjWQGfjI^%FM%x)O@^s^DqyMnrCi|22K{AGZ$HW5}%~@b=Bs| zte!NhuG-jt%mgVq(?>WAXr7Fo(f`~}6Fxz1#MG^)dY)>CvrQs&Ijwz7CF zqRF0Wys>1w#JqU8Lnt>P^*{FsC5@LmkK3(#v`}KlM4nPO5{C`o7pJ`QgcQtI+B*bO zi)sYCAO$n_wQlZC+HRB1nSj1KPC}~>L2oRR(7s84&MC*vPF%9!E(N*WQ)D!%p~|4S zlw`#B1CpNATKB~~?eCVk+TX3iX`NAx56P1is6&Q2_}FVi4K4kj+;kczPg^uy^XR5S zV_t#yNXo1j8LOhI!KL(+ml~t`d!Bd-xC4b7o8867f8DHTm3Epa`@t-j=(}~He$e1>U zH)w|%UEym%Up=(c0b{g%ufKb%fy%9!8t2}&b8z}p?Y;hJ?QiVUG}}jwRc1e{DRtHm ziuQ$47mU*EzHtXr%j^4z12v^CeDYsP-F1{yYNc{G{o$(C$5D(IOlk||(r@&IN$sLP zHQcViE&ZUtJz}@*^i!uRx+aub4+`9XR@YM-_`ft13S6(+;!kA-{&f;9@K9q~cSYuZ zX>dBHS8MRw*B;j3sb`SV)nJ9;ntgeL6(b(&xTsE&Uo;u(OStzb#rB zhDbfi?(5K_|GAI+`e4#|s{XBCf1EJAXw;0m;pgTYc^lBoAqSY&qW06z)!089mfucF z!*z%JwdbyNMD;%&NLPZ?R;_<6_?M-BZD6(eZaElb9@;Oui$!wgLCsyy*j}J*hwVKO z=WwUjtB0xqVaQO&+y# zbk`8A&ghM=bEru^r*68`7^8p#hg{o)sEw4xH|d!WHAR^{kv4^>gOpx<$QY`2X)tq; zBU;lPdV*Pcf6UUyA(5Su?5f3rCf#XnsM^*Ugy9rgs9LW<=Znql$8A za6Zv5-)V_4$+yj3JV@EDac(C5m-}+AE#0B}$06J8ap`k^IXWS7`0xOQaC)R#-`EqF zcucB$grz;dKdlW^+Z*~yK|h)ISV1rDE4}!EDK9@x()VxxNm{_s@FuXIGYK;iZL1+a z!4}T!BW>X~4IMgw=__dJHd(WU-(|4$zliUtF>ss)JgN#5YBzbFB;Z!{$!-iJZRQZf9|bW&(YE9 zVDE(}P&0J>&C_%`S{_6$3ST#cY^&DUj zV#@>?DN1=D$xu4R9%AEUc|zA_D(^;(8o+ny)Qz5Opf<)P@-+?Afq`8mq}CJ;JDmeL zxn;jvo*YLZacZNk!2@b41_;G{;ob6`lCN_6A#6^T4tem>FrN+J?JBbdfbH@NNqp50 zCv$A>PFFOPd)pGynpVW&w)!@A=-nhWfh=)qeBBRO%n(yGBHI5gg~zKQelg?z)%Ht+ z+fvrYNhXijY{U}~7P)Nsbq^KBt1)R$1K8dj+y-tnx8a3hT4a>FN$w1pH4y-R9m zon~D!%vwp`#;eh~Kj?P68m_GBLP0`JRJ^-Vu2AdiJ9NpXnY~;2kY{$XoAGd-R!1>f zfvX;d(sH3j8lRt_rJj?iTZYm%LXGWT^0$)Ze#kJ(gLw-wyM4hHYjR_Lp8c78#u16j zmZdo5f%IU0eiw%p@GP4Zd5E74rH7>mYn>}MbGZ+t9;#|Kw(gA4p<<|_$)$Az%~jQr z#&KP0k~n(^*;F*kJf0#F)UeL+eg4&vZd{;zfH^z291DQh1c~l-9K+J~LgG`WJfGsd zkYDDq8N?M1I+Q;KnF8kYyL5eQ|vll2Qwh&_UN^YdLi(`^LG+XC>O4^@K7ehezuVEi#%iV?Z@tNrVAaLFexe;I zYJ!Vl7UEb=WKB`y-TC`f)*qoLH2Al~2&$i=CM!KVQQs6buHH6$C9o#7=W{{rWE7x~ zCTcxhDy242yC~Z#X>JpBZ1QMr$4pQRhh5vULs-W3LqztWHf`UHwxLRHy|FeVbE=vO z%QPTWU8dyzM*pO$zq)YP!1^^^O;ZESOYWQoR|lB$tXF>rf9;ImhMxmQ*7;!rDL!3I zQ*Yna^0~#9ftB}d*2^*_<3NyiBPOxN_SH7;1A|+ufwt-c80tRD*HIbs5s!^$?))@4|D?1~#I<_K>=-pOe z$1v6{Ahin)4=yY_+o-6i>R0p`gS=)3VUx#*%N<3k^vqD|3~FV-^DYY;C}a)9=X9Zu zRtu7FxgF?`6=8i3d2$q5#lRdUU1~4IIW|*ME;R>9Mer>g#jp|!Q$EqM^(!!P8Q}ii zpdhBU9m>_H=L56{cC418CT%s|vG{PT041!l(wiuZa#k0uyu6^NlN}K^MxqIGpz-4e zUTK@p7M;cJaJ*>gNP{fzB~$yY1;(z5rGOrE%(ZbQ}mbR9`)uErP|WucG8 z;_IRs2AX*=J+P=Y5x#*R<5%rP+ydjKLikI6(V35aS*r{MJMi4@A6Z(tQiNbZ=$RvF2*7k4vWX=>k$6 z)8a?%j@Z{y?v5BR`PoOiby(ux2ad^OR_ozOmh9rmiMHo~pWvu{+(LSFK2Q;ENuQmX zSGTA?J8CUkFrj`p-G`%$+VRHGz1ev0+N7by!K{aN4vc=%t@>&-E$FsHR-vv-d91sd z*FqlB8Su=bn}zv0H%oe}>3yZQ+8An~d8_<{c6H5=?ON&oV7nswX%6UHk(3i}(@>s+NklK%VGE(e%styw6wJvVOrmV2SPZlxmDXVqC3*j)@p28VpDj5zyU6% zkI**p!>7RfvPn*PFKv?oukAa?ghq~pdgC(n;jPrWURfLJN>7)GLxZk4(UYy!pv-#Q z>2}}oBc=yS;8?508YB!>Y;H67rM!Y}!xb-ZTkVuNHh9G3v?X1~G!t!TgDG1qJ~`7? zjSOhOD52t96AvxLAj)PMlt-D!Ei%kni~QU&d_KHvtPxwY0eEi`>scq2HE*}s4IOQ{Mtj>KCh{d+Xs1RiA2uQP_G;gNosA(zhYm4?mH7@D zEibpC=f0y?7p6cYrbfv8t!NZ6XJ<(oWT(tT=doNgpkeklS`K=-&w+@fvp$%a! zaf~_Rs)=9y!Bs;Y-pzNwx|>^vcrA}r)&=&LntXVU)3Buh_nkO+aH2cV;XC%W6tqN( z^%g5(XiZEO2+pg-_sb7Fysc~lt;klRy_cY67)vdk*Rj!D4g_@ccyfps$>|QJbVvdk zEeZ@PZF%Qi*kqK^%Q8JvC?ZEqFxIu!1#dG>HDXqWoQHv%n^(sjETg>+Db@^(Y&*g+j0ys)9PH6b3Ft(hn{ZOMIE zOFPqwj<{CD+Ch!OXY_nKs?QlK+jA9#X2=uv!3-S45IK53cPH{%6zfaZKTD~VKGAX< z7cEo<$eO&Pnb$WRis z*?(?lHOsg}f~)JIAIpKf1Ucq(8~r#AR9o$&8ech8ftnq8^bGH(NSm=n_lc1ne`>@>#l~GX2D@(8=C}4w}&j!)9KIdYPc&K z7waG$?Ou~qj~e$-+caMrTQwf+00R(SsSD$c>yH6=QMS}{K1MTs^WlN8e&lHs)efO8 zJ+R6c8B5K3s<}ZP0Mj-P#s$df-$Vyp7FyL4p_FCtw;rNv)Q0ACs;3&IBph1O3o9_k zE>K7>b(qpDno4@9t(BW*`mC4Qu;t=t;FhtA?a)V_mak1|hIohY262+(Cc%=X)#PVW7%?Y|RDq{i9 z9$22uG&fCdE>;rSP(U9w-SciFPsjh(jwTk`TJ{06&Gc6) zE$E|0ge^qhSUcOZm`onq7=))yA7#+7KI+rT`=L9*I<&}aQHGJPFRzx7l5 zD!!4_p+8J!yKtJ?UyUu=9U+gZEeA>1T*qGK&bS9z0^3Lcva@6h+j$6E0c5z)hsPC9 zY;@tw0!->MB{Ps-#fVbCTVLQgY1v5})8ua(*4Q+VO<@C6S7n$h?eD7wo5Gv@tD!BD zsqX+au4Bm!#eKhF7Cw_FDKT0;!}etaA)0g5M$4U7fkL?HDZjKE03MtW(m z+9CGiwc3PdE*vCFt%cglI)0ft`@Rk6GmHO03}XX6ub}!CEra=xTST(U5s(0s&D1Ce zJv0aDHDe50!;#nn`Rr0jKenuOfILtKS`A*U-K<}6pK7s^tde`ELHb*EW#lNy-S=V0 zaa4e%p3ldv!(XZxqSg;t#&}r|?2Oz8oIBZwBgPj%M~A3QW!%K!J~IMF=(34A3{?|# zZRy#eYIw8_eO8m?abb2JspL*SVA|M(eI$BwC_*G#Y5P#Mz0zV2_lv6QM2W-H9Hq-Y zw0@XcKlC*wgUtiTfRl^cV)^jqNf3!0k2C1%Ftwr5`{Qj9!&QsUxx^11k0C21&~G@N zV3DzX$S`Zqb?hU;ey?Ay)3d|W;P^?0Ps119B0ZIy@(FI%m;YdIPLr9MCZK5|^&F`- zjBU@6E6xWEmh_J>;ZLbvhd){yiEU~lB6E)38>#jlbZ9rnLF}Ge9!ExItw2f$QmhIy zSM%wl6f`SFPD6W&6q5CDEx9h49n9H2j%&Vngy}@XMj_b!tuM_RrG|!Fl0tx(8gr>S zruMoo1mrVXG$|#ns7E6_H$*;8XPKbE|GK%XBY>Gb8-Yvnq2R5CV~3Tq*~+0#rp|6t z45d666bKfK4WmYlfruJ=@5UZX zELdZCY_UdzvG!*pKgs{_!Si zT@Cff6M?D~?l3X^sPGcdcU zbs?-fCXM@qd}qu|fmm@-7Lfe;k%S)cq1Qy}{(I=p36FxnNfb8{rn7 z_?j!dSI4WYi0MTy!>t{^zENb?zdL*2ud5w?Y{OJ^k`|=qIq&g0^IV$y?jo zJX~1a5m{3IzKtez%u-|9A_l^)sz4L2f>rMZWirq~yWm)@seS>j9l;A7>LVxCc>K@> z)f3(wEO#vq-F(4_?i|xn?0V{s>fXprG?+ejYz>B8Evx=<6`J2y&J67je9U9p0R2$M z74`9veY*VUiZ(JS+chis^nf9(wdc&e;FKAt;B$;S^0-rWlbujOiZQ6{bYUM0)OF= zCON`x&A|8oK?_`{Qh&L8!e^x%eT9x?d*d(}h6epBba!#qIE2CrF>;4qg1nwZ*Qk)} zA}U`4E1MV*m9=|8m?CmT>&Zu3I z8_mbDNb2Pbb93zwL5;;$m@3GHL7PkOgxNjogXX1*cNt)-Uk`30RED4QgG zX>uJT8{D6&hBdDpVxm)H6N4#xkQ|`fQ0g=Kb3C}r(6Srih7~yQAC;oxz;aF0l5x{0 zl68NT{ES|i6oU(tV&)U6>0l&(gXGm;fq8!|J$f*07%ca6`48&gxS&KAzcxflM|ISE zh}_UP45_w2?qZPOON6Qp;xT$iN&0Ds+(-ITqS!CwCRM+cR7NcZa_;v)bSzMbJ(Kw( zW2OE^O5bPj_ss$DK1Y7Xsre&Oc4}sT{7z;X-=hR{nls*DDE-ulktym$T#}QS64n|j zc_T&K4ufEM5xh#ajq+YruORM+(RFeXVflS%-dclKI3Rf{{-VDLpJxygXcVddzJ6=B zIAo(C^DPbq_vkF>MwDRn-?&q#Iu0-~fdwFdo5w&7-V!)8N{gu`O?IN@>u?=!3FAp7O01s%&$53fa_?5%#J-N)s&wYC|*{yvbmQo(&WsSd)KL!Ri4Qk(a>~ms{iM z5Ns-~OQ;baI>Ek&L|js!M+mJfP!^jb&=29L<_oQqww2XdM~Y99Z|U()f@}EqCPT#Y zcM9s`vTFG^?!oLAK`JcEfzY=;N!6-3*;yvw<2t_S@4f5d;1|u*ikDkdGESd8qV-R( zB>3W)Y9I3!gQwfC&s6(NLPtGa4sKik?^P61R>AuSPXzc|)wGKyW?Xj`GasC-`r&wT z|3{urWTS4+^=o9Ejt$dI^Kk#0+(>uksb-E0)f^S*pGNU|Z+0)ZqU;=}$w1jNrl>+1yw`cj@1^tv~b!ldrpt3$B(B zaLHZ%eTR{)3_Jv*wvYrT{qgK*yS&7F4sqn@M}1PD1TJ2UlOyTJkeD(-KMjCe8mRiV zx$p~yx|l!dgCwj@-7uf(MM{Mv|IjD7u=T-R{Mk&D9rEz?AE`WeWp&{idyuY{j1CCx zgv7V({tp7CGh7+K;X59jqDgC1v;F*V3{kpAs%K!{Yy#X8j z&pVuE2~JjX#m>yBH$<`OyW)wiy7z8_LAUPmHQN1gLKEq4SimnSlK-drB?Y+rovSWI zl1FA}#oDK~xtGXKu_6q^_#2vGET48@kCKl$uh!E1zi9nSSxzaLdo*Wg(RD)}Hv zaeB-(Ur(*38vJ!dPfJjT2T{R+kJT1X>2qe)4@4a8n?|;GjdWY2j+SQk9+Wv8E53g` zQX9~?;j(|3mEE<aWclQn~t-@@j$E?>;ZF0e%Xa=Pp#lV!}5B92aQGb}anI;AGWj z<7vARZ%RkkiaO6fb1APVNgw|7L2j|YB2cQ0HS9Lh;7*H6Z&XE_R^ZXsPq+0!F5UVk z$;W*$K$uDEJQIC=CN|YYjNSGtf=qzhdOE4Hp22W^CJ#E}5aFXCcKn{bMt`18N|WyA z(r0I5eRVu8^TlXcWA1B4*x8cjmqSfvtN>egWxr80p8<#&`?h86x_~~`eDEw*K-7DQ zA>IZ1g1}WXxXxUZOU`GLqTGr!9zRD>>RA{Xzg0tb1%i;{la>NTMDj2fd@MxqGk+8X zLz~_(bPV8FlzX`>N-_k)0yLh*L@8!MZxEoH)6q%Z`y-dU?;Ct7pZ-H7FdhW#)ukB9 zt=9Bs^%%cO1UT-!-n7JM4Ldp4L<671#YrIRKSnlrx!X_MabGyRE#-}ogQ{M&h@x;@ zjFs{!G}>d+2)4rB6iY5;+>Y|s&Bx2){V_i7UjwVC1(+9QlheTHVBJvk!H;Sh_2Po8 zL=&=6Qo&}=J1D8aZQTwg)SfU(ba@KVeL&`fmawwu+$2Q#69 zVpoZ6MOP^zG-<;>EY=(_l+ilbI5GimTu6&R&yjCLs%1R07=Ms;`vtlc4YTTv&T5oIjdv@t5yXJwM&dBL|!;q8lWA#2zS1}*%9<^(I2W`2&+#&$ zNsPgUV{Kug*ribw^l#kL`57ORHfNSlhoR@U#4zM~`^ZJ#HHmJ?rj3)%=T09VIYE&b@>JUKP6I@T>vXjn|;#uIE_{0H9=>Vlp^ zqLW>dtLoO?%oS0oD$vd1g9of?)sI15pnG&6K1^+$FNZpPO;_j3^~*fH!DbwaFa2Y1 zlk>N-e@O}7glJv&d2<7|({$(5qY!D}?+TiM|BfU1{?j>U%Fy!){RV=@yd zXk%iXb9f_u56Hw#J%4CtY1RVSXtG|1=BQg*I|r#A2d-I*0*ajmM3d(#!cE>O3r&an zfuZzO_v9D};j_HUMjPV302LvuW`Gy`!7E?fEU9`>pK5o;TGF8eH@oa%ik~5l(F)oG z^PIH_%NX1Q{RMZ5MeXU<{n&sHUS1P~?e|I+{3(X<&gL&Kv59X2^xf5=tb%zwqh^9d zFU38KrRRcRgWC#oT z_D#z+kwmpu8@4Klf8wcB32)MZF0^F_o z&f?h)PpI(r(u&&b@0q)yPG}b+s>KrIEcmIH2@gFFnjAI4s&^68q-&J8Sk9>S{S_e; zLL#VjzAE}@t;o$ov1no03#qL7*`S51p2c(GRr+DETqDF}N9_&_th?VZx4=BRDA%g5 zCIDlulJgR|X2iwIHXinyI-iJn?^E;e#@rd51SM?ny+S>f$SvJl0o>*zo?K!G(}iEo zrKd~ehRUT&AT&w!1czS`f{K3Nu~=PsY97*vsT~78u3f%NeU{2KbWe8Iriv#GA-V>a zRL{>Z0aP3zMREZ%+1AfE}obYohlbyO)C0n>xE_5T%;R&E|+E(%F}g^&*oBYg=mBB3|<o<8{rEd~BsGr-yR#&P6O#|H=&uc8>XRM<6R-;7YV&6*ky>{0{8?E!XJED!IMx`boHVtL2n(i%trwo7CEFG6(-ox9S>w zuxCvU*0llD{?*VV2`}c=IIsKriCn6&My}+)_XOkZYsHl%L+b=B_fMiDZ@XGH=q3Vg z`WiX4Tq6NDw6jXTvw8JRy0u2msdD`nZJL^qoADXXLf_JJ7xFFbDK0QtUuG%4?<|S# zW_}6E`zNXV$b3!-a1lM21yKe?-y^!$K}?F@9FEt=3_J+v?`EZN))jnKMwVp|dQ1j+ z1ni<1+na-q2LpG7Q&OK|+r}7lxur|c=yfqZ&S#DzcLsbHZl~8KBSq=#{9V*5j!@s4r zD9vO6ngCF~f}RI zxCM2}#E9BZ)EyhQEw#mnmkidJp&laLR+Q7l0}i13TxssWn7C&axLEQyNbR@E>6t?S zq;;IQv3b(YLN@rOUxmT_%nfRa+MR9Q048AAo6T}}FUGz+EIbH8*$3(FR=HO6v;E*E zCW`8K70#duAjHzoGqFw&zzg354&+ksi@0POzfF$Rb=jXw^}aJy($&GsWuyf8fe^l| zh+P7WCr9nsC%E~UpDLnCh}LyXaI>1*Co#9pz`zy1`)TZUIY(#O zmrM7yP)0H_~4}B zKDxaFL%@Z-x#VItgqB;k7gkGg8pN)Ii}|np=pQ{T20xm$Q~uD9icDCNKLwdbp;yRH zk@~5CefQIUJ7s^5Ly1h=|7WJ{^D~9~DCg;B@5!Z>|3pPO?V*i7%8gk5!P)E=C=@$C=psZAiR>9I4EcRWnvRlaeklY}2&t_;;Y^vCMw^qyUZDHSd6cm$a z)FB*59k!X49FpUz)(6I$oyb_!V@OnNd|o#18ofA#UAen9QQN~%W%4GPdKeq#GB?qc z!?IBd-9#@A@|SL;p+{t+^O%k3-p`hz$Bm6fsqIF5fdz2JM!E)|NN;53 zi;J-&(JW?jvuL>QZRTC)T%&47<*!}xHn80WOSJff!64P#K(~&{SuTICFP?gGJvBKd zf2hw#>YF+|CgGYG>DV#3nak^S#j!50quR$|lWps$BmbYZj>a9Ao4RBQD3p};I*{;7X)3%TuWDe0i4@fDt`h+cX^GFMJ(T4 zM`!g`3GZA(?N7*crG9H@9 zCG9*7q_mZE?R1B1@u1N%4M8yQ%IOxq7)pNJOpety1c3&fZ0n&&bNiwn(=t z^&U?r9r>0)_}I%n$S8c*;s3vTVce^(IwY3iYT{dNjSJ(oE|z8S(BMh+bu7E!*jTXq zerr^WzQVB;0KbP(+JZ|=OIVKcQBrPnYk$`m`!>QS21R>`x{G(J^ za=KxvP`V-i*hULS<9lgLAw6X1@|+gzMeFA#S8#s%J$~eS)84sC1^3JN!F|{_fhJn* zYz(B%zsk{7$01c%+H=fR>>)+h+S{DC6XVti{IcptgB$`g71GXMas7LXLb~-UDtK5S zd7qc-dF@}Wk{lMm-QtPwsq=X`y=;G^S}g7Y{d?MsBtumsi7|h{B9KNzFMvmZvk15C zeL=1rbZ8mNq0UfJ80a!e&8j~PM63Si<<$QIrjwln&``Ux#qscgB%G}j43L(t0;!ci z`baz*E~h^(z}X60PTm(G`jcgpaS=!Hjx3`d{Cmza`u3vSz$@`PRh9d`tV*qA^z0&n zGMJZA^-FR^*PF<->Mwt%`4N3C$)Sm3mkOP-%&#FXhMsZUTeaI}Z+zzE22_i$@aYL3 zWdweY@94-SIVIdr#SAuEw+d)6YX6OQOJxD=C7|8Dqv*?WR&e1GVHx51O=O^*sHeCz z5=3wz)>2KR!pm}~M>d1vz$Z(BLFV#hIUxAi4-Sdu7Xm#+phpPw5F2{<6?jIc76aYp z8HF5Hy@;WDHpnkasLK^OC1@~%Xg<)yd7xH+FD3Bvz!+LCp?z26jBq~&Vh_){*}?nv z8rDxhdkJW_C9Gc5^%DJ5wO5WGxkch!HoGI%aIL&}>`BLEY z7C2pjBizVa*U%Px7n1XJR1Hs&=W5IQ;JO@|zGZ=sAv)keWb zl<5&)RFgu{BxXSld_P@C%dg9|f=x`tW@~mO8wj9e0hF-N=F{GQFgL%YS~nofMWA3y z!~!jE2 z0(0R8;iZ*1ZV3&)Df_uB1QF~j_?EuCDc6fI0a?HW6|gGN^!SPp`Oik5Wcz=pzZ?nIY{A_gn5+to1Bp{n}H?`ZEGG02qF4GTq(t)5l-f! zd$;v1ZJEFnF)JG`2n?D>yML4G1o;)?(h#VYC;{XzfPCgr$Zb^HZF3x=%;#2cOw7Ty z1J|nGIG6GP6xRwsZ+cy`RydT6#E@obEzs%-w2Zm*$4HGC!^a$0Cfom|6iBQ!AqO0E!sQKwMcr0|c0g zzO@-cb2M9g{1?z&DVKi9P70+;cTrbdiV+@$7HbINnnlwAWXMv1M7th_zMNMp8w!ZJv*^)1xpt^|hJ(y;lrY>rRJHag_dup&7t ztQG&}LhH5EQE1cAz>8XA9tf6JeWP#4QiL;2uclI^`?8Op3AuvA!ji1uV^FZ_i>Bq$ zf0@Q;UFhK26jj?8Dh;1Xi|@->Qo>X!x-WOs9h;I%CF>YtbaU`hy^b-|t?v}ol#x2x z^gynuMBz7D6xPvsIa%1U)pu$+osyVU<*CfdfG56>QCc%&gmi8)efX!`Uza->-|?Fn zW2ETG>IRE8t&E)m{ z3il7(=}$)=$|)s%fk`*MF@{KwCX(_99{86NY5pU*o1~veu8-wj<-RKr!rRx|XDlJ7 z#W8-g~d+2->M^O=`??p1b)lG0qfb~Id=lh|66V;MU1B>f6J9a zN`VTm7{9Yq*!K0S{P&_pOZ&!&FMjT;$EkJW^Z$}){Y8V1G-Vu3`3Fa2>mx(h@a8CW z4NFuO6Ps?tY*n1QFIgNDSX%Y*;u(!6SDA(FlPgL+uTjhsl-!qNY5o&AR?0@0O5Oe5$C?kt}6&!_l+ zn^zWxxcB^KC__`9$^CTxW7GwB-OGG@@|a%3SDH43YCcEJo|7-EVlt0X$4=3B5~pZ2 z<0*5PJO;GI#=5k&N=2#5HJY;9&{${vGMCzRG=`ME^d)~iq4jC{3erzsQr-)BhW=gw zC=~Q_$tvjPm1RAEljSozUn94dh>g2&G>v=->-8AT+o(c=>Wzj3yo2iXKS?FHL>bUtUS^FygzYj+)&Wp*BEDgYtq7gXgMq7tDS|jJL6LjrzyD^Abw(ph zG(I969*NBYCu72#M$+(C=&)Ljq(iSTO->j|MX&Io>FJ1E+V)Bbr|j2qXh7iz@R~F< z)_h|KD7V3i+ej9C1&9a_b;)Zv#J8mY65FB7^WdwC3M-ALtFLAMfP0Ubd$K@Fz|&FS zbfdd5u=Ma;?A9(`_SsgsjubSO5WQ@$)MMyOZzWeMJ9Kj&<-BD01yeao34A)X>W_Yr zTVgkVGuVYM3E<#H?RCm9spb&6rBh5&`&txOQi(1=K8QfgCD z2@6Ps)VKyfgqJpZeueI2lgOVxgyxo1BBa)X=|D-Px;}Xbn;Su>t>y|A7@;CI!wey> zQc8%Seoc3LfPRe|?)hX}I0kd`d9x@z^JHpLN(pc|Hki$IU@-M5rDRHn2at=eQbl?+ zm#&vme5J7i=xHe>w4T!-wsOrAxkA>=Mv#?HkqlM)uHHhMzl||CZ zestDZsS{EjlQ9wfB||)M3=#1J6x5DUbO+h z(*1Xr$u;gzW>+QFw?iMc)@$VL9_O%DEsAkd{6_E#f|my6Tze+Gf|Dz3{wu9 zv6v2gAe6jnEF6qD-v`yPn8qK@Q8m5{ zF*J>nki-)8y2BFfMaFwIKG;*$xWZUfm34Q2FS*26p<(>ll`;P^>p^hEB%N)w_Pz;$%0l?^Oeq;32ASSkK zi5-38|IW%E=~rSvc747uZV2*rXFJTdon0+#MQuc7Q?shj7nKyx=3P`i6KmT|wimMu zo5>j2s$Ymg*ILvKa}9Xu+@jn-Tr*fz&2e9NizRy8lj?XXZJavNVo#-^6w`y&`6#U^ z%S&lqZd4CQw9Qe2_Y}|?FQs}&7A95iYjs<@X{|1%Cmk-Y)T74UO1;ua7+Nfq=~KNj zRm$i|ayca+!>5_BSM?0L9^Ei?D2h^h24;yJ<-L*OkFpu|kpO<2ZZ~eNnKZ7P64J6y zSAbN*LSHxhXZ?!8SxNMpVm<5UUwc2eROTEg&7-J#$`;75#C(Rf=yRtDR zOIx~8E4?y9TGWmH)WZfJH4$nJsA;zW&n#>o=)$zK_1+Hw!va#Yc_CU8`MX-YJ(eHRU1^j%r+PTI4p*BqJ0%GxD4!|4lb`J zQmilfiG&W+z!&|*>d!^B|DutQ>S2mqs?@18!56KjKLEJeM_*#1BRbMHUnNR9-IIRz zRYFGgZ;u`U$A_a)x^-0=U6_X23oMudO$K3n+F^nbL`=C%AaJpALKZHoQK2 zowam~lP!2=2k+sFOcN-f2SEnz;>xan2^& zjT4N?kr3*hc{nIKK3j;9_YIiU}f8i3U;}kuwj$xb{o#0K#@jNuydcO)|z;h$z5+tt&ECcWFgRY zbKBSKWM(fCbs-s_hj(x~DuOtrc_T+PoP97_rE)r1&2F)hn1l`8`M5?|rHj-|8#!v^3Jsm>3Jr%P+BG~fj$T#7$e{xO z6We*3iIs)E|7+y91jynehd=z^w?~d6bp)UL33mM|VH#-DPaQd|Xdrg|CTRL~1#wNk zL7IBjQ!G(aY>Ew1NSZx?Mg*fh-Tj1S2P>f?+kPq(lWPlxOXBT{J;bC=tNy9Kp!(Z4 z=ZNYb0W?-tKJ!n$F6QWP2IjAtEt;WQ`cEY)cRApR!Qk9{rU;judMtajc-LyDl4B!;jL&} z1ZwcQ92yaVUf^C!q7d`~d98&Rlnf!r(ipoL9_8E3u=*Ixum_!vW7k3?&_oqFE~?Nc zB!w!8zI9u2FK`oiyBS=o-NDo-RPpQXi%hK(;7og!zLqXnmtfXvCr}NnZ8l&~Os!G6 znhoxmuOC&b^a$|LY;X)oERK&5=kwNdIuye~=Z_H;9XbylL$$)7bJdTjWf*i`3`x+X z_e>LvZ_9R_V=*(%UaU3 zD#{>fVmorHs(jXBBRE6JQ8k59h#DohJ^b8j&R^rM{ z2IbwcC={)9PxL})c_pRFNR`!+Dt0w5V9CSQB`^YZLA4+!SqU=y*Mv#lGjH0L1Y!2` z*nLUpch9_-Qe~xv^mSbtEGtzdV`o|{D`_ER5)m$8{6jIoJ{!QgwTN{@<3L$HX-qmr zQKW)S0>R^p1P4OL0W?Zcx_QNct&mz&;eI{nrJ~G~?l+)sVw7g(sR4vaL7cMzQ`vj< z!oXOMp2sL*622}3$0`+cbsJ#Q?ZdL=rLA?TNvskmm1{uVV^OcDA&reyT1byt)Ad*- z%Fm}^Zi%=nMIYc+y+&;ALt^c1cpIt|r$nb(z1>BuW)7*=?-RH#h)L*D!gJ=HF&D8i z!`BexAM!I_`kZ?Tt2u8s7x+d{#2l|7jgM2p1El`#8cpgHES&7(!AAaWPsievdj6MV z9Fav1V%jR=)ne}bAw|V2hMFzvp{5oLbU}CsEc*JQaA8-hX=m{k5-hM-!-fKhLr=WJ zp+t`8r?Qc%>su#Rjf2r#OE1IFLG&m|3DA{8<}1Y3aQ&yYinNO2OiDFrOKs|GQfdYd zLNeO&Ti#Bd2JDYp*#WJqTpQo?4kZ~}wDcdTQZhLIoQd7)h@Ij3U8YL>QyR_b?T|ic zuxxPAlK-PyoGc-^Cd+c?0~LHeWz{!3(%MK;(8h~R`^eF!zjTeCx+s7!x`$$uRc?A6Qo}6OAKElU}e7my^ zya6~ksGgF}D)>w=&#Y%VxmiC=l~|tuG?u4mLz2UFrN$HF1m`wkEb2XSNsBRf*Mh-| z2L}kJ(*qDp(W{ZsO`=-rhZMvO@l^T(k~^135d1#^hwKdHonrT?u&H8*VB&9n{~O3h zY(eG3dNxxqgtY1dAhU6uAu(*_F(H^Zty?Z)#`G)w$YURc71wlz)gbvjC?kQ*75@($4~jp(U0Otdn%;>&BQ z7V4uYRkOSr>SINXw-nx%s)qU)R6|v^V42A)20*N<8wkEf*TX0{*0ljhi^374WmhKs zrn&<)$=0l9$x1+UtNsPRP!b&Jqhu5|aZ{b;rr>hPZ;ACZwOFW+kCRo|M5vEAfUsDK!!{HqG6_Vi`WqmL?|R9(bYdD>(aOU@KkB?V z(IA<$!szUyRm-Byh6>Ip0DO>;T=8bxfTkUa)oSF2Nva`4Oph=?vJTgNpgygY;QCi;+1twdVtQPy{*Ijfnw+D5 zU^&l%9`>*$<c`?K<~J?A&d3+lgp;&uoQ`i6x9Vw?a%y#`wIay_Hkb+ptojLfb|lZ4 za(tPTL-sOLgNW$-t7-MZ&sHx|evkEaFUDL18;M+z_ztrz8necQZ1qKu2In+DuI5gI zNvgV<2JJM&#BPMGPKcp_wKlE5&)vlg&Y@f#eWxWMm(K4r$dQay`F$21wF)`#Pi~kG z7Ggl0D$QzS45&09fla;tty%JCNM%d8+Qxhk}JSak1y3g8Bmw@lheH2r(# zwaZycCzt^r$pD5=#%aqE6WCH0~Y9aGfCk-vmV{~AMkYblw!&VQ7pK~0iBaZ*$_>|T@PU^khy#;5;p7t`W2 zCDwBvP#w+-2FNLfc&8}JoMKR<6h!~zof6}w7y{fQwPwZfPnr&mt>A5mR+%nd#7zH| zbE$Snjap9e77>7 z!Ktv*!KhraOf`hKFN$LBPtC0sCQ;S@5>uSI)AWB4(-^@&nQh_9gaG$$4!(42vv&0Z;L@roodoMZK5riO0cv%f-Yt%q2)(ufTrSrk8)ijoN)y77xRK|)q*V> zp%!edZ`Fd`8LkT4Z$1lrAc9WURstj^PxZ+Cpm22qPM&~zYWAO>LzyaG6@hn#Fiitk0D*H(K6hw1v2}GV{z1baPXyl&HhD-h)-4}<`!-2A2 zC1(wrKe{u}3{+_9%!4dcUM9Kx8YvImUZwqyPXE`9cxU4>$ja{o6&ZaJ@Ce5urxy)ppAtvthYH~EHvX)c$+Yq8aqKjl|9=W#NbA&em4YA(^{E_hO7Fl zRMXxdVJ)K{4sO?uK4MFbdKonMOd6B%cqXL2gk-#N} zhUQfOp_*Xst%_1q8|gIagdvdHuQvEMJ8aNUZ(Mk11%DOtSm6#+S>Y|93AOQjyDzgh z>Nc~$E0*{6g_dcmAwNg;qiF->3L!B_0rR(dNq-IQ^pqrQm|XX>!9PgHzPMvMU?N&4 z7KzxfR{aGm9^9{yVl>^WqpGMzyupQ|$UKWQ{*Js+<;3dWysv&u$pU$J)CRbcb7iA< zpxC3w{YZf`BGCkuq+@wXKwuIUD7bq-44piO1lF)7SY|s_vxdnp0HY>0&Gh%?1z2ra z#u|1#fwJ2v0bak=Rt?s5GBf)zkiKZAMAaPxB-Z=QN}PvOHm*Nl?Uj-RdV9r3Yz1Aa z1=R5mP)B#2hP`uA65TmsP;^KA)gT(a8~oo|o<|sr1<1@eYN)MCkZRL2uXuwqj@NTf zC!*Li(gOuvuh@f&O_5ehE+!I45LLM|Cp1EWEv!xdAY^5+Z5tuW(L0I~IO<2uJHW+} zBpTR3`6RlbpD0TE!9z=6Fh~~*<+GCe+~T2pp8Kkye2lkziz@40Z`%bw#R;1cg1KT+s8r3DET1bW?$0W)+p`E@2y=Jb5Egn^B;4eL z0d96Nabbcz#7%`)l>Je3fb#@ET4o~h!-LrJ$-VG{dyP(GaN;5xT?oD$LO2w&Es{-{ zEH#pix%qz(5<;g!*dlkj+fj)zIRl;3ZWw`X26F^P2YQk8zVJ1d^=eJ|W;~`9d=@{i z6IRbjPh&eNm0Tu*6vAdzpmm*;Iw@5Fi=H6ZTkG-Y3y}f22Xh)Bx1`g0fU*>~tt->qdd? z0VEE}BK_q<(Os0*5-!&n+eOKVH~9$R{me~H2;oJA^m(cZf8}cyUIsm@zN&au0-f`x zFlfGfnYp!9SLGK;dg@74dnhw>d7im6;vB+Let?&6&l%#}Jv_PK4|6%wrE`Yh^0z%S zW|xNvXDHiz>kKvOseG#I=b;@M9{DSx-PZD;n?05K(%)q%s+Uq(I$4&Q^ioo#X=Q0r zFQuU_v#b_WC9Etsa8Q-q=MC|?qB6Pk&v}FFzO#&@#zkK+1WQxP(B$4YHq!2-I9D9m zOaP2x$%Nz4mh06T+y(1sT1dg6&ocCPZzW#$)Ez-kE*MPiKe`JIOlHJJ5W!ML^igV6 zX^T`xJ?4JrP5&hw`IaZt_MasE$S{6ygu_Qg-QNXt;Y?4s0G!2(PD8*&hU<9T0YJt)Yuez z`=T3Z=1#Z!;wx<#cZ%$%q)1y`sZ&3tqQ_c*!b2C&rEa(->Ok9@*vuKAz z?%zTm@KBtt@)y89ctQ}h&eN_dhU!7vTpaN8u_eMXMZj+d8H^;Zl;2;$88ZOAdB$w{ z4N*sfxq;Ofh-PdQX!QhIhATbkucXy9JKGCi9LwiYHYYG9x6z{mxGz-C;I6?|2g_oZ z31O1J3-dY`>N7ye4DJjPHg!^O0w_%YbrC@AU9?^6<^f7*ycdJ=;nA@ebGa&dj`pi(zzc4-HT?n9tTF99@108MwMr2~}=$yA!|4pb_- z#){NPXWLQ4K}u-w;ZhC)#~*{sLg{W&1r++jig(ZreRd z(~&_+ikBZicw{^^m{Tj4rt*W8bi?724j!uz-J@s;LRSM4%Pys;_h6-VQ0HP04FO?E zv*J(RMF6!gMQ1^#sVjiqi67GZiYO63a}mPy!h+f6zkrWnsy8k$*!@}*bZ<6c)OnD_ zeTyJ_5rlAFv?O&HqNIlp)Q1A~V%^gg(*mbwDEw}9?il8z5iTFTEQXo&FF z+|nOBL`&z^AbLpAs4gQL!L6_w0L z;`kyfqt*X0*zUs6R|l{u6$ok7hwI2=s4_z5Wxh&NhAQ)JBfNm*;5RFvArJPqZIw&Se7mI|4NGMtARSx>4lQDXV9i@cmj{FD4qm+2h z=DnHBa^zx}ma#{HOjYC_z8MuJ;eXI*7@ zzWbcXsNq{axsHZ!kMnYT7aMwLTdVkN*_Ny19JFkVJYjU*s#Fl+O~B zu%?s7?%_^9z)ED!SAuorpXZSK7$sb1d6pwSh?YM4EC=xs$n(BI@45}|Otd%k7^Bo} z)CE69O>FzSos8)Hn4ozj<|Bxy7mIfmXyQ4w0{qjB1*!`PuvUbp%_|XMV%pr=n)H?O zrL(U6qa6Biq9W@O@N$2m(nxpvA*?wGDI57EHOVO9e*q_Sz{^g4NyJOhB&D&-?LTuc zLZ?fVF(~Gn2CSp(DccQT=`QKE}iSpd!ohE1l(_vl@CK7J>WX$n7g&z z4(oG+JcOgyws0}oQ((&{ZurR6ngkZtu zv#mvM*UId+k(y6aTDtUMaC=mkMn#k{81?_|G$p}BXUlpV8*h~U$f5Lal=`~qc$xT( z(#R$A4-IpDhZ>wcYq}B?_R7K*-pTV;Hr@%03~#v)S1dVHd%9A`Wja7b(QtGaT`wfB zo32#XRmY2EIyCadi^~jXe$SjkrWs08-E_Q6oPn(Vcv(3Ej!_z3Zq5K!X}tK)1n~KL zIaGfpV7B6=^Gsm1$IFj10TYQA5k}8t7EJx*ZVpBA%UQgL&@ij+iqf=)hKXJ;IxX&# z?cSAT>l)$0={f^z>nzk26R=RHf?3@%?}n%G2V7}VWEact#SE=G@Out@Lg2m>FT+Ub ztTW-|9--GLj~DmZN~G@IogB&tFdE$t-4U(JWWK&o_4@kFR;t#3PxF?a*Bfw_d!DM_ z)f-2e+5c*R-*SIAfMS)vzoH49$L=vT4#mz@{G8AIhUB&yXv3WJBe^l*vP=^N@7UI&eU{IA9xlGq&9WJgj#Aflb*oJ+9M{g-S}O zAA@M3bjO)8i>n~!FNpcziK<82oQvKAoZOQtr-ejnEsDC+K5(ZA=O-___^EwIKE$@*XX#)A?u3|z~SjX%)wdUIi06! zg-Wf!73WwChgah-A~wOR8AUX?Q1SOU0Pn`qS73e#7FPYRU)6FyS*V0YMKC%CN$CwR zo6gNksZQwedR(#<1(x5hRB?q;H+_?>V+C~NS*?-Bu0(%x__Txj{W$>L4o3)GBS@iZ zJVX6ff_rBbNKDjMtp-qQ0n|kRwLe2=8OWs=rK_3H<2feKsEYzfcZL#H0jThlU?(Wu zM-f}*2%r_f#ple^G<}uQwsHi3;Hb1>N9EOpcly(M_eP}HU?Nc0HWK>bmiw0mgqy3C%$m}%r z*=csh7{U&rn7W9mVzqW^t(o)z*P0t3iPnJUREj;46fB*{p)c1c?Q|x*d~Yy@>&oJ# z+*+g*{*ptZttO*x9A0Rx($Y)$MV0SvI+HS;px0}aI=V~8bExh*CEmA#NZtY<)HghB z)6g4!#yF`vf=I4Y*6UUr%b|hmmBzlUj&Z|UgyCN5#}P?qY#s~GBT`9GC#!xS zSYVJipvrb-8)T~p5>DF@j>9;ODs5MSJmx2{jDG^#syFYa#@m&Tb%Xci(Dh+3MALnm zjE5(`DPz_&y1QLz=JxkgaB6`*X`jlY)ei7j{4)-B?NHJ@f~qs^J|KckLi_004yBd! zd@qIVL~Mr>dntRT66XH>UX4Y?Ni=Gw5+qHULW_4QAt9}hQPcx%P%ZAEs9IuuJ$njR z_P~Wa;Ig<|v>p*(vC92=4+a0IBzTNZWR_Qei!DTZDep%dAZfja4*aNuluX=%6_VKa z=m4q09&-5!F={QlF{xS_;}ddrH<)$e25(<{MrUEw??gIx4E++4RA!@p0<$5zX&L_e zZ$@3?+_DMG^fQo0KwCWVSuoO=B!#W`-hY>(^Va>S_DLd0t;sxQ9=*Ut3w}Gf4Xs0Y zsm-vEORoAnEM@h{i4KuQ?xcl#@!4d^ICaWaV`mNpKQ)Eu%H!pcTeM#P-;SJO=UN~K zyuBS%?PsN)v}gydKc)CcfpcfY8^fdyJ7~wxN}6v7FtoKq(en2&%LQ}jW{k0l>vmBN zt;`k4Gu9X_tsf!y`h0`*{GqMX2)o&4AB+d|__!g&kkQr|J#9TbFWHhC*OJ^-^-9WKO|%v-4XK_yN4ehUpcs5JBaXbYq7 z0ZrBFywqU+WhSjp0OKv>a!AS3ZQP8NnM7lh(`K4|NNMVuu$gfV+i@7%yk-WSN;Fof zut}IL*sR2}C#Oz%q%SG;u+q$X5;FO#r6~Adv&RhTUCn63uBu&!u@5d|6J0y3B)Yq+ zBuwVYz0?R!iAR)*x}zI&D6G0M*=Zw9IifVyrQ!GE53zo@uex!m+Xl5-rd3Iz-bu#D z%Gz)DDoMD=?Tu=T0Tbs#H((VZ$rx1{aF(N`#3w=bqV+0~rD0mcqhHoj(_=~>_fNEp za>x+TL${fXah_V@kD*-GJvLDIapePP!#e7IT=8>Thg^6$c+#JZ;sa^LaV07;YaJLA z?BtEsj-3fY9))wzaPEdMG1Gc_eH^v+!dj~L3s!AUA{Xw4cO+l`(+MTi_vxDVlxg3|lzKu5 zs(foG$c)~{Es`m<8v{yh1*P4Pz^dQARu$?SP?9>Xp=~GdU9*kIY9X?ceI-GkYf7MUV098$2UjzfJ*!n&3r<1S9;?)bW-g$Er=Te13zj!EmV?Iu z`MmfnR81hqXvl`sN~rhMm27=`GtMcXrl*yPK1K{SmNo)u2PDIFn5*c^(@I8qYleTj zPdGOL9NAy$<__bI-p7@LZTusb)@AXfC|OWRSf$GCcLuC){=ls5olxEcYIFt_rB6k! zKKsB2i^3~a_{o6x?aJ_Px7gpu(~&bugimBIu(}Aoh?OT$<22NOvoO*16{?9^kEa%A zVbD?t!ewbDpf`Z3Rln*7mC-^#hqqqAeH5niZ80jL<^{7ZwTA=cFW3~Pz6DAH6=l{r zi1w_IMSH9C`i-NMb4q2e8v`)5^~Ncw?t6u6e!_+c3PU+@x{Ch@wGxXn*?`hbtN-H@FdA4=GRQND&eb9+OHa_Tx$-<;b z{6Qx;WjRL3_eK7@%^B~D{C5-?Vwdh{`t-b#6*icEi%(7?g#v>5KG=#Q|Mde$tfzcW zN6zD9uW>m!T~K`dIwMz**zM2yc?%%-?{n11f5B5TIr3ka(=r-;LCMlJTZTpE+QyF3 zcR3vRFP8r!?q8VOcWQ6*p9x@6c77n5~0?fnjiTxpAVL?FsUGStPVWr zPbZUdN%=Sd*NbQU9Mm82m$FD|)%^vtoYfJ?{Iwnj7SUnQR6uPBu~jx2t+92YL8kyn(c;J(Pi z*_OAwmQRt&6Q%NcI(kJhMFrruXmojb4naR3%mP@=Kb2NAka9G=sit4dn*^Mxu=F+&nm2FdC| z`$8WU;W5Aw*AY+bu3y{0sK=#MlZQlC)lFTfdOQ^dgTk&@8xP^}e2%;cWs}4Fk;UfU z(v>xI+`Lu22R*xnNIe%9P~Gb|_qu)o^|-E740>N+zPs%9_!}5E3%{j- zHQ&8*<&-wTF2;10XMO2r00Ch z)|wczq>%aa@}|;F+A@zi-%>u5y3V6bx0J5luji_=Zt-A>E&!jBZLBMOKbHpmh7NS$ zJZ3RRJg3j2rN8la^SN{vd9JY{HFBP9W#G0F8gh7!qXHlQN)@($StflDj4;^D#l)?t zF|k}90q7`($N!JEH;=FRdj7|AUpKM{ZjhTq)|=#p?B^9BVhIVs4Y6-QVyBdr5=+s> z5=%rB#VA^|+M-&z*!NOPRa>R4qAe};y3*QO`*MHJIj@_=^8P+P|NQdD&Fgi}%$zxM z=FFM1&1)d-{xhMKQtK_MbP*A?E-$5y?_sNEL+9XX!Cx-bnVeXKZ^g(%fZq*-St6s1f;P{{%O2QoLEp+uR!F3g%rBwM+LZ0#t_Bk&lsKycG z+;)}WIa`yikq-LpQbMczyGzQev!b)!9oAH4s<#1%J5Ts6?T`8qUUxqFAH|1oaV6+* z={-k#GxRHj+2AB?dJl&Kw~(VMT!X~L-m33g=YyE`BrrQKl91ZsZIleugw`J;_pgOiLir?j9;U4s*ihZbYYVqtKU_wvQi z-sw%{Cn4`>!To5#<%A|RJ0O)$3{Jo6{0ii^ed+w=gofRX$lxP$=so!8BL_s426}Q| zDHnO#*6u((qbV_lizVE`g-+X5QdqX@+0+`(;v!jf0mu`Ey0Q~>Wym;{2ePVH=UytEw~L;-(Jth8*VA) za=f9=4ZD2_g0ZIr7s9C}mTz{U z0FKE^Q?6lr&n~7z*AhYk@4B$W+sM2O{FYaL2(3&@N}`*&Xw;o^rE>VtgzGp9G;FS} z9AOf7Ofem}o}ku^M(T5}b>GCirM#+O$a$vyevvK~&x=L6Sa31@MuIu`5MDt>|9We! zP~G)=#gPmy56q=8Hxh=lXaSHJNAn`3bt-&-c(fb23FQglIw+y+2ymOtpq1~fpA3dE zlNYufku~8nbsCUR%i9n6+*4LQX7V?RsO8OszQzf2a_NP>2)5j54jsRlFk1PwkkbE6 zs2BLcBL>?(hvoP=e#>QruY!Ialt}yk#llI$Lb~)X^xnPD*`aoR9%S?-thQ zZOCj__&BpTKTX@^0X><1$inqH-(9z)+I^prf0r4$1A=Hf{DQ3l^luj$xi=`-s183z&9*k|pcLfN{H+bGl|Z315^rz; z8SW(vGk-Q6R1xlWdVbmA{?HDTe=kAxjK9Og^MRcGLU4Fsd#8`HewdV~Q-eXQaQn?{@O;|gTUX*m5eG`PF* z@2R4m?yE790_=XexN-dx33lC4+ecDKCqh1lxoY&j4&YEVyM*90NMn6R)SD z=vQ#Vei{oaW*Sv_h_#5LQ>fd+gb1HO|1l3+0FR}0`MZ89&3%}VZA_VhGZY(>A`Mfh z>Z1gkayXxQJc5qDmrs)(A>7Lg`Ly*>Lc3c2`6!Fy_vSa&TQ7796RR~vpGMYwoDku6 z_;rTS!}m5GDCOy|{eVa;j}vMI^tj0^tOFTrhv2uoYthJM@qvcdY3JjFHA?PK>hDO{ zqdc0tI84-5ew@5GQ^Y9iCNCZ<8b^$njD;W809zp1*9UP@oS4u zijK;?Nfc=iQ6858j}Xa|shdGGQ)W-1B?i&Zr|LE4q_52DDSx|6rauj0gOWO!7ARt# z^5|6xGm2P6n@H`AqLniHRa#k8 zJPoTPCb^}I2gltIU{?6h<4VG!_)Z{;o9I*ZopH$cMKb--cq(!enTl;3{p1F&lYxVV z98YfUqN#FvEVXh+*|~Nudn3>wMfv;QxUFxx3!6bn&!b;GL=Yw#_dUSvxiQq-Q?yn- z%A+Dr(K9Gz40}+zy=VeIsAM8$g?UUTId4g2kzqbP8UTnXqkV_m^yz!#$V)vdi~2t8 z|Kd_tfdckX|QBI3bgmyR`;nnaoCH_S&sxheHd#R(zP(=(dZXcB^?;R?OM^RxF5o;Mb ziU-W6r)Zs#%I-?#i*%}rNQw2vJCxO5YrR^QBb}zRHr>j7{wJ4H3EVi$j#1R0s>n6Y z9*HdpT%pcjM zQ;iBy*sptwW#)y$A)Y5qu_KvQRu|dE$l>}hMb&GF2*qUtCD#zmBi6hK1gG4$MM8FL z6FdCI46Ft%hk5yMT3$m~5~?@i%uy;nO=~J}#Y-1kQCgIY!^Q=2sPm>w3!aJnQbV$X zcn8m6NSLv2Z0)@mO(9jp;+=CPBvo$5<)HLR=jaz7F+fQlLaAnvsC@DQjWUbcVTXdx z;%n3|dzZ|14!QMXD@%IuXPMpnp|sU3Y|8OGGWd!>#bqdk_zFJ*ZoK-6E6Vwf6z(Sm zDNU!)Y(FtVNqK>8`-#-bb~f*Y`!fqmsM+quLDbV2$O5;bB+?fA&3!% zia7%gvNfu2NQkm$09isr8{=nva`iA% zi~8t|m_-XhL_=d|q<{QdY@la7XZqiCBSZ{Tu&LWUR75Lpqfi`$R$%fz2?ZBzbM^gm zuh?8H%ydFamh`8v5pgxiWI->vl0)?@BE@%8j$X>p@42Eo0E@M2Y|2~2Kx2a(9IDfT ztcrIIRSOe!m2NH%Wnl`CUkd66@{!?(%w{j7R{)VKhL*Sq(kBjF-7)DO9A!X}{I%NNrOPMqyz zjR37Ld*)I+hqUUc7lBAu5#n_vxF;E`!pGCynY@=mtU|3Bf~ZDl+@ac6xc616O^ffhH~ zljF_{NM&mjdW7~yiG1a^E|eB6I#e(2qNle!%!N@`xZzrCIDHuno6@Wc{T?mUI#mIK zxwpUO1J;4-)XnP&9lp{33wU zst2)kjlTl*eV{5GaEN44e26inG#pOs!&pD#Ib@sX#)n3?e}?i;&L}v2zGTXqP&Agg zK&ANYdi9O)_h~Wksxw~2x~`@D50ZW5(z(&Sy|b4C-zjlN!4=W{l0isKFQ&nVDUib;PW5b2+Ob zH(zGy+)Ui}|Kw&l+%xJn4*~-GQ=<2E3Md35a1;fFJIpV(%cZyH#U~o8opsQOehtHv zwobX9sqTywmhFi3Q_eUjy??{7=(f*Loq%toaaGm5Om!NH#W6mxzS9xgz=-)Mjr}4h z)aU`S{nw@VDE+T4j*p9ULk0&moA5h>8dWZ9($P7I+TT377!0Eh);DN zeugtI)7tZDkkZDd#MchQZO!jichlWrNM=J|A2 z0Q{*2@^p$aeHXL&CJJ_#i%|liXT@h_fZFqeXL)^q>CpcUo>hels#t+Z6U&2R8PBwK zYFV~1&Qy7GYrPIH?WC~Q!s7bTX*A&AZ9$>Y$dGL`{C6^sFM_tiJP5yK7EG#> z60({$H5XyVl2*=^T4r*pldVPrKf@}rv=CtllUh9^gA`l}V0E(WVEUs`tiwD4zoyVM z>YH6RM2R~`*OC)M6fpMt%(yJ2B?jMFagm0Wl$42?X78VAOr{98DtR5TshY;5;{4HI zWrG1yFyn6tZ$yyekJvz;yBU%RTtefJA{H$L%0f3jYZz=ioPi@r2+NWDDL~|rq>=&p za2%yI#pfeSx+3g6C?U-h%Hb9AeA*Rn}2YnA|e`QKSh2iD)^K?Q6C^Ys{FH+ z>)Z?!@rj_8^l=+u^=$m3Q;W!ZR$M@X(=DEn*bD^GVeHo=_IIF#?P~lwLyteuGX8K- z+ck(h*CLm^+c%7%T@j*6=pzoE7U*0d(xaxWf0JDd9s&9-q&;iu)iSh@E{w?qrE(G5 zw#YdfopX%~i21qsGpu|e`x)XI0}eOtfhC7W`h8bW-73R>D(h!Pl0moTyT!RG&7BIe z&D$9cZ}{#Y-zpDEtBtv+1YXi7!8I=ZnG(A@d-lA|T;h4KgpZ8k7sJW*!T8p7)H&+# zLA?%2I_`OVP%no6fAc}T0RAtdVBMGl$MRtnYH+%7FC`5(O)fO^r;koI4yv4nve-fS z^>gYJFI1Beg_kKgj+G&g?cyD(W*4qo0C+463NbE8&DA4NjdFrSeOh5|yRUhI*?17J6BxEO zU^xbrmx~j2+ooi?oSx9w@GT`>$6c5vP9BFgPYCe(wn7y-6jn^bCOWS-HgXkPfJOyr zRS3FMU5_;tcBk?4ytE5QWlei)W3nE?3F8?$Q)%R>3COOvF5_iG0(vd39aba9yFmf2 z5lE2XiTzkocPYy4JdEgF?1>UzM5E0?^IokB~hinz+9MJ$Y@5g$O_Zb;DO zeWt$TqSxUDQr>daY5DI?d51^B>ZQOkmd~PA0*)E!@ewn!4s=I`wObby6KTft)3KuZzm9Dh!*5*sAz`>QFq`Jyj;$T}brOTJl*(g8 z`Iduz#Gx9}F3HB-0CbV;gw}%5TK5VUeMp zme$3B1wuWAPp&J=DeIe0kLtTtvzGhnfu0%yxcoS!ET}e;xS-$OXQ8V}x#ZWYVXE(R zaHj`)YB~*%QtkemA_F}s>l5WDTX|8Uj+*s8{n1$TF>dfXM=hF&k*>YIgMOCuZWy4f zQ0a#zBC%>MCNfScU5J6aQ$?tfs#0RAh-~evRy0gt1&dWkDv6UPB8E-@VkIpspC|g0 zI+kzBILyBa^jnPmGwf)TRqWr^q)n-!Qmw7XV9`GXze>=zsUj@wHOAwkpBFJ^Gl3uw zC^2CcfMRj*T_x|P!pF-7Fcyr0YUCLg=rulG#}dF&-;4D%SDeKc_4Viawb0`! zg+>Dwaw+fMlDC>p3dg8MN6}Kb=SMv|iZG9x5DG7s%cw~Sug9UP&ZtS)j(6B+RePZPqZOrJDVp>H~hU~>|1vWfC)R7mKBFcsu@ur-w5sionN}$;8yN7o71R?hH;RGW?U#Ut|D{2bs@wFSNj7|* ztCDtt%LYyRFW?v7jHaDkM5wtrsB!9gUr?(oskM%wYh8qYL_DZIKZeqSC|xH$r$-SG14ZTw5c`9}asf;m~I&~9`jE`!xpgu$SpuO&!_;o@4)4oYF zWNsRYk4iX!L}dUyFY40G+CWZ29ZE-PI5xl4#*jNKA;5&Z-UsBUjODgL=jp}nBFeMP zGG=@Q2z!lg*w|2g>(AZA9z~fILDW+$GTsQ!r9QrKLCO!|l+{br^?E0qowy??LZQN5 z!eSnS1bG6sXdr@@9)xCJ@Hj_%dWlhfSHoD?Qc&gR2{KRW_b_&ZIq%_5=q*CMW-usE z;NoyuwN-FU+Kg1i7Dhky7U7CDjPCaqO^kn8^iu^MM=ZP;iaQfIIapJkXQBByBEhQ< zr#fSN9nAr6Nk{;1QRsQ9&f97?@U|{Km;!S}7q3r38DWa7%&WQRrZbW7B*K5#;rJ*Q zi{#Zp$-9qeqI@4h?fZzH%GeNE*GB}oUWVexvpPzH5W3t)B-Kztq>c^Md(u$t=ARVT zR}82j9eVR54IfGddBF zSkuh;k+^Hg9qkXCDChq@DXyQe1bbrm#VeXc?y42Q^4eE`1TH`YW3`_z<-ZpM^pSi; z|5}2DRs%~J6HEvCiDq>IkcI9u)WuZr8VJdrx6>wX_R1!?k}2zZVi;p zFSkBp_XB9U1lk4~IMy3P1^q>OP!9&;;~?Me1B8I##1$_I(JhEB^cNXHcLF4Ys=dq* z?NKd+js}*)d@GRB2Z#jat3Y~XfM^jqlp&qRf~&xbw%3!u!zA#)Ksq@<;3{PWpt;`w z=oF-k<#GvR45XxiB0X_!tun=s=CGeO?pN3cC+*6EVSolmGy*lO4hGQs14Xmc6p6Nq z^-0D(Va?KbRaC0>vO$oTQ||8^L&<;1!~TJiggJo12B8ky{LAZ*^BaH)B+zzH#(|(( z`dNpigM_tiKZazDfiEMR;oT&M1)T8D=&QmoHu)hY>-za=oo*!^wWcdHQ2(CrI-1;$Q9E&+Oauo zOyJ7a8X}r`AN18N-DH{itse~?0a=UtS{LEvyNyw zOF$o?Lw)Ik7ev#z6cCav^g9op&OL1-2(g_$JDckiFA?ha;_le}#D^8ot7Os2IRSWtqRx%8hkF5MXMxcSy2FN&$|%E`*qb~HKy zBBD(iE!rzxD$_B%8zU-XCq5q5+qE(Ui~+zYPin*e*Lc#zF`|P<7f*H1iaY5dbIm(CrD z4^j@gQLk~LgEGdAJ{kuhv_Qt>qwzuRRyWwSxz3O|E=b*rROLb?N*yl}Vm__Jg>Pf7 zq&6*OZJG?-d9wVljH@{a!sK;N%iUhtiQqHT8#VRAc&i`LG(yCWQrbm*5LJM(x9K~cL(?rog`AH$$ zL=miPRp^C@5W*~lh~MFEZkZ^Olq7{p81BA-tdr1WUmIkTrErraptF`AmXrg^M+O=* z38rD7fp$*9o>Pp$IW~Jt#wdKtg`y`5i}9q39wJBG;38WVA#!SS4buVJ0qfQOLR!S~_EIh6gnsB2t^hvL@-j!Qpg;x>hj|5DH2aERRU zMRJYvk8)TuN9hCfD9!Q}wa*s`LsucajBh@fT|PUIVw!?{yF1qTc|7&TA{750i$zQs znvY+qIupn1gEFL(t@U~j_G^e7Vtaradrd_K$?>%^wfPvK?Ek_7+OP7D-9Xi*h&1E3 z4|Ax~6d~MKJOt2K=&T{w$2b+K?@STF#{PKNGe!6tTjAjsqS5>WodOsX|7ntwD-XqPDcP5H-LdWc^Xa? zEXKpT(=g8&{D5vO5Pqah6KZu=&|s3gUSdmE4KUHuMYC#O+(YrN;))x{+|QwLNDF!a zX^<=eA#t1yhX-d42fD*N_`Z|kbP;cHXE5j4#7PYKB3cS}HzcG2V)YAP#DBb(LjwxH z&HMKlwo))j__S#ud2(8&-12CFPjPuBbV{lu?cx9HbhRnJJ+NGoY4(FVw?l2Zb!R;LS ze3nSJL@|uB{Bv)CS`lJT;R{)kS_GhcW{b4wBmc>orD)A|mkZ*(&0I4Hz7JGTvs=hO=WlXiyS`3eJMX?>&9pd)J$g9~@tB&oE2 z>GmAaG*r7$Ufm$}CfiCNJBTAr(M`R)p+(?j{*4@ZqX@jrmU+{hc}F=AYPTAW3JPI zVv!#0uMNF38*Iq3r9K?<9B|+DL6xOk?A^x98 zZ){_r;z|9qz3}K{6>B$fk)w&JU={2(%93-aRmir#fdsgTl0cnnWO_ri3_bEsc_|wp z9eb<XFFWPT?=Wb`$|JCu{{2vgax8i)Y=k>VADc%1Q{r%CYPbeGFE{gPK^Am zm+|QWVGU}{C{EICm+GYd?Zc#7;&j>q%!srrdt&JdDbXv(_~J>6B%gl~SkDw5hZ!8&eJIDt&!t^B*~S zoQ*f|(62=GHUE)QVPOkw;Oqgib}kdGjDdK#&=E06YAqKvjn^;a(B7VLR#gNPf3 z)$n{Tp3R}gtAV^74=fnV!n01~U&z@&-n02Zy4KpkrL=yv=wb{#o3rFy)cyXM99sQ2 zB{2Nl87NWFq*3gAfB!L@?qY#X~^sY6AsQnt@@3w_u-k?rvgxP&L zKM$#_GcW}ZE7yo*&(=ug{(x(&YecM9P3P;ew#n4Y(9mrB_vajndmmGu@A1&#eK9(u z_s^j7GYk;Qm8S6wB0tz$O6!JiaQyzhFso@GQ<}YjvxB7BkFJosn3HG8tqhiTqi6Z^JLy^XWjwMtB<@Z>qJwJDl*UQ z6shY)hH>o=IW&H~hzidIWfU|UHD&hRS%iX)${~5uNiJyYNiL|NF&qew){CT?b7umg zwS>6$14I1#gU(9!M<{O92~5g9LIv6_mUPcM>Hfx!^>*2!!zI?>ALztKpj!h7RX2!e zkH;sN*xeJHf8-%syw6}qHT#lUi65`bpdT~D>*H{~1_kdk(M+dcle#&Qg|s0k~K2_Q@Awh|vQN9Bt1{I}97yj#hVuB|py3AIfLr zXNRFjn?=J~^FXoG)ZHHgCCHR|IUa|*Hw%9y_Xz#68K&v+A-x5*G-9E89HE3Qa23`b zLXX>mBQ8ZC?J&C+RkR9P`W2WjojPI>pyV}dc1egO zhjcaGycKHP;wxM^*eV*=`YMIX@{bYCA@S0KzRp(W`3+>pAE|jr1M+W@!3f8mzwLz1p@*Z5E z+OdZQ?Gd9C+aCI953WY~?WNGY!oSv|J>`927=kL;J!N0`YY%nYE82U%zFV(Iaw2m+ zdk=lG7wfc9yXp2`oJ$A>GPHG%uF3WGp{if)qMrLiYmY@h#D%imw00jhT~cG58zmhS;jIpDlS-){X)XO8u-!0@PbwZNI}pSwgwX=9C9gk`E{*&2 zZM6KLsHF_uMq3VwX7P0Zhgzqx-o^IOHGAM#)c&ljy=6OBDnS1FY^U(AM0)J@t+Mg` zwSBp)m3kD8HFID%M&eQv=-~u|{9U_^=6xkvCE9cZeE&m7kbU{RHO}E6hJ}|Z5gN$f z#BEgRkm#dq-$H{AiSUTUTmE;CPGP?D3Y{Tz0Ql@6m5Nl>^IOr$^02cKx`iBvL`H*O zHaqKay}Q0Sh@RzK3w;Oujd8|BGjm5k!D0SV{_fgB6Axo+J99Jrco?%8zs))2;W|8T zej!^(TTe+xL|WWByn>`OJ0JJbh1)HPNq&g3adHB`rDlUx&WsB-o?mvB_8t+#jW<8X zTFk6Ct8(mf>T*;}R@!||Cyt76*XYloPV)4&2S8RI6g8EbpHbY`!d7?fXR<;TZDTeD z^$BRe(jyKI^IYU{jWTL;@k>AidwZYL2VaY())dBbvbmv$Uh|8Q43{k7R4!8M>(h|Z zkDgYBP4`J`y`VVFxRd=Z9=EqzWn6aQqRY>^BX{g5jT zft-4gmVJx+-T^Xy#76r1ThYsS_v0LLA0BM2dg5bA(_ix+j$!5P;n>(6^_|G{8z|HD zTlVAgrIGa4-g%L}|4!8M``5x{wFEmj4Ub>vnMeA*R)YXHARZVpe@ReuKpNB|bC@Fy zu5709b&nnVATOTAe1r_g;dX_udzalz<&H4=g5G#9O8UD#ritH+Ix*EZG+e^ zz(L{%c_OGM$Oq7zUqW;v3}SV*)=q z%Xk)GhXgq41i1Lf;c)T~fq)Yf@gWzHGRMt@I-L}Oh7@|~qzH14>-yMXk9bVWPl{F% zZw!H*ECepD8*?3^wAYZr4RI-m8@R!G4EihGgDLow2vGi6O$|?p5xRKSof7ThT78Iy z!F&*Vs7tvjd7V&hA`)%IyR1{tVlp$U@T!)xg2I0kjeTPpmFHnvgwAtS^IAb;eiU<* z&=1M+BQA&tLCt;=FDTXC-@5iE(b!<{q`PNC(2~=lwc#q+P9y0g6`U3g46|t0Y0)=* z_j_gf@e?|v?Qns08)&0p9EUTkLEQe}?RD_>CLqgrV~yhS>lRSIUqqLfnE))zvo^u4 zMbnHb+`~|dO{}uTUs%5fw>vCIbeP{?O(V{Tp^-1*E#0)`@btTiZTe*EP={$o4<{Da zok`D5p?haUlACG9UKBqa+y=i_3tc&%$3wZ$&%KiV5v6!`Wue(*U5wU__MKsabgF zLK_d&y0^o$WU|$Svn1p1IUMcyI{V9bket|xpaxJNhj}GRw|A#0=frJ8F^&IKeB-(G z4r|937!Luz1&w|Xn;ehJIceO!UL->R%*)o>qIJxQD8<`&E$ zow?(0Q~TeAZ~o@Db!+@mb$N@9*-;_L-<&>ccO)ZR^*cE(U2Gh%XC|KZVA(?V8IG_A zgt!Pw?*n`F#NTj?=v}r?F5S|5q<2s6mfp3CZR+2rv87Y^V`kwU5Mf#IjUcR&ev)Hw zq3eH$I#lhv=%(ynOoPvh)MgV`z!fpJO#COas97e@Nda`|Pf^3h1#&k!g8$r&(hB}D6?}mgrvx?`5HAM2bXbb2 zuu0QK7fLYuD5hEBeBEj{i$0uYyF;T2ZIJD-5uFhWJ1~6}NLJ{{7gDIh zC6VI#2&~flOTte%x0u#l63Kn59Co#G1yJ2FOHf^y7iJf`)-EoPYKGM{eTzYReU_Wc z>HmWh=du7q#UtdD;Z=(bFuG)NWi1TR+weA zmT_dhFpX~fCH#D!szrZqH>~O!$W0TSV_%;B`cP%;P^XcZYt4p|+X|0lC0@79IBGLx zehHz&?jA=m^tUDg^2fY~uFXcw=>sCBgJm%~;yPR57m`!-2Y-7wMlWb5E|5T7y#dYY zDg{}^&eE5UKK3yWF&30c(Cj&_LPy1Q=jXJ*Ev2#>oh}g;YH(Rpjj+GSd{%dhzSmZw0Z2g&h*fWRWzA%4El8eg+VWh}l2fm2NOw`q8Z=w1B>S1*XjX-rK8VFvoSM8sBSezf%S2>G+wAi1C>e~Xm- zDtT4QxcKy(!_g9rsHdQoTIe87xtKSO)s|HyGw~2Cy9{TNX|myH>sQ78Y~B?jiyEW z3eb`B`oL(gYI2?DGvOVbSWgrGu}9M0e?(NkY`pUjY5_B_2Q0Ko+&Ft5k=s?#FsL}k zWv?94N8I4)GKO0?C1{1L;`mcrdtVg`4gPGBL7;FydZ%}K;UUcpRi4tsN>#3lfX2EC zR%kkYdNy(Ea`FJGsk z(Mn_+N;yvJs}CGA{sYI1BL%w4roTw#x(1TWb4P7#7V3HofMvZjwe73iOU*k6WXsKk z{AYV9rlFXwnY`e>qQOlCh%qA-S8`MEGbr*xi*#;vwbZV`AIhBm$WcN0asXGxU9(?x zhDK?Z+M>3Jx9!X)?qiCo3`KClupVR!o`p_``>jjjg@&3zV+#df``v z`%4n@L8RhN85ZaE&Vd`p3-HLY?~=veC>mk)&M2y9l||kzJc7YI58pd?s%!<>C@7w- zYk9{U1ve6-lB-MJn9{Ck+YYMyH^sK(UlB86LVXER#U-y?Qx^UBgr?lW4Vns?ayv)r zsY~9(vW7@%rMHR{_K;+N8^NuvE0;6z+5umSpM#C!xvl&?`*A=2ZB*q!_Q z^hbvAf%zPFBmETCHJ;qb{A9LVLU5RmFUOp?sFgQ6ur>G_^;Ho~4Gs|6aP)_$(A0x$ z{mPCm=IX-WS;kI`v!*Q>)}c1^SX3d;n%xB4BE*$T6D1D>Z|8aK;M<;%M%rwOxh1;i z@0%v4mIYrS`4CK)E)ZKWDg=^Qg?4R-yBp6wCh)La&>yt23O87@0bV%5S|tmrjSt+y zzh{Zz2uPMSjzx$6^-SIw)_y#Wr~u~dQ7lllUHmecVJ-RaHVRm3S=J~{wJ-?VMLb90 zajE_ohsPCoWRN%p;TJsD!{ZwLF$IrnC5W>Oyzj$1`#8)1Gs685<1*_Kh7+cpkJnT9 z&pOP-+VK7j{$~|!kkDCZ)oq*(v&H%NFv}a;Er^Q%^4FwPZ>Bqb##FJRqra(OC(y9)ddz6I8!zqN$Zn`U$|($#ySaaT;r0hEA3&NR4%PsuDY~>-qEGC`4#H>KqTi+VOpT+!aSg1M)3=G zBz~D-ikd6M&25B+V(S66CmW-S())(l8+zzYEIe0KYXJWDNl{w6JLjTofyu+ z+f?vPK44{A9KZu=kK+K|SWN{tk__%Zqxg!2%V@LAgmbvj#(d-<#~yA3?Wb>8g;4d9 zCWwy?)QXUq6(Kk1kktWN0}+kEoU;`2GpLbuH@?P7GZ`i`&ss&h&_QpSo{WT5l1?|+ zU3w2l<@*ve(x8q~E)~-SgW4^8GA~LmSSy!iI?1IAxLNvYb0_3!KlP<-MXk@DqEOTT zgU{A=ikf9e?C~m^4(iemrC|AAs}soU;cl6YTY2P-)R(PG$qbwbG7YGpu@Kz^CKSMg_{T{ok*KP> z>Q_a_s|-A4FsbfpJ>~QS8se@-E5ELz1@3A;r6iC3bywq46d-dwO$F1z3)?T1$E*}v zQI0hYu4s#K Yx4`L2 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

#E0wGjw|+ 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

    (;B(mm`;tGLLAVpC%uc*4Hnuw5U=_w3PBj9jyZ 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 |&~1XfLe>c`V-!H4v1U9Xu0Qf&hUlh?%-^zD}p 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

    k`QMvBF0eON8HOr@F9AWR@)T07mEgJs?s;Ij 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

    xpFQQ_t#ugc0L{YkIBwo<< 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

    QZ~*KN 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ⅅD8 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

    Rg8#m4YvDTP>|~6o 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

    {Zgq7&lJI@Kz@ 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�tA=>=X!`mj{_}>&Z#>?A% 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%0DE_y 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 ?2 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АXcLg&S7!WfbyN z{O5FSD67+vH~m`c$qFqoR(`EjQHu7(PPo3-#52f;B0Ki+%Pwtl(JM3Za25CQUNf-V zSbiVBF+<~3_F^YlPW*g(3*l?p9Ta-`X4xZDJ<89248C-xmd0)xJaCp)mPH%9*(_}Y zJKvUXM_AG|*aPYEXY`vt&76r#f07Zv!)9v}I~Da18A)FjkthiJh>V&e0t1y$Xcz8FRE6*4G2+{NGu4H(;ciRVD`-gH!oK%uU59B#nwmn$&Xwfz;DR>KUA;XB9A}o{;<@{McMA zF=4AkB;OXf9qf6OGzuz8BC@deBw`2+mm>gROrNXa8=_!LpU3pv62kk=(*oT40hm5d z3v`qr`N@|Mt!G}eX+R3u4M+l6A1P%|MIfy&EM*6qOiQwTdIFP$J(dS3molznLV|>v z1lSzxZqFw?IRgx|{Tm6%Q?tc7Pib%pb!QMosJqe%fYVYcSKSJt;MxeuMeXgsz`SnN z-piWy4iYh_?g2frh+uRIq8tOy%;VSrq)_t(2_vtDx1#9|P8dO*bgLne@CLidg@wzR zz+~YA3`&L{KF&tS1zJeCAAmz;qOYs|_5b(l z>N>>Pu>jroGA1%$rObd^&|sR8PcXy5oroc?)VB2(%uCV-6J0C|*p4-rc1hf0C7O)k^=qrm2+UM&7OZDsKLdFs`3 z3*iU2mKrxNI~clYJbx`b*R2156c%p*AaoyW2BOpQ5rOo@NfdloH<luX8evALf79kk;b#|QWq%S6UAlw znbL_yk0;P7;CSID-gT)~*3p(Ei%w{vj6hA-ejr6E56F>9^L>UbV{O z{~UhtR-2Oi!7^-iy7HcvTCT;%b$?H$&{aw6DVd~|KLIJya2x>PS~{&sI4x%p$TVzE z6qjv;$%Lzt*1zXJEXTLxs_*&fE?|x3(FrkW^*kf|#&kpnXiy~3i_%%tSlCT8J zA@JfF0_jB$qTn7ClM7z#c_+PS_>LPZv}TSsZ&4kcCzP08QTbvTotQYVoom7aERkg= zXF6@$L3B&L0tvLNVX8zGpIuCUk!!YGEyxltBK;W{b(|Mo9*)!m@J;R3tQf~ znDDkMDWI*ii)CCI!cr~Whxo#`{y9oUOC<&Z>02|Yq`Rr4LblHWQi2PtkR;{b^7G$n zH4=Y%0|nN!B4QeAE1Ks;-5dpX5kq+Kyn*l{G*`i1qM(JQM5V^%lDO-p;9DNI60K%; zk}MjR^)dpPP3mO9%8{BbrJhA#7Az@sgvo@;xgHp^a@GYX8yBbTzt^OoH!O(EQ!o$_ z#JIg;X_7;k9wasNml`m=dD92Yj3R%H8-o~%Vq z+l5!;2laJ6M2wOm8&iC04KhI%BHz{%BEv|epd2BJ%XXXm1Jh?~S4Cb2%e5=lE8c6h z_7f}lC$GImo57AY;)mC0b(yOXf4N3G#X67Y2i9ssLL(Z}=8(c3SiwIVhV2^G!}+kc z!8;tsK=NJ2@*&X)Svy*x``^|z;$7Ei)#@%qT*2l(lTI>%doM7ql*{2^^$MoYc8@NN z@Rg^CyuSjgKcyS--RrPE8FP)lT8GUm^)DFB*K7YV=KB+G{vCE$y-MH#6@lb^A#7P-b zgaFFxvb4D8v}g!q#@quRgy+i>-UxBcjoham89`gLZxwbdj-TC%@9~8*pha#50#mzc zxo#wbAch1L0SHZXstQdb5x@;JB<~&yO@4Vy?GV>#+Y7u~GCr!=vV(HvRU$ED`ESHU zmfx)s;?@SHS0I6c^qmxV&g80%wJBh-g7B6A?FI~tP7g4@A2@(_+S=pSEoHIb9rDna z`~x;cwm2ArFH!0Je6d6LXtb-XM;z|_ji34#ibL2LT1W74qX4^G;#^T)_;7U9Iq@Pb zFLa$xtJ6Ni=khu`khe|m=On)qcA9y+5lG>B5&+>u-%27;)+3OK5=<0)8#I}aw@zRv zZ;@hN_xa5o*z(h93oo-%tJ-n$-=u`rP|sA(Rq_}};nFe!D5buyD3oL&kO?)CC>Wxf zOu1{Q$NvtNTRkHF=9_kExV-Tv-fx!{+M&ihQbd!Hkz}x}f3ypm;cnmMMR#izny&)I#6a11xMoYnC=3D zh2@meSw#i!z^g{u5pAlXtThTA(`qS5{l>?&7VNJuKJmErNPpT8AA(#_K}xG2r>)&( zktIK2lmw~sd~!`OKoAMl7l1G&1&f`i_`4Cvglb9@mrXaBa2{>mWq#sE&Bd1gW+a@@ zUV3zOoFgx3!gH!nuIMZvh3KUKgy`=)h3HHKQuJ`5;Ob7vlzufCQ=SvRnYoZ~&RBk4 zn_56|85Mum7PBOeqd9rqPRZ4i45Vxf0SG;HvH>_PBN51Y|L%y;<7YA%1C<3YI)nTEiq-3(D zg+K`Q=p5nJYue9A8HaMrua%8>vQi|1MP#Tu@siZyH0yC%Iv{{l6ozZyLo&UG8e)R^ zwGfxg2l3z`qt2h&B_-+TfgI@tNPv`H^Pp+-U!V%PuDFhZQ06hae*mOF@o zB}%9!O})?!AK-0oXew*9-}wFpzKSI6HgcT!_*TY3$69``nM4;s6{2s^7|3Y}z(`Ie z(R!j_xyfWg5`6^5Ormtd=yO{u=&p1#hTqc;dEgqFkbktZjJ?{(-#yWS+OOV7TF*g1 zW@fI|qTd0ePVo?^LhH}e1v)L%Ez1^iK2h*xQ8GolI$cbjm+feNLQ5SRn%nf|VGi|2di}k^7nHR;dzokbgwr5<4 zR!-xkqKsDD`?)rU4mv;A_PZzbUY(Ow-&02Y5-TZqX7m*xXw=2+kg_Lq58!;Xf^v9s9_i{HIkZ`S<$* zxC%C+624hs`36U$P<%3idG&0Jf}ecg1GBMLTQyc>YaPaYM5o`3OMjn%Gun#& zi3Kg<<(Tfv$}KX&n7&?NPsSU!-Sudugc#NG>pv*$@=N2d0(yA`TZlh-=!@C0=LYxG zJ1BI0bU{6y1;z75h4hEYOrv~Z9nakraPf9U^vM;|zJ~R=x7w_%A%YEw9X zzbvAUXAjbi;YIauhF2B0d+DY1cVAHg;?*o$_!%ULOu# zxZYXGZzPn^uj8gE9^#|d>@r{q46>%pk%#>@{|GhGs7oMVNAOW$*aZ4RhPjRba9Vys zAPwsvqS;1CMr&HUcn)Ok6k~^vK3`!QCh^v#^a^p^CXwG4;le7CAXS_KQZ&&i0EGCo zBSQQM1hUy`Lli6%noNuFzUw5uyOiEA@rpwjepTM^?3X9CIk8ZJJV6X0_0wS?)kabU z?Z66OHAlr81IH;%&bhf|N=BJ;Z?cY1jSmCcXRJuJG zLrEP68TCmvlG+nnmc|m_J)t{4V4>Rw-3D}bLLhZt2P^nK?UQ_{rgae4tfoiC@LB3>YvKZY+0&|9#6c!VMV^-dqd-v#KEd#)Qz7EwF@4{4N{UyO87HXQ|3 znDfgX0C-p%{gO0i5m9gZzHy%KAI`P#_`H}Jw@qZys4r`F{QN8teRdtAJvOsJvcwR zdYVtFtG80-8z<`Oq0I5-i(q`|`kVH~J&1Qzs)UVLz{t%>?rUJ6bSJ1Mr3xD8E1(K@ z9;_7ZR9OXg1Z4|RaQuF?WWweocQ9q)>hSS7o0Hm{EZ}y!mDroq;&fynJTsCT=!+FL zDS#IZ(+9IZyYmTQdP?bD-N9KM1MD<6(e~8O@}%8KuU_(kjr5`Hy4@JpNbjK}W@@4& zo{`nNCN;y--UMCbB5p4OAndg+1K_l@LjZeG=KcUL?0vIbFyWyXq%>Y2(RoM{{WN={ z@;6QN?r}p@N{#u5xQhfx>DPc1GNuC%()m&V*u#lHO1BdQJ-^9R2v?e`e05X3NskBJ zNFwd{2$n=#JHC>x%Wf-JI;|Ai6_N#77E1|5g_$INg|5NHpXNMz~v6WWR(($-WXtJ8uW{(HSrG*aK)vM z_lig#R5-@f+-el)d+ex()ws&9x6nVZKlU2eTIwSdx_vl8pQ5B27b5hR?(Cl({Kshh z9h>0C569@Um`7WqZLA)nC`o*A8~s~l9uJGte__WranE?YhuSTce4!QBRW9KR5>vk7 z0)jAQ1OVaBq;%oWQ3TST)!{aOh8T}J>MCO!S{SW6>lUTQ=x|ts2W&8WgYf;6g;nwC|0H8v zeiG9Lknui&*z}CQQ}9<fUam z;Hq4*1cd^(YZU^cxLpecoD-F9ntp&W(cXyk%#1#kY(B87{&kOEY^0PbPf3#(zT$m= zFtEg>-_Ch1;0~e*&CezPa9V1^0GSg8QCzmGFvYAqMZijx=QQ@z+l<6+I_^Qos~ehr zO6gOy5vil{+i9F+Ks_A?B=&n91xs}LkJ&yI=sy+XP-^p?mJtYK{dpTkF>R$KFW&Tc zNu6CR;%80(D0E&9<9^-s7?vK!`*hcn+7|$iTk%nz*yE> zKf;s_hSpENpeQws8Uyq}N>XqzSwnqW^3YssmH;W5{Ad8enxw(P8jb+gpqnU16ud%Z zGNFx12F4uZE5ZEnK;5_7;hH3zx^hdN@aj3juYoFrXABa;gK~v$BZ|v*aR>s8A(DEI-D&%+ zI*F$5Bz}3Kn-iZHUxFIF*dMY#4G^+zBwLV95D5d!K|FMbUMgQKdMx%g{X2vA9;!c}oBW39e(br6_Zz17VSz#X!Z6+6akVNLLtB3X zNS!SA5kcf9ny8nc3O`vtVM|K{@Dr4?M8T8y{UsB&{sw@Fw*Gzvid(WB#PgDf9#8as zPe4H9jlFm%sY{4|addb!%f`M!bOr*Mgu|rhIVN}dRcl&3%!{*q4ih2$YGxHNOP+WU zk5E@NHVsF$QVtsDN9uU5$Ifdc>vop?mElU(`zY=m^A+STQ}mvtv&+Le+ISuKBi<00 z>2qm|7~0I;!-uBo{n?l0`PEdtMS&)WcDmJYer%WS&uybItNZo|pEg<_!#V|WWeggO zf`LZd7=4X}1)k;4C+Y*)yV6|$Qm@0_P2h9C)Nd(Y@(~VwsnW(MJV}qX*oPHIa#+)t z94qbUDANng=Vk0A1r3KXFoF<&C*rS2?HLIoNq$CvBtc)*ktk>kB)2uKjA&)n6%RIc zOvPl3p4pkE-%vIgTc+zBnbMgnF1=HUA%#ix)c@%AI~t^XRXErf;L>L)U4JepjEa!^ z#H?}BDC$TGks@<`b_=nDJx^i)I4vQuIVEv7QP7dJK|s?93k2b9;X=W@{Y?F%`%2GX z-fos&F(JW|$^xA(SPWjXbR7aRcNEm0{!mRC9Sy)~F%Zbo-b_TZb)0+KGEd*Yw(R1Q=Ic$}7v~G+N9OC% z?(zA8je-mG-U`d;V+>!YPj!#G@Lx^XN@s9=XlNnpa^vipuoX2f6I5aATk4CQ7V5iY zg*!qNe7D3{fS6j~q+T{}u|#jgM*hbqEYXAUP|2nx`ZxLFzI;a-Xiey3<6I4ofE1;4 z2>_vCBXyTfO9ld2euojoWt(F%VNdtTcYN*&-J7+3$2Y9dcd-wup?{0f7%Tjm-(97z zN(_GmRhapsq=VDhAXnW+5QIZX0ED`}^@O_h2&6;7L_tq3nXGBWM0IhzGOn!FCo4)P zqw89|oqLNi&&V4(WYIG;SNt>}h4=;lg!sr>Li|(&QhZ^eU>*_5GdJjEO1GCB$lN`V9aT3KY-|!V$0u=38wfN>5so*(AE7f=>in8?3Q94e;G4PBiRaB}2-zFR+_!Z8HvZe8 zZ&8%*jm^9C_ZH;`W5a&^oIAVU)EISGpQd0P>BAA-T~WFi*+=yejCGFZBY)HnLU#(dmlQw&%B;1p4Hin?MyVQ!b%TxCE*&_BJ(g1jRsQWP89+ucj{Anf~&(2$lUpOMkx0;nV`G_;+na0>kPO4qL*U!?nclr z`dFn{;Tm$C(UtN84ZSng89OfOFAFLjeEnbgS4sRpjuAIMLaa2x{UD_P`i32#3L}oa z2jH|QA8MMlZaPu$OKG|eBML=`MmEX0H@_00_pQ4qF|b4GC|!Rz?eSw+GV)z>4z0%oe_JVW_v1M z@Ie2E4PR_bd8i*#Sg~zJWF{6z{RVa`D9#9^7NkQ2S&MPo+ydHG{%2QXbCzD*oh|y# zIQm4-hugt78mFG>g&F(HlV5(We+ii#U+6E`hP6iPm%2mg@@X|Zv8D}^Gqv!)VW0G- z&l*ZRwDaBogf|uM3U7KLKv@Un5qRNEA<2Y0BaT|r>WUd!*cx8$m3|@d$|@nfz3fE& z?&V5v4pvdho*;&h{^_=mZo4C-qx_v93Wjo|l{(PnV%@LgY99VtUo+_TN+J0I=J_$A zt_I3@R8=F1lLm_aB9aj9E`_&3AT#0|QE+k$+8`Xee64BoAPNU?!Hd6{Y4|4$T)C3B zeWM>{(JOh;x4MfR_?Cb3R&UP6eQR8QtB+Jz{uR9ad#r!mTh2$l*JD|mhWyBTJ%YW9 z<(3b6Y@2-zJaONSc#zQN-zdOdajvmpSuqmRuP%+X>id+!UEZPD;<|QJ@U#Apu`{Lq zzcw)tTNfk-VtqpS(hquFX2kGcKIlG82a&KMajxNES*{qy&h{(df{;UHf0tMRP6VZO*|j7 zFII{f%b9(M!rJ&7h4a~aDr`VIW4OgWQYn6C4rTPzNgn2^%KJISzxnO{in7-z?qTnO z#geCda3OmfMC>bM|3zUJ9`mn@*jHeiakZlMI7i!bQBcfX@^jC@auo0)_{0r@dB`Sb zzN`|8JRV4G6E3PnAUzHhk!>AJb|lj@!Z^k0BGjMBXo~Sf#&n^}M=n2CrpR>f0Aj}q z{^k9ttEQ6q0x^XqMQUo1tLY3;@VW0S0`S(vj1|02+ce+9*h)& znHgX4bH(iQyY-z)QfV^3$uk^;rToV|i2|?RI z6($`0QJCO%BFBVjM8SOwlF6D@`USO7*MQ9|3Q%yfUTNG_!agH$vy&`H`yfBeJcST5 z1-(fMmAW4hLn!+7H~`$&1${E3mJkIib&|=Nc0_!b$#NQlyzPcNJLlk)eC^SWBnSCH zt4=G9f4W1?3TU2u19u3c;hmLVG|z3Ve7nCrlG(=@Z~g7q_V9i*nMuRe7W;C|>^lY^ z<%JIcluDHh08UG91TvMfzz3$nCKFoM7Km%Mt`|ldfr0j5rg-wU73@=$GW>D{`$@&y zSX$A(OG!!@nWOXIPDqhSa|cM#m(2knbbh;C=)8ph&LYM85(T$+n@rGo5EyeL7&FpX zSJ}SOUFpxGs@ePXDmj#t&=g1CmaAk6kQhwUvI@SM(I0ZI=@tM^%LD|{Id>7wR!K5i z({718=mc!$Wq`E*9%HZ}+^f31hBDG1=)yi+dh@qtly2q>uNYj}dg;BYoA!GDsbJ?>1|)J;a@WK6k7bQ`Nlx3EPr^l{F}V&{W@XpC|br5|x-KgWr|nU;3gP+!O1r);f0loR4$ z>as<+Nh9xIYUMsWw65LOW9ffTSr437i$Z0MoJSjnpyc%?FUR}Fr4I_Dw28*naiFro z5xMpUVam;$9=W%wF1#=7IBF4gjCdyO2z_T$CFH3hydX`8oCl4{l&@#;$$AhcvL4 zW*5HT(;L`lv6rv8Z$o=Tmwr8<8jUu!lsHpH3Zz57fFKorLuVPWM>iHCd^)z`4`~>!t5{H z&xLsMg!yVIp3=x(gRKcSRyVR+F_tUDcQ&?HWfck;*Bjf5Fm~cF&uVJVVpk3sS8eu= z%BsW~IgaxAE$#i-k%#3-y)Uf(ynMAduNp15A-=uTzFQZL>iXhoo(PmY*=FJ3uH&UM5x`@I|z(h`szaHMjDir4qo%o~m1(atB2tk3tBbl53k+ zODLY0t2mz!Wvgs*q1)&NjM-H^>`;@B@b@dh%S76jyEmyI3W*QD7-?V3{;JCdM%l-? z`(}Ichf(%kMYiss+)cTNa&X`PPik$C@=^&!`6{q|4)86l?Ohy)+mWpUkdT(Sutp|i z_4Z~tzXqzXHDi`AHE4EDIp0PUe44>@f|x?HeJo(gsumggcjmw59mN$td7_{^z`qx9 z)|$67uLBa*XC#;-(%&Mvcv`f*eyrPF54X%sw8lQOenEm)NO;9N!1DlG_Z$W3`56zo z*Vj7D0n|S8Zr@|pDZ}ws-0%D026vCK_rg;;{bKBs-6wqTy7vy~U=b6$M4Ah?^g0OTY0FeOOTmUc@M<9JjB?`vklF6EO z2GT(!>|`< zdBu}F>8}CcNglf4@YYGQnmqrL7m2e+7Mk~tqDL$5 zu68$@&*d!$HbP~#{cbTZ(ii& zy4;ArZsp;L_QWE+^H3wU@>PlUQC@$q{has77Nb%pdlSWL$(qm6Yc}&?o$X;o5`ZoD6g%@B^<%co5Z6IV~ifjZDM+}BkH9Wi8Pr0)jQ}`tfqoeKolV*>uw0j-(cs94{f~O5# z@^lF0gE+HS5XtAzT4R82FQ#~H!gX6f-^5Kk5MT0J{`mPbCfnI`35CzYR&nN&J3(D)DAR?$yJ-hc&2WoatfrQwAn4$&dTA zc2$}L&uDak!|505=On2QX_k#t;|8XinV<@@-X;UUXNHsoUX>%d&$zY!3#juY-C+r^S;cKiIXOHz_(Ql1MU)v)J zuz}Bw+Vkuc3$Xzkd8g&}SXTKvzG}I>te*dU4JsU{nDI?0$v&%?gF!;ViHhk1 z>^hKQ9moYhXj+Pn0Gj?pfKrC_(l3bOvQ06WN@e>Pz+_n?ToW%-Swoa&Z}|`Z$Jv+1 z$9#Q%&wP@QAZ9{_SVEAH#FkiN2?+@k9UA*SpHFPH)SeKE36m*fI+~WaT2*N)p<0zz z(?NxVTH6w8rfs2e>-Y_ZoDGt2A#odp^1N}A3r!kZ{CjwtqwZ}I;vzF-<8x*H8&f7Xj^-5 z6&1#~y7%NbYsyZbrXOJM@Djsa75RiNZ}dYtViQHEyJOB4;~MPP8ywzSjUy#B-ueC- zrW4fwX8+n471$o<+_84c!U>*S;OT58!UZrkIP0=MzBUd}H)oOCjCP;w88nf3Ex*7H zZ!>n0-sr`oZ?J|Qw1oBg#@Jn&^C3(7#)z|e#pMJ_CUGMA;v+=z{?o(5=`%c(_XUF^ zI=>O6;2fE|5c(n+G45_x!yCJow6OF`an|XllLO;$+|lm)ovwB|RcaaXb8*+RD|k{vg;8&h$G?=~CzEtdUPZnM;HjctYp-Y!RZ zLW>HNt%&6869o@Pxo-$Zc{v=xJ~b)D7Twle2q;fLjGKKP-eNDmHD;+>u}{Axx zR#TZk(~@{u>+1}5@q6POY1VDWfIMn+(gAkl2V+D3Q}duQ7dU5+x#vn-{0sEfj$h)Y z;}6CmQol}YfW!D!_4;$M5*ed6M{oFn6*!EogMXQWh+t^hmqvQ4(q1&2AF$KC_Bubu z*LoiPaDN8#+iCn#a&BiyyNo(^awn`QS{~RYJyDo;H|!q9S!^u1XU4IuyN#^^p3QVG zeKMAdrB4B>xIIiPeLTvhwPeS?#}`ure>7@_v&-dlyAoJIH;>=lbo&meoNk2;INj6@ z%jvd)Qm`NGE(E$=L5!Pj(=IdfkC-@S?qEIk82d{T%*?*W7$Y^E$xiGsMoMv)n3Rw8 zl;=6tH6IG%t&41SzENNA<5{Hd^9LK9bZTEee&`;cp4yq6$~WFJ)VM&nno#ZF`W3jQ zB9hC27UUrb(J6rAIuDMJ5HHVjTpPIy4a6C&3(RAmv3*TDQdB8RdHJp9S=>J3KL42A zG#qtNi2Jbh1p_WEPva$o*!_dX>5{Q48*|7wEy8}1mvrXOffPs%)8 znT2?o(1x$YCcgzo6Gt5NRT6u8$k?)Bh)4+&DTO$P0o!^FV(WPyu^xwwFFee>eOUJ+ z#;4M+yB!^m8dKExLho0{jKR{jg^rWQj6K!TCNrz`i!rX@pFa}@+#EalC7m6qcqj1Y z&uq>w#-@h0$RR$oHLpemxz_VySeW&Lhm&h$HBPRN;D~`9L@C(es4O(^XLjuuV;$+q zF;?~q4AFhqBX+s|3vS=S(!D2Xq%RDIs&w*DK8iklx zbKeoJl=1kyE6rlpjPIbDW=X$7W5mk zPn*M5|7Pqdy*R*5{|1$H`fOJ3w6VQ^fxEP3d27gjDtUIgLpf~>ts*5)V=FHj=Slxe zWzr>Mo8Vuj!eotyIEzzZ00r!-RBM9uSIzQ4h@hhFrm`WIFtS}!*z!xpA=MuCq^+jb z^<#b5txLu;9<_S8x$Add$sV*81k0*#qOoMPi|f4@DA4ECTutdcd(^5F-z~>!9KZZ>{N4XUm}{< z+|TRc>{kXWoinPr$Gci@@qw`91QvD6I7{;KW?OF=ht|B`k7`OTq))v)2xq>xjs0tW zS{r61ybF&Hduj0o`r4_>GVXl*NKJ3?rQ9= z)7Z^)rUjpQueH7>UP52>4N;L!EuVP+Yy}mcBa)B7FUdnx+<6}!vsW1$K}8d#*rKy4 z3!Om>eWKD5f+14U2n=VAjzF9$<#P_JKc%xx#l~S$T4(l8v2m^Ms6GUw173289^0I4 zyo+h?)O36!)A*sZJcOl`7=wnl`I_(}tvZEJ5cqwJNWR0F01wA+L~#W;z-btz z*rMmV3jwFE5#z?G3%t~+an@(C))V-AWizPwN1TKHArTrK)`<9m4sS*Vv)>;Xrv$#* zM4ZWA^_zB<#Mz?Y05>X+H?i@LjY~{_d;vT)>0dzDpxTgIs9oUNkC(;7{0uobuHK|@ zo4xdKND#PmCnyD9#Gp2aLLn%G&)gthl{^tiXDXAPQ)Ze;za$T}edix7RRaOA46bOr zNdU|erEqf@`$v_Vg-4Of>m31)IXVpgs+9GBS(&BB7cJTKOZ@`b@xP!f-T9nV{o5F6 zvVBhYK)s1Rb)y2+r-Z-~Z`0oW8;kfApOr&D z@dvUC8vlhz4*d&wIO5-3gU9Uk2#!E}0j1cYKXw-a@Dqq}10O@>ZSO?s?eBQ+4uW*V zcN=`XssLtt+#`(Qwo6#apn0<0PmH^UCuWr+!LC;zu?La7H>Z(@dUN4bcyJO2j_Ax- zO2MIScOf9b5aZ_L;4F6RDGXCpv)G$u#_9FeZA20sc-a-h^?<1@xgHSSft@Kc8l?6c zS+i%x=q6jDpkma4j(=MZ4-O`#ii3$`I#Q{Q)#|aO&y4=s;+`DH$Kf2vsP-)Tnen7_ zWrHL2Ijja!pY>~BVixVTo;7%h<@$%+*yxwWy{48Q^ES~-*Xw@9;Yjqdo8(O>g+^M| zAdECy|AO?R$v*6))aTVF@ zweJ|m3#V@TPzucOm4!aBvE4P~AgPv(U9KT_m%<0K`o3~AsaYB8=_^~M;Fau{uN?B` zX)D!uEY?bQwgP^xs5E*dRSKDL4;gr+FOI-t_Ud+&SBjMVlwym%0W64ucuOQ;rK3kp zdAB5uS?+kQmGK$l7wy=zTJoFH)^?6Pwd6XIRBIMH?JuvE>@yuxbaI#^&6)04Q&)aN zEp>dGT@R4+q*c=#>*~p!)Y7D>j-vWMzW7uy^(1e%k${2{PM=~ zY|p$)UMe;tNUqCj2gyTdwGt#7tKPZjh1wp8gquO~Fx^}pGqFI(>G6YuFfwKKW)pcd zKC}8&6Zx%b3-5W8l|69TJ=UUl&OY#V~0 z${Va#`&+NLbc+$i>GB@rJm~TZ93-Oi22%>IM!Ab)uz!gdk~om~ohCYVwv+pKO2d-Y zc9!=^M-Mm(JIgQC(pn=c=ps*(!iGfp;1frD9>c!ZA+ za;%7#SE%u(zF%Jq0zAerL@#%il6Eq?UJjQ|{N&iBm)EGr&g)MtA?tDeDr(7V*5k`a zg2>|5;}zs5sm0!d2UpkO2&rYH6kJ{BrMYSMd&H1wH?BY1+D{&({*rmUA;%40XrP)% zyDGG!h#2#?5y{13CV8lWO*E9uUN_;03Z_wtE&4roAyBFiF>bLqz~IPwL-tquBp$~N zLVNTFU0J?Cekn-<*q;7!a=WJikZ4w{ahy#LmbmEY0)L1-`$M|8fR6YB2|h@OeR9Y< z*$*jQFR*a~zu`9N+jA9A66@(X%xu1=nocg&MeY z;}BC)dnaF?U=s(=6y=)^8Vpveap$x(^^r@1U*#)Rkt=gyqc!Vc{StUpcvfiI?GTYr$eWXRMw)S^kx7N?-MSt@Eeg#-?7DLlNU z3^T8(2#%;}IHlmsttg~Pp8_CY*EqIqusm6c_GWd5$ZMnv5sqy`B~%sy6CRf2~hz49)w4a4M-lILzm*)SQOK)%wGX-CLK(yyO8{um*D z?$O}!YPt<-j==G9oY=)vH?sn4iL?rnSF?>{Qv0jSYYWPrs%*V?oA_JXSFGM6~pIKCv#*0Zn-3LeTUmpSW zEq5U>#wUm&#sJrab!G<@S&d6nWW@w_Ed$x+e7Y?K4n^5J9Jrz)>>8!(02|+whpZPQeka9Z4y+=!K#Xo;&~$ zJVe!+eKl3CH^kM7L*6bR_o{^aRypK4<&Z-Jt&$ZGEyS z6(>^8u*7L{`?^yQEUBTl;YI9Jt#kd%5?aLcIm14lCeKh0aQM6}xAKrK)p2y5DbJLo z=HENE%$CE{(tFz+zsg!W z_5TCR=Xhu8KftuB1mpiJRr*r^i)his-&^woeQkb}%)b6y4l>R9Zv=W&mi+D)UNRJ? zz`V8tiQ=_{1a{pBAx#BB03PZJ8G8nq?OtOj4fhM&$cnUBq}lp~&?KeKm z{4dNeovR=~``7A!1O2+2a|5TPfP42euk(@-b=LfkI**?YXKgphb(_@yb&6>?pu%4$ zr|*B{Tz#6|_(E=7?{w>mnwS1u+48nV7Pd(aj6SsLzi>TuwxUE-MG0G99J3n^+covM z!1M8M%)Ci%XI|HiS(8g`C_6|pDKTLRDSYX5=W2fy(~DzMLDd*$DRO%Z9Ms9cTz zNE?Z?48p?R@%c7RJQIXXNN+?}KI`qyfaerMui+az|5JKmWxB`zl-{1wy?}r#ahaW7ZZ$9~8VQ-%h?2Y4lfBG@)7P+ysZ4`^# zBDa*b{Kkx1TrzCt#}lvPDUn0G z?x4{J^w!6EE9}FEFs9H7ZkyX$QPSS=iIrGKH=Rn_VQAJ{QG5Z4rxzr9=c>V+0FnmN zlq#hj*yOX3APAOKDt#4NJcONK{lAnOdanl*S4itaZ1$IOlaZkq+u7Ya>- zzQkpYE^GoRud#drvxa^rV5oi|pVhJgFRhugGw`pPWX@hg?jZ{W)%u+i-=5TVdeW@9 zdV8Mg0W0}Zu0J*Rp35~=JBoKnNnyCk+)Zt;=0!oDN%6r5Id2bFm9)lb@SVt~w{K9r zASaV5K@sP(REbJxEPeTN6BcitoUg)X-SyV0&b#2IsjgU=3O-;nx5`b2zHvbUmRMm` z+ZnFXXgxGx7D7R9>kVpI3g9K1Q-awAqZ2L^260I71qNGoD7>@|XhVbL( zdVAo7@tfK0t#X^8lWziIr~pR|#KGaPDu6*i_0~S5hjt8VsoE6<4t~5zZw@hrVOllV zcx&~xj-g1@(^q<8A_#3%@9P`HrhFw2^7*4JXlwKOBb5F06=tsV#_aA_a@(r8307u<*RsD!H2Nw&E`{!*^We@-zq+Y20L2Z~ZKa6R+XZ!O^S$-v>@@T$Fqo;G>OF$` z4nh99y(NQpwrnjc7qYp?0^#BFU913Q6j=Uzlss_SlYU)V6-6Fb(w;u;c z&`_Kr$=9QdrNCh2cft(T={WbSCdL9b*g}HfbcSCNU=f5ydy`BCZdC1dp2wEQ@wbV# zt$$MuilAEw!51je>N^zCXf58H?iK}YMJPao3aDXP3w0qnJmw{Pdz;)ztDr8#ri$3a zm+ZT3a$Kui5qsVZN0fAm5S{f#iHL_B&b5D7m2c!GQ_6TEF-g96De$pnYT=+`y(u4% zKu8l7pG6pOuZ6k?p(v4%3$I=zP!kavAwoqWG+Bg#Tx{Tva(#`KD5SNl6R|OD;g50y zbuN4OjoiSLCQ3L(T7(FdxpSf%*pc@Smh0j4C?k!RX{7-P*53`*#|hT6Q@uywv+CDK zSU*ajXFyBv`f1T5y=KP#NeP-6Cnjm$#=&iqJqScXb4LJVaRO$dgf0adbJJGg@Yy@~ z>6aejFT2|LVAzGu2ls##pwZoK04BF*VmNNzF2|Xun+x@hV&ef#jj3XZrJdB}$ zGmhwHODN%KjW0+jyhS4_Mg+6K0EzGd5j`C2JkikXW%Ca*sGra21>GEnZ?bTqBtEhL zi<2D?j`((BW})8vyryQ&{-9%NY-+Att@^w3>Z+#JM8g}OXQo`afuwrDKFO8qna-oT zt?F_)};GrAW}gc?-wlkTe&SC;TYbqKHvu15wr%K z3D(1mip(lb%O?*O zpSw7GE_R9V(+)+%;1EXvY_gze4M}gabNE(+ggL8!t{xtoJm!EkesL1s-nSEEiVsb2 z9fG7NU@gCsn?@3Rh*J*ve?znyzgH41@d#V;9juB$EblwHi8_Ft`%dmaL8vU5_-H0V+A_+1snViT5wk3jko6$V5jvk1fOjK?-59{KdMkO^0B87ret& z8#sWGIgZwghdk@G)w}lVt-oQ0r3&?7=c%w}c?~Md>uJhSFcJMMD#~K-?*0tL&|JJ9 zNT}CzV_BZVV{9(uFDSy>pg}4X1g@Gj2dJI&xT4KYbAdkW2rZ5wVC*rJrAymb%^DDt zBp<$Ek3GtKUZ<_O=t5g2mBQ1H(5_vqVti!xx zSMBR~WN6}p+KSK6XTldElVu%Sz-CjC#q>vhO8vaOJpRDiDe@d5-VtQ4Z&OY`oY z=D3?;Us45vi#T&q@56B&JX7B4eb}=@n+X%gLCd0|y;mAKcnPL#{vXUASl%kUQld8z z`$reQ2cYh{(w?hD$+iF^ z6l@;-HJ7S{oWTS4+}tJk{_b2}J9B0HZQ zo$LPkm+ETLq^|*^5leI3RhucM=?z?4)n?PE(`dKpxsx}7JNc90#9=U`3fI0uxS8vO zyJ3rPvv&%2+Y#aBorn7f1@pO1sI>q+wtcr;#{^heeEeCiVYayxyhcwU9E1Pyi48|EamY1mOQs&jrOSr07B)wM3wIia&>tp>>lz zpQ!GPPbH3Cj71B^bS_4Ff9v10R&cMlu)Alcd0!KnSwJlh-4Mf_>(^%tYNrmiU z#yxUF6NU&^0IVBmdWS6I%9F+~Q$bMUw_z4{veUS;ErUCGS=`Cbg%by81;Vu#2{-ea za5t0+H(LeG6Zy7jg_{>7-28}VE)yDz9`&=tAP)Ei0eOTHc?dn~XHi4~jRJ(|*>;r3 zg*Qzkl!}B55uzCcX;~r^1O`GVSAvAeVi3a8HW3aM$lK~*bzv^^OlhP)?DYu(+%jp8?(g4HUkzye z!)_aFA=_&r*`~{_*^)W-%44ftXb5hUMrhJQS(ANejGZJxjqktDdrB(haaE^Y;i3n2jJ$!EyE>;fG zEp{SL(L=^o%ur%cY`LUK{|LZ1Y`gCU6USbNCF>RIP%OqDIb$LritL$02!pkq-a3k` zv~xM^Ua(hq%@SU`-Y_S~V4WIju#Sr`SQkdQ*Zqi&K{U-SD|#hDLm*O;3NFka&L$tL zRcSwxbx6V(Z4K11c0(5T2sD@Br7m3q>J@t`FKH)sKxhRI;f%Ds#;+(%O-%z!Sqsg@ z(o|^_u-6!lS&nMb8$xp0GUIr5uD3u^bLE*7+rqm+P}GPs5o=21nTD=|e}wQtvE}0n zLVhyE*z#xbg!~v@BtK1rh+&bI;ZCEFeW9Ya=4Vkh`|cy8CRp=xDP+wgX4fy>U&JFb zXCV?7uR$+U6eU>mA`p`@+kEYnxlF>TGWzmZazk1#VByCribtzE9Hdrb7xy@y+p?fD z(a4+Xg5n&X1@iG)Y-4eSeEK@rm0tJ|?{QmC~j($HoI*AuFSAIvtKLAT*#kW*&&O4QPcTr6^)K z8mq}X9Bb}@3qlrxsIe?SDL^8En4{3CQxNWNn}n62B^PmNQFuGcW|>8K(`!7EhT=0h_OZ z$JsL|2Qos4>($WwGie?Gv}jR;ggru?MJP>#J`{8${Z9k+xif0z?`$21s5LY43Aq}M5t60 z94tZ+B5kY)m5H>zA`~UkioJP-TSVFo5y};z)*|Ol5o#eq1utACNErT1g~6Hvjd1Zg zO!y=VNbjMc2~}PpCv0dM8JK2)DWO^@aCXtFQKV53NP8ZQ(Z`$8@QDIV`d1JsWDAijcKXui0+rtWbCz29Qi*7=73=&9bkM3SE%;KOyWF&9e0z zS{`LJzw*(T->(bkWZ5<0I}@nK!r98+-COTrCl-p2JPH#PW*KPt?m>6hLPL#)-(kxL z5P|#Ri$nwsI^m%}15*u55Mz_a#-{ab3l5^9{AMDKbN9D8ore0G zG441dy=~M60;7kqi`MK|*>Vfgha0ep3&yF#G}!(Lm?k~B3iWo%3R;;zh&)=-9ro-3 z1k~7^;V|5}HR%J~Ia${uawxYZLNRfp`y=&1LYG7HqKX^9fC@ASJ6Im-a?8Ue><=CC zYz%t?tQ!Keu=B{SS$@B|c$q+>S6{`H&#^rA2^L%x^ zgSET2!J6#t&9Mvo9Vwc0ZEb3W*AdEAc*7GHAOcGDwjlv!ZcVF?^P47Q=@J_x+-7-3 zSe*(8S*-E7VGh=Lj=E6JI*uZ6c3qcYFwy#rbbb}eM*fVQRpMtiT=1+`WYSP3%}8ew zWiISf;L zQ6&W8_euyB0pS2as5R1gwgtz920=LxK4%Iz+*=8W22v%YZUWMTq28*g+L6xp1SA?f zdQ*9`ct|vhyp^CL{srox#G5sQkg7S-*-L;5dYwQ#ARv*q5>gOJxpHN><7N)YqzfQW zbw)bNn)CLlUI)bk0u*^GK`H-2YOFwN?GVwvZv-Tg3AiXDdW{D(argiRH%$T(?$v>z zOm5x1V|@Xm^AORnX@HSnJ(OTQgYwAg6eUV4@h?_$O88TO+9RD^d4ZqEq*u|zLn2EZ zcb2-yg0XL~UZ1KqqT7xqXyTaR7g?iL*qC3yi&On+C~Y8?#1uz%D}+i#D1bs%JK3}h z*33{ikRc-xLnbhYf+#pBoSDnBXU0%I=%=6ovg<`?mPkwFp`k@c7(rgEU4f%Fo3bav zB_PB;NS(zqa=D98st6H3Ae1ITF`|MMB1AomvN2Edi4b)sLR%;Vjh9E7 zxEM$5P9B?Az#VRJ=x_ZMBdiD!kYVS!lYfmTTbw*(DdkRP8F%tjrJN{aRv@uX1_f*$ z2f={`%@;dxypb0srq9Dw`$8fAr<%Ntc#3}$w=Qea2SRQ_``txJWH=Tp5v=U64@-gt z%eq0d;{|n_1P$NsT8Vw(b3c5{fFQvQ+Q#j@?62+hE^D z(kZ!9Ni0Oa2@^Lqp8kiz?RjCYkhqHgMs<>``2-N(*Z7R~p!tp*sXbcoyelKdckY*)*M~rj7vrK0t;j zWB_3+OkI>p8^mW~m2JlrrvE6mkypMGbXX9r%Ml z5AwrXgzCx51{TS6s*(0}iu8i3?ENA+bW&%W;leN-jnPa5fLvC*>w4t0j#G^HvBjT&K2LGDL{$)K<$n z5#nRuchRK1Ujx6-4PU#Fl}HY*+^( z*abRyF@kaQgsCm_Jcn{ov+N9y3F4lPOuJEd7>isMPM zkTcGfmrDg~_A(BYZ{hS0dl;+v@Q~ZCkkAE1YM}(40MuA~cu^AiNG605x}4N3dkh

      7q+SYRg=3dL%)altV#*JV)S&DmLS_>zv~ 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

      IVSr_^r!X$QUqr$@l*+OOS%JoKc1|Mt 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±-i%XUPLl(`&SXng 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

      k0Jy<2XunK#Tqd2KsiNd=rdn8?E~_qWsZJ}5 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%0DLiN!8uU^cl9cU2BPr{AFZMz0<6 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 z

      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 T4YGT@T-CNoeWZC`x6A(nH!Qhk diff --git a/packages/opcore-graph-core-linux-x64/opcore-graph-core.sha256 b/packages/opcore-graph-core-linux-x64/opcore-graph-core.sha256 index 9ef64d0..4022cb9 100644 --- a/packages/opcore-graph-core-linux-x64/opcore-graph-core.sha256 +++ b/packages/opcore-graph-core-linux-x64/opcore-graph-core.sha256 @@ -1 +1 @@ -c3a771435a7a8172a9e5f0bb20b0d111453efc70901e95308094664f77e7bdf1 opcore-graph-core +70402bf42a09c01ea01f72199ee1985684a217da940cd1454bd4614ba63b12cf opcore-graph-core diff --git a/packages/opcore/src/advanced/asp-warm/warm-project-registry.ts b/packages/opcore/src/advanced/asp-warm/warm-project-registry.ts index a56129b..d30fc80 100644 --- a/packages/opcore/src/advanced/asp-warm/warm-project-registry.ts +++ b/packages/opcore/src/advanced/asp-warm/warm-project-registry.ts @@ -1,12 +1,15 @@ -import { existsSync, readFileSync, realpathSync, readdirSync } from "node:fs"; -import { join, relative, resolve } from "node:path"; +import { existsSync, readFileSync, realpathSync } from "node:fs"; +import { join, resolve } from "node:path"; import type { Project, SourceFile } from "ts-morph"; import type { CommandTimingProcessState } from "@the-open-engine/opcore-contracts"; -import { isSupportedSymbolSourcePath } from "@the-open-engine/opcore-edit"; +import { + isPathInside as isInside, + listSymbolEditLanguageServiceSourceFiles +} from "@the-open-engine/opcore-edit"; import { createInspectLanguageServiceProject, type InspectLanguageServiceProjectScope -} from "../inspect-language-service.js"; +} from "../inspect-typescript-project.js"; export type WarmProjectScope = InspectLanguageServiceProjectScope; @@ -203,48 +206,16 @@ function refreshSourceFile(sourceFile: SourceFile): void { function currentSourceFileFingerprint(repoRoot: string, scope: WarmProjectScope): string { if (scope !== "whole_repo") return "scoped"; - return listWarmSourceFiles(repoRoot).join("\n"); + return listSymbolEditLanguageServiceSourceFiles(repoRoot).join("\n"); } function addWarmEditSourceFiles(project: Project, repoRoot: string, scope: WarmProjectScope): void { if (scope !== "whole_repo") return; - for (const filePath of listWarmSourceFiles(repoRoot)) { + for (const filePath of listSymbolEditLanguageServiceSourceFiles(repoRoot)) { if (project.getSourceFile(filePath) === undefined) project.addSourceFileAtPath(filePath); } } -function listWarmSourceFiles(repoRoot: string): string[] { - const files: string[] = []; - visit(repoRoot); - return files.sort(); - - function visit(directory: string): void { - for (const entry of readdirSync(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name))) { - const path = join(directory, entry.name); - if (entry.isDirectory()) { - if (!excludedDirectories.has(entry.name)) visit(path); - } else if (entry.isFile() && isSupportedSymbolSourcePath(path) && isInside(repoRoot, path)) { - files.push(resolve(path)); - } - } - } -} - -const excludedDirectories = new Set([ - ".agents", - ".claude", - ".codex", - ".gemini", - ".git", - ".lattice", - ".opencode", - ".pnpm", - "dist", - "node_modules", - "target", - "vendor" -]); - function currentGitEpoch(repoRoot: string): string { const gitDir = resolveGitDir(repoRoot); if (gitDir === undefined) return "nogit"; @@ -279,8 +250,3 @@ function realpathIfPossible(path: string): string { return path; } } - -function isInside(root: string, target: string): boolean { - const relativePath = relative(resolve(root), resolve(target)); - return relativePath === "" || (!relativePath.startsWith("..") && !relativePath.startsWith("/") && !/^[A-Za-z]:/.test(relativePath)); -} diff --git a/packages/opcore/src/advanced/inspect-language-service.ts b/packages/opcore/src/advanced/inspect-language-service.ts index 2ef200c..788f070 100644 --- a/packages/opcore/src/advanced/inspect-language-service.ts +++ b/packages/opcore/src/advanced/inspect-language-service.ts @@ -1,5 +1,5 @@ -import { existsSync, readFileSync, realpathSync, readdirSync, statSync } from "node:fs"; -import { dirname, extname, join, relative, resolve } from "node:path"; +import { realpathSync } from "node:fs"; +import { extname, relative, resolve } from "node:path"; import type { GraphFactEdge, GraphNodeKind, @@ -15,18 +15,34 @@ import type { InspectSymbolSummary, InspectSymbolTarget } from "@the-open-engine/opcore-contracts"; +import { + isPathInside as isInside, + isSafeExistingFileInsideRepo, + normalizeModulePath, + type TypeScriptProjectContext +} from "@the-open-engine/opcore-edit"; import { Node, - Project, SyntaxKind, - ts, type ClassDeclaration, type InterfaceDeclaration, type ParameterDeclaration, + type Project, type SourceFile, type Symbol as MorphSymbol, type TypeParameterDeclaration } from "ts-morph"; +import { + inspectProjectService, + type InspectLanguageServiceOptions, + type InspectLanguageServiceProjectScope +} from "./inspect-typescript-project.js"; + +export { + createInspectLanguageServiceProject, + type InspectLanguageServiceOptions, + type InspectLanguageServiceProjectScope +} from "./inspect-typescript-project.js"; export interface InspectReferenceRequest { path: string; @@ -101,35 +117,7 @@ export type InspectImplementationResolution = candidates?: readonly InspectSymbolTarget[]; }; -export type InspectLanguageServiceProjectScope = "import_closure" | "whole_repo"; - -export interface InspectLanguageServiceOptions { - project?: Project; - projectScope?: InspectLanguageServiceProjectScope; - projectTsconfigPath?: string; - includeDependents?: boolean; - snapshotProject?: (project: Project) => unknown; - revertProject?: (project: Project, snapshot: unknown) => void; -} - -const inspectProjectScopes = new WeakMap(); - -const sourceFileExtensions = new Set([".ts", ".tsx", ".js", ".jsx"]); const implementationSourceFileExtensions = new Set([".ts", ".tsx"]); -const excludedDirectories = new Set([ - ".agents", - ".claude", - ".codex", - ".gemini", - ".git", - ".lattice", - ".opencode", - ".pnpm", - "dist", - "node_modules", - "target", - "vendor" -]); type ReferenceEntry = { getNode(): Node; @@ -147,7 +135,7 @@ type ReferenceFindableNode = Node & { }; export function isSupportedInspectSourcePath(path: string): boolean { - return sourceFileExtensions.has(extname(path).toLowerCase()); + return inspectProjectService.isSupportedSourcePath(path); } export function isSupportedInspectImplementationSourcePath(path: string): boolean { @@ -180,8 +168,11 @@ export function resolveInspectSignatures( } try { - for (const context of createProjectContexts(normalizedRepoRoot, request.path, options)) { - const resolution = withProjectSnapshot(context, () => resolveInspectSignatureInProject(normalizedRepoRoot, context, absoluteTargetPath, request, baseTarget)); + for (const context of inspectProjectService.createContexts(normalizedRepoRoot, request.path, options)) { + const resolution = inspectProjectService.withSnapshot( + context, + () => resolveInspectSignatureInProject(normalizedRepoRoot, context, absoluteTargetPath, request, baseTarget) + ); return resolution; } return { @@ -284,12 +275,12 @@ export function resolveInspectReferences( try { const projectScope = options.projectScope ?? "import_closure"; const includeDependents = projectScope === "whole_repo" ? options.includeDependents === true : true; - for (const context of createProjectContexts(normalizedRepoRoot, request.path, { + for (const context of inspectProjectService.createContexts(normalizedRepoRoot, request.path, { ...options, includeDependents, projectScope })) { - const resolution = withProjectSnapshot(context, () => { + const resolution = inspectProjectService.withSnapshot(context, () => { const sourceFile = context.project.getSourceFile(absoluteTargetPath) ?? context.project.addSourceFileAtPath(absoluteTargetPath); const target = findReferenceTarget(normalizedRepoRoot, sourceFile, request); if (!target.ok) return target; @@ -348,8 +339,12 @@ export function resolveInspectImplementations( try { const projectScope: InspectLanguageServiceProjectScope = request.allowGraphless ? "whole_repo" : (options.projectScope ?? "import_closure"); - for (const context of createProjectContexts(normalizedRepoRoot, preflight.path, { ...options, projectScope })) { - const resolution = withProjectSnapshot(context, () => { + for (const context of inspectProjectService.createContexts( + normalizedRepoRoot, + preflight.path, + { ...options, projectScope } + )) { + const resolution = inspectProjectService.withSnapshot(context, () => { const targetResolution = resolveImplementationTargetInProject(context.project, normalizedRepoRoot, request, preflight.candidate); if (!targetResolution.ok) return targetResolution; const targetCandidate = targetResolution.candidate; @@ -1281,329 +1276,7 @@ function applyLimit(entries: readonly InspectReferenceEntry[], limit: number | u return limit === undefined ? entries : entries.slice(0, limit); } -type ProjectContext = { - project: Project; - tsconfigPath?: string; - snapshotProject?: (project: Project) => unknown; - revertProject?: (project: Project, snapshot: unknown) => void; -}; - -export function createInspectLanguageServiceProject( - repoRoot: string, - preferredRepoPath: string, - options: InspectLanguageServiceOptions = {} -): Project { - const preferredTsconfigPath = resolveInspectTsconfigPath(repoRoot, options.projectTsconfigPath) ?? tsconfigForInspectRoot(repoRoot); - return createProjectForTsconfig(repoRoot, preferredTsconfigPath, preferredRepoPath, { - includeDependents: options.includeDependents === true, - scope: options.projectScope ?? "import_closure" - }); -} - -function createProjectContexts(repoRoot: string, preferredRepoPath: string, options: InspectLanguageServiceOptions = {}): ProjectContext[] { - const projectScope = options.projectScope ?? "import_closure"; - const preferredTsconfigPath = resolveInspectTsconfigPath(repoRoot, options.projectTsconfigPath) ?? tsconfigForInspectRoot(repoRoot); - if (options.project !== undefined && canUseInjectedProject(options.project, projectScope)) { - if (projectScope === "import_closure") { - addScopedSourceFilesToProject(repoRoot, preferredTsconfigPath, options.project, [preferredRepoPath], { - includeDependents: options.includeDependents === true - }); - } - return [ - { - project: options.project, - ...(options.projectTsconfigPath ? { tsconfigPath: options.projectTsconfigPath } : {}), - ...(options.snapshotProject ? { snapshotProject: options.snapshotProject } : {}), - ...(options.revertProject ? { revertProject: options.revertProject } : {}) - } - ]; - } - return [ - { - project: createProjectForTsconfig(repoRoot, preferredTsconfigPath, preferredRepoPath, { - includeDependents: options.includeDependents === true, - scope: projectScope - }), - ...(preferredTsconfigPath ? { tsconfigPath: preferredTsconfigPath } : {}) - } - ]; -} - -function canUseInjectedProject(project: Project, requiredScope: InspectLanguageServiceProjectScope): boolean { - return requiredScope !== "whole_repo" || inspectProjectScopes.get(project) === "whole_repo"; -} - -function withProjectSnapshot(context: ProjectContext, run: () => T): T { - if (context.snapshotProject === undefined || context.revertProject === undefined) return run(); - const snapshot = context.snapshotProject(context.project); - try { - return run(); - } finally { - context.revertProject(context.project, snapshot); - } -} - -function createProjectForTsconfig( - repoRoot: string, - tsconfigPath: string | undefined, - preferredRepoPath: string, - options: { - includeDependents: boolean; - scope: InspectLanguageServiceProjectScope; - } -): Project { - const projectOptions = { - tsConfigFilePath: tsconfigPath, - skipAddingFilesFromTsConfig: true, - skipFileDependencyResolution: true, - compilerOptions: { - allowJs: true, - checkJs: false - } - }; - const project = new Project(projectOptions); - const sourceFiles = options.scope === "whole_repo" - ? listSourceFiles(repoRoot) - : scopedSourceFiles(repoRoot, tsconfigPath, [preferredRepoPath], { includeDependents: options.includeDependents }); - for (const filePath of sourceFiles) { - if (project.getSourceFile(filePath)) continue; - project.addSourceFileAtPath(filePath); - } - inspectProjectScopes.set(project, options.scope); - return project; -} - -function addScopedSourceFilesToProject( - repoRoot: string, - tsconfigPath: string | undefined, - project: Project, - rootRepoPaths: readonly string[], - options: { includeDependents: boolean } -): void { - for (const filePath of scopedSourceFiles(repoRoot, tsconfigPath, rootRepoPaths, options)) { - if (project.getSourceFile(filePath) === undefined) project.addSourceFileAtPath(filePath); - } -} - -interface ImportResolutionOptions { - baseUrl: string; - hasBaseUrl: boolean; - paths: Readonly>; -} - -type TsconfigJson = { - compilerOptions?: { - baseUrl?: unknown; - paths?: unknown; - }; -}; - -const extensionlessCandidates = [".ts", ".tsx", ".js", ".jsx", ".d.ts"] as const; - -function scopedSourceFiles( - repoRoot: string, - tsconfigPath: string | undefined, - rootRepoPaths: readonly string[], - options: { includeDependents: boolean } -): string[] { - const importOptions = importResolutionOptions(repoRoot, tsconfigPath); - const importTargetsByFile = new Map(); - const allSourceFiles = options.includeDependents ? listSourceFiles(repoRoot) : []; - const roots = rootSourceFiles(repoRoot, rootRepoPaths); - const reverseTargets = new Set(roots); - const selected = new Set(); - addForwardClosure(roots, selected); - - if (options.includeDependents) { - let changed = true; - while (changed) { - changed = false; - for (const filePath of allSourceFiles) { - if (selected.has(filePath)) continue; - const importsReverseTarget = importTargets(filePath).some((importedPath) => reverseTargets.has(importedPath)); - if (!importsReverseTarget) continue; - const beforeSize = selected.size; - addForwardClosure([filePath], selected); - reverseTargets.add(filePath); - if (selected.size !== beforeSize) changed = true; - } - } - } - - return [...selected].sort(); - - function addForwardClosure(rootFiles: readonly string[], selectedFiles: Set): void { - const pending = [...rootFiles].sort(); - for (let index = 0; index < pending.length; index += 1) { - const filePath = pending[index]; - if (selectedFiles.has(filePath)) continue; - selectedFiles.add(filePath); - for (const importedPath of importTargets(filePath)) { - if (!selectedFiles.has(importedPath) && !pending.includes(importedPath)) pending.push(importedPath); - } - } - } - - function importTargets(filePath: string): readonly string[] { - const cached = importTargetsByFile.get(filePath); - if (cached !== undefined) return cached; - const resolvedTargets = moduleImportSpecifiers(readFileSync(filePath, "utf8")) - .flatMap((specifier) => { - const resolvedImport = resolveImportSpecifier(repoRoot, filePath, specifier, importOptions); - return resolvedImport === undefined ? [] : [resolvedImport]; - }) - .sort(); - importTargetsByFile.set(filePath, resolvedTargets); - return resolvedTargets; - } -} - -function rootSourceFiles(repoRoot: string, rootRepoPaths: readonly string[]): string[] { - return uniqueSorted( - rootRepoPaths - .map((path) => resolve(repoRoot, path)) - .filter((path) => isSupportedInspectSourcePath(path) && isSafeExistingFileInsideRepo(repoRoot, path)) - ); -} - -function moduleImportSpecifiers(text: string): readonly string[] { - const specifiers = new Set(); - for (const match of text.matchAll(/\b(?:import|export)\s+(?:type\s+)?(?:[^"'`;]*?\s+from\s+)?["']([^"']+)["']/gu)) { - if (match[1]) specifiers.add(match[1]); - } - for (const match of text.matchAll(/ `${basePath}${candidateExtension}`), - ...extensionlessCandidates.map((candidateExtension) => join(basePath, `index${candidateExtension}`)) - ]); -} - -function importResolutionOptions(repoRoot: string, tsconfigPath: string | undefined): ImportResolutionOptions { - const configDirectory = tsconfigPath === undefined ? repoRoot : dirname(tsconfigPath); - const config = tsconfigPath === undefined ? undefined : parseTsconfigForImports(tsconfigPath); - const compilerOptions = config?.compilerOptions; - const baseUrl = typeof compilerOptions?.baseUrl === "string" && compilerOptions.baseUrl.length > 0 - ? resolve(configDirectory, compilerOptions.baseUrl) - : configDirectory; - return { - baseUrl, - hasBaseUrl: typeof compilerOptions?.baseUrl === "string" && compilerOptions.baseUrl.length > 0, - paths: normalizePathMappings(compilerOptions?.paths) - }; -} - -function parseTsconfigForImports(tsconfigPath: string): TsconfigJson | undefined { - try { - const parsed = ts.parseConfigFileTextToJson(tsconfigPath, readFileSync(tsconfigPath, "utf8")); - return parsed.error === undefined ? parsed.config as TsconfigJson : undefined; - } catch { - return undefined; - } -} - -function normalizePathMappings(paths: unknown): Readonly> { - if (paths === null || typeof paths !== "object" || Array.isArray(paths)) return {}; - const normalized: Record = {}; - for (const [pattern, targets] of Object.entries(paths)) { - if (Array.isArray(targets)) normalized[pattern] = targets.filter((target): target is string => typeof target === "string"); - } - return normalized; -} - -function sortedPathMappings(paths: Readonly>): readonly [string, readonly string[]][] { - return Object.entries(paths) - .filter((entry): entry is [string, readonly string[]] => entry[1].length > 0) - .sort((left, right) => pathPatternRank(right[0]) - pathPatternRank(left[0])); -} - -function pathPatternRank(pattern: string): number { - const starIndex = pattern.indexOf("*"); - if (starIndex === -1) return pattern.length * 2 + 1; - return pattern.length - 1; -} - -function matchPathPattern(pattern: string, specifier: string): string | undefined { - const starIndex = pattern.indexOf("*"); - if (starIndex === -1) return pattern === specifier ? "" : undefined; - const prefix = pattern.slice(0, starIndex); - const suffix = pattern.slice(starIndex + 1); - if (!specifier.startsWith(prefix) || !specifier.endsWith(suffix)) return undefined; - return specifier.slice(prefix.length, specifier.length - suffix.length); -} - -function applyPathMappingTarget(target: string, wildcard: string): string { - return target.includes("*") ? target.replaceAll("*", wildcard) : target; -} - -function tsconfigForInspectRoot(repoRoot: string): string | undefined { - const tsconfigPath = join(repoRoot, "tsconfig.json"); - return isSafeExistingFileInsideRepo(repoRoot, tsconfigPath) ? resolve(tsconfigPath) : undefined; -} - -function resolveInspectTsconfigPath(repoRoot: string, tsconfigPath: string | undefined): string | undefined { - if (tsconfigPath === undefined) return undefined; - const absolutePath = resolve(repoRoot, tsconfigPath); - return isSafeExistingFileInsideRepo(repoRoot, absolutePath) ? absolutePath : undefined; -} - -function listSourceFiles(repoRoot: string): string[] { - const files: string[] = []; - visit(repoRoot); - return files.sort(); - - function visit(directory: string): void { - for (const entry of readdirSync(directory, { withFileTypes: true }).sort((left: { name: string }, right: { name: string }) => left.name.localeCompare(right.name))) { - const path = join(directory, entry.name); - if (entry.isDirectory()) { - if (!excludedDirectories.has(entry.name)) visit(path); - } else if (entry.isFile() && isSupportedInspectSourcePath(path) && isSafeExistingFileInsideRepo(repoRoot, path)) { - files.push(resolve(path)); - } - } - } -} +type ProjectContext = TypeScriptProjectContext; function symbolIdentity(node: Node): string { const declaration = symbolDeclaration(node.getSymbol()) ?? node; @@ -1770,51 +1443,6 @@ function compareSignatureEntries(left: InspectSignatureEntry, right: InspectSign ); } -function isSafeExistingFileInsideRepo(repoRoot: string, absolutePath: string): boolean { - if (!isInside(repoRoot, absolutePath) || !existsSync(absolutePath)) return false; - try { - return isInside(repoRoot, realpathSync(absolutePath)) && statSync(absolutePath).isFile(); - } catch { - return false; - } -} - -function isInside(root: string, target: string): boolean { - const relativePath = relative(resolve(root), resolve(target)); - return relativePath === "" || (!relativePath.startsWith("..") && !relativePath.startsWith("/") && !/^[A-Za-z]:/.test(relativePath)); -} - -function normalizeModulePath(path: string): string { - return path.replaceAll("\\", "/"); -} - -function isRepoResolvableSpecifier(specifier: string): boolean { - return specifier.length > 0 && !specifier.startsWith("/") && !specifier.includes("://"); -} - -function isRelativeSpecifier(specifier: string): boolean { - return specifier.startsWith("./") || specifier.startsWith("../"); -} - -function sourceExtension(path: string): string | undefined { - if (path.endsWith(".d.ts")) return ".d.ts"; - const match = /\.[^./]+$/u.exec(path); - return match?.[0]; -} - -function replaceExtension(path: string, extension: string): string { - if (path.endsWith(".d.ts")) return `${path.slice(0, -".d.ts".length)}${extension}`; - return path.replace(/\.[^./]+$/u, extension); -} - -function uniqueSorted(values: readonly string[]): string[] { - return [...new Set(values)].sort(); -} - -function unique(values: readonly string[]): readonly string[] { - return [...new Set(values)]; -} - function errorMessage(error: unknown): string { return error instanceof Error ? error.message : String(error); } diff --git a/packages/opcore/src/advanced/inspect-typescript-project.ts b/packages/opcore/src/advanced/inspect-typescript-project.ts new file mode 100644 index 0000000..20cab76 --- /dev/null +++ b/packages/opcore/src/advanced/inspect-typescript-project.ts @@ -0,0 +1,30 @@ +import { + TypeScriptProjectService, + defaultTypeScriptProjectExcludedDirectories, + type TypeScriptProjectOptions, + type TypeScriptProjectScope +} from "@the-open-engine/opcore-edit"; +import type { Project } from "ts-morph"; + +export type InspectLanguageServiceProjectScope = TypeScriptProjectScope; + +export interface InspectLanguageServiceOptions extends TypeScriptProjectOptions {} + +export const inspectProjectService = new TypeScriptProjectService({ + sourceExtensions: [".ts", ".tsx", ".js", ".jsx"], + extensionlessImportCandidates: [".ts", ".tsx", ".js", ".jsx", ".d.ts"], + excludedDirectories: defaultTypeScriptProjectExcludedDirectories, + allowDirectoryRoots: false, + configMode: "root", + discoverAllConfigs: false, + tolerateMalformedImportConfig: true, + defaultIncludeDependents: false +}); + +export function createInspectLanguageServiceProject( + repoRoot: string, + preferredRepoPath: string, + options: InspectLanguageServiceOptions = {} +): Project { + return inspectProjectService.createProject(repoRoot, preferredRepoPath, options); +} diff --git a/packages/opcore/src/init-action-helpers.ts b/packages/opcore/src/init-action-helpers.ts new file mode 100644 index 0000000..b14ac85 --- /dev/null +++ b/packages/opcore/src/init-action-helpers.ts @@ -0,0 +1,19 @@ +import type { OpcoreInitAction } from "@the-open-engine/opcore-contracts"; +import { AGENT_SKILL_PATHS } from "./init-constants.js"; +import type { InitScope } from "./init-types.js"; + +export function actionPath(scope: InitScope, path: string): string { + return scope === "global" ? `~/${path}` : path; +} + +export function createSkillActions(scope: InitScope, enabled: boolean): OpcoreInitAction[] { + if (!enabled) return []; + return AGENT_SKILL_PATHS.map((path) => ({ + kind: "write", + path: actionPath(scope, path), + targetScope: scope, + summary: "Install the Opcore agent skill.", + requiresApproval: true, + outsideOpcore: true + })); +} diff --git a/packages/opcore/src/init-actions-global.ts b/packages/opcore/src/init-actions-global.ts new file mode 100644 index 0000000..d3d9cbd --- /dev/null +++ b/packages/opcore/src/init-actions-global.ts @@ -0,0 +1,49 @@ +import type { OpcoreInitAction } from "@the-open-engine/opcore-contracts"; +import { + AGENT_GATE_HOOK_PATH, + CLAUDE_SETTINGS_PATH, + CODEX_HOOKS_PATH +} from "./init-constants.js"; +import { actionPath, createSkillActions } from "./init-action-helpers.js"; +import type { ParsedInitArgs } from "./init-types.js"; + +export function createGlobalInitActions(options: ParsedInitArgs): OpcoreInitAction[] { + return [ + ...globalHookAction(options.writeGateHooks), + ...createSkillActions("global", options.agentSkill), + ...globalHarnessActions(options.writeGateHooks) + ]; +} + +function globalHookAction(enabled: boolean): OpcoreInitAction[] { + return enabled ? [{ + kind: "create_hook", + path: actionPath("global", AGENT_GATE_HOOK_PATH), + targetScope: "global", + summary: "Install the global Opcore write-gate adapter script.", + requiresApproval: false, + outsideOpcore: false + }] : []; +} + +function globalHarnessActions(enabled: boolean): OpcoreInitAction[] { + if (!enabled) return []; + return [ + { + kind: "wire_harness", + path: actionPath("global", CLAUDE_SETTINGS_PATH), + targetScope: "global", + summary: "Merge the Opcore Claude Code PreToolUse write gate.", + requiresApproval: true, + outsideOpcore: true + }, + { + kind: "wire_harness", + path: actionPath("global", CODEX_HOOKS_PATH), + targetScope: "global", + summary: "Merge the Opcore Codex PreToolUse write gate guardrail.", + requiresApproval: true, + outsideOpcore: true + } + ]; +} diff --git a/packages/opcore/src/init-actions-repo.ts b/packages/opcore/src/init-actions-repo.ts new file mode 100644 index 0000000..844f934 --- /dev/null +++ b/packages/opcore/src/init-actions-repo.ts @@ -0,0 +1,93 @@ +import type { OpcoreInitAction } from "@the-open-engine/opcore-contracts"; +import { + ACTIVE_PRE_COMMIT_HOOK_PATH, + AGENT_GATE_HOOK_PATH, + CLAUDE_SETTINGS_PATH, + CODEX_HOOKS_PATH, + CONFIG_PATH, + FAIL_CLOSED_HOOK_ACTIVATION_COMMAND, + GITIGNORE_PATH, + HOOK_PATH +} from "./init-constants.js"; +import { createSkillActions } from "./init-action-helpers.js"; +import type { ParsedInitArgs } from "./init-types.js"; + +export interface RepoActionInput { + agentFiles: readonly string[]; + options: ParsedInitArgs; + gitignoreWritePlanned: boolean; + activePreCommitWritePlanned: boolean; +} + +export function createRepoInitActions(input: RepoActionInput): OpcoreInitAction[] { + const actions = [ + configAction(), + ...guidanceActions(input.agentFiles), + ...createSkillActions("repo", input.options.agentSkill), + ...writeGateActions(input.options.writeGateHooks) + ]; + if (input.gitignoreWritePlanned) actions.push(gitignoreAction()); + if (input.activePreCommitWritePlanned) actions.push(preCommitAction()); + if (input.options.failClosedHook) actions.push(failClosedAction()); + return actions; +} + +function configAction(): OpcoreInitAction { + return { + kind: "write", path: CONFIG_PATH, targetScope: "repo", + summary: "Write additive Opcore init config.", requiresApproval: false, outsideOpcore: false + }; +} + +function guidanceActions(agentFiles: readonly string[]): OpcoreInitAction[] { + return agentFiles.map((path) => ({ + kind: "upsert_block", path, targetScope: "repo", + summary: "Add or update delimited Opcore agent guidance.", + requiresApproval: true, outsideOpcore: true + })); +} + +function writeGateActions(enabled: boolean): OpcoreInitAction[] { + if (!enabled) return []; + return [ + { + kind: "create_hook", path: AGENT_GATE_HOOK_PATH, targetScope: "repo", + summary: "Install the repo-local Opcore write-gate adapter script.", + requiresApproval: false, outsideOpcore: false + }, + { + kind: "wire_harness", path: CLAUDE_SETTINGS_PATH, targetScope: "repo", + summary: "Merge the Opcore Claude Code PreToolUse write gate.", + requiresApproval: true, outsideOpcore: true + }, + { + kind: "wire_harness", path: CODEX_HOOKS_PATH, targetScope: "repo", + summary: "Merge the Opcore Codex PreToolUse write gate guardrail.", + requiresApproval: true, outsideOpcore: true + } + ]; +} + +function gitignoreAction(): OpcoreInitAction { + return { + kind: "write", path: GITIGNORE_PATH, targetScope: "repo", + summary: "Append managed .opcore/ gitignore entry.", requiresApproval: true, outsideOpcore: true + }; +} + +function preCommitAction(): OpcoreInitAction { + return { + kind: "create_hook", path: ACTIVE_PRE_COMMIT_HOOK_PATH, targetScope: "repo", + summary: "Install active Git pre-commit hook that runs `opcore check --changed`.", + requiresApproval: true, outsideOpcore: true + }; +} + +function failClosedAction(): OpcoreInitAction { + return { + kind: "create_hook", path: HOOK_PATH, targetScope: "repo", + summary: `Manual install required: create fail-closed pre-commit hook script; ` + + `activate with \`${FAIL_CLOSED_HOOK_ACTIVATION_COMMAND}\`.`, + requiresApproval: false, outsideOpcore: false + }; +} diff --git a/packages/opcore/src/init-actions.ts b/packages/opcore/src/init-actions.ts new file mode 100644 index 0000000..103747d --- /dev/null +++ b/packages/opcore/src/init-actions.ts @@ -0,0 +1,17 @@ +import type { OpcoreInitAction } from "@the-open-engine/opcore-contracts"; +import { createGlobalInitActions } from "./init-actions-global.js"; +import { createRepoInitActions } from "./init-actions-repo.js"; +import type { InitScope, ParsedInitArgs } from "./init-types.js"; + +export interface InitActionInput { + scope: InitScope; + agentFiles: readonly string[]; + options: ParsedInitArgs; + gitignoreWritePlanned: boolean; + activePreCommitWritePlanned: boolean; +} + +export function createInitActions(input: InitActionInput): OpcoreInitAction[] { + if (input.scope === "global") return createGlobalInitActions(input.options); + return createRepoInitActions(input); +} diff --git a/packages/opcore/src/init-apply.ts b/packages/opcore/src/init-apply.ts new file mode 100644 index 0000000..ff32cf8 --- /dev/null +++ b/packages/opcore/src/init-apply.ts @@ -0,0 +1,69 @@ +import { rmSync } from "node:fs"; +import { removeEmptyOpcoreHookDir } from "./init-files.js"; +import { + assertMutationPath, + resolveRepoPath +} from "./init-paths.js"; +import { + readUndoMetadata, + readUndoMetadataIfExists, + undoPathForScope +} from "./init-undo-metadata.js"; +import { + priorEntry, + restoreUndoEntry, + writeScopedFile +} from "./init-write.js"; +import type { + InitScope, + PlannedWrite, + UndoMetadata +} from "./init-types.js"; + +export function applyInit(root: string, scope: InitScope, writes: readonly PlannedWrite[]): void { + const scopedWrites = writes.filter((write) => write.targetScope === scope); + const previous = readUndoMetadataIfExists(root, scope); + const previousPaths = previous === undefined + ? [] + : previous.entries.map((entry) => entry.path); + const touchedPaths = uniqueStrings([ + ...previousPaths, + ...scopedWrites.map((write) => write.path), + undoPathForScope(scope) + ]); + for (const path of touchedPaths) assertMutationPath(root, path, `Opcore ${scope} init target`); + const metadata: UndoMetadata = { + schemaVersion: 1, + kind: scope === "global" ? "opcore_global_init_undo" : "opcore_init_undo", + ...(scope === "global" ? { homeRoot: root } : { repoRoot: root }), + entries: touchedPaths.map((path) => { + const previousEntry = previous?.entries.find((entry) => entry.path === path); + return previousEntry ?? + priorEntry(root, path, scopedWrites.find((write) => write.path === path)); + }) + }; + for (const write of scopedWrites) writeScopedFile(root, write); + writeScopedFile(root, { + kind: "write", path: undoPathForScope(scope), targetScope: scope, + content: `${JSON.stringify(metadata, null, 2)}\n` + }); +} + +export function applyUndo(root: string, scope: InitScope): void { + const metadata = readUndoMetadata(root, scope); + const undoPath = undoPathForScope(scope); + for (const entry of metadata.entries) { + assertMutationPath(root, entry.path, "Opcore init undo target"); + } + for (const entry of metadata.entries.filter((entry) => entry.path !== undoPath)) { + restoreUndoEntry(root, entry); + } + const undoEntry = metadata.entries.find((entry) => entry.path === undoPath); + if (undoEntry) restoreUndoEntry(root, undoEntry); + else rmSync(resolveRepoPath(root, undoPath), { force: true }); + removeEmptyOpcoreHookDir(root); +} + +function uniqueStrings(values: readonly string[]): string[] { + return [...new Set(values)]; +} diff --git a/packages/opcore/src/init-args.ts b/packages/opcore/src/init-args.ts new file mode 100644 index 0000000..7b179d3 --- /dev/null +++ b/packages/opcore/src/init-args.ts @@ -0,0 +1,116 @@ +import type { + InitScope, + OpcoreSetupCommand, + ParsedInitArgs +} from "./init-types.js"; + +declare const process: { cwd(): string }; + +type FlagHandler = (parsed: ParsedInitArgs) => void; + +interface RepoArgumentResult { + handled: boolean; + consumed: number; + message?: string; +} + +export function parseOpcoreInitArgs( + args: readonly string[], + command: OpcoreSetupCommand +): { ok: true; args: ParsedInitArgs } | { ok: false; message: string } { + const parsed = createDefaultArgs(command); + const handlers = createFlagHandlers(command); + for (let index = 0; index < args.length; index += 1) { + const repoArgument = parseRepoArgument(args, index, parsed, command); + if (repoArgument.message) return { ok: false, message: repoArgument.message }; + if (repoArgument.handled) { + index += repoArgument.consumed; + continue; + } + const handler = handlers[args[index]]; + if (!handler) return { ok: false, message: `opcore ${command}: unsupported argument ${args[index]}` }; + handler(parsed); + } + return { ok: true, args: parsed }; +} + +function createDefaultArgs(command: OpcoreSetupCommand): ParsedInitArgs { + return { + command, + repo: process.cwd(), + repoExplicit: false, + scope: "repo", + scopeExplicit: false, + approved: false, + dryRun: false, + failClosedHook: false, + agentSkill: command === "install", + writeGateHooks: true, + activePreCommitHook: command === "install", + undo: command === "uninstall" + }; +} + +function createFlagHandlers(command: OpcoreSetupCommand): Record { + const handlers: Record = { + "--global": (parsed) => setScope(parsed, "global"), + "--local": (parsed) => setScope(parsed, "repo"), + "--approve": approve, + "--yes": approve, + "--dry-run": (parsed) => { + parsed.dryRun = true; + }, + "--fail-closed-hook": (parsed) => { + parsed.failClosedHook = true; + } + }; + if (command === "install") { + handlers["--no-pre-commit"] = (parsed) => { + parsed.activePreCommitHook = false; + }; + handlers["--no-skill"] = (parsed) => { + parsed.agentSkill = false; + }; + } else { + handlers["--undo"] = (parsed) => { + parsed.undo = true; + }; + } + return handlers; +} + +function parseRepoArgument( + args: readonly string[], + index: number, + parsed: ParsedInitArgs, + command: OpcoreSetupCommand +): RepoArgumentResult { + const arg = args[index]; + if (arg === "--repo") { + const value = args[index + 1]; + if (!value || value.startsWith("--")) { + return { handled: true, consumed: 0, message: `opcore ${command}: --repo requires a path` }; + } + setRepo(parsed, value); + return { handled: true, consumed: 1 }; + } + if (!arg.startsWith("--repo=")) return { handled: false, consumed: 0 }; + const value = arg.slice("--repo=".length); + if (!value) return { handled: true, consumed: 0, message: `opcore ${command}: --repo requires a path` }; + setRepo(parsed, value); + return { handled: true, consumed: 0 }; +} + +function setRepo(parsed: ParsedInitArgs, repo: string): void { + parsed.repo = repo; + parsed.repoExplicit = true; + setScope(parsed, "repo"); +} + +function setScope(parsed: ParsedInitArgs, scope: InitScope): void { + parsed.scope = scope; + parsed.scopeExplicit = true; +} +function approve(parsed: ParsedInitArgs): void { + parsed.approved = true; +} diff --git a/packages/opcore/src/init-config.ts b/packages/opcore/src/init-config.ts new file mode 100644 index 0000000..4743995 --- /dev/null +++ b/packages/opcore/src/init-config.ts @@ -0,0 +1,59 @@ +import type { + OpcoreInitScanSummary, + OpcoreInitSettings +} from "@the-open-engine/opcore-contracts"; +import { CONFIG_PATH } from "./init-constants.js"; +import { isPlainObject } from "./init-data.js"; +import { readJsonObject } from "./init-files.js"; + +export interface InitConfigInput { + repoRoot: string; + failClosedHook: boolean; + activePreCommitHook: boolean; + writeGateHooks: boolean; + scan: OpcoreInitScanSummary; + settings: OpcoreInitSettings; +} + +export function createConfig(input: InitConfigInput): Record { + const existing = readJsonObject(input.repoRoot, CONFIG_PATH); + const hooks = isPlainObject(existing.hooks) ? existing.hooks : {}; + const guidance = isPlainObject(existing.guidance) ? existing.guidance : {}; + const onboarding = isPlainObject(existing.onboarding) ? existing.onboarding : {}; + return { + ...existing, + schemaVersion: 1, + kind: "opcore_init_config", + onboarding: { + ...onboarding, + scan: isPlainObject(onboarding.scan) ? onboarding.scan : input.scan, + languages: Array.isArray(onboarding.languages) ? onboarding.languages : input.settings.languages, + timingPayload: true + }, + guidance: { + ...guidance, + checkCommand: "opcore check --changed", + preserveExistingGuardrails: true, + treatUnsupportedCoverageHonestly: true, + directProductAuthority: "opcore" + }, + hooks: createHookConfig(hooks, input) + }; +} + +function createHookConfig( + existing: Record, + input: InitConfigInput +): Record { + const harnesses = input.writeGateHooks + ? ["claude-code", "codex"] + : Array.isArray(existing.harnesses) ? existing.harnesses : []; + return { + ...existing, + failClosedPreCommit: + existing.failClosedPreCommit === true || input.failClosedHook || input.activePreCommitHook, + activePreCommit: existing.activePreCommit === true || input.activePreCommitHook, + writeGate: existing.writeGate === true || input.writeGateHooks, + harnesses + }; +} diff --git a/packages/opcore/src/init-constants.ts b/packages/opcore/src/init-constants.ts new file mode 100644 index 0000000..c4fa0bb --- /dev/null +++ b/packages/opcore/src/init-constants.ts @@ -0,0 +1,28 @@ +export const HELP_ARGS = new Set(["--help", "-h", "help"]); +export const AGENT_FILE_CANDIDATES = [ + "AGENTS.md", + "CLAUDE.md", + "GEMINI.md", + ".github/copilot-instructions.md", + ".codex/AGENTS.md", + ".opencode/AGENTS.md" +] as const; + +export const BEGIN_MARKER = ""; +export const END_MARKER = ""; +export const CONFIG_PATH = ".opcore/config"; +export const UNDO_PATH = ".opcore/init-undo.json"; +export const HOOK_PATH = ".opcore/hooks/pre-commit-opcore-check.sh"; +export const AGENT_GATE_HOOK_PATH = ".opcore/hooks/opcore-agent-gate.mjs"; +export const REPO_AGENT_SKILL_PATH = ".agents/skills/opcore/SKILL.md"; +export const CLAUDE_AGENT_SKILL_PATH = ".claude/skills/opcore/SKILL.md"; +export const AGENT_SKILL_PATHS = [REPO_AGENT_SKILL_PATH, CLAUDE_AGENT_SKILL_PATH] as const; +export const CLAUDE_SETTINGS_PATH = ".claude/settings.json"; +export const CODEX_HOOKS_PATH = ".codex/hooks.json"; +export const ACTIVE_PRE_COMMIT_HOOK_PATH = ".git/hooks/pre-commit"; +export const FAIL_CLOSED_HOOK_ACTIVATION_COMMAND = + "cp .opcore/hooks/pre-commit-opcore-check.sh .git/hooks/pre-commit"; +export const GITIGNORE_PATH = ".gitignore"; +export const OPCORE_IGNORE_LINE = ".opcore/"; +export const DEFAULT_INIT_PROGRESS_INTERVAL_MS = 5000; +export const GLOBAL_UNDO_PATH = ".opcore/init-undo.json"; diff --git a/packages/opcore/src/init-context-payload.ts b/packages/opcore/src/init-context-payload.ts new file mode 100644 index 0000000..2395230 --- /dev/null +++ b/packages/opcore/src/init-context-payload.ts @@ -0,0 +1,26 @@ +import type { OpcoreInitPlanPayload } from "@the-open-engine/opcore-contracts"; +import type { InitContext, ParsedInitArgs } from "./init-types.js"; + +type InitContextPayload = Pick< + OpcoreInitPlanPayload, + "scan" | "settings" | "interaction" | "timings" +>; + +export function initContextPayload(context: InitContext): InitContextPayload { + return { + scan: context.scan, + settings: context.settings, + interaction: context.interaction, + timings: context.timings + }; +} + +export function initPayloadOptions( + options: ParsedInitArgs +): OpcoreInitPlanPayload["options"] { + return { + scope: options.scope, + failClosedHook: options.failClosedHook, + dryRun: options.dryRun + }; +} diff --git a/packages/opcore/src/init-data.ts b/packages/opcore/src/init-data.ts new file mode 100644 index 0000000..a3d1431 --- /dev/null +++ b/packages/opcore/src/init-data.ts @@ -0,0 +1,15 @@ +export function isPlainObject(value: unknown): value is Record { + return typeof value === "object" && value !== null && !Array.isArray(value); +} + +export function trimRightPreserve(text: string): string { + return text.replace(/\s+$/u, ""); +} + +export function trimLeftPreserve(text: string): string { + return text.replace(/^\s+/u, ""); +} + +export function errorMessage(error: unknown): string { + return error instanceof Error ? error.message : String(error); +} diff --git a/packages/opcore/src/init-files.ts b/packages/opcore/src/init-files.ts new file mode 100644 index 0000000..5f07453 --- /dev/null +++ b/packages/opcore/src/init-files.ts @@ -0,0 +1,32 @@ +import { readFileSync, readdirSync, rmSync } from "node:fs"; +import { + assertExistingRepoPath, + lstatIfExists, + repoPathExists, + resolveRepoPath +} from "./init-paths.js"; +import { isPlainObject } from "./init-data.js"; + +export function readJsonObject(root: string, path: string): Record { + const content = readOptionalRepoFile(root, path); + if (content === undefined) return {}; + const parsed = JSON.parse(content) as unknown; + if (!isPlainObject(parsed)) throw new Error(`${path} must contain a JSON object`); + return parsed; +} + +export function readJsonObjectIfExists(root: string, path: string): Record { + return repoPathExists(root, path) ? readJsonObject(root, path) : {}; +} + +export function readOptionalRepoFile(root: string, path: string): string | undefined { + if (!repoPathExists(root, path)) return undefined; + return readFileSync(assertExistingRepoPath(root, path, "Existing repo file", "file"), "utf8"); +} + +export function removeEmptyOpcoreHookDir(root: string): void { + const hooksDir = resolveRepoPath(root, ".opcore/hooks"); + if (!lstatIfExists(hooksDir)) return; + assertExistingRepoPath(root, ".opcore/hooks", "Opcore hooks directory", "directory"); + if (readdirSync(hooksDir).length === 0) rmSync(hooksDir, { recursive: true, force: true }); +} diff --git a/packages/opcore/src/init-format-summary.ts b/packages/opcore/src/init-format-summary.ts new file mode 100644 index 0000000..80663e2 --- /dev/null +++ b/packages/opcore/src/init-format-summary.ts @@ -0,0 +1,53 @@ +import type { OpcoreInitPlanPayload } from "@the-open-engine/opcore-contracts"; + +export function approvalLine( + payload: OpcoreInitPlanPayload, + applied: boolean, + approvalFlag?: string +): string { + if (payload.interaction.promptState === "requested") return "Approval: awaiting TTY response."; + if (payload.interaction.promptState === "declined") return "Approval: declined; no files written."; + if (applied) return "Approval: applied."; + const required = approvalFlag ?? (payload.mode === "undo" ? "--undo --approve" : "--approve"); + return payload.mode === "undo" + ? `Approval: required; rerun with ${required} to restore/remove recorded files.` + : `Approval: required; rerun with ${required} to write this setup.`; +} + +export function languageSummary(payload: OpcoreInitPlanPayload): string { + return payload.scan.languages.length === 0 + ? "none" + : payload.scan.languages.map((entry) => `${entry.language} ${entry.files}`).join(", "); +} + +export function unsupportedSummary(payload: OpcoreInitPlanPayload): string { + return payload.scan.unsupportedStacks.length === 0 + ? "none" + : payload.scan.unsupportedStacks.map((stack) => `${stack.language} ${stack.count}`).join(", "); +} + +export function degradedToolSummary(payload: OpcoreInitPlanPayload): string { + return payload.scan.degradedRustTools.length === 0 + ? "none" + : payload.scan.degradedRustTools.map((tool) => `${tool.adapter}:${tool.tool}`).join(", "); +} + +export function warningLines(payload: OpcoreInitPlanPayload): string[] { + return payload.warnings.length === 0 + ? [" none"] + : payload.warnings.map((warning) => ` ${warning}`); +} + +export function pythonManagerSummary(payload: OpcoreInitPlanPayload): string { + const managers = payload.settings.python?.dependencyManagers; + return managers?.length + ? managers.map((manager) => `${manager.kind}:${manager.path}`).join(", ") + : "none"; +} + +export function pythonEnvironmentSummary(payload: OpcoreInitPlanPayload): string { + const environments = payload.settings.python?.virtualEnvironments; + return environments?.length + ? environments.map((environment) => environment.path).join(", ") + : "none"; +} diff --git a/packages/opcore/src/init-format.ts b/packages/opcore/src/init-format.ts new file mode 100644 index 0000000..1bc8425 --- /dev/null +++ b/packages/opcore/src/init-format.ts @@ -0,0 +1,80 @@ +import type { OpcoreInitPlanPayload } from "@the-open-engine/opcore-contracts"; +import { + ACTIVE_PRE_COMMIT_HOOK_PATH, + CLAUDE_SETTINGS_PATH +} from "./init-constants.js"; +import type { OpcoreSetupCommand } from "./init-types.js"; +import { + approvalLine, + degradedToolSummary, + languageSummary, + pythonEnvironmentSummary, + pythonManagerSummary, + unsupportedSummary, + warningLines +} from "./init-format-summary.js"; + +export function formatSetupPlan( + payload: OpcoreInitPlanPayload, + applied: boolean, + command: OpcoreSetupCommand +): string { + if (command === "install") return formatInstallPlan(payload, applied); + if (command === "uninstall") return formatUninstallPlan(payload, applied); + return formatInitPlan(payload, applied); +} + +function formatInstallPlan(payload: OpcoreInitPlanPayload, applied: boolean): string { + const skillEnabled = payload.actions.some((action) => action.path.endsWith("/skills/opcore/SKILL.md")); + const hooksEnabled = payload.actions.some((action) => action.path.endsWith(CLAUDE_SETTINGS_PATH)); + const preCommitEnabled = payload.actions.some((action) => action.path === ACTIVE_PRE_COMMIT_HOOK_PATH); + const scanSummary = + `Analyzed ${payload.scan.totalFiles} files; validation=${payload.scan.validationStatus}; ` + + `diagnostics=${payload.scan.diagnosticCount}.`; + return [ + "Opcore install:", ` ${scanSummary}`, "Setup choices:", + `${skillEnabled ? "[x]" : "[ ]"} Install Opcore agent skill`, + `${hooksEnabled ? "[x]" : "[ ]"} Install Claude Code and Codex write-gate hooks`, + `${preCommitEnabled ? "[x]" : "[ ]"} Install Git pre-commit hook`, "", + formatInitPlan(payload, applied, "--yes") + ].join("\n"); +} + +function formatUninstallPlan(payload: OpcoreInitPlanPayload, applied: boolean): string { + return [ + "Opcore uninstall:", + " Restore or remove only files recorded in .opcore/init-undo.json.", + "", + formatInitPlan(payload, applied, "--yes") + ].join("\n"); +} + +function formatInitPlan(payload: OpcoreInitPlanPayload, applied: boolean, approvalFlag?: string): string { + const actions = payload.actions.map((action) => `- ${action.kind} ${action.path}: ${action.summary}`); + return [ + "Coverage:", ` files=${payload.scan.totalFiles}`, + ` graph-supported=${payload.scan.graphSupportedFiles}`, + ` validation-supported=${payload.scan.validationSupportedFiles}`, + ` validation-retained=${payload.scan.validationRetainedFiles}`, + ` unsupported=${unsupportedSummary(payload)}`, ` languages=${languageSummary(payload)}`, + ` degraded-validation-tools=${degradedToolSummary(payload)}`, + ` python-dependency-managers=${pythonManagerSummary(payload)}`, + ` python-virtualenvs=${pythonEnvironmentSummary(payload)}`, + "Findings:", ` diagnostics=${payload.scan.diagnosticCount}`, + ` validation=${payload.scan.validationStatus}`, + ` failed-checks=${payload.scan.failedChecks.length === 0 ? "none" : payload.scan.failedChecks.join(", ")}`, + ` graph=${payload.scan.graphState}`, ` activation=${payload.scan.activationLevel}`, + "Warnings:", ...warningLines(payload), payload.mode === "undo" ? "Undo:" : "Setup:", + `Repo: ${payload.repo.root}`, `Scope: ${payload.options.scope}`, `Mode: ${payload.mode}`, + `Approved: ${payload.approved ? "yes" : "no"}`, "Actions:", ...actions, + approvalLine(payload, applied, approvalFlag), "Timing:", + ` first-output-ms=${payload.timings.firstOutputMs} scan-ms=${payload.timings.scanMs} ` + + `total-ms=${payload.timings.totalMs}` + ].join("\n"); +} + +export function formatInteractiveOutcome(payload: OpcoreInitPlanPayload, command: OpcoreSetupCommand): string { + return payload.approved + ? `opcore ${command} applied\nApproval: applied.` + : `opcore ${command} declined\nApproval: declined; no files written.`; +} diff --git a/packages/opcore/src/init-gitignore.ts b/packages/opcore/src/init-gitignore.ts new file mode 100644 index 0000000..b4230aa --- /dev/null +++ b/packages/opcore/src/init-gitignore.ts @@ -0,0 +1,46 @@ +import { + OPCORE_IGNORE_LINE +} from "./init-constants.js"; +import type { ManagedLineUndoEntry } from "./init-types.js"; + +export function gitignoreIgnoresOpcore(content: string): boolean { + let ignored = false; + for (const rawLine of content.split(/\r\n|\n|\r/u)) { + const line = rawLine.trim(); + if (line.length === 0 || line.startsWith("#")) continue; + const negated = line.startsWith("!"); + const pattern = negated ? line.slice(1).trim() : line; + if (isOpcoreGitignorePattern(pattern)) ignored = !negated; + } + return ignored; +} + +function isOpcoreGitignorePattern(pattern: string): boolean { + return pattern === ".opcore" || + pattern === ".opcore/" || + pattern === "/.opcore" || + pattern === "/.opcore/" || + pattern === ".opcore/**" || + pattern === "/.opcore/**"; +} + +export function appendManagedGitignoreLine(existing: string | undefined): string { + if (existing === undefined || existing.length === 0) return `${OPCORE_IGNORE_LINE}\n`; + return `${existing.endsWith("\n") || existing.endsWith("\r") ? "" : "\n"}${OPCORE_IGNORE_LINE}\n`; +} + +export function removeManagedGitignoreLine( + current: string, + entry: ManagedLineUndoEntry +): { content: string; removed: boolean } { + if (entry.appended !== undefined && current.endsWith(entry.appended)) { + return { content: current.slice(0, -entry.appended.length), removed: true }; + } + const matchedChunks = current.match(/[^\r\n]*(?:\r\n|\n|\r|$)/gu); + const chunks = matchedChunks === null ? [] : matchedChunks; + const meaningful = chunks.filter((chunk) => chunk.length > 0); + const index = meaningful.findIndex((chunk) => chunk.replace(/(?:\r\n|\n|\r)$/u, "") === entry.line); + if (index < 0) return { content: current, removed: false }; + meaningful.splice(index, 1); + return { content: meaningful.join(""), removed: true }; +} diff --git a/packages/opcore/src/init-guidance.ts b/packages/opcore/src/init-guidance.ts new file mode 100644 index 0000000..abd8c9b --- /dev/null +++ b/packages/opcore/src/init-guidance.ts @@ -0,0 +1,73 @@ +import { + AGENT_FILE_CANDIDATES, + BEGIN_MARKER, + END_MARKER +} from "./init-constants.js"; +import { trimLeftPreserve, trimRightPreserve } from "./init-data.js"; +import { readOptionalRepoFile } from "./init-files.js"; +import { assertExistingRepoPath, repoPathExists } from "./init-paths.js"; + +export function detectAgentFiles(repoRoot: string): string[] { + const existing = AGENT_FILE_CANDIDATES.filter((path) => repoPathExists(repoRoot, path)); + for (const path of existing) { + assertExistingRepoPath(repoRoot, path, "Existing agent guidance file", "file"); + } + return existing.length > 0 ? [...existing] : ["AGENTS.md"]; +} + +export function upsertOpcoreBlock(existing: string | undefined): string { + const block = guidanceBlock(); + if (existing === undefined || existing.length === 0) return `${block}\n`; + const begin = existing.indexOf(BEGIN_MARKER); + const end = existing.indexOf(END_MARKER); + if ((begin === -1) !== (end === -1) || (begin !== -1 && end < begin)) { + throw new Error("existing Opcore init guidance markers are unbalanced"); + } + if (begin !== -1) { + const replacementEnd = end + END_MARKER.length; + return ( + `${trimRightPreserve(existing.slice(0, begin))}\n\n${block}\n` + + trimLeftPreserve(existing.slice(replacementEnd)) + ).replace(/\n{3,}/g, "\n\n"); + } + return `${trimRightPreserve(existing)}\n\n${block}\n`; +} + +export function agentGuidanceWrite(repoRoot: string, path: string): string { + return upsertOpcoreBlock(readOptionalRepoFile(repoRoot, path)); +} + +function guidanceBlock(): string { + return [ + BEGIN_MARKER, + "## Opcore", + "", + "- Run `opcore check --changed` before finalizing edits.", + "- Preserve existing repo lint/test/CI/pre-commit guardrails.", + "- Treat unsupported stacks and degraded tools honestly.", + "- For Python repos, require one configured per-project type authority; treat absent, conflicting, " + + "unavailable, or deferred authority and missing ruff/pytest as degraded coverage, not a pass.", + "- Use Opcore validation directly; ASP hosts retain their own decision authority.", + END_MARKER + ].join("\n"); +} + +export function opcoreAgentSkillContent(): string { + return [ + "---", + "name: opcore", + "description: Use when working in a repository that has installed Opcore robustness checks.", + "---", + "", + "# Opcore", + "", + "Use Opcore as the repository-local robustness gate for coding-agent edits.", + "", + "- Run `opcore status` to inspect activation and coverage before broad work.", + "- Run `opcore check --changed` before finalizing source edits.", + "- Treat unsupported stacks and degraded tools honestly; do not report them as clean coverage.", + "- Preserve existing lint, test, CI, pre-commit, and agent guardrails.", + "- The installed write gate is a hook guardrail for supported edit tools, not host authority.", + "" + ].join("\n"); +} diff --git a/packages/opcore/src/init-help.ts b/packages/opcore/src/init-help.ts new file mode 100644 index 0000000..e9cfc1d --- /dev/null +++ b/packages/opcore/src/init-help.ts @@ -0,0 +1,62 @@ +import type { OpcoreSetupCommand } from "./init-types.js"; + +export function opcoreSetupHelpMessage(command: OpcoreSetupCommand): string { + if (command === "install") return opcoreInstallHelpMessage(); + if (command === "uninstall") return opcoreUninstallHelpMessage(); + return opcoreInitHelpMessage(); +} + +function opcoreInitHelpMessage(): string { + return [ + "Usage:", + " opcore init [--repo ] [--local|--global] [--approve] [--json]", + " opcore init --undo --approve [--repo ] [--local|--global] [--json]", + "Flags:", + " --repo Repository root to set up.", + " --local Force repo-scoped setup.", + " --global Install the write gate in user-level agent settings.", + " --approve Apply the proposed additive setup.", + " --undo Revert files recorded in .opcore/init-undo.json.", + " --fail-closed-hook Add the optional fail-closed pre-commit hook.", + " --json Emit structured JSON.", + "Defaults:", + " Without --approve, init is plan-only outside an interactive approval prompt.", + " Inside a Git repo on a TTY, init asks whether to install for this repo or globally.", + "Examples:", + " opcore init --repo . --json", + " opcore init --repo . --approve", + " opcore init --global --approve", + "Exit codes: 0 planned or applied, 1 setup error, 64 unsupported." + ].join("\n"); +} + +function opcoreInstallHelpMessage(): string { + return [ + "Usage:", " opcore install [--repo ] [--local|--global] [--yes] [--json]", + "Flags:", " --repo Repository root to set up.", + " --local Force repo-scoped setup.", + " --global Install user-level agent skills and write-gate hooks.", + " --yes Apply the proposed setup without prompting.", + " --no-skill Do not install the Opcore agent skill.", + " --no-pre-commit Do not install the repo Git pre-commit hook.", + " --json Emit structured JSON.", + "Defaults:", + " install scans first, then applies on --yes or an interactive default-yes approval prompt.", + "Examples:", " opcore install", " opcore install --repo . --yes", + "Exit codes: 0 planned or applied, 1 setup error, 64 unsupported." + ].join("\n"); +} + +function opcoreUninstallHelpMessage(): string { + return [ + "Usage:", " opcore uninstall [--repo ] [--local|--global] [--yes] [--json]", + "Flags:", " --repo Repository root to restore/remove recorded setup from.", + " --local Force repo-scoped uninstall.", + " --global Restore/remove user-level recorded setup.", + " --yes Apply the uninstall without prompting.", + " --json Emit structured JSON.", + "Defaults:", " uninstall restores or removes only files recorded in .opcore/init-undo.json.", + "Examples:", " opcore uninstall --repo . --yes", " opcore uninstall --global --yes", + "Exit codes: 0 planned or applied, 1 setup error, 64 unsupported." + ].join("\n"); +} diff --git a/packages/opcore/src/init-hooks.ts b/packages/opcore/src/init-hooks.ts new file mode 100644 index 0000000..8f39674 --- /dev/null +++ b/packages/opcore/src/init-hooks.ts @@ -0,0 +1,81 @@ +import { + FAIL_CLOSED_HOOK_ACTIVATION_COMMAND +} from "./init-constants.js"; +import { isPlainObject } from "./init-data.js"; +import type { InitScope } from "./init-types.js"; + +export function failClosedHookContent(): string { + return [ + "#!/usr/bin/env sh", + "# Manual install required.", + "# This script is not active until installed.", + `# Activation command: ${FAIL_CLOSED_HOOK_ACTIVATION_COMMAND}`, + "set -eu", + "opcore check --changed", + "" + ].join("\n"); +} + +export function activePreCommitHookContent(): string { + return [ + "#!/usr/bin/env sh", + "# Installed by opcore install. Remove with opcore uninstall.", + "set -eu", + "opcore check --changed", + "" + ].join("\n"); +} + +export function mergeClaudeSettings(existing: Record, scope: InitScope): Record { + return mergePreToolUseHook(existing, { + matcher: "Edit|MultiEdit|Write", + command: agentGateCommand("claude", scope), + statusMessage: "Running Opcore write gate" + }); +} + +export function mergeCodexHooks(existing: Record, scope: InitScope): Record { + return mergePreToolUseHook(existing, { + matcher: "apply_patch|Edit|Write", + command: agentGateCommand("codex", scope), + statusMessage: "Running Opcore write gate" + }); +} + +function mergePreToolUseHook( + existing: Record, + hook: { matcher: string; command: string; statusMessage: string } +): Record { + const hooks = isPlainObject(existing.hooks) ? existing.hooks : {}; + const preToolUse = Array.isArray(hooks.PreToolUse) ? [...hooks.PreToolUse] : []; + const groupIndex = preToolUse.findIndex((entry) => isPlainObject(entry) && entry.matcher === hook.matcher); + const hookEntry = { + type: "command", command: hook.command, timeout: 30, statusMessage: hook.statusMessage + }; + if (groupIndex >= 0) { + const group = preToolUse[groupIndex]; + if (isPlainObject(group)) { + const groupHooks = Array.isArray(group.hooks) ? [...group.hooks] : []; + const alreadyPresent = groupHooks.some( + (entry) => isPlainObject(entry) && + typeof entry.command === "string" && + entry.command.includes("opcore-agent-gate.mjs") + ); + preToolUse[groupIndex] = { + ...group, + hooks: alreadyPresent ? groupHooks : [...groupHooks, hookEntry] + }; + } + } else { + preToolUse.push({ matcher: hook.matcher, hooks: [hookEntry] }); + } + return { ...existing, hooks: { ...hooks, PreToolUse: preToolUse } }; +} + +function agentGateCommand(harness: "claude" | "codex", scope: InitScope): string { + const hook = scope === "global" + ? "$HOME/.opcore/hooks/opcore-agent-gate.mjs" + : '"$(git rev-parse --show-toplevel)/.opcore/hooks/opcore-agent-gate.mjs"'; + const repo = scope === "global" ? "" : ' --repo "$(git rev-parse --show-toplevel)"'; + return `node ${hook} --harness ${harness}${repo}`; +} diff --git a/packages/opcore/src/init-language-settings.ts b/packages/opcore/src/init-language-settings.ts new file mode 100644 index 0000000..c2e081d --- /dev/null +++ b/packages/opcore/src/init-language-settings.ts @@ -0,0 +1,96 @@ +import type { + OpcoreInitLanguageSetting, + OpcoreInitPythonEnvironment, + OpcoreRepoStatePayload +} from "@the-open-engine/opcore-contracts"; + +export interface LanguageSettingContext { + unsupportedLanguages: ReadonlySet; + degradedRustTools: readonly string[]; + degradedPythonTools: readonly string[]; + rustHasActiveValidationInput: boolean; + retainedFiles: number; + pythonProject: OpcoreInitPythonEnvironment; +} + +export function createLanguageSetting( + language: OpcoreRepoStatePayload["coverage"]["languages"][number], + context: LanguageSettingContext +): OpcoreInitLanguageSetting { + const validation = languageValidationState(language, context); + return { + language: language.language, + files: language.files, + state: validation, + graph: language.graphSupported ? "supported" : "unsupported", + validation, + checks: checksForLanguage(language.language, validation), + notes: languageNotes(language.language, validation, context) + }; +} + +function languageValidationState( + language: OpcoreRepoStatePayload["coverage"]["languages"][number], + context: LanguageSettingContext +): OpcoreInitLanguageSetting["validation"] { + const rustRetainedOnly = + language.language === "Rust" && + !context.rustHasActiveValidationInput && + context.retainedFiles > 0; + if (context.unsupportedLanguages.has(language.language) && !language.validationSupported) return "unsupported"; + if (rustRetainedOnly) return "retained"; + if ( + language.validationSupported && + degradedValidationTools(language.language, context).length > 0 + ) { + return "degraded"; + } + return language.validationSupported ? "supported" : "unsupported"; +} + +function degradedValidationTools( + language: string, + context: LanguageSettingContext +): readonly string[] { + if (language === "Rust") return context.degradedRustTools; + if (language === "Python") return context.degradedPythonTools; + return []; +} + +function checksForLanguage(language: string, validation: OpcoreInitLanguageSetting["validation"]): string[] { + if (validation === "unsupported" || validation === "retained") return []; + if (language === "TypeScript" || language === "JavaScript") { + return [ + "typescript.syntax", "typescript.types", "typescript.import-graph", "typescript.dead-code", + "typescript.function-metrics", "typescript.relevant-tests", "typescript.file-length" + ]; + } + if (language === "Rust") { + return [ + "rust.source-hygiene", "rust.fmt", "rust.cargo-check", "rust.clippy", "rust.rustdoc", + "rust.import-graph", "rust.dead-code", "rust.unused-deps", "rust.file-length", "rust.function-metrics" + ]; + } + if (language === "Python") { + return [ + "python.syntax", "python.source-hygiene", "python.types", "python.import-graph", + "python.dead-code", "python.relevant-tests", "python.pytest" + ]; + } + return []; +} + +function languageNotes( + language: string, + validation: OpcoreInitLanguageSetting["validation"], + context: LanguageSettingContext +): string[] { + if (validation === "unsupported") return ["Unsupported stack counted without fabricated checks."]; + if (validation === "retained") return ["Retained for compatibility; no active checks configured."]; + const degradedTools = language === "Python" ? context.degradedPythonTools : context.degradedRustTools; + const notes = validation === "degraded" + ? [`${language} validation tools degraded: ${degradedTools.join(", ")}.`] + : []; + if (language === "Python") notes.push(...context.pythonProject.notes); + return notes; +} diff --git a/packages/opcore/src/init-messages.ts b/packages/opcore/src/init-messages.ts new file mode 100644 index 0000000..128441c --- /dev/null +++ b/packages/opcore/src/init-messages.ts @@ -0,0 +1,101 @@ +import type { + OpcoreInitPlanPayload, + OpcoreInitScanSummary +} from "@the-open-engine/opcore-contracts"; +import { + FAIL_CLOSED_HOOK_ACTIVATION_COMMAND +} from "./init-constants.js"; +import type { + InitScope, + OpcoreSetupCommand, + ParsedInitArgs +} from "./init-types.js"; + +export interface InitWarningInput { + scan: OpcoreInitScanSummary; + git: boolean; + failClosedHook: boolean; + scope: InitScope; + activePreCommitHook: boolean; + activePreCommitRequested: boolean; + linkedGitWorktree: boolean; +} + +export function initWarnings(input: InitWarningInput): string[] { + const warnings: string[] = []; + if (input.scan.unsupportedStacks.length > 0) { + const stacks = input.scan.unsupportedStacks.map((stack) => `${stack.language} (${stack.count})`); + warnings.push(`Unsupported stacks: ${stacks.join(", ")}`); + } + if (input.scan.degradedRustTools.length > 0) { + const tools = input.scan.degradedRustTools.map((tool) => tool.tool); + warnings.push(`Degraded validation tools: ${tools.join(", ")}`); + } + if (!input.git) warnings.push("No Git repository detected; .opcore/ ignore entry not written."); + warnings.push("Do not weaken existing lint, test, CI, pre-commit, or agent guardrails."); + warnings.push(scopeWarning(input.scope)); + warnings.push(hookWarning(input)); + return warnings; +} + +function scopeWarning(scope: InitScope): string { + return scope === "global" + ? "Global write-gate hooks apply across repos; undo removes only Opcore-recorded global hook entries." + : "Repo write-gate hooks are additive; Codex project hooks may require trust review before they run."; +} + +function hookWarning(input: InitWarningInput): string { + if (input.failClosedHook) { + return `Fail-closed hook script is opt-in. Manual install required: ${FAIL_CLOSED_HOOK_ACTIVATION_COMMAND}`; + } + if (input.activePreCommitHook && input.git) { + return "Git pre-commit hook will run opcore check --changed when no existing .git/hooks/pre-commit is present."; + } + if (input.linkedGitWorktree) { + return "Linked Git worktree detected; Opcore will not install .git/hooks/pre-commit from this checkout."; + } + if (input.activePreCommitRequested) { + return "Existing .git/hooks/pre-commit detected; Opcore will not overwrite it."; + } + return "Fail-closed hooks are opt-in and are not created unless --fail-closed-hook is approved."; +} + +export function initNextActions(options: ParsedInitArgs): string[] { + const approveFlag = options.command === "install" ? "--yes" : "--approve"; + const scopedCommand = options.scope === "global" + ? `opcore ${options.command} --global ${approveFlag}` + : `opcore ${options.command} ${approveFlag}`; + const actions = options.dryRun + ? [`Run ${scopedCommand} to apply this plan.`] + : [`Review this plan, then run ${scopedCommand} to write setup.`]; + actions.push(options.scope === "repo" + ? "Claude Code and Codex write-gate hooks are installed by Opcore setup; " + + "review Codex project hook trust with /hooks if Codex asks." + : "Global Claude Code and Codex write-gate hooks are installed by Opcore setup; " + + "review Codex hook trust with /hooks if Codex asks."); + if (options.failClosedHook) actions.push(failClosedHookManualInstallAction()); + return actions; +} + +export function appliedInitNextActions( + payload: OpcoreInitPlanPayload, + command: OpcoreSetupCommand +): string[] { + const undoCommand = command === "install" + ? payload.options.scope === "global" ? "opcore uninstall --global --yes" : "opcore uninstall --yes" + : payload.options.scope === "global" + ? "opcore init --global --undo --approve" + : "opcore init --undo --approve"; + const actions = [`Run ${undoCommand} to restore or remove recorded setup files.`]; + actions.push(payload.options.scope === "repo" + ? "Claude Code write calls are blocked on non-ok receipts. " + + "Codex uses a PreToolUse guardrail and may require hook trust review." + : "Global Claude Code write calls are blocked on non-ok receipts. " + + "Codex uses a PreToolUse guardrail and may require hook trust review."); + if (payload.options.failClosedHook) actions.push(failClosedHookManualInstallAction()); + return actions; +} + +function failClosedHookManualInstallAction(): string { + return `Manual install required before the fail-closed hook is active: ${FAIL_CLOSED_HOOK_ACTIVATION_COMMAND}`; +} diff --git a/packages/opcore/src/init-paths.ts b/packages/opcore/src/init-paths.ts new file mode 100644 index 0000000..0c7185b --- /dev/null +++ b/packages/opcore/src/init-paths.ts @@ -0,0 +1,95 @@ +import { lstatSync, realpathSync, statSync } from "node:fs"; +import { dirname, isAbsolute, relative, resolve, sep } from "node:path"; +import { errorMessage } from "./init-data.js"; +import { isMissingPathError, resolveRepoPath } from "./repo-paths.js"; + +export { resolveRepoPath } from "./repo-paths.js"; + +interface ExistingPathRequest { + root: string; + absolute: string; + displayPath: string; + label: string; + expected: "file" | "directory"; +} + +export function assertMutationPath(root: string, path: string, label: string): string { + const absolute = resolveRepoPath(root, path); + assertExistingAncestorInsideRepo(root, absolute, path, label); + if (lstatIfExists(absolute)) assertExistingRepoPath(root, path, label, "file"); + return absolute; +} + +export function assertExistingRepoPath( + root: string, + path: string, + label: string, + expected: "file" | "directory" +): string { + const absolute = resolveRepoPath(root, path); + assertExistingAncestorInsideRepo(root, absolute, path, label); + return assertExistingAbsolutePath({ root, absolute, displayPath: path, label, expected }); +} + +function assertExistingAbsolutePath(request: ExistingPathRequest): string { + const lstat = lstatIfExists(request.absolute); + if (!lstat) throw new Error(`${request.label} does not exist: ${request.displayPath}`); + if (lstat.isSymbolicLink()) throw new Error(`${request.label} must not be a symlink: ${request.displayPath}`); + let realPath: string; + try { + realPath = realpathSync(request.absolute); + } catch (error) { + throw new Error(`${request.label} symlink cannot be resolved for ${request.displayPath}: ${errorMessage(error)}`); + } + if (!isInsideRoot(request.root, realPath)) { + throw new Error(`${request.label} resolves outside repository through a symlink: ${request.displayPath}`); + } + const stat = statSync(request.absolute); + if (request.expected === "file" && !stat.isFile()) { + throw new Error(`${request.label} is not a file: ${request.displayPath}`); + } + if (request.expected === "directory" && !stat.isDirectory()) { + throw new Error(`${request.label} is not a directory: ${request.displayPath}`); + } + return request.absolute; +} + +function assertExistingAncestorInsideRepo(root: string, absolute: string, path: string, label: string): void { + const parent = relative(resolve(root), dirname(absolute)); + if (parent === "") return; + if (parent.startsWith("..") || isAbsolute(parent)) { + throw new Error(`${label} parent cannot be resolved inside repository: ${path}`); + } + let current = resolve(root); + for (const segment of parent.split(sep)) { + if (!segment) continue; + current = resolve(current, segment); + if (!lstatIfExists(current)) return; + const displayPath = relative(resolve(root), current) || "."; + assertExistingAbsolutePath({ + root, absolute: current, displayPath, label: `${label} parent`, expected: "directory" + }); + } +} + +export function repoPathExists(root: string, path: string): boolean { + return lstatIfExists(resolveRepoPath(root, path)) !== undefined; +} + +export function isLinkedGitWorktree(root: string): boolean { + return lstatIfExists(resolveRepoPath(root, ".git"))?.isFile() === true; +} + +export function lstatIfExists(path: string): ReturnType | undefined { + try { + return lstatSync(path); + } catch (error) { + if (isMissingPathError(error)) return undefined; + throw error; + } +} + +function isInsideRoot(root: string, path: string): boolean { + const normalized = relative(resolve(root), resolve(path)); + return normalized === "" || (!normalized.startsWith("..") && !isAbsolute(normalized)); +} diff --git a/packages/opcore/src/init-payloads.ts b/packages/opcore/src/init-payloads.ts new file mode 100644 index 0000000..0fffcdd --- /dev/null +++ b/packages/opcore/src/init-payloads.ts @@ -0,0 +1,38 @@ +import type { OpcoreInitPlanPayload } from "@the-open-engine/opcore-contracts"; +import { appliedInitNextActions } from "./init-messages.js"; +import { repoPathExists } from "./init-paths.js"; +import { undoPathForScope } from "./init-undo-metadata.js"; +import { undoAppliedNextAction } from "./init-undo-plan.js"; +import type { + InitScope, + OpcoreSetupCommand +} from "./init-types.js"; + +export function appliedInitPayload( + payload: OpcoreInitPlanPayload, + root: string, + scope: InitScope, + command: OpcoreSetupCommand +): OpcoreInitPlanPayload { + return { + ...payload, + mode: "apply", + approved: true, + nextActions: appliedInitNextActions(payload, command), + undoAvailable: repoPathExists(root, undoPathForScope(scope)) + }; +} + +export function appliedUndoPayload( + payload: OpcoreInitPlanPayload, + root: string, + scope: InitScope, + command: OpcoreSetupCommand +): OpcoreInitPlanPayload { + return { + ...payload, + approved: true, + nextActions: [undoAppliedNextAction(command)], + undoAvailable: repoPathExists(root, undoPathForScope(scope)) + }; +} diff --git a/packages/opcore/src/init-plan-flow.ts b/packages/opcore/src/init-plan-flow.ts new file mode 100644 index 0000000..91a384b --- /dev/null +++ b/packages/opcore/src/init-plan-flow.ts @@ -0,0 +1,104 @@ +import type { + CommandRouterResult, + OpcoreInitPlanPayload +} from "@the-open-engine/opcore-contracts"; +import { applyInit } from "./init-apply.js"; +import { + formatInteractiveOutcome, + formatSetupPlan +} from "./init-format.js"; +import { appliedInitPayload } from "./init-payloads.js"; +import { planInit } from "./init-plan.js"; +import { + approvalPromptSuffix, + isApprovedAnswer, + parseScopeAnswer, + scopeRoot, + shouldPromptForApproval, + shouldPromptForScope +} from "./init-prompts.js"; +import { createInitRouterResult } from "./init-result.js"; +import { elapsedMs, nowMs, withContext } from "./init-timing.js"; +import type { + PlannedInit, + SetupSession +} from "./init-types.js"; + +interface ApprovalOutcome { + approved: boolean; + prompted: boolean; + payload: OpcoreInitPlanPayload; +} + +export async function routeOpcoreInitPlanOrApply( + session: SetupSession +): Promise { + await promptForScope(session); + const planStartedAt = nowMs(); + const planned = planInit({ + repoRoot: session.repoRoot, requestedPath: session.requestedPath, + git: session.git, homeRoot: session.homeRoot, + options: session.options, context: session.context + }); + session.timing.planMs = elapsedMs(planStartedAt); + const outcome = await promptForApproval(session, planned); + const root = scopeRoot(session.repoRoot, session.homeRoot, session.options.scope); + if (outcome.approved) { + const applyStartedAt = nowMs(); + applyInit(root, session.options.scope, planned.writes); + session.timing.applyMs = elapsedMs(applyStartedAt); + } + let payload = outcome.approved + ? appliedInitPayload(outcome.payload, root, session.options.scope, session.command) + : outcome.payload; + payload = withContext(payload, session.context, session.timing); + return createInitRouterResult({ + argv: session.argv, json: session.json, status: "ok", + message: outcome.prompted + ? formatInteractiveOutcome(payload, session.command) + : formatSetupPlan(payload, outcome.approved, session.command), + canonicalCommand: ["opcore", session.command], opcoreInit: payload + }); +} + +async function promptForScope(session: SetupSession): Promise { + if (!shouldPromptForScope(session.json, session.options, session.git, session.runtime)) return; + const startedAt = nowMs(); + const answer = await session.runtime.readLine( + "Install the Opcore write gate for THIS repo, or GLOBALLY for all repos? [repo/global] " + ); + session.timing.promptMs += elapsedMs(startedAt); + session.options = { + ...session.options, scope: parseScopeAnswer(answer), scopeExplicit: true + }; +} + +async function promptForApproval( + session: SetupSession, + planned: PlannedInit +): Promise { + let payload = withContext(planned.payload, session.context, session.timing); + let approved = session.options.approved && !session.options.dryRun; + if (!shouldPromptForApproval(session.json, session.options, session.runtime)) { + return { approved, prompted: false, payload }; + } + session.context.interaction = { tty: true, promptState: "requested" }; + payload = withContext(payload, session.context, session.timing); + const startedAt = nowMs(); + const answer = await session.runtime.readLine( + `${formatSetupPlan(payload, false, session.command)}\n` + + `Apply setup? ${approvalPromptSuffix(session.command)} ` + ); + session.timing.promptMs += elapsedMs(startedAt); + approved = isApprovedAnswer(answer, session.command); + session.context.interaction = { + tty: true, promptState: approved ? "approved" : "declined" + }; + if (!approved) { + payload = { + ...payload, + nextActions: [`No files written. Rerun opcore ${session.command} when ready.`] + }; + } + return { approved, prompted: true, payload }; +} diff --git a/packages/opcore/src/init-plan-global.ts b/packages/opcore/src/init-plan-global.ts new file mode 100644 index 0000000..dce3020 --- /dev/null +++ b/packages/opcore/src/init-plan-global.ts @@ -0,0 +1,78 @@ +import { opcoreAgentGateHookScriptContent } from "./agent-gate.js"; +import { + AGENT_GATE_HOOK_PATH, + AGENT_SKILL_PATHS, + CLAUDE_SETTINGS_PATH, + CODEX_HOOKS_PATH, + GLOBAL_UNDO_PATH +} from "./init-constants.js"; +import { createInitActions } from "./init-actions.js"; +import { readJsonObjectIfExists } from "./init-files.js"; +import { opcoreAgentSkillContent } from "./init-guidance.js"; +import { mergeClaudeSettings, mergeCodexHooks } from "./init-hooks.js"; +import { initContextPayload, initPayloadOptions } from "./init-context-payload.js"; +import { initNextActions, initWarnings } from "./init-messages.js"; +import type { InitPlanInput } from "./init-plan.js"; +import { repoPathExists } from "./init-paths.js"; +import type { PlannedInit, PlannedWrite } from "./init-types.js"; + +export function planGlobalInit(input: InitPlanInput): PlannedInit { + const writes = createGlobalWrites(input); + return { + writes, + payload: { + schemaVersion: 1, + mode: "plan", + approved: false, + repo: { root: input.repoRoot, requestedPath: input.requestedPath }, + options: initPayloadOptions(input.options), + agentFiles: [], + actions: createInitActions({ + scope: "global", agentFiles: [], options: input.options, + gitignoreWritePlanned: false, activePreCommitWritePlanned: false + }), + warnings: initWarnings({ + scan: input.context.scan, git: true, failClosedHook: false, scope: "global", + activePreCommitHook: false, activePreCommitRequested: false, linkedGitWorktree: false + }), + nextActions: initNextActions(input.options), + undoAvailable: repoPathExists(input.homeRoot, GLOBAL_UNDO_PATH), + ...initContextPayload(input.context) + } + }; +} + +function createGlobalWrites(input: InitPlanInput): PlannedWrite[] { + const writes: PlannedWrite[] = []; + if (input.options.writeGateHooks) { + writes.push({ + kind: "write", path: AGENT_GATE_HOOK_PATH, targetScope: "global", + content: opcoreAgentGateHookScriptContent(), executable: true + }); + } + if (input.options.agentSkill) { + writes.push(...AGENT_SKILL_PATHS.map((path) => ({ + kind: "write" as const, path, targetScope: "global" as const, + content: opcoreAgentSkillContent() + }))); + } + if (input.options.writeGateHooks) writes.push(...globalHarnessWrites(input.homeRoot)); + return writes; +} + +function globalHarnessWrites(homeRoot: string): PlannedWrite[] { + return [ + { + kind: "write", path: CLAUDE_SETTINGS_PATH, targetScope: "global", + content: `${JSON.stringify(mergeClaudeSettings( + readJsonObjectIfExists(homeRoot, CLAUDE_SETTINGS_PATH), "global" + ), null, 2)}\n` + }, + { + kind: "write", path: CODEX_HOOKS_PATH, targetScope: "global", + content: `${JSON.stringify(mergeCodexHooks( + readJsonObjectIfExists(homeRoot, CODEX_HOOKS_PATH), "global" + ), null, 2)}\n` + } + ]; +} diff --git a/packages/opcore/src/init-plan-repo.ts b/packages/opcore/src/init-plan-repo.ts new file mode 100644 index 0000000..4dfd061 --- /dev/null +++ b/packages/opcore/src/init-plan-repo.ts @@ -0,0 +1,52 @@ +import { + ACTIVE_PRE_COMMIT_HOOK_PATH, + GITIGNORE_PATH, + UNDO_PATH +} from "./init-constants.js"; +import { createInitActions } from "./init-actions.js"; +import { detectAgentFiles } from "./init-guidance.js"; +import { initContextPayload, initPayloadOptions } from "./init-context-payload.js"; +import { initNextActions, initWarnings } from "./init-messages.js"; +import type { InitPlanInput } from "./init-plan.js"; +import { createRepoWrites } from "./init-plan-writes.js"; +import { isLinkedGitWorktree, repoPathExists } from "./init-paths.js"; +import type { PlannedInit } from "./init-types.js"; + +export function planRepoInit(input: InitPlanInput): PlannedInit { + const agentFiles = detectAgentFiles(input.repoRoot); + const linkedGitWorktree = input.git && isLinkedGitWorktree(input.repoRoot); + const activePreCommitWritePlanned = + input.options.activePreCommitHook && + input.git && + !linkedGitWorktree && + !repoPathExists(input.repoRoot, ACTIVE_PRE_COMMIT_HOOK_PATH); + const writes = createRepoWrites({ plan: input, agentFiles, activePreCommitWritePlanned }); + const gitignoreWritePlanned = writes.some((write) => write.path === GITIGNORE_PATH); + return { + writes, + payload: { + schemaVersion: 1, + mode: "plan", + approved: false, + repo: { root: input.repoRoot, requestedPath: input.requestedPath }, + options: initPayloadOptions(input.options), + agentFiles, + actions: createInitActions({ + scope: "repo", agentFiles, options: input.options, + gitignoreWritePlanned, activePreCommitWritePlanned + }), + warnings: initWarnings({ + scan: input.context.scan, + git: input.git, + failClosedHook: input.options.failClosedHook, + scope: "repo", + activePreCommitHook: activePreCommitWritePlanned, + activePreCommitRequested: input.options.activePreCommitHook && input.git, + linkedGitWorktree + }), + nextActions: initNextActions(input.options), + undoAvailable: repoPathExists(input.repoRoot, UNDO_PATH), + ...initContextPayload(input.context) + } + }; +} diff --git a/packages/opcore/src/init-plan-writes.ts b/packages/opcore/src/init-plan-writes.ts new file mode 100644 index 0000000..3917c68 --- /dev/null +++ b/packages/opcore/src/init-plan-writes.ts @@ -0,0 +1,110 @@ +import { opcoreAgentGateHookScriptContent } from "./agent-gate.js"; +import { + ACTIVE_PRE_COMMIT_HOOK_PATH, + AGENT_GATE_HOOK_PATH, + AGENT_SKILL_PATHS, + CLAUDE_SETTINGS_PATH, + CODEX_HOOKS_PATH, + CONFIG_PATH, + GITIGNORE_PATH, + HOOK_PATH, + OPCORE_IGNORE_LINE +} from "./init-constants.js"; +import { createConfig } from "./init-config.js"; +import { readJsonObjectIfExists, readOptionalRepoFile } from "./init-files.js"; +import { gitignoreIgnoresOpcore } from "./init-gitignore.js"; +import { agentGuidanceWrite, opcoreAgentSkillContent } from "./init-guidance.js"; +import { + activePreCommitHookContent, + failClosedHookContent, + mergeClaudeSettings, + mergeCodexHooks +} from "./init-hooks.js"; +import type { InitPlanInput } from "./init-plan.js"; +import type { PlannedWrite } from "./init-types.js"; + +export interface RepoWriteInput { + plan: InitPlanInput; + agentFiles: readonly string[]; + activePreCommitWritePlanned: boolean; +} + +export function createRepoWrites(input: RepoWriteInput): PlannedWrite[] { + const writes = baseRepoWrites(input); + if (input.plan.options.writeGateHooks) writes.push(...writeGateWrites(input.plan.repoRoot)); + if (input.plan.options.agentSkill) writes.push(...skillWrites()); + if (input.plan.git) writes.push(...gitWrites(input)); + if (input.plan.options.failClosedHook) { + writes.push({ + kind: "write", path: HOOK_PATH, targetScope: "repo", + content: failClosedHookContent(), executable: true + }); + } + return writes; +} + +function baseRepoWrites(input: RepoWriteInput): PlannedWrite[] { + const config = createConfig({ + repoRoot: input.plan.repoRoot, + failClosedHook: input.plan.options.failClosedHook, + activePreCommitHook: input.activePreCommitWritePlanned, + writeGateHooks: input.plan.options.writeGateHooks, + scan: input.plan.context.scan, + settings: input.plan.context.settings + }); + return [ + { + kind: "write", path: CONFIG_PATH, targetScope: "repo", + content: `${JSON.stringify(config, null, 2)}\n` + }, + ...input.agentFiles.map((path) => ({ + kind: "write" as const, path, targetScope: "repo" as const, + content: agentGuidanceWrite(input.plan.repoRoot, path) + })) + ]; +} + +function writeGateWrites(repoRoot: string): PlannedWrite[] { + return [ + { + kind: "write", path: AGENT_GATE_HOOK_PATH, targetScope: "repo", + content: opcoreAgentGateHookScriptContent(), executable: true + }, + { + kind: "write", path: CLAUDE_SETTINGS_PATH, targetScope: "repo", + content: `${JSON.stringify(mergeClaudeSettings( + readJsonObjectIfExists(repoRoot, CLAUDE_SETTINGS_PATH), "repo" + ), null, 2)}\n` + }, + { + kind: "write", path: CODEX_HOOKS_PATH, targetScope: "repo", + content: `${JSON.stringify(mergeCodexHooks( + readJsonObjectIfExists(repoRoot, CODEX_HOOKS_PATH), "repo" + ), null, 2)}\n` + } + ]; +} + +function skillWrites(): PlannedWrite[] { + return AGENT_SKILL_PATHS.map((path) => ({ + kind: "write", path, targetScope: "repo", content: opcoreAgentSkillContent() + })); +} + +function gitWrites(input: RepoWriteInput): PlannedWrite[] { + const writes: PlannedWrite[] = []; + const gitignore = readOptionalRepoFile(input.plan.repoRoot, GITIGNORE_PATH); + if (!gitignoreIgnoresOpcore(gitignore ?? "")) { + writes.push({ + kind: "append_managed_line", path: GITIGNORE_PATH, + targetScope: "repo", line: OPCORE_IGNORE_LINE + }); + } + if (input.activePreCommitWritePlanned) { + writes.push({ + kind: "write", path: ACTIVE_PRE_COMMIT_HOOK_PATH, targetScope: "repo", + content: activePreCommitHookContent(), executable: true + }); + } + return writes; +} diff --git a/packages/opcore/src/init-plan.ts b/packages/opcore/src/init-plan.ts new file mode 100644 index 0000000..0c0c17a --- /dev/null +++ b/packages/opcore/src/init-plan.ts @@ -0,0 +1,20 @@ +import type { + InitContext, + ParsedInitArgs, + PlannedInit +} from "./init-types.js"; +import { planGlobalInit } from "./init-plan-global.js"; +import { planRepoInit } from "./init-plan-repo.js"; + +export interface InitPlanInput { + repoRoot: string; + requestedPath: string; + git: boolean; + homeRoot: string; + options: ParsedInitArgs; + context: InitContext; +} + +export function planInit(input: InitPlanInput): PlannedInit { + return input.options.scope === "global" ? planGlobalInit(input) : planRepoInit(input); +} diff --git a/packages/opcore/src/init-prompts.ts b/packages/opcore/src/init-prompts.ts new file mode 100644 index 0000000..1a43cff --- /dev/null +++ b/packages/opcore/src/init-prompts.ts @@ -0,0 +1,66 @@ +import { realpathSync } from "node:fs"; +import { homedir } from "node:os"; +import { resolve } from "node:path"; +import type { + InitScope, + OpcoreInitRuntime, + OpcoreSetupCommand, + ParsedInitArgs +} from "./init-types.js"; + +export function shouldPromptForApproval( + json: boolean, + options: ParsedInitArgs, + runtime: OpcoreInitRuntime +): runtime is OpcoreInitRuntime & { readLine: (prompt: string) => Promise } { + return !json && + !options.approved && + !options.dryRun && + !options.undo && + isInteractiveRuntime(runtime) && + typeof runtime.readLine === "function"; +} + +export function shouldPromptForScope( + json: boolean, + options: ParsedInitArgs, + git: boolean, + runtime: OpcoreInitRuntime +): runtime is OpcoreInitRuntime & { readLine: (prompt: string) => Promise } { + return git && + !json && + !options.approved && + !options.dryRun && + !options.undo && + !options.scopeExplicit && + !options.repoExplicit && + isInteractiveRuntime(runtime) && + typeof runtime.readLine === "function"; +} + +export function parseScopeAnswer(answer: string | undefined): InitScope { + const normalized = (answer ?? "").trim().toLowerCase(); + return normalized === "g" || normalized === "global" ? "global" : "repo"; +} + +export function initHomeRoot(runtime: OpcoreInitRuntime): string { + return realpathSync(resolve(runtime.homeDir ?? homedir())); +} + +export function scopeRoot(repoRoot: string, homeRoot: string, scope: InitScope): string { + return scope === "global" ? homeRoot : repoRoot; +} + +export function isInteractiveRuntime(runtime: OpcoreInitRuntime): boolean { + return runtime.stdinIsTTY === true && runtime.stdoutIsTTY === true; +} + +export function isApprovedAnswer(answer: string | undefined, command: OpcoreSetupCommand): boolean { + const normalized = (answer ?? "").trim().toLowerCase(); + if (command === "install") return normalized === "" || normalized === "y" || normalized === "yes"; + return normalized === "y" || normalized === "yes"; +} + +export function approvalPromptSuffix(command: OpcoreSetupCommand): string { + return command === "install" ? "[Y/n]" : "[y/N]"; +} diff --git a/packages/opcore/src/init-python-settings.ts b/packages/opcore/src/init-python-settings.ts new file mode 100644 index 0000000..758ddb8 --- /dev/null +++ b/packages/opcore/src/init-python-settings.ts @@ -0,0 +1,70 @@ +import type { + OpcoreInitPythonEnvironment, + OpcoreRepoStatePayload +} from "@the-open-engine/opcore-contracts"; +import { relative } from "node:path"; + +const managerKinds = { + pip: "requirements", + uv: "uv", + poetry: "poetry", + pdm: "pyproject", + pipenv: "pipfile" +} as const; +type PythonContexts = NonNullable; + +export function pythonEnvironmentFromContexts(contexts: PythonContexts): OpcoreInitPythonEnvironment { + const managers = new Map(); + const environments = new Map(); + for (const context of contexts) { + collectManagers(context, managers); + const path = pythonEnvironmentPath(context); + if (path) environments.set(path, { kind: "venv", path }); + } + const projectRoots = uniqueSorted(contexts.map((context) => context.projectRoot)); + const outcomes = uniqueSorted(contexts.map((context) => context.outcome)); + const evidence = uniqueSorted(contexts.flatMap((context) => context.evidence.map((entry) => entry.path))); + return { + dependencyManagers: [...managers.values()].sort((left, right) => left.path.localeCompare(right.path)), + virtualEnvironments: [...environments.values()].sort((left, right) => left.path.localeCompare(right.path)), + notes: contexts.length === 0 ? [] : contextNotes(projectRoots, evidence, outcomes), + contexts + }; +} + +function collectManagers( + context: PythonContexts[number], + managers: Map +): void { + for (const manager of context.managers) { + const path = manager.lockFiles[0] ?? manager.configFiles[0]; + if (!path) continue; + const value = { kind: managerKinds[manager.kind], path }; + managers.set(`${value.kind}\0${value.path}`, value); + } +} + +function pythonEnvironmentPath(context: PythonContexts[number]): string | undefined { + if (context.interpreter?.source !== "project_local_environment") return undefined; + const executable = context.interpreter.executable.replaceAll("\\", "/"); + const suffix = executable.match(/\/(?:bin\/python[^/]*|Scripts\/python\.exe|python\.exe)$/u)?.[0]; + if (!suffix) return undefined; + const path = relative(context.repositoryRoot, executable.slice(0, -suffix.length)).replaceAll("\\", "/"); + return path.length > 0 && !path.startsWith("..") ? path : undefined; +} + +function contextNotes(projectRoots: string[], evidence: string[], outcomes: string[]): string[] { + return [ + `Canonical Python project contexts: ${projectRoots.join(", ") || "."}.`, + `Canonical Python project evidence: ${evidence.join(", ")}.`, + `Python context outcomes: ${outcomes.join(", ")}.` + ]; +} + +function uniqueSorted(values: readonly string[]): string[] { + return [...new Set(values)].sort(); +} + +export function hasPythonEnvironmentSignals(environment: OpcoreInitPythonEnvironment): boolean { + return (environment.contexts?.length ?? 0) > 0; +} diff --git a/packages/opcore/src/init-result.ts b/packages/opcore/src/init-result.ts new file mode 100644 index 0000000..1c49e76 --- /dev/null +++ b/packages/opcore/src/init-result.ts @@ -0,0 +1,27 @@ +import type { + CommandRouterResult, + OpcoreInitPlanPayload +} from "@the-open-engine/opcore-contracts"; +import { createCommandRouterResult } from "@the-open-engine/opcore-contracts"; + +export interface InitRouterResultInput { + argv: readonly string[]; + json: boolean; + status: "ok" | "error"; + message: string; + canonicalCommand?: readonly string[]; + opcoreInit?: OpcoreInitPlanPayload; +} + +export function createInitRouterResult(input: InitRouterResultInput): CommandRouterResult { + return createCommandRouterResult({ + bin: "opcore", + argv: input.argv, + canonicalCommand: input.canonicalCommand ?? ["opcore", "init"], + owner: "runtime", + status: input.status, + json: input.json, + message: input.message, + opcoreInit: input.opcoreInit + }); +} diff --git a/packages/opcore/src/init-router.ts b/packages/opcore/src/init-router.ts new file mode 100644 index 0000000..1bd7f71 --- /dev/null +++ b/packages/opcore/src/init-router.ts @@ -0,0 +1,73 @@ +import type { + CommandRouterResult, + ParsedCommandArgv +} from "@the-open-engine/opcore-contracts"; +import { parseOpcoreInitArgs } from "./init-args.js"; +import { opcoreSetupHelpMessage } from "./init-help.js"; +import { createInitRouterResult } from "./init-result.js"; +import { runResolvedSetup } from "./init-session.js"; +import { + HELP_ARGS +} from "./init-constants.js"; +import type { + OpcoreInitRuntime, + OpcoreSetupCommand +} from "./init-types.js"; +import { createInstallWizard } from "./init-wizard-render.js"; +import { resolveRepo } from "./status.js"; + +export async function routeOpcoreInit( + argv: readonly string[], + parsed: ParsedCommandArgv, + runtime: OpcoreInitRuntime = {} +): Promise { + return routeOpcoreSetup(argv, parsed, runtime, "init"); +} + +export async function routeOpcoreInstall( + argv: readonly string[], + parsed: ParsedCommandArgv, + runtime: OpcoreInitRuntime = {} +): Promise { + return routeOpcoreSetup(argv, parsed, runtime, "install"); +} + +export async function routeOpcoreUninstall( + argv: readonly string[], + parsed: ParsedCommandArgv, + runtime: OpcoreInitRuntime = {} +): Promise { + return routeOpcoreSetup(argv, parsed, runtime, "uninstall"); +} + +async function routeOpcoreSetup( + argv: readonly string[], + parsed: ParsedCommandArgv, + runtime: OpcoreInitRuntime, + command: OpcoreSetupCommand +): Promise { + const rest = parsed.args.slice(1); + if (rest.some((arg) => HELP_ARGS.has(arg))) { + return createInitRouterResult({ + argv, json: parsed.json, status: "ok", message: opcoreSetupHelpMessage(command), + canonicalCommand: ["opcore", command, "help"] + }); + } + const initArgs = parseOpcoreInitArgs(rest, command); + if (!initArgs.ok) { + return createInitRouterResult({ + argv, json: parsed.json, status: "error", message: initArgs.message + }); + } + const resolution = resolveRepo(initArgs.args.repo, `opcore ${command}`); + if (!resolution.ok) { + return createInitRouterResult({ + argv, json: parsed.json, status: "error", message: resolution.message + }); + } + return runResolvedSetup({ + argv, parsed, runtime, command, options: initArgs.args, + resolution: resolution.resolution, + wizard: createInstallWizard(parsed.json, initArgs.args, runtime, command) + }); +} diff --git a/packages/opcore/src/init-session.ts b/packages/opcore/src/init-session.ts new file mode 100644 index 0000000..2281882 --- /dev/null +++ b/packages/opcore/src/init-session.ts @@ -0,0 +1,102 @@ +import type { CommandRouterResult } from "@the-open-engine/opcore-contracts"; +import type { InstallWizardRenderer } from "./install-wizard.js"; +import { createOpcoreScanAnalysis } from "./scan.js"; +import { initHomeRoot } from "./init-prompts.js"; +import { routeOpcoreInitPlanOrApply } from "./init-plan-flow.js"; +import { createInitRouterResult } from "./init-result.js"; +import { createInitScanSummary, createInitSettings } from "./init-settings.js"; +import { + createTimingState, + elapsedMs, + finalizeTimings, + nowMs, + startInitScanProgress, + type InitScanProgress +} from "./init-timing.js"; +import type { + OpcoreInitRuntime, + OpcoreSetupCommand, + ParsedInitArgs, + SetupSession +} from "./init-types.js"; +import { routeOpcoreInitUndo } from "./init-undo-flow.js"; +import { runInstallWizardFlow } from "./init-wizard-flow.js"; +import { + repoDisplayLabel, + startWizardScanProgress +} from "./init-wizard-render.js"; +import type { RepoResolution } from "./status.js"; +import type { ParsedCommandArgv } from "@the-open-engine/opcore-contracts"; +import { errorMessage } from "./init-data.js"; + +export interface ResolvedSetupInput { + argv: readonly string[]; + parsed: ParsedCommandArgv; + runtime: OpcoreInitRuntime; + command: OpcoreSetupCommand; + options: ParsedInitArgs; + resolution: RepoResolution; + wizard?: InstallWizardRenderer; +} + +export async function runResolvedSetup(input: ResolvedSetupInput): Promise { + try { + const timing = createTimingState(); + const analysis = await scanForSetup(input, timing); + const context = { + scan: createInitScanSummary(analysis.repoState, analysis.validationResult), + settings: createInitSettings(analysis.repoState), + interaction: { tty: isInteractive(input.runtime), promptState: "not_requested" as const }, + timings: finalizeTimings(timing) + }; + const session: SetupSession = { + argv: input.argv, json: input.parsed.json, parsed: input.parsed, + repoRoot: input.resolution.root, requestedPath: input.resolution.requestedPath, + git: input.resolution.git, homeRoot: initHomeRoot(input.runtime), + options: input.options, context, timing, runtime: input.runtime, command: input.command + }; + if (session.options.undo) return routeOpcoreInitUndo(session); + if (input.wizard) return await runInstallWizardFlow({ ...session, wizard: input.wizard }); + return await routeOpcoreInitPlanOrApply(session); + } catch (error) { + return createInitRouterResult({ + argv: input.argv, json: input.parsed.json, status: "error", + message: `opcore ${input.command} failed: ${errorMessage(error)}` + }); + } finally { + input.wizard?.showCursor(); + } +} + +async function scanForSetup( + input: ResolvedSetupInput, + timing: ReturnType +): Promise>> { + const startedAt = nowMs(); + const progress = setupProgress(input); + const scan = input.runtime.scanAnalysis ?? createOpcoreScanAnalysis; + try { + const analysis = await scan(input.resolution); + timing.scanMs = elapsedMs(startedAt); + progress?.complete(timing.scanMs, analysis.repoState.coverage.totalFiles); + return analysis; + } catch (error) { + timing.scanMs = elapsedMs(startedAt); + progress?.fail(timing.scanMs); + throw error; + } +} + +function setupProgress(input: ResolvedSetupInput): InitScanProgress | undefined { + if (input.wizard) { + return startWizardScanProgress( + input.wizard, input.runtime, + repoDisplayLabel(input.resolution.root, initHomeRoot(input.runtime)) + ); + } + return startInitScanProgress(input.parsed.json, input.runtime, input.command); +} + +function isInteractive(runtime: OpcoreInitRuntime): boolean { + return runtime.stdinIsTTY === true && runtime.stdoutIsTTY === true; +} diff --git a/packages/opcore/src/init-settings.ts b/packages/opcore/src/init-settings.ts new file mode 100644 index 0000000..ffdb743 --- /dev/null +++ b/packages/opcore/src/init-settings.ts @@ -0,0 +1,66 @@ +import type { + OpcoreInitScanSummary, + OpcoreInitSettings, + OpcoreRepoStatePayload, + ValidationResult +} from "@the-open-engine/opcore-contracts"; +import { + createLanguageSetting, + type LanguageSettingContext +} from "./init-language-settings.js"; +import { + hasPythonEnvironmentSignals, + pythonEnvironmentFromContexts +} from "./init-python-settings.js"; +import { failedValidationCheckIds } from "./scan-presentation.js"; +import { scanValidationDiagnosticTotal } from "./scan-validation-preview.js"; + +const rustActiveValidationKinds = new Set([".rs", ".inc", "Cargo.toml"]); + +export function createInitScanSummary( + repoState: OpcoreRepoStatePayload, + validationResult: ValidationResult +): OpcoreInitScanSummary { + return { + totalFiles: repoState.coverage.totalFiles, + graphSupportedFiles: repoState.coverage.graph.supportedFiles, + validationSupportedFiles: repoState.coverage.validation.supportedFiles, + validationRetainedFiles: repoState.coverage.validation.retainedFiles, + unsupportedFiles: repoState.coverage.unsupported.totalFiles, + languages: repoState.coverage.languages, + unsupportedStacks: repoState.coverage.unsupported.stacks, + degradedRustTools: repoState.validation.degradedToolchains, + diagnosticCount: scanValidationDiagnosticTotal(validationResult), + validationStatus: validationResult.status, + failedChecks: failedValidationCheckIds(validationResult), + graphState: repoState.graph.state, + activationLevel: repoState.activation.level + }; +} + +export function createInitSettings(repoState: OpcoreRepoStatePayload): OpcoreInitSettings { + const contexts = repoState.validation.pythonProjectContexts; + const pythonProject = pythonEnvironmentFromContexts( + contexts === undefined ? [] : contexts + ); + const context: LanguageSettingContext = { + unsupportedLanguages: new Set(repoState.coverage.unsupported.stacks.map((stack) => stack.language)), + degradedRustTools: degradedTools(repoState, "rust"), + degradedPythonTools: degradedTools(repoState, "python"), + rustHasActiveValidationInput: repoState.coverage.validation.extensions.some((entry) => + rustActiveValidationKinds.has(entry.extension) + ), + retainedFiles: repoState.coverage.validation.retainedFiles, + pythonProject + }; + return { + languages: repoState.coverage.languages.map((language) => createLanguageSetting(language, context)), + ...(hasPythonEnvironmentSignals(pythonProject) ? { python: pythonProject } : {}) + }; +} + +function degradedTools(repoState: OpcoreRepoStatePayload, adapter: "rust" | "python"): string[] { + return repoState.validation.degradedToolchains + .filter((tool) => tool.adapter === adapter) + .map((tool) => tool.tool); +} diff --git a/packages/opcore/src/init-timing.ts b/packages/opcore/src/init-timing.ts new file mode 100644 index 0000000..7730fab --- /dev/null +++ b/packages/opcore/src/init-timing.ts @@ -0,0 +1,104 @@ +import type { OpcoreInitPlanPayload, OpcoreInitTiming } from "@the-open-engine/opcore-contracts"; +import type { + InitContext, + OpcoreInitRuntime, + OpcoreSetupCommand, + TimingState +} from "./init-types.js"; +import { DEFAULT_INIT_PROGRESS_INTERVAL_MS } from "./init-constants.js"; + +export interface InitScanProgress { + complete(scanMs: number, totalFiles?: number): void; + fail(scanMs: number): void; +} + +export function createTimingState(): TimingState { + return { + startedAt: nowMs(), + scanMs: 0, + planMs: 0, + promptMs: 0, + applyMs: 0 + }; +} + +export function withContext( + payload: OpcoreInitPlanPayload, + context: InitContext, + timing: TimingState +): OpcoreInitPlanPayload { + return { + ...payload, + scan: context.scan, + settings: context.settings, + interaction: context.interaction, + timings: finalizeTimings(timing) + }; +} + +export function startInitScanProgress( + json: boolean, + runtime: OpcoreInitRuntime, + command: OpcoreSetupCommand +): InitScanProgress | undefined { + if (json || runtime.stderrIsTTY !== true || typeof runtime.writeStderr !== "function") return undefined; + const startedAt = nowMs(); + const intervalMs = normalizeProgressIntervalMs(runtime.initProgressIntervalMs); + let finished = false; + const write = (text: string) => writeProgress(runtime.writeStderr, text); + const writeProgressLine = (text: string) => write(`\r\x1b[2K${text}`); + write(`Opcore ${command}: scanning repository before setup...`); + const timer = setInterval(() => { + if (finished) return; + const elapsedSeconds = Math.max(1, Math.floor(elapsedMs(startedAt) / 1000)); + writeProgressLine(`Opcore ${command}: still scanning repository before setup (${elapsedSeconds}s elapsed)...`); + }, intervalMs) as ReturnType & { unref?: () => void }; + timer.unref?.(); + return { + complete: (scanMs) => { + if (finished) return; + finished = true; + clearInterval(timer); + writeProgressLine(`Opcore ${command}: scan complete in ${scanMs}ms.\n`); + }, + fail: (scanMs) => { + if (finished) return; + finished = true; + clearInterval(timer); + writeProgressLine(`Opcore ${command}: scan failed after ${scanMs}ms.\n`); + } + }; +} + +function normalizeProgressIntervalMs(value: number | undefined): number { + return typeof value === "number" && Number.isFinite(value) && value > 0 + ? Math.max(1, Math.floor(value)) + : DEFAULT_INIT_PROGRESS_INTERVAL_MS; +} + +export function writeProgress(writeStderr: ((text: string) => void) | undefined, text: string): void { + try { + writeStderr?.(text); + } catch { + // Progress output must never change init scan/apply semantics. + } +} + +export function nowMs(): number { + return Date.now(); +} + +export function elapsedMs(startedAt: number): number { + return Math.max(0, Date.now() - startedAt); +} + +export function finalizeTimings(timing: TimingState): OpcoreInitTiming { + return { + scanMs: timing.scanMs, + planMs: timing.planMs, + promptMs: timing.promptMs, + applyMs: timing.applyMs, + totalMs: elapsedMs(timing.startedAt), + firstOutputMs: timing.scanMs + }; +} diff --git a/packages/opcore/src/init-types.ts b/packages/opcore/src/init-types.ts new file mode 100644 index 0000000..9aad1ae --- /dev/null +++ b/packages/opcore/src/init-types.ts @@ -0,0 +1,125 @@ +import type { + OpcoreInitInteraction, + OpcoreInitPlanPayload, + OpcoreInitScanSummary, + OpcoreInitSettings, + OpcoreInitTiming, + ParsedCommandArgv +} from "@the-open-engine/opcore-contracts"; +import type { InstallWizardRenderer } from "./install-wizard.js"; +import type { OpcoreScanAnalysis } from "./scan.js"; +import type { RepoResolution } from "./status.js"; + +export type OpcoreSetupCommand = "init" | "install" | "uninstall"; +export type InitScope = "repo" | "global"; + +export interface ParsedInitArgs { + command: OpcoreSetupCommand; + repo: string; + repoExplicit: boolean; + scope: InitScope; + scopeExplicit: boolean; + approved: boolean; + dryRun: boolean; + failClosedHook: boolean; + agentSkill: boolean; + writeGateHooks: boolean; + activePreCommitHook: boolean; + undo: boolean; +} + +export interface OpcoreInitRuntime { + stdinIsTTY?: boolean; + stdoutIsTTY?: boolean; + stderrIsTTY?: boolean; + stderrColor?: boolean; + stderrTrueColor?: boolean; + homeDir?: string; + writeStderr?: (text: string) => void; + scanAnalysis?: (resolution: RepoResolution) => Promise; + initProgressIntervalMs?: number; + readLine?: (prompt: string) => Promise; + readKey?: () => Promise; + initWizardMotion?: boolean; +} + +export interface InitContext { + scan: OpcoreInitScanSummary; + settings: OpcoreInitSettings; + interaction: OpcoreInitInteraction; + timings: OpcoreInitTiming; +} + +export interface TimingState { + startedAt: number; + scanMs: number; + planMs: number; + promptMs: number; + applyMs: number; +} + +export interface PlannedInit { + payload: OpcoreInitPlanPayload; + writes: readonly PlannedWrite[]; +} + +export type PlannedWrite = PlannedFileWrite | PlannedManagedLineAppend; + +export interface PlannedFileWrite { + kind: "write"; + path: string; + targetScope: InitScope; + content: string; + executable?: boolean; +} + +export interface PlannedManagedLineAppend { + kind: "append_managed_line"; + path: string; + targetScope: "repo"; + line: string; +} + +export interface UndoMetadata { + schemaVersion: 1; + kind: "opcore_init_undo" | "opcore_global_init_undo"; + repoRoot?: string; + homeRoot?: string; + entries: readonly UndoEntry[]; +} + +export type UndoEntry = FileUndoEntry | ManagedLineUndoEntry; + +export interface FileUndoEntry { + kind?: "restore_file"; + path: string; + existed: boolean; + content?: string; +} + +export interface ManagedLineUndoEntry { + kind: "append_managed_line"; + path: string; + existed: boolean; + line: string; + appended?: string; +} + +export interface SetupSession { + argv: readonly string[]; + json: boolean; + parsed: ParsedCommandArgv; + repoRoot: string; + requestedPath: string; + git: boolean; + homeRoot: string; + options: ParsedInitArgs; + context: InitContext; + timing: TimingState; + runtime: OpcoreInitRuntime; + command: OpcoreSetupCommand; +} + +export interface WizardSession extends SetupSession { + wizard: InstallWizardRenderer; +} diff --git a/packages/opcore/src/init-undo-entry.ts b/packages/opcore/src/init-undo-entry.ts new file mode 100644 index 0000000..c48c5db --- /dev/null +++ b/packages/opcore/src/init-undo-entry.ts @@ -0,0 +1,98 @@ +import { + GITIGNORE_PATH, + OPCORE_IGNORE_LINE +} from "./init-constants.js"; +import { isPlainObject } from "./init-data.js"; +import { resolveRepoPath } from "./init-paths.js"; +import type { + ManagedLineUndoEntry, + UndoEntry +} from "./init-types.js"; + +export interface UndoEntryParseInput { + value: unknown; + allowedPaths: ReadonlySet; + root: string; + seenPaths: Set; +} + +export function parseUndoEntry(input: UndoEntryParseInput): UndoEntry { + const entry = input.value; + if (!isPlainObject(entry) || typeof entry.path !== "string" || typeof entry.existed !== "boolean") { + throw new Error(".opcore/init-undo.json contains an invalid entry"); + } + validateEntryPath(entry.path, input); + const kind = typeof entry.kind === "string" ? entry.kind : "restore_file"; + if (kind === "append_managed_line") return parseManagedLineEntry(entry, input.root); + return parseFileEntry(entry, kind, input.root); +} + +function validateEntryPath(path: string, input: UndoEntryParseInput): void { + if (!input.allowedPaths.has(path)) { + throw new Error(`.opcore/init-undo.json contains unsupported path: ${path}`); + } + if (input.seenPaths.has(path)) { + throw new Error(`.opcore/init-undo.json contains duplicate path: ${path}`); + } + input.seenPaths.add(path); +} + +function parseManagedLineEntry( + entry: Record, + root: string +): ManagedLineUndoEntry { + const path = entry.path as string; + if (path !== GITIGNORE_PATH) { + throw new Error(`.opcore/init-undo.json append-managed-line entry targets unsupported path: ${path}`); + } + if (entry.line !== OPCORE_IGNORE_LINE) { + throw new Error(`.opcore/init-undo.json append-managed-line entry for ${path} has invalid line`); + } + validateAppendedText(entry, path); + resolveRepoPath(root, path); + return { + kind: "append_managed_line", + path, + existed: entry.existed as boolean, + line: OPCORE_IGNORE_LINE, + ...(typeof entry.appended === "string" ? { appended: entry.appended } : {}) + }; +} + +function validateAppendedText(entry: Record, path: string): void { + if (!("appended" in entry) || entry.appended === undefined) return; + if (entry.appended === `${OPCORE_IGNORE_LINE}\n`) return; + if (entry.appended === `\n${OPCORE_IGNORE_LINE}\n`) return; + throw new Error(`.opcore/init-undo.json append-managed-line entry for ${path} has invalid appended text`); +} + +function parseFileEntry( + entry: Record, + kind: string, + root: string +): UndoEntry { + const path = entry.path as string; + if (path === GITIGNORE_PATH) { + throw new Error(".opcore/init-undo.json .gitignore entry must use managed-line undo metadata"); + } + if (kind !== "restore_file") { + throw new Error(`.opcore/init-undo.json contains unsupported entry kind: ${kind}`); + } + validateFileContent(entry, path); + resolveRepoPath(root, path); + return { + kind: "restore_file", + path, + existed: entry.existed as boolean, + ...(typeof entry.content === "string" ? { content: entry.content } : {}) + }; +} + +function validateFileContent(entry: Record, path: string): void { + if (entry.existed && typeof entry.content !== "string") { + throw new Error(`.opcore/init-undo.json restore entry for ${path} is missing string content`); + } + if (!entry.existed && "content" in entry && entry.content !== undefined && typeof entry.content !== "string") { + throw new Error(`.opcore/init-undo.json remove entry for ${path} has invalid content`); + } +} diff --git a/packages/opcore/src/init-undo-flow.ts b/packages/opcore/src/init-undo-flow.ts new file mode 100644 index 0000000..55409ef --- /dev/null +++ b/packages/opcore/src/init-undo-flow.ts @@ -0,0 +1,43 @@ +import type { CommandRouterResult } from "@the-open-engine/opcore-contracts"; +import { applyUndo } from "./init-apply.js"; +import { formatSetupPlan } from "./init-format.js"; +import { appliedUndoPayload } from "./init-payloads.js"; +import { scopeRoot } from "./init-prompts.js"; +import { createInitRouterResult } from "./init-result.js"; +import { elapsedMs, nowMs, withContext } from "./init-timing.js"; +import type { SetupSession } from "./init-types.js"; +import { planUndo } from "./init-undo-plan.js"; + +export function routeOpcoreInitUndo(session: SetupSession): CommandRouterResult { + const planStartedAt = nowMs(); + const undo = planUndo({ + repoRoot: session.repoRoot, + requestedPath: session.requestedPath, + homeRoot: session.homeRoot, + options: session.options, + context: session.context + }); + session.timing.planMs = elapsedMs(planStartedAt); + const approved = session.options.approved && !session.options.dryRun; + const root = scopeRoot(session.repoRoot, session.homeRoot, session.options.scope); + if (approved) { + const applyStartedAt = nowMs(); + applyUndo(root, session.options.scope); + session.timing.applyMs = elapsedMs(applyStartedAt); + } + const payload = withContext( + approved + ? appliedUndoPayload(undo, root, session.options.scope, session.command) + : undo, + session.context, + session.timing + ); + return createInitRouterResult({ + argv: session.argv, + json: session.json, + status: "ok", + message: formatSetupPlan(payload, approved, session.command), + canonicalCommand: ["opcore", session.command], + opcoreInit: payload + }); +} diff --git a/packages/opcore/src/init-undo-metadata.ts b/packages/opcore/src/init-undo-metadata.ts new file mode 100644 index 0000000..7213677 --- /dev/null +++ b/packages/opcore/src/init-undo-metadata.ts @@ -0,0 +1,77 @@ +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { + ACTIVE_PRE_COMMIT_HOOK_PATH, + AGENT_FILE_CANDIDATES, + AGENT_GATE_HOOK_PATH, + AGENT_SKILL_PATHS, + CLAUDE_SETTINGS_PATH, + CODEX_HOOKS_PATH, + CONFIG_PATH, + GITIGNORE_PATH, + GLOBAL_UNDO_PATH, + HOOK_PATH, + UNDO_PATH +} from "./init-constants.js"; +import { isPlainObject } from "./init-data.js"; +import { assertExistingRepoPath, repoPathExists } from "./init-paths.js"; +import { parseUndoEntry } from "./init-undo-entry.js"; +import type { + InitScope, + UndoMetadata +} from "./init-types.js"; + +const allowedRepoPaths = new Set([ + CONFIG_PATH, UNDO_PATH, HOOK_PATH, AGENT_GATE_HOOK_PATH, ...AGENT_SKILL_PATHS, + CLAUDE_SETTINGS_PATH, CODEX_HOOKS_PATH, ACTIVE_PRE_COMMIT_HOOK_PATH, + GITIGNORE_PATH, ...AGENT_FILE_CANDIDATES +]); +const allowedGlobalPaths = new Set([ + GLOBAL_UNDO_PATH, AGENT_GATE_HOOK_PATH, ...AGENT_SKILL_PATHS, + CLAUDE_SETTINGS_PATH, CODEX_HOOKS_PATH +]); + +export function readUndoMetadata(root: string, scope: InitScope): UndoMetadata { + const path = undoPathForScope(scope); + const raw = readFileSync(assertExistingRepoPath(root, path, "Opcore init undo metadata", "file"), "utf8"); + const parsed = JSON.parse(raw) as unknown; + const envelope = validateUndoEnvelope(parsed, root, scope); + const seenPaths = new Set(); + const allowedPaths = scope === "global" ? allowedGlobalPaths : allowedRepoPaths; + const entries = envelope.entries.map((value) => + parseUndoEntry({ value, allowedPaths, root, seenPaths }) + ); + return { + schemaVersion: 1, + kind: envelope.kind, + ...(scope === "global" ? { homeRoot: envelope.recordedRoot } : { repoRoot: envelope.recordedRoot }), + entries + }; +} + +function validateUndoEnvelope( + value: unknown, + root: string, + scope: InitScope +): { kind: UndoMetadata["kind"]; recordedRoot: string; entries: unknown[] } { + const expectedKind = scope === "global" ? "opcore_global_init_undo" : "opcore_init_undo"; + if (!isPlainObject(value) || + value.schemaVersion !== 1 || + value.kind !== expectedKind || + !Array.isArray(value.entries)) { + throw new Error(".opcore/init-undo.json is not valid Opcore init undo metadata"); + } + const recordedRoot = scope === "global" ? value.homeRoot : value.repoRoot; + if (typeof recordedRoot !== "string" || resolve(recordedRoot) !== resolve(root)) { + throw new Error(".opcore/init-undo.json repoRoot does not match this repository"); + } + return { kind: expectedKind, recordedRoot, entries: value.entries }; +} + +export function readUndoMetadataIfExists(root: string, scope: InitScope): UndoMetadata | undefined { + return repoPathExists(root, undoPathForScope(scope)) ? readUndoMetadata(root, scope) : undefined; +} + +export function undoPathForScope(_scope: InitScope): string { + return UNDO_PATH; +} diff --git a/packages/opcore/src/init-undo-plan.ts b/packages/opcore/src/init-undo-plan.ts new file mode 100644 index 0000000..dc6ad80 --- /dev/null +++ b/packages/opcore/src/init-undo-plan.ts @@ -0,0 +1,80 @@ +import type { OpcoreInitPlanPayload } from "@the-open-engine/opcore-contracts"; +import { AGENT_FILE_CANDIDATES } from "./init-constants.js"; +import { actionPath } from "./init-action-helpers.js"; +import { initContextPayload, initPayloadOptions } from "./init-context-payload.js"; +import { scopeRoot } from "./init-prompts.js"; +import { readUndoMetadata } from "./init-undo-metadata.js"; +import type { + InitContext, + OpcoreSetupCommand, + ParsedInitArgs +} from "./init-types.js"; + +export interface UndoPlanInput { + repoRoot: string; + requestedPath: string; + homeRoot: string; + options: ParsedInitArgs; + context: InitContext; +} + +export function planUndo(input: UndoPlanInput): OpcoreInitPlanPayload { + const root = scopeRoot(input.repoRoot, input.homeRoot, input.options.scope); + const metadata = readUndoMetadata(root, input.options.scope); + return { + schemaVersion: 1, + mode: "undo", + approved: input.options.approved && !input.options.dryRun, + repo: { root: input.repoRoot, requestedPath: input.requestedPath }, + options: initPayloadOptions(input.options), + agentFiles: metadata.entries + .map((entry) => entry.path) + .filter((path) => AGENT_FILE_CANDIDATES.includes( + path as (typeof AGENT_FILE_CANDIDATES)[number] + )), + actions: metadata.entries.map((entry) => ({ + kind: entry.kind === "append_managed_line" ? "remove" : entry.existed ? "restore" : "remove", + path: actionPath(input.options.scope, entry.path), + targetScope: input.options.scope, + summary: undoSummary(input.options.scope, entry), + requiresApproval: !entry.path.startsWith(".opcore/"), + outsideOpcore: !entry.path.startsWith(".opcore/") + })), + warnings: [], + nextActions: input.options.approved && !input.options.dryRun + ? [undoAppliedNextAction(input.options.command)] + : [undoPreviewNextAction(input.options)], + undoAvailable: true, + ...initContextPayload(input.context) + }; +} + +function undoSummary( + scope: ParsedInitArgs["scope"], + entry: ReturnType["entries"][number] +): string { + const path = actionPath(scope, entry.path); + if (entry.kind === "append_managed_line") { + return `Remove managed ${entry.line} gitignore entry from ${entry.path}.`; + } + return entry.existed + ? `Restore ${path} from Opcore init backup.` + : `Remove ${path} created by Opcore init.`; +} + +export function undoAppliedNextAction(command: OpcoreSetupCommand): string { + return command === "uninstall" + ? "Opcore setup metadata was restored or removed; rerun opcore install to recreate setup." + : "Opcore init metadata was restored or removed; rerun opcore init to recreate setup."; +} + +function undoPreviewNextAction(options: ParsedInitArgs): string { + if (options.command === "uninstall") { + return `Run ${options.scope === "global" + ? "opcore uninstall --global --yes" + : "opcore uninstall --yes"} to restore or remove recorded setup files.`; + } + return `Run ${options.scope === "global" + ? "opcore init --global --undo --approve" + : "opcore init --undo --approve"} to restore or remove recorded setup files.`; +} diff --git a/packages/opcore/src/init-wizard-flow.ts b/packages/opcore/src/init-wizard-flow.ts new file mode 100644 index 0000000..798514e --- /dev/null +++ b/packages/opcore/src/init-wizard-flow.ts @@ -0,0 +1,122 @@ +import type { + CommandRouterResult, + OpcoreInitPlanPayload +} from "@the-open-engine/opcore-contracts"; +import type { InstallWizardChoices } from "./install-wizard.js"; +import { applyInit } from "./init-apply.js"; +import { appliedInitPayload } from "./init-payloads.js"; +import { planInit, type InitPlanInput } from "./init-plan.js"; +import { scopeRoot } from "./init-prompts.js"; +import { createInitRouterResult } from "./init-result.js"; +import { elapsedMs, nowMs, withContext } from "./init-timing.js"; +import type { + PlannedInit, + WizardSession +} from "./init-types.js"; +import { + createInstallWizardGroups, + installWizardPlanView +} from "./init-wizard-plan.js"; +import { repoDisplayLabel } from "./init-wizard-render.js"; + +export async function runInstallWizardFlow(session: WizardSession): Promise { + await session.wizard.coverage(session.context.scan); + if (!await chooseScope(session)) return declinedInstallWizardResult(session); + const planFor = createInstallPlanCache(session); + const probe = planFor({ agentSkill: true, writeGateHooks: true, activePreCommitHook: true }); + session.context.interaction = { tty: true, promptState: "requested" }; + const promptStartedAt = nowMs(); + const outcome = await session.wizard.planApproval({ + groups: createInstallWizardGroups( + session.options.scope, session.git, session.repoRoot, probe.payload.actions + ), + initial: { + agentSkill: session.options.agentSkill, + writeGateHooks: session.options.writeGateHooks, + activePreCommitHook: session.options.activePreCommitHook + }, + planView: (choices) => installWizardPlanView(planFor(choices).payload.actions) + }); + session.timing.promptMs += elapsedMs(promptStartedAt); + session.options = { ...session.options, ...outcome.choices }; + if (!outcome.confirmed) return declinedInstallWizardResult(session); + return applyWizardPlan(session); +} + +async function chooseScope(session: WizardSession): Promise { + if (!session.git || session.options.scopeExplicit || session.options.repoExplicit) return true; + const startedAt = nowMs(); + const scope = await session.wizard.selectScope(repoDisplayLabel(session.repoRoot, session.homeRoot)); + session.timing.promptMs += elapsedMs(startedAt); + if (scope === null) return false; + session.options = { ...session.options, scope, scopeExplicit: true }; + return true; +} + +async function applyWizardPlan(session: WizardSession): Promise { + session.context.interaction = { tty: true, promptState: "approved" }; + const planStartedAt = nowMs(); + const planned = planInit(toPlanInput(session)); + session.timing.planMs += elapsedMs(planStartedAt); + const root = scopeRoot(session.repoRoot, session.homeRoot, session.options.scope); + const applyStartedAt = nowMs(); + applyInit(root, session.options.scope, planned.writes); + session.timing.applyMs = elapsedMs(applyStartedAt); + await session.wizard.applyCascade( + planned.payload.actions.map((action) => action.path), + session.timing.applyMs + ); + const undo = session.options.scope === "global" + ? "opcore uninstall --global --yes" + : "opcore uninstall"; + session.wizard.doneCard(planned.payload.actions.length, session.options.scope, undo); + const payload = withContext( + appliedInitPayload(planned.payload, root, session.options.scope, session.command), + session.context, + session.timing + ); + return createInitRouterResult({ + argv: session.argv, json: false, status: "ok", + message: `opcore ${session.command} applied`, + canonicalCommand: ["opcore", session.command], opcoreInit: payload + }); +} + +function declinedInstallWizardResult(session: WizardSession): CommandRouterResult { + session.wizard.cancelled(); + session.context.interaction = { tty: true, promptState: "declined" }; + const startedAt = nowMs(); + const planned = planInit(toPlanInput(session)); + session.timing.planMs += elapsedMs(startedAt); + const payload: OpcoreInitPlanPayload = { + ...withContext(planned.payload, session.context, session.timing), + nextActions: [`No files written. Rerun opcore ${session.command} when ready.`] + }; + return createInitRouterResult({ + argv: session.argv, json: false, status: "ok", + message: `opcore ${session.command} declined`, + canonicalCommand: ["opcore", session.command], opcoreInit: payload + }); +} + +function createInstallPlanCache(session: WizardSession): (choices: InstallWizardChoices) => PlannedInit { + const cache = new Map(); + return (choices) => { + const key = `${choices.agentSkill}|${choices.writeGateHooks}|${choices.activePreCommitHook}`; + const cached = cache.get(key); + if (cached) return cached; + const planned = planInit(toPlanInput(session, { ...session.options, ...choices })); + cache.set(key, planned); + return planned; + }; +} + +function toPlanInput( + session: WizardSession, + options = session.options +): InitPlanInput { + return { + repoRoot: session.repoRoot, requestedPath: session.requestedPath, + git: session.git, homeRoot: session.homeRoot, options, context: session.context + }; +} diff --git a/packages/opcore/src/init-wizard-plan.ts b/packages/opcore/src/init-wizard-plan.ts new file mode 100644 index 0000000..a424884 --- /dev/null +++ b/packages/opcore/src/init-wizard-plan.ts @@ -0,0 +1,81 @@ +import type { OpcoreInitAction } from "@the-open-engine/opcore-contracts"; +import type { + InstallWizardFileRow, + InstallWizardGroup, + InstallWizardGroupKey, + InstallWizardPlanView +} from "./install-wizard.js"; +import { + ACTIVE_PRE_COMMIT_HOOK_PATH, + AGENT_GATE_HOOK_PATH, + CLAUDE_SETTINGS_PATH, + CODEX_HOOKS_PATH, + GITIGNORE_PATH +} from "./init-constants.js"; +import { isLinkedGitWorktree } from "./init-paths.js"; +import type { InitScope } from "./init-types.js"; + +export function createInstallWizardGroups( + scope: InitScope, + git: boolean, + repoRoot: string, + actions: readonly OpcoreInitAction[] +): InstallWizardGroup[] { + const groups: InstallWizardGroup[] = [ + { key: "skill", label: "agent skill", available: true }, + { key: "hooks", label: "write-gate hooks", available: true } + ]; + if (scope === "global") return groups; + if (actions.some((action) => action.path === ACTIVE_PRE_COMMIT_HOOK_PATH)) { + groups.push({ key: "precommit", label: "pre-commit hook", available: true }); + return groups; + } + groups.push({ + key: "precommit", + label: "pre-commit hook", + available: false, + unavailableNote: unavailablePreCommitNote(git, repoRoot) + }); + return groups; +} + +function unavailablePreCommitNote(git: boolean, repoRoot: string): string { + if (!git) return "no git repo"; + return isLinkedGitWorktree(repoRoot) ? "linked worktree — skipped" : "existing hook kept"; +} + +export function installWizardPlanView(actions: readonly OpcoreInitAction[]): InstallWizardPlanView { + const baseRows: InstallWizardFileRow[] = []; + const groupRows: Record = { + skill: [], hooks: [], precommit: [] + }; + for (const action of actions) { + const row: InstallWizardFileRow = { + path: action.path, + mark: actionMark(action), + outsideOpcore: action.outsideOpcore + }; + const group = installWizardGroupForPath(action.path); + if (group) groupRows[group].push(row); + else baseRows.push(row); + } + return { + baseRows, + groupRows, + totalWrites: actions.length, + outsideWrites: actions.filter((action) => action.outsideOpcore).length + }; +} + +function actionMark(action: OpcoreInitAction): InstallWizardFileRow["mark"] { + if (action.kind === "upsert_block" || action.kind === "wire_harness") return "~"; + return action.path === GITIGNORE_PATH ? "»" : "+"; +} + +function installWizardGroupForPath(path: string): InstallWizardGroupKey | undefined { + if (path.endsWith("skills/opcore/SKILL.md")) return "skill"; + if (path.endsWith(AGENT_GATE_HOOK_PATH) || + path.endsWith(CLAUDE_SETTINGS_PATH) || + path.endsWith(CODEX_HOOKS_PATH)) return "hooks"; + return path.endsWith(ACTIVE_PRE_COMMIT_HOOK_PATH) ? "precommit" : undefined; +} diff --git a/packages/opcore/src/init-wizard-render.ts b/packages/opcore/src/init-wizard-render.ts new file mode 100644 index 0000000..f2c8174 --- /dev/null +++ b/packages/opcore/src/init-wizard-render.ts @@ -0,0 +1,74 @@ +import { sep } from "node:path"; +import { + createInstallWizardRenderer, + type InstallWizardRenderer +} from "./install-wizard.js"; +import { isInteractiveRuntime } from "./init-prompts.js"; +import { elapsedMs, nowMs, writeProgress, type InitScanProgress } from "./init-timing.js"; +import type { + OpcoreInitRuntime, + OpcoreSetupCommand, + ParsedInitArgs +} from "./init-types.js"; + +export function createInstallWizard( + json: boolean, + options: ParsedInitArgs, + runtime: OpcoreInitRuntime, + command: OpcoreSetupCommand +): InstallWizardRenderer | undefined { + if (command !== "install" || json || options.approved || options.dryRun || options.undo) return undefined; + if (!isInteractiveRuntime(runtime) || runtime.stderrIsTTY !== true) return undefined; + if (typeof runtime.readKey !== "function" || typeof runtime.writeStderr !== "function") return undefined; + const writeStderr = runtime.writeStderr; + return createInstallWizardRenderer( + { + write: (text) => writeProgress(writeStderr, text), + readKey: runtime.readKey, + color: runtime.stderrColor === true, + motion: runtime.initWizardMotion !== false + }, + runtime.stderrTrueColor === true + ); +} + +export function startWizardScanProgress( + wizard: InstallWizardRenderer, + runtime: OpcoreInitRuntime, + repoLabel: string +): InitScanProgress { + wizard.hideCursor(); + wizard.header(repoLabel); + const startedAt = nowMs(); + let finished = false; + let frame = 0; + wizard.scanFrame(frame, 0); + const intervalMs = runtime.initWizardMotion === false ? 0 : 120; + const timer = intervalMs > 0 + ? (setInterval(() => { + if (finished) return; + frame += 1; + wizard.scanFrame(frame, elapsedMs(startedAt)); + }, intervalMs) as ReturnType & { unref?: () => void }) + : undefined; + timer?.unref?.(); + return { + complete: (scanMs, totalFiles) => { + if (finished) return; + finished = true; + if (timer) clearInterval(timer); + wizard.scanDone(scanMs, totalFiles); + }, + fail: (scanMs) => { + if (finished) return; + finished = true; + if (timer) clearInterval(timer); + wizard.scanFailed(scanMs); + } + }; +} + +export function repoDisplayLabel(root: string, homeRoot: string): string { + if (root === homeRoot) return "~"; + return root.startsWith(`${homeRoot}${sep}`) ? `~${root.slice(homeRoot.length)}` : root; +} diff --git a/packages/opcore/src/init-write.ts b/packages/opcore/src/init-write.ts new file mode 100644 index 0000000..1461bb5 --- /dev/null +++ b/packages/opcore/src/init-write.ts @@ -0,0 +1,83 @@ +import { + appendFileSync, + chmodSync, + mkdirSync, + readFileSync, + rmSync, + writeFileSync +} from "node:fs"; +import { dirname } from "node:path"; +import { appendManagedGitignoreLine, removeManagedGitignoreLine } from "./init-gitignore.js"; +import { readOptionalRepoFile } from "./init-files.js"; +import { + assertExistingRepoPath, + assertMutationPath, + repoPathExists +} from "./init-paths.js"; +import type { + PlannedWrite, + UndoEntry +} from "./init-types.js"; + +export function priorEntry(root: string, path: string, write: PlannedWrite | undefined): UndoEntry { + if (write?.kind === "append_managed_line") { + const existing = readOptionalRepoFile(root, path); + return { + kind: "append_managed_line", path, existed: existing !== undefined, + line: write.line, appended: appendManagedGitignoreLine(existing) + }; + } + if (!repoPathExists(root, path)) return { kind: "restore_file", path, existed: false }; + return { + kind: "restore_file", + path, + existed: true, + content: readFileSync(assertExistingRepoPath( + root, path, "Existing Opcore init target", "file" + ), "utf8") + }; +} + +export function writeScopedFile(root: string, write: PlannedWrite): void { + const absolute = assertMutationPath(root, write.path, "Opcore init write target"); + mkdirSync(dirname(absolute), { recursive: true }); + if (write.kind === "append_managed_line") { + appendFileSync(absolute, appendManagedGitignoreLine(readOptionalRepoFile(root, write.path))); + return; + } + writeFileSync(absolute, write.content, "utf8"); + if (write.executable) { + assertMutationPath(root, write.path, "Opcore init chmod target"); + chmodSync(absolute, 0o755); + } +} + +export function restoreUndoEntry(root: string, entry: UndoEntry): void { + const absolute = assertMutationPath(root, entry.path, "Opcore init undo target"); + if (entry.kind === "append_managed_line") { + restoreManagedLine(root, absolute, entry); + return; + } + if (!entry.existed) { + rmSync(absolute, { force: true }); + return; + } + if (entry.content === undefined) throw new Error(`Undo entry for ${entry.path} is missing content`); + mkdirSync(dirname(absolute), { recursive: true }); + writeFileSync(absolute, entry.content, "utf8"); +} + +function restoreManagedLine( + root: string, + absolute: string, + entry: Extract +): void { + if (!repoPathExists(root, entry.path)) return; + const removal = removeManagedGitignoreLine(readFileSync(absolute, "utf8"), entry); + if (!removal.removed) return; + if (!entry.existed && removal.content.length === 0) { + rmSync(absolute, { force: true }); + return; + } + writeFileSync(absolute, removal.content, "utf8"); +} diff --git a/packages/opcore/src/init.ts b/packages/opcore/src/init.ts index 29baae4..6592811 100644 --- a/packages/opcore/src/init.ts +++ b/packages/opcore/src/init.ts @@ -1,2300 +1,7 @@ -import type { - CommandRouterResult, - OpcoreInitAction, - OpcoreInitInteraction, - OpcoreInitLanguageSetting, - OpcoreInitPythonEnvironment, - OpcoreInitPlanPayload, - OpcoreInitScanSummary, - OpcoreInitSettings, - OpcoreInitTiming, - OpcoreRepoStatePayload, - ParsedCommandArgv, - ValidationResult -} from "@the-open-engine/opcore-contracts"; -import { createCommandRouterResult } from "@the-open-engine/opcore-contracts"; -import { - appendFileSync, - chmodSync, - lstatSync, - mkdirSync, - readFileSync, - realpathSync, - readdirSync, - rmSync, - statSync, - writeFileSync -} from "node:fs"; -import { homedir } from "node:os"; -import { dirname, isAbsolute, relative, resolve, sep } from "node:path"; -import { opcoreAgentGateHookScriptContent } from "./agent-gate.js"; -import { - createInstallWizardRenderer, - type InstallWizardChoices, - type InstallWizardFileRow, - type InstallWizardGroup, - type InstallWizardGroupKey, - type InstallWizardPlanView, - type InstallWizardRenderer -} from "./install-wizard.js"; -import { createOpcoreScanAnalysis, type OpcoreScanAnalysis } from "./scan.js"; -import { failedValidationCheckIds } from "./scan-presentation.js"; -import { scanValidationDiagnosticTotal } from "./scan-validation-preview.js"; -import { commonSkippedPathSegments, resolveRepo, type RepoResolution } from "./status.js"; - -declare const process: { - cwd(): string; -}; - -const helpArgs = new Set(["--help", "-h", "help"]); -export const AGENT_FILE_CANDIDATES = [ - "AGENTS.md", - "CLAUDE.md", - "GEMINI.md", - ".github/copilot-instructions.md", - ".codex/AGENTS.md", - ".opencode/AGENTS.md" -] as const; - -const beginMarker = ""; -const endMarker = ""; -const configPath = ".opcore/config"; -const undoPath = ".opcore/init-undo.json"; -const hookPath = ".opcore/hooks/pre-commit-opcore-check.sh"; -const agentGateHookPath = ".opcore/hooks/opcore-agent-gate.mjs"; -const repoAgentSkillPath = ".agents/skills/opcore/SKILL.md"; -const claudeAgentSkillPath = ".claude/skills/opcore/SKILL.md"; -const agentSkillPaths = [repoAgentSkillPath, claudeAgentSkillPath] as const; -const claudeSettingsPath = ".claude/settings.json"; -const codexHooksPath = ".codex/hooks.json"; -const activePreCommitHookPath = ".git/hooks/pre-commit"; -const failClosedHookActivationCommand = "cp .opcore/hooks/pre-commit-opcore-check.sh .git/hooks/pre-commit"; -const gitignorePath = ".gitignore"; -const opcoreIgnoreLine = ".opcore/"; -const defaultInitProgressIntervalMs = 5000; -type OpcoreSetupCommand = "init" | "install" | "uninstall"; -const allowedUndoPaths = new Set([ - configPath, - undoPath, - hookPath, - agentGateHookPath, - repoAgentSkillPath, - claudeAgentSkillPath, - claudeSettingsPath, - codexHooksPath, - activePreCommitHookPath, - gitignorePath, - ...AGENT_FILE_CANDIDATES -]); -const globalUndoPath = ".opcore/init-undo.json"; -const allowedGlobalUndoPaths = new Set([ - globalUndoPath, - agentGateHookPath, - repoAgentSkillPath, - claudeAgentSkillPath, - claudeSettingsPath, - codexHooksPath -]); -const rustActiveValidationKinds = new Set([".rs", ".inc", "Cargo.toml"]); - -interface ParsedInitArgs { - command: OpcoreSetupCommand; - repo: string; - repoExplicit: boolean; - scope: "repo" | "global"; - scopeExplicit: boolean; - approved: boolean; - dryRun: boolean; - failClosedHook: boolean; - agentSkill: boolean; - writeGateHooks: boolean; - activePreCommitHook: boolean; - undo: boolean; -} - -export interface OpcoreInitRuntime { - stdinIsTTY?: boolean; - stdoutIsTTY?: boolean; - stderrIsTTY?: boolean; - stderrColor?: boolean; - stderrTrueColor?: boolean; - homeDir?: string; - writeStderr?: (text: string) => void; - scanAnalysis?: (resolution: RepoResolution) => Promise; - initProgressIntervalMs?: number; - readLine?: (prompt: string) => Promise; - readKey?: () => Promise; - initWizardMotion?: boolean; -} - -interface InitContext { - scan: OpcoreInitScanSummary; - settings: OpcoreInitSettings; - interaction: OpcoreInitInteraction; - timings: OpcoreInitTiming; -} - -interface TimingState { - startedAt: number; - scanMs: number; - planMs: number; - promptMs: number; - applyMs: number; -} - -interface PlannedInit { - payload: OpcoreInitPlanPayload; - writes: readonly PlannedWrite[]; -} - -type PlannedWrite = PlannedFileWrite | PlannedManagedLineAppend; - -interface PlannedFileWrite { - kind: "write"; - path: string; - targetScope: "repo" | "global"; - content: string; - executable?: boolean; -} - -interface PlannedManagedLineAppend { - kind: "append_managed_line"; - path: string; - targetScope: "repo"; - line: string; -} - -interface UndoMetadata { - schemaVersion: 1; - kind: "opcore_init_undo" | "opcore_global_init_undo"; - repoRoot?: string; - homeRoot?: string; - entries: readonly UndoEntry[]; -} - -type UndoEntry = FileUndoEntry | ManagedLineUndoEntry; - -interface FileUndoEntry { - kind?: "restore_file"; - path: string; - existed: boolean; - content?: string; -} - -interface ManagedLineUndoEntry { - kind: "append_managed_line"; - path: string; - existed: boolean; - line: string; - appended?: string; -} - -export async function routeOpcoreInit( - argv: readonly string[], - parsed: ParsedCommandArgv, - runtime: OpcoreInitRuntime = {} -): Promise { - return routeOpcoreSetup(argv, parsed, runtime, "init"); -} - -export async function routeOpcoreInstall( - argv: readonly string[], - parsed: ParsedCommandArgv, - runtime: OpcoreInitRuntime = {} -): Promise { - return routeOpcoreSetup(argv, parsed, runtime, "install"); -} - -export async function routeOpcoreUninstall( - argv: readonly string[], - parsed: ParsedCommandArgv, - runtime: OpcoreInitRuntime = {} -): Promise { - return routeOpcoreSetup(argv, parsed, runtime, "uninstall"); -} - -async function routeOpcoreSetup( - argv: readonly string[], - parsed: ParsedCommandArgv, - runtime: OpcoreInitRuntime, - command: OpcoreSetupCommand -): Promise { - const rest = parsed.args.slice(1); - if (rest.some((arg) => helpArgs.has(arg))) { - return createInitRouterResult(argv, parsed.json, "ok", opcoreSetupHelpMessage(command), ["opcore", command, "help"]); - } - - const parsedInit = parseOpcoreInitArgs(rest, command); - if (!parsedInit.ok) { - return createInitRouterResult(argv, parsed.json, "error", parsedInit.message); - } - const resolution = resolveRepo(parsedInit.args.repo, `opcore ${command}`); - if (!resolution.ok) { - return createInitRouterResult(argv, parsed.json, "error", resolution.message); - } - - const wizard = createInstallWizard(parsed.json, parsedInit.args, runtime, command); - try { - const timing: TimingState = { - startedAt: nowMs(), - scanMs: 0, - planMs: 0, - promptMs: 0, - applyMs: 0 - }; - const scanStartedAt = nowMs(); - const progress = wizard - ? startWizardScanProgress(wizard, runtime, repoDisplayLabel(resolution.resolution.root, initHomeRoot(runtime))) - : startInitScanProgress(parsed.json, runtime, command); - const scanAnalysis = runtime.scanAnalysis ?? createOpcoreScanAnalysis; - let analysis: OpcoreScanAnalysis; - try { - analysis = await scanAnalysis(resolution.resolution); - timing.scanMs = elapsedMs(scanStartedAt); - progress?.complete(timing.scanMs, analysis.repoState.coverage.totalFiles); - } catch (error) { - timing.scanMs = elapsedMs(scanStartedAt); - progress?.fail(timing.scanMs); - throw error; - } - const context: InitContext = { - scan: createInitScanSummary(analysis.repoState, analysis.validationResult), - settings: createInitSettings(analysis.repoState), - interaction: { - tty: isInteractiveRuntime(runtime), - promptState: "not_requested" - }, - timings: finalizeTimings(timing) - }; - - if (parsedInit.args.undo) { - return routeOpcoreInitUndo( - argv, - parsed.json, - resolution.resolution.root, - resolution.resolution.requestedPath, - initHomeRoot(runtime), - parsedInit.args, - context, - timing, - command - ); - } - - if (wizard) { - return await runInstallWizardFlow( - argv, - resolution.resolution.root, - resolution.resolution.requestedPath, - resolution.resolution.git, - initHomeRoot(runtime), - parsedInit.args, - context, - timing, - wizard, - command - ); - } - - return await routeOpcoreInitPlanOrApply( - argv, - parsed.json, - resolution.resolution.root, - resolution.resolution.requestedPath, - resolution.resolution.git, - initHomeRoot(runtime), - parsedInit.args, - context, - timing, - runtime, - command - ); - } catch (error) { - return createInitRouterResult(argv, parsed.json, "error", `opcore ${command} failed: ${errorMessage(error)}`); - } finally { - wizard?.showCursor(); - } -} - -function createInstallWizard( - json: boolean, - options: ParsedInitArgs, - runtime: OpcoreInitRuntime, - command: OpcoreSetupCommand -): InstallWizardRenderer | undefined { - if (command !== "install" || json || options.approved || options.dryRun || options.undo) return undefined; - if (!isInteractiveRuntime(runtime) || runtime.stderrIsTTY !== true) return undefined; - if (typeof runtime.readKey !== "function" || typeof runtime.writeStderr !== "function") return undefined; - const writeStderr = runtime.writeStderr; - const readKey = runtime.readKey; - return createInstallWizardRenderer( - { - write: (text) => writeProgress(writeStderr, text), - readKey, - color: runtime.stderrColor === true, - motion: runtime.initWizardMotion !== false - }, - runtime.stderrTrueColor === true - ); -} - -function startWizardScanProgress( - wizard: InstallWizardRenderer, - runtime: OpcoreInitRuntime, - repoLabel: string -): { complete(scanMs: number, totalFiles?: number): void; fail(scanMs: number): void } { - wizard.hideCursor(); - wizard.header(repoLabel); - const startedAt = nowMs(); - let finished = false; - let frame = 0; - wizard.scanFrame(frame, 0); - const intervalMs = runtime.initWizardMotion === false ? 0 : 120; - const timer = intervalMs > 0 - ? (setInterval(() => { - if (finished) return; - frame += 1; - wizard.scanFrame(frame, elapsedMs(startedAt)); - }, intervalMs) as ReturnType & { unref?: () => void }) - : undefined; - timer?.unref?.(); - return { - complete: (scanMs: number, totalFiles?: number) => { - if (finished) return; - finished = true; - if (timer) clearInterval(timer); - wizard.scanDone(scanMs, totalFiles); - }, - fail: (scanMs: number) => { - if (finished) return; - finished = true; - if (timer) clearInterval(timer); - wizard.scanFailed(scanMs); - } - }; -} - -function repoDisplayLabel(root: string, homeRoot: string): string { - if (root === homeRoot) return "~"; - return root.startsWith(`${homeRoot}${sep}`) ? `~${root.slice(homeRoot.length)}` : root; -} - -function routeOpcoreInitUndo( - argv: readonly string[], - json: boolean, - repoRoot: string, - requestedPath: string, - homeRoot: string, - options: ParsedInitArgs, - context: InitContext, - timing: TimingState, - command: OpcoreSetupCommand -): CommandRouterResult { - const planStartedAt = nowMs(); - const undo = planUndo(repoRoot, requestedPath, homeRoot, options, context); - timing.planMs = elapsedMs(planStartedAt); - const approved = options.approved && !options.dryRun; - if (approved) { - const applyStartedAt = nowMs(); - applyUndo(scopeRoot(repoRoot, homeRoot, options.scope), options.scope, undo); - timing.applyMs = elapsedMs(applyStartedAt); - } - const payload = withContext( - approved ? appliedUndoPayload(undo, scopeRoot(repoRoot, homeRoot, options.scope), options.scope, command) : undo, - context, - timing - ); - return createInitRouterResult(argv, json, "ok", formatSetupPlan(payload, approved, command), ["opcore", command], payload); -} - -async function routeOpcoreInitPlanOrApply( - argv: readonly string[], - json: boolean, - repoRoot: string, - requestedPath: string, - git: boolean, - homeRoot: string, - options: ParsedInitArgs, - context: InitContext, - timing: TimingState, - runtime: OpcoreInitRuntime, - command: OpcoreSetupCommand -): Promise { - if (shouldPromptForScope(json, options, git, runtime)) { - const promptStartedAt = nowMs(); - const scopeAnswer = await runtime.readLine("Install the Opcore write gate for THIS repo, or GLOBALLY for all repos? [repo/global] "); - timing.promptMs += elapsedMs(promptStartedAt); - options = { - ...options, - scope: parseScopeAnswer(scopeAnswer), - scopeExplicit: true - }; - } - const planStartedAt = nowMs(); - const planned = planInit(repoRoot, requestedPath, git, homeRoot, options, context); - timing.planMs = elapsedMs(planStartedAt); - let payload = withContext(planned.payload, context, timing); - let approved = options.approved && !options.dryRun; - let prompted = false; - - if (shouldPromptForApproval(json, options, runtime)) { - prompted = true; - context.interaction = { tty: true, promptState: "requested" }; - payload = withContext(payload, context, timing); - const promptStartedAt = nowMs(); - const answer = await runtime.readLine(`${formatSetupPlan(payload, false, command)}\nApply setup? ${approvalPromptSuffix(command)} `); - timing.promptMs += elapsedMs(promptStartedAt); - if (isApprovedAnswer(answer, command)) { - approved = true; - context.interaction = { tty: true, promptState: "approved" }; - } else { - context.interaction = { tty: true, promptState: "declined" }; - payload = { - ...payload, - nextActions: [`No files written. Rerun opcore ${command === "uninstall" ? "uninstall" : command} when ready.`] - }; - } - } - - if (approved) { - const applyStartedAt = nowMs(); - applyInit(scopeRoot(repoRoot, homeRoot, options.scope), options.scope, planned.writes); - timing.applyMs = elapsedMs(applyStartedAt); - } - payload = approved ? appliedInitPayload(payload, scopeRoot(repoRoot, homeRoot, options.scope), options.scope, command) : payload; - payload = withContext(payload, context, timing); - const message = prompted ? formatInteractiveOutcome(payload, command) : formatSetupPlan(payload, approved, command); - return createInitRouterResult(argv, json, "ok", message, ["opcore", command], payload); -} - -async function runInstallWizardFlow( - argv: readonly string[], - repoRoot: string, - requestedPath: string, - git: boolean, - homeRoot: string, - options: ParsedInitArgs, - context: InitContext, - timing: TimingState, - wizard: InstallWizardRenderer, - command: OpcoreSetupCommand -): Promise { - await wizard.coverage(context.scan); - - if (git && !options.scopeExplicit && !options.repoExplicit) { - const promptStartedAt = nowMs(); - const scope = await wizard.selectScope(repoDisplayLabel(repoRoot, homeRoot)); - timing.promptMs += elapsedMs(promptStartedAt); - if (scope === null) { - return declinedInstallWizardResult(argv, repoRoot, requestedPath, git, homeRoot, options, context, timing, wizard, command); - } - options = { ...options, scope, scopeExplicit: true }; - } - - const planFor = createInstallPlanCache(repoRoot, requestedPath, git, homeRoot, options, context); - const probe = planFor({ agentSkill: true, writeGateHooks: true, activePreCommitHook: true }); - const model = { - groups: createInstallWizardGroups(options.scope, git, repoRoot, probe.payload.actions), - initial: { - agentSkill: options.agentSkill, - writeGateHooks: options.writeGateHooks, - activePreCommitHook: options.activePreCommitHook - }, - planView: (choices: InstallWizardChoices) => installWizardPlanView(planFor(choices).payload.actions) - }; - context.interaction = { tty: true, promptState: "requested" }; - const promptStartedAt = nowMs(); - const outcome = await wizard.planApproval(model); - timing.promptMs += elapsedMs(promptStartedAt); - options = { ...options, ...outcome.choices }; - if (!outcome.confirmed) { - return declinedInstallWizardResult(argv, repoRoot, requestedPath, git, homeRoot, options, context, timing, wizard, command); - } - - context.interaction = { tty: true, promptState: "approved" }; - const planStartedAt = nowMs(); - const planned = planInit(repoRoot, requestedPath, git, homeRoot, options, context); - timing.planMs += elapsedMs(planStartedAt); - const root = scopeRoot(repoRoot, homeRoot, options.scope); - const applyStartedAt = nowMs(); - applyInit(root, options.scope, planned.writes); - timing.applyMs = elapsedMs(applyStartedAt); - await wizard.applyCascade(planned.payload.actions.map((action) => action.path), timing.applyMs); - const undoCommand = options.scope === "global" ? "opcore uninstall --global --yes" : "opcore uninstall"; - wizard.doneCard(planned.payload.actions.length, options.scope, undoCommand); - const payload = withContext(appliedInitPayload(planned.payload, root, options.scope, command), context, timing); - return createInitRouterResult(argv, false, "ok", `opcore ${command} applied`, ["opcore", command], payload); -} - -function declinedInstallWizardResult( - argv: readonly string[], - repoRoot: string, - requestedPath: string, - git: boolean, - homeRoot: string, - options: ParsedInitArgs, - context: InitContext, - timing: TimingState, - wizard: InstallWizardRenderer, - command: OpcoreSetupCommand -): CommandRouterResult { - wizard.cancelled(); - context.interaction = { tty: true, promptState: "declined" }; - const planStartedAt = nowMs(); - const planned = planInit(repoRoot, requestedPath, git, homeRoot, options, context); - timing.planMs += elapsedMs(planStartedAt); - const payload: OpcoreInitPlanPayload = { - ...withContext(planned.payload, context, timing), - nextActions: [`No files written. Rerun opcore ${command} when ready.`] - }; - return createInitRouterResult(argv, false, "ok", `opcore ${command} declined`, ["opcore", command], payload); -} - -function createInstallPlanCache( - repoRoot: string, - requestedPath: string, - git: boolean, - homeRoot: string, - options: ParsedInitArgs, - context: InitContext -): (choices: InstallWizardChoices) => PlannedInit { - const cache = new Map(); - return (choices) => { - const key = `${choices.agentSkill}|${choices.writeGateHooks}|${choices.activePreCommitHook}`; - const cached = cache.get(key); - if (cached) return cached; - const planned = planInit(repoRoot, requestedPath, git, homeRoot, { ...options, ...choices }, context); - cache.set(key, planned); - return planned; - }; -} - -function createInstallWizardGroups( - scope: ParsedInitArgs["scope"], - git: boolean, - repoRoot: string, - probeActions: readonly OpcoreInitAction[] -): InstallWizardGroup[] { - const groups: InstallWizardGroup[] = [ - { key: "skill", label: "agent skill", available: true }, - { key: "hooks", label: "write-gate hooks", available: true } - ]; - if (scope === "global") return groups; - const preCommitPlanned = probeActions.some((action) => action.path === activePreCommitHookPath); - if (preCommitPlanned) { - groups.push({ key: "precommit", label: "pre-commit hook", available: true }); - } else { - groups.push({ - key: "precommit", - label: "pre-commit hook", - available: false, - unavailableNote: !git - ? "no git repo" - : isLinkedGitWorktree(repoRoot) - ? "linked worktree — skipped" - : "existing hook kept" - }); - } - return groups; -} - -function installWizardPlanView(actions: readonly OpcoreInitAction[]): InstallWizardPlanView { - const baseRows: InstallWizardFileRow[] = []; - const groupRows: Record = { skill: [], hooks: [], precommit: [] }; - for (const action of actions) { - const row: InstallWizardFileRow = { - path: action.path, - mark: action.kind === "upsert_block" || action.kind === "wire_harness" - ? "~" - : action.path === gitignorePath - ? "»" - : "+", - outsideOpcore: action.outsideOpcore - }; - const group = installWizardGroupForPath(action.path); - if (group) groupRows[group].push(row); - else baseRows.push(row); - } - return { - baseRows, - groupRows, - totalWrites: actions.length, - outsideWrites: actions.filter((action) => action.outsideOpcore).length - }; -} - -function installWizardGroupForPath(path: string): InstallWizardGroupKey | undefined { - if (path.endsWith("skills/opcore/SKILL.md")) return "skill"; - if (path.endsWith(agentGateHookPath) || path.endsWith(claudeSettingsPath) || path.endsWith(codexHooksPath)) return "hooks"; - if (path.endsWith(activePreCommitHookPath)) return "precommit"; - return undefined; -} - -function createInitRouterResult( - argv: readonly string[], - json: boolean, - status: "ok" | "error", - message: string, - canonicalCommand: readonly string[] = ["opcore", "init"], - opcoreInit?: OpcoreInitPlanPayload -): CommandRouterResult { - return createCommandRouterResult({ - bin: "opcore", - argv, - canonicalCommand, - owner: "runtime", - status, - json, - message, - opcoreInit - }); -} - -function createInitScanSummary(repoState: OpcoreRepoStatePayload, validationResult: ValidationResult): OpcoreInitScanSummary { - const failedChecks = failedValidationCheckIds(validationResult); - return { - totalFiles: repoState.coverage.totalFiles, - graphSupportedFiles: repoState.coverage.graph.supportedFiles, - validationSupportedFiles: repoState.coverage.validation.supportedFiles, - validationRetainedFiles: repoState.coverage.validation.retainedFiles, - unsupportedFiles: repoState.coverage.unsupported.totalFiles, - languages: repoState.coverage.languages, - unsupportedStacks: repoState.coverage.unsupported.stacks, - degradedRustTools: repoState.validation.degradedToolchains, - diagnosticCount: scanValidationDiagnosticTotal(validationResult), - validationStatus: validationResult.status, - failedChecks, - graphState: repoState.graph.state, - activationLevel: repoState.activation.level - }; -} - -function createInitSettings(repoState: OpcoreRepoStatePayload): OpcoreInitSettings { - const unsupportedLanguages = new Set(repoState.coverage.unsupported.stacks.map((stack) => stack.language)); - const degradedRustTools = repoState.validation.degradedToolchains.filter((tool) => tool.adapter === "rust").map((tool) => tool.tool); - const degradedPythonTools = repoState.validation.degradedToolchains.filter((tool) => tool.adapter === "python").map((tool) => tool.tool); - const rustHasActiveValidationInput = repoState.coverage.validation.extensions.some((entry) => - rustActiveValidationKinds.has(entry.extension) - ); - const pythonProject = pythonEnvironmentFromContexts(repoState.validation.pythonProjectContexts ?? []); - return { - languages: repoState.coverage.languages.map((language): OpcoreInitLanguageSetting => { - const rustRetainedOnly = - language.language === "Rust" && - !rustHasActiveValidationInput && - repoState.coverage.validation.retainedFiles > 0; - const rustDegraded = language.language === "Rust" && degradedRustTools.length > 0 && language.validationSupported && !rustRetainedOnly; - const pythonDegraded = language.language === "Python" && degradedPythonTools.length > 0 && language.validationSupported; - const unsupported = unsupportedLanguages.has(language.language) && !language.validationSupported; - const validation = unsupported - ? "unsupported" - : rustRetainedOnly - ? "retained" - : rustDegraded || pythonDegraded - ? "degraded" - : language.validationSupported - ? "supported" - : "unsupported"; - const state = validation === "unsupported" - ? "unsupported" - : validation === "retained" - ? "retained" - : validation === "degraded" - ? "degraded" - : "supported"; - return { - language: language.language, - files: language.files, - state, - graph: language.graphSupported ? "supported" : "unsupported", - validation, - checks: checksForLanguage(language.language, validation), - notes: notesForLanguage( - language.language, - validation, - language.language === "Python" ? degradedPythonTools : degradedRustTools, - language.language === "Python" ? pythonProject : undefined - ) - }; - }), - ...(hasPythonEnvironmentSignals(pythonProject) ? { python: pythonProject } : {}) - }; -} - -function checksForLanguage(language: string, validation: OpcoreInitLanguageSetting["validation"]): string[] { - if (validation === "unsupported" || validation === "retained") return []; - if (language === "TypeScript" || language === "JavaScript") { - return [ - "typescript.syntax", - "typescript.types", - "typescript.import-graph", - "typescript.dead-code", - "typescript.function-metrics", - "typescript.relevant-tests", - "typescript.file-length" - ]; - } - if (language === "Rust") { - return [ - "rust.source-hygiene", - "rust.fmt", - "rust.cargo-check", - "rust.clippy", - "rust.rustdoc", - "rust.import-graph", - "rust.dead-code", - "rust.unused-deps", - "rust.file-length", - "rust.function-metrics" - ]; - } - if (language === "Python") { - return [ - "python.syntax", - "python.source-hygiene", - "python.types", - "python.import-graph", - "python.dead-code", - "python.relevant-tests", - "python.pytest" - ]; - } - return []; -} - -function notesForLanguage( - language: string, - validation: OpcoreInitLanguageSetting["validation"], - degradedTools: readonly string[], - pythonProject?: OpcoreInitPythonEnvironment -): string[] { - if (validation === "unsupported") return ["Unsupported stack counted without fabricated checks."]; - if (validation === "retained") return ["Retained for compatibility; no active checks configured."]; - const notes: string[] = []; - if (validation === "degraded") notes.push(`${language} validation tools degraded: ${degradedTools.join(", ")}.`); - if (language === "Python" && pythonProject !== undefined) { - notes.push(...pythonProject.notes); - } - return notes; -} - -function pythonEnvironmentFromContexts( - contexts: NonNullable -): OpcoreInitPythonEnvironment { - const managerKind = { - pip: "requirements", - uv: "uv", - poetry: "poetry", - pdm: "pyproject", - pipenv: "pipfile" - } as const; - const managers = new Map(); - const environments = new Map(); - for (const context of contexts) { - for (const manager of context.managers) { - const path = manager.lockFiles[0] ?? manager.configFiles[0]; - if (path !== undefined) { - const value = { kind: managerKind[manager.kind], path }; - managers.set(`${value.kind}\0${value.path}`, value); - } - } - if (context.interpreter?.source === "project_local_environment") { - const executable = context.interpreter.executable.replaceAll("\\", "/"); - const suffix = executable.match(/\/(?:bin\/python[^/]*|Scripts\/python\.exe|python\.exe)$/u)?.[0]; - if (suffix !== undefined) { - const environmentRoot = executable.slice(0, -suffix.length); - const path = relative(context.repositoryRoot, environmentRoot).replaceAll("\\", "/"); - if (path.length > 0 && !path.startsWith("..")) environments.set(path, { kind: "venv", path }); - } - } - } - const projectRoots = [...new Set(contexts.map((context) => context.projectRoot))].sort(); - const outcomes = [...new Set(contexts.map((context) => context.outcome))].sort(); - const evidenceFiles = [...new Set(contexts.flatMap((context) => context.evidence.map((entry) => entry.path)))].sort(); - return { - dependencyManagers: [...managers.values()].sort((left, right) => left.path.localeCompare(right.path)), - virtualEnvironments: [...environments.values()].sort((left, right) => left.path.localeCompare(right.path)), - notes: contexts.length === 0 - ? [] - : [ - `Canonical Python project contexts: ${projectRoots.join(", ") || "."}.`, - `Canonical Python project evidence: ${evidenceFiles.join(", ")}.`, - `Python context outcomes: ${outcomes.join(", ")}.` - ], - contexts - }; -} - -function hasPythonEnvironmentSignals(environment: OpcoreInitPythonEnvironment): boolean { - return (environment.contexts?.length ?? 0) > 0; -} - -function withContext(payload: OpcoreInitPlanPayload, context: InitContext, timing: TimingState): OpcoreInitPlanPayload { - return { - ...payload, - scan: context.scan, - settings: context.settings, - interaction: context.interaction, - timings: finalizeTimings(timing) - }; -} - -function shouldPromptForApproval(json: boolean, options: ParsedInitArgs, runtime: OpcoreInitRuntime): runtime is OpcoreInitRuntime & { - readLine: (prompt: string) => Promise; -} { - return ( - !json && - !options.approved && - !options.dryRun && - !options.undo && - isInteractiveRuntime(runtime) && - typeof runtime.readLine === "function" - ); -} - -function shouldPromptForScope( - json: boolean, - options: ParsedInitArgs, - git: boolean, - runtime: OpcoreInitRuntime -): runtime is OpcoreInitRuntime & { readLine: (prompt: string) => Promise } { - return ( - git && - !json && - !options.approved && - !options.dryRun && - !options.undo && - !options.scopeExplicit && - !options.repoExplicit && - isInteractiveRuntime(runtime) && - typeof runtime.readLine === "function" - ); -} - -function parseScopeAnswer(answer: string | undefined): ParsedInitArgs["scope"] { - const normalized = (answer ?? "").trim().toLowerCase(); - return normalized === "g" || normalized === "global" ? "global" : "repo"; -} - -function initHomeRoot(runtime: OpcoreInitRuntime): string { - return realpathSync(resolve(runtime.homeDir ?? homedir())); -} - -function scopeRoot(repoRoot: string, homeRoot: string, scope: ParsedInitArgs["scope"]): string { - return scope === "global" ? homeRoot : repoRoot; -} - -function isInteractiveRuntime(runtime: OpcoreInitRuntime): boolean { - return runtime.stdinIsTTY === true && runtime.stdoutIsTTY === true; -} - -function isExplicitYes(answer: string): boolean { - const normalized = answer.trim().toLowerCase(); - return normalized === "y" || normalized === "yes"; -} - -function isApprovedAnswer(answer: string | undefined, command: OpcoreSetupCommand): boolean { - const normalized = (answer ?? "").trim().toLowerCase(); - if (command === "install") return normalized === "" || normalized === "y" || normalized === "yes"; - return isExplicitYes(answer ?? ""); -} - -function approvalPromptSuffix(command: OpcoreSetupCommand): string { - return command === "install" ? "[Y/n]" : "[y/N]"; -} - -function startInitScanProgress( - json: boolean, - runtime: OpcoreInitRuntime, - command: OpcoreSetupCommand -): { complete(scanMs: number, totalFiles?: number): void; fail(scanMs: number): void } | undefined { - if (json || runtime.stderrIsTTY !== true || typeof runtime.writeStderr !== "function") return undefined; - const startedAt = nowMs(); - const intervalMs = normalizeProgressIntervalMs(runtime.initProgressIntervalMs); - let finished = false; - const write = (text: string) => writeProgress(runtime.writeStderr, text); - const writeProgressLine = (text: string) => write(`\r\x1b[2K${text}`); - write(`Opcore ${command}: scanning repository before setup...`); - const timer = setInterval(() => { - if (finished) return; - const elapsedSeconds = Math.max(1, Math.floor(elapsedMs(startedAt) / 1000)); - writeProgressLine(`Opcore ${command}: still scanning repository before setup (${elapsedSeconds}s elapsed)...`); - }, intervalMs) as ReturnType & { unref?: () => void }; - timer.unref?.(); - return { - complete: (scanMs: number) => { - if (finished) return; - finished = true; - clearInterval(timer); - writeProgressLine(`Opcore ${command}: scan complete in ${scanMs}ms.\n`); - }, - fail: (scanMs: number) => { - if (finished) return; - finished = true; - clearInterval(timer); - writeProgressLine(`Opcore ${command}: scan failed after ${scanMs}ms.\n`); - } - }; -} - -function normalizeProgressIntervalMs(value: number | undefined): number { - return typeof value === "number" && Number.isFinite(value) && value > 0 - ? Math.max(1, Math.floor(value)) - : defaultInitProgressIntervalMs; -} - -function writeProgress(writeStderr: ((text: string) => void) | undefined, text: string): void { - try { - writeStderr?.(text); - } catch { - // Progress output must never change init scan/apply semantics. - } -} - -function nowMs(): number { - return Date.now(); -} - -function elapsedMs(startedAt: number): number { - return Math.max(0, Date.now() - startedAt); -} - -function finalizeTimings(timing: TimingState): OpcoreInitTiming { - return { - scanMs: timing.scanMs, - planMs: timing.planMs, - promptMs: timing.promptMs, - applyMs: timing.applyMs, - totalMs: elapsedMs(timing.startedAt), - firstOutputMs: timing.scanMs - }; -} - -function parseOpcoreInitArgs( - args: readonly string[], - command: OpcoreSetupCommand -): { ok: true; args: ParsedInitArgs } | { ok: false; message: string } { - const parsed: ParsedInitArgs = { - command, - repo: process.cwd(), - repoExplicit: false, - scope: "repo", - scopeExplicit: false, - approved: false, - dryRun: false, - failClosedHook: false, - agentSkill: command === "install", - writeGateHooks: true, - activePreCommitHook: command === "install", - undo: command === "uninstall" - }; - for (let index = 0; index < args.length; index += 1) { - const arg = args[index]; - if (arg === "--repo") { - const value = args[index + 1]; - if (!value || value.startsWith("--")) return { ok: false, message: `opcore ${command}: --repo requires a path` }; - parsed.repo = value; - parsed.repoExplicit = true; - parsed.scope = "repo"; - parsed.scopeExplicit = true; - index += 1; - continue; - } - if (arg.startsWith("--repo=")) { - const value = arg.slice("--repo=".length); - if (!value) return { ok: false, message: `opcore ${command}: --repo requires a path` }; - parsed.repo = value; - parsed.repoExplicit = true; - parsed.scope = "repo"; - parsed.scopeExplicit = true; - continue; - } - if (arg === "--global") { - parsed.scope = "global"; - parsed.scopeExplicit = true; - continue; - } - if (arg === "--local") { - parsed.scope = "repo"; - parsed.scopeExplicit = true; - continue; - } - if (arg === "--approve" || arg === "--yes") { - parsed.approved = true; - continue; - } - if (arg === "--dry-run") { - parsed.dryRun = true; - continue; - } - if (arg === "--fail-closed-hook") { - parsed.failClosedHook = true; - continue; - } - if (arg === "--no-pre-commit" && command === "install") { - parsed.activePreCommitHook = false; - continue; - } - if (arg === "--no-skill" && command === "install") { - parsed.agentSkill = false; - continue; - } - if (arg === "--undo" && command !== "install") { - parsed.undo = true; - continue; - } - return { ok: false, message: `opcore ${command}: unsupported argument ${arg}` }; - } - return { ok: true, args: parsed }; -} - -function planInit( - repoRoot: string, - requestedPath: string, - git: boolean, - homeRoot: string, - options: ParsedInitArgs, - context: InitContext -): PlannedInit { - if (options.scope === "global") return planGlobalInit(repoRoot, requestedPath, homeRoot, options, context); - const agentFiles = detectAgentFiles(repoRoot); - const linkedGitWorktree = git && isLinkedGitWorktree(repoRoot); - const activePreCommitWritePlanned = options.activePreCommitHook && - git && - !linkedGitWorktree && - !repoPathExists(repoRoot, activePreCommitHookPath); - const config = createConfig( - repoRoot, - options.failClosedHook, - activePreCommitWritePlanned, - options.writeGateHooks, - context.scan, - context.settings - ); - const writes: PlannedWrite[] = [ - { - kind: "write", - path: configPath, - targetScope: "repo", - content: `${JSON.stringify(config, null, 2)}\n` - }, - ...agentFiles.map((path) => ({ - kind: "write" as const, - path, - targetScope: "repo" as const, - content: upsertOpcoreBlock(readOptionalRepoFile(repoRoot, path)) - })) - ]; - if (options.writeGateHooks) { - writes.push( - { - kind: "write", - path: agentGateHookPath, - targetScope: "repo", - content: opcoreAgentGateHookScriptContent(), - executable: true - }, - { - kind: "write", - path: claudeSettingsPath, - targetScope: "repo", - content: `${JSON.stringify(mergeClaudeSettings(readJsonObjectIfExists(repoRoot, claudeSettingsPath), "repo"), null, 2)}\n` - }, - { - kind: "write", - path: codexHooksPath, - targetScope: "repo", - content: `${JSON.stringify(mergeCodexHooks(readJsonObjectIfExists(repoRoot, codexHooksPath), "repo"), null, 2)}\n` - } - ); - } - if (options.agentSkill) { - writes.push(...agentSkillPaths.map((path) => ({ - kind: "write" as const, - path, - targetScope: "repo" as const, - content: opcoreAgentSkillContent() - }))); - } - if (git) { - const gitignore = readOptionalRepoFile(repoRoot, gitignorePath); - if (!gitignoreIgnoresOpcore(gitignore ?? "")) { - writes.push({ - kind: "append_managed_line", - path: gitignorePath, - targetScope: "repo", - line: opcoreIgnoreLine - }); - } - if (activePreCommitWritePlanned) { - writes.push({ - kind: "write", - path: activePreCommitHookPath, - targetScope: "repo", - content: activePreCommitHookContent(), - executable: true - }); - } - } - if (options.failClosedHook) { - writes.push({ - kind: "write", - path: hookPath, - targetScope: "repo", - content: failClosedHookContent(), - executable: true - }); - } - const actions = createInitActions( - options.scope, - agentFiles, - options, - writes.some((write) => write.path === gitignorePath), - activePreCommitWritePlanned - ); - return { - writes, - payload: { - schemaVersion: 1, - mode: "plan", - approved: false, - repo: { - root: repoRoot, - requestedPath - }, - options: { - scope: options.scope, - failClosedHook: options.failClosedHook, - dryRun: options.dryRun - }, - agentFiles, - actions, - warnings: initWarnings( - context.scan, - git, - options.failClosedHook, - options.scope, - activePreCommitWritePlanned, - options.activePreCommitHook && git, - linkedGitWorktree - ), - nextActions: initNextActions(options), - undoAvailable: repoPathExists(repoRoot, undoPath), - scan: context.scan, - settings: context.settings, - interaction: context.interaction, - timings: context.timings - } - }; -} - -function planGlobalInit( - repoRoot: string, - requestedPath: string, - homeRoot: string, - options: ParsedInitArgs, - context: InitContext -): PlannedInit { - const writes: PlannedWrite[] = [ - ...(options.writeGateHooks - ? [{ - kind: "write" as const, - path: agentGateHookPath, - targetScope: "global" as const, - content: opcoreAgentGateHookScriptContent(), - executable: true - }] - : []), - ...(options.agentSkill ? agentSkillPaths.map((path) => ({ - kind: "write" as const, - path, - targetScope: "global" as const, - content: opcoreAgentSkillContent() - })) : []), - ...(options.writeGateHooks - ? [ - { - kind: "write" as const, - path: claudeSettingsPath, - targetScope: "global" as const, - content: `${JSON.stringify(mergeClaudeSettings(readJsonObjectIfExists(homeRoot, claudeSettingsPath), "global"), null, 2)}\n` - }, - { - kind: "write" as const, - path: codexHooksPath, - targetScope: "global" as const, - content: `${JSON.stringify(mergeCodexHooks(readJsonObjectIfExists(homeRoot, codexHooksPath), "global"), null, 2)}\n` - } - ] - : []) - ]; - return { - writes, - payload: { - schemaVersion: 1, - mode: "plan", - approved: false, - repo: { - root: repoRoot, - requestedPath - }, - options: { - scope: options.scope, - failClosedHook: options.failClosedHook, - dryRun: options.dryRun - }, - agentFiles: [], - actions: createInitActions(options.scope, [], options, false, false), - warnings: initWarnings(context.scan, true, false, options.scope, false, false, false), - nextActions: initNextActions(options), - undoAvailable: repoPathExists(homeRoot, globalUndoPath), - scan: context.scan, - settings: context.settings, - interaction: context.interaction, - timings: context.timings - } - }; -} - -function appliedInitPayload( - payload: OpcoreInitPlanPayload, - root: string, - scope: ParsedInitArgs["scope"], - command: OpcoreSetupCommand -): OpcoreInitPlanPayload { - return { - ...payload, - mode: "apply", - approved: true, - nextActions: appliedInitNextActions(payload, command), - undoAvailable: repoPathExists(root, undoPathForScope(scope)) - }; -} - -function initNextActions(options: ParsedInitArgs): string[] { - const approveFlag = options.command === "install" ? "--yes" : "--approve"; - const scopedCommand = options.scope === "global" - ? `opcore ${options.command} --global ${approveFlag}` - : `opcore ${options.command} ${approveFlag}`; - const actions = options.dryRun - ? [`Run ${scopedCommand} to apply this plan.`] - : [`Review this plan, then run ${scopedCommand} to write setup.`]; - if (options.scope === "repo") { - actions.push("Claude Code and Codex write-gate hooks are installed by Opcore setup; review Codex project hook trust with /hooks if Codex asks."); - } else { - actions.push("Global Claude Code and Codex write-gate hooks are installed by Opcore setup; review Codex hook trust with /hooks if Codex asks."); - } - if (options.failClosedHook) actions.push(failClosedHookManualInstallAction()); - return actions; -} - -function appliedInitNextActions(payload: OpcoreInitPlanPayload, command: OpcoreSetupCommand): string[] { - const undoCommand = command === "install" - ? payload.options.scope === "global" ? "opcore uninstall --global --yes" : "opcore uninstall --yes" - : payload.options.scope === "global" ? "opcore init --global --undo --approve" : "opcore init --undo --approve"; - const actions = [`Run ${undoCommand} to restore or remove recorded setup files.`]; - if (payload.options.scope === "repo") { - actions.push("Claude Code write calls are blocked on non-ok receipts. Codex uses a PreToolUse guardrail and may require hook trust review."); - } else { - actions.push("Global Claude Code write calls are blocked on non-ok receipts. Codex uses a PreToolUse guardrail and may require hook trust review."); - } - if (payload.options.failClosedHook) actions.push(failClosedHookManualInstallAction()); - return actions; -} - -function failClosedHookManualInstallAction(): string { - return `Manual install required before the fail-closed hook is active: ${failClosedHookActivationCommand}`; -} - -function planUndo( - repoRoot: string, - requestedPath: string, - homeRoot: string, - options: ParsedInitArgs, - context: InitContext -): OpcoreInitPlanPayload { - const root = scopeRoot(repoRoot, homeRoot, options.scope); - const metadata = readUndoMetadata(root, options.scope); - return { - schemaVersion: 1, - mode: "undo", - approved: options.approved && !options.dryRun, - repo: { - root: repoRoot, - requestedPath - }, - options: { - scope: options.scope, - failClosedHook: options.failClosedHook, - dryRun: options.dryRun - }, - agentFiles: metadata.entries - .map((entry) => entry.path) - .filter((path) => AGENT_FILE_CANDIDATES.includes(path as (typeof AGENT_FILE_CANDIDATES)[number])), - actions: metadata.entries.map((entry) => ({ - kind: entry.kind === "append_managed_line" ? "remove" : entry.existed ? "restore" : "remove", - path: actionPath(options.scope, entry.path), - targetScope: options.scope, - summary: entry.kind === "append_managed_line" - ? `Remove managed ${entry.line} gitignore entry from ${entry.path}.` - : entry.existed - ? `Restore ${actionPath(options.scope, entry.path)} from Opcore init backup.` - : `Remove ${actionPath(options.scope, entry.path)} created by Opcore init.`, - requiresApproval: !entry.path.startsWith(".opcore/"), - outsideOpcore: !entry.path.startsWith(".opcore/") - })), - warnings: [], - nextActions: options.approved && !options.dryRun - ? [undoAppliedNextAction(options.command)] - : [undoPreviewNextAction(options)], - undoAvailable: true, - scan: context.scan, - settings: context.settings, - interaction: context.interaction, - timings: context.timings - }; -} - -function appliedUndoPayload( - payload: OpcoreInitPlanPayload, - root: string, - scope: ParsedInitArgs["scope"], - command: OpcoreSetupCommand -): OpcoreInitPlanPayload { - return { - ...payload, - approved: true, - nextActions: [undoAppliedNextAction(command)], - undoAvailable: repoPathExists(root, undoPathForScope(scope)) - }; -} - -function undoAppliedNextAction(command: OpcoreSetupCommand): string { - if (command === "uninstall") return "Opcore setup metadata was restored or removed; rerun opcore install to recreate setup."; - return "Opcore init metadata was restored or removed; rerun opcore init to recreate setup."; -} - -function undoPreviewNextAction(options: ParsedInitArgs): string { - if (options.command === "uninstall") { - return `Run ${options.scope === "global" ? "opcore uninstall --global --yes" : "opcore uninstall --yes"} to restore or remove recorded setup files.`; - } - return `Run ${options.scope === "global" ? "opcore init --global --undo --approve" : "opcore init --undo --approve"} to restore or remove recorded setup files.`; -} - -function applyInit(root: string, scope: ParsedInitArgs["scope"], writes: readonly PlannedWrite[]): void { - const scopedWrites = writes.filter((write) => write.targetScope === scope); - const previousMetadata = readUndoMetadataIfExists(root, scope); - const touchedPaths = uniqueStrings([ - ...(previousMetadata?.entries.map((entry) => entry.path) ?? []), - ...scopedWrites.map((write) => write.path), - undoPathForScope(scope) - ]); - for (const path of touchedPaths) assertMutationPath(root, path, `Opcore ${scope} init target`); - const metadata: UndoMetadata = { - schemaVersion: 1, - kind: scope === "global" ? "opcore_global_init_undo" : "opcore_init_undo", - ...(scope === "global" ? { homeRoot: root } : { repoRoot: root }), - entries: touchedPaths.map((path) => { - const previousEntry = previousMetadata?.entries.find((entry) => entry.path === path); - if (previousEntry) return previousEntry; - return priorEntry(root, path, scopedWrites.find((write) => write.path === path)); - }) - }; - for (const write of scopedWrites) writeScopedFile(root, write); - writeScopedFile(root, { - kind: "write", - path: undoPathForScope(scope), - targetScope: scope, - content: `${JSON.stringify(metadata, null, 2)}\n` - }); -} - -function applyUndo(root: string, scope: ParsedInitArgs["scope"], payload: OpcoreInitPlanPayload): void { - const metadata = readUndoMetadata(root, scope); - const scopedUndoPath = undoPathForScope(scope); - for (const entry of metadata.entries) assertMutationPath(root, entry.path, "Opcore init undo target"); - for (const entry of metadata.entries.filter((entry) => entry.path !== scopedUndoPath)) restoreUndoEntry(root, entry); - const undoEntry = metadata.entries.find((entry) => entry.path === scopedUndoPath); - if (undoEntry) restoreUndoEntry(root, undoEntry); - else rmSync(resolveScopedPath(root, scopedUndoPath), { force: true }); - removeEmptyOpcoreHookDir(root); - void payload; -} - -function createInitActions( - scope: ParsedInitArgs["scope"], - agentFiles: readonly string[], - options: ParsedInitArgs, - gitignoreWritePlanned: boolean, - activePreCommitWritePlanned: boolean -): OpcoreInitAction[] { - const skillActions: OpcoreInitAction[] = options.agentSkill - ? agentSkillPaths.map((path) => ({ - kind: "write" as const, - path: actionPath(scope, path), - targetScope: scope, - summary: "Install the Opcore agent skill.", - requiresApproval: true, - outsideOpcore: true - })) - : []; - if (scope === "global") { - return [ - ...(options.writeGateHooks - ? [{ - kind: "create_hook" as const, - path: actionPath(scope, agentGateHookPath), - targetScope: scope, - summary: "Install the global Opcore write-gate adapter script.", - requiresApproval: false, - outsideOpcore: false - }] - : []), - ...skillActions, - ...(options.writeGateHooks - ? [ - { - kind: "wire_harness" as const, - path: actionPath(scope, claudeSettingsPath), - targetScope: scope, - summary: "Merge the Opcore Claude Code PreToolUse write gate.", - requiresApproval: true, - outsideOpcore: true - }, - { - kind: "wire_harness" as const, - path: actionPath(scope, codexHooksPath), - targetScope: scope, - summary: "Merge the Opcore Codex PreToolUse write gate guardrail.", - requiresApproval: true, - outsideOpcore: true - } - ] - : []) - ]; - } - const actions: OpcoreInitAction[] = [ - { - kind: "write", - path: configPath, - targetScope: scope, - summary: "Write additive Opcore init config.", - requiresApproval: false, - outsideOpcore: false - }, - ...agentFiles.map((path) => ({ - kind: "upsert_block" as const, - path, - targetScope: scope, - summary: "Add or update delimited Opcore agent guidance.", - requiresApproval: true, - outsideOpcore: true - })), - ...skillActions, - ...(options.writeGateHooks - ? [ - { - kind: "create_hook" as const, - path: agentGateHookPath, - targetScope: scope, - summary: "Install the repo-local Opcore write-gate adapter script.", - requiresApproval: false, - outsideOpcore: false - }, - { - kind: "wire_harness" as const, - path: claudeSettingsPath, - targetScope: scope, - summary: "Merge the Opcore Claude Code PreToolUse write gate.", - requiresApproval: true, - outsideOpcore: true - }, - { - kind: "wire_harness" as const, - path: codexHooksPath, - targetScope: scope, - summary: "Merge the Opcore Codex PreToolUse write gate guardrail.", - requiresApproval: true, - outsideOpcore: true - } - ] - : []) - ]; - if (gitignoreWritePlanned) { - actions.push({ - kind: "write", - path: gitignorePath, - targetScope: scope, - summary: "Append managed .opcore/ gitignore entry.", - requiresApproval: true, - outsideOpcore: true - }); - } - if (activePreCommitWritePlanned) { - actions.push({ - kind: "create_hook", - path: activePreCommitHookPath, - targetScope: scope, - summary: "Install active Git pre-commit hook that runs `opcore check --changed`.", - requiresApproval: true, - outsideOpcore: true - }); - } - if (options.failClosedHook) { - actions.push({ - kind: "create_hook", - path: hookPath, - targetScope: scope, - summary: `Manual install required: create fail-closed pre-commit hook script; activate with \`${failClosedHookActivationCommand}\`.`, - requiresApproval: false, - outsideOpcore: false - }); - } - return actions; -} - -function detectAgentFiles(repoRoot: string): string[] { - const existing = AGENT_FILE_CANDIDATES.filter((path) => repoPathExists(repoRoot, path)); - for (const path of existing) assertExistingRepoPath(repoRoot, path, "Existing agent guidance file", "file"); - return existing.length > 0 ? [...existing] : ["AGENTS.md"]; -} - -function upsertOpcoreBlock(existing: string | undefined): string { - const block = guidanceBlock(); - if (existing === undefined || existing.length === 0) return `${block}\n`; - const begin = existing.indexOf(beginMarker); - const end = existing.indexOf(endMarker); - if ((begin === -1) !== (end === -1) || (begin !== -1 && end < begin)) { - throw new Error("existing Opcore init guidance markers are unbalanced"); - } - if (begin !== -1) { - const replacementEnd = end + endMarker.length; - return `${trimRightPreserve(existing.slice(0, begin))}\n\n${block}\n${trimLeftPreserve(existing.slice(replacementEnd))}`.replace(/\n{3,}/g, "\n\n"); - } - return `${trimRightPreserve(existing)}\n\n${block}\n`; -} - -function guidanceBlock(): string { - return [ - beginMarker, - "## Opcore", - "", - "- Run `opcore check --changed` before finalizing edits.", - "- Preserve existing repo lint/test/CI/pre-commit guardrails.", - "- Treat unsupported stacks and degraded tools honestly.", - "- For Python repos, require one configured per-project type authority; treat absent, conflicting, unavailable, or deferred authority and missing ruff/pytest as degraded coverage, not a pass.", - "- Use Opcore validation directly; ASP hosts retain their own decision authority.", - endMarker - ].join("\n"); -} - -function createConfig( - repoRoot: string, - failClosedHook: boolean, - activePreCommitHook: boolean, - writeGateHooks: boolean, - scan: OpcoreInitScanSummary, - settings: OpcoreInitSettings -): Record { - const existing = readJsonObject(repoRoot, configPath); - const existingHooks = isPlainObject(existing.hooks) ? existing.hooks : {}; - const existingGuidance = isPlainObject(existing.guidance) ? existing.guidance : {}; - const existingOnboarding = isPlainObject(existing.onboarding) ? existing.onboarding : {}; - const onboardingScan = isPlainObject(existingOnboarding.scan) ? existingOnboarding.scan : scan; - const onboardingLanguages = Array.isArray(existingOnboarding.languages) ? existingOnboarding.languages : settings.languages; - return { - ...existing, - schemaVersion: 1, - kind: "opcore_init_config", - onboarding: { - ...existingOnboarding, - scan: onboardingScan, - languages: onboardingLanguages, - timingPayload: true - }, - guidance: { - ...existingGuidance, - checkCommand: "opcore check --changed", - preserveExistingGuardrails: true, - treatUnsupportedCoverageHonestly: true, - directProductAuthority: "opcore" - }, - hooks: { - ...existingHooks, - failClosedPreCommit: existingHooks.failClosedPreCommit === true || failClosedHook || activePreCommitHook, - activePreCommit: existingHooks.activePreCommit === true || activePreCommitHook, - writeGate: existingHooks.writeGate === true || writeGateHooks, - harnesses: writeGateHooks - ? ["claude-code", "codex"] - : Array.isArray(existingHooks.harnesses) ? existingHooks.harnesses : [] - } - }; -} - -function initWarnings( - scan: OpcoreInitScanSummary, - git: boolean, - failClosedHook: boolean, - scope: ParsedInitArgs["scope"], - activePreCommitHook: boolean, - activePreCommitRequested: boolean, - linkedGitWorktree: boolean -): string[] { - const warnings: string[] = []; - if (scan.unsupportedStacks.length > 0) { - warnings.push(`Unsupported stacks: ${scan.unsupportedStacks.map((stack) => `${stack.language} (${stack.count})`).join(", ")}`); - } - if (scan.degradedRustTools.length > 0) { - warnings.push(`Degraded validation tools: ${scan.degradedRustTools.map((tool) => tool.tool).join(", ")}`); - } - if (!git) { - warnings.push("No Git repository detected; .opcore/ ignore entry not written."); - } - warnings.push("Do not weaken existing lint, test, CI, pre-commit, or agent guardrails."); - warnings.push( - scope === "global" - ? "Global write-gate hooks apply across repos; undo removes only Opcore-recorded global hook entries." - : "Repo write-gate hooks are additive; Codex project hooks may require trust review before they run." - ); - warnings.push( - failClosedHook - ? `Fail-closed hook script is opt-in. Manual install required: ${failClosedHookActivationCommand}` - : activePreCommitHook && git - ? "Git pre-commit hook will run opcore check --changed when no existing .git/hooks/pre-commit is present." - : linkedGitWorktree - ? "Linked Git worktree detected; Opcore will not install .git/hooks/pre-commit from this checkout." - : activePreCommitRequested - ? "Existing .git/hooks/pre-commit detected; Opcore will not overwrite it." - : "Fail-closed hooks are opt-in and are not created unless --fail-closed-hook is approved." - ); - return warnings; -} - -function failClosedHookContent(): string { - return [ - "#!/usr/bin/env sh", - "# Manual install required.", - "# This script is not active until installed.", - `# Activation command: ${failClosedHookActivationCommand}`, - "set -eu", - "opcore check --changed", - "" - ].join("\n"); -} - -function activePreCommitHookContent(): string { - return [ - "#!/usr/bin/env sh", - "# Installed by opcore install. Remove with opcore uninstall.", - "set -eu", - "opcore check --changed", - "" - ].join("\n"); -} - -function opcoreAgentSkillContent(): string { - return [ - "---", - "name: opcore", - "description: Use when working in a repository that has installed Opcore robustness checks.", - "---", - "", - "# Opcore", - "", - "Use Opcore as the repository-local robustness gate for coding-agent edits.", - "", - "- Run `opcore status` to inspect activation and coverage before broad work.", - "- Run `opcore check --changed` before finalizing source edits.", - "- Treat unsupported stacks and degraded tools honestly; do not report them as clean coverage.", - "- Preserve existing lint, test, CI, pre-commit, and agent guardrails.", - "- The installed write gate is a hook guardrail for supported edit tools, not host authority.", - "" - ].join("\n"); -} - -function mergeClaudeSettings(existing: Record, scope: ParsedInitArgs["scope"]): Record { - return mergePreToolUseHook(existing, { - matcher: "Edit|MultiEdit|Write", - command: agentGateCommand("claude", scope), - statusMessage: "Running Opcore write gate" - }); -} - -function mergeCodexHooks(existing: Record, scope: ParsedInitArgs["scope"]): Record { - return mergePreToolUseHook(existing, { - matcher: "apply_patch|Edit|Write", - command: agentGateCommand("codex", scope), - statusMessage: "Running Opcore write gate" - }); -} - -function mergePreToolUseHook( - existing: Record, - hook: { matcher: string; command: string; statusMessage: string } -): Record { - const hooks = isPlainObject(existing.hooks) ? existing.hooks : {}; - const preToolUse = Array.isArray(hooks.PreToolUse) ? [...hooks.PreToolUse] : []; - const groupIndex = preToolUse.findIndex((entry) => isPlainObject(entry) && entry.matcher === hook.matcher); - const hookEntry = { - type: "command", - command: hook.command, - timeout: 30, - statusMessage: hook.statusMessage - }; - if (groupIndex >= 0) { - const group = preToolUse[groupIndex]; - if (isPlainObject(group)) { - const groupHooks = Array.isArray(group.hooks) ? [...group.hooks] : []; - const alreadyPresent = groupHooks.some( - (entry) => isPlainObject(entry) && typeof entry.command === "string" && entry.command.includes("opcore-agent-gate.mjs") - ); - preToolUse[groupIndex] = { - ...group, - hooks: alreadyPresent ? groupHooks : [...groupHooks, hookEntry] - }; - } - } else { - preToolUse.push({ - matcher: hook.matcher, - hooks: [hookEntry] - }); - } - return { - ...existing, - hooks: { - ...hooks, - PreToolUse: preToolUse - } - }; -} - -function agentGateCommand(harness: "claude" | "codex", scope: ParsedInitArgs["scope"]): string { - const hook = scope === "global" - ? "$HOME/.opcore/hooks/opcore-agent-gate.mjs" - : '"$(git rev-parse --show-toplevel)/.opcore/hooks/opcore-agent-gate.mjs"'; - const repo = scope === "global" ? "" : ' --repo "$(git rev-parse --show-toplevel)"'; - return `node ${hook} --harness ${harness}${repo}`; -} - -function priorEntry(repoRoot: string, path: string, write: PlannedWrite | undefined): UndoEntry { - if (write?.kind === "append_managed_line") { - const existing = readOptionalRepoFile(repoRoot, path); - return { - kind: "append_managed_line", - path, - existed: existing !== undefined, - line: write.line, - appended: appendManagedGitignoreLine(existing) - }; - } - if (!repoPathExists(repoRoot, path)) return { kind: "restore_file", path, existed: false }; - return { - kind: "restore_file", - path, - existed: true, - content: readFileSync(assertExistingRepoPath(repoRoot, path, "Existing Opcore init target", "file"), "utf8") - }; -} - -function writeRepoFile(repoRoot: string, write: PlannedWrite): void { - const absolute = assertRepoMutationPath(repoRoot, write.path, "Opcore init write target"); - mkdirSync(dirname(absolute), { recursive: true }); - if (write.kind === "append_managed_line") { - const existing = readOptionalRepoFile(repoRoot, write.path); - appendFileSync(absolute, appendManagedGitignoreLine(existing)); - return; - } - writeFileSync(absolute, write.content, "utf8"); - if (write.executable) { - assertRepoMutationPath(repoRoot, write.path, "Opcore init chmod target"); - chmodSync(absolute, 0o755); - } -} - -function writeScopedFile(root: string, write: PlannedWrite): void { - writeRepoFile(root, write); -} - -function restoreUndoEntry(repoRoot: string, entry: UndoEntry): void { - const absolute = assertRepoMutationPath(repoRoot, entry.path, "Opcore init undo target"); - if (entry.kind === "append_managed_line") { - if (!repoPathExists(repoRoot, entry.path)) return; - const removal = removeManagedGitignoreLine(readFileSync(absolute, "utf8"), entry); - if (!removal.removed) return; - if (!entry.existed && removal.content.length === 0) { - rmSync(absolute, { force: true }); - return; - } - writeFileSync(absolute, removal.content, "utf8"); - return; - } - if (!entry.existed) { - rmSync(absolute, { force: true }); - return; - } - if (entry.content === undefined) { - throw new Error(`Undo entry for ${entry.path} is missing content`); - } - mkdirSync(dirname(absolute), { recursive: true }); - writeFileSync(absolute, entry.content, "utf8"); -} - -function readUndoMetadata(root: string, scope: ParsedInitArgs["scope"]): UndoMetadata { - const scopedUndoPath = undoPathForScope(scope); - const raw = readFileSync(assertExistingRepoPath(root, scopedUndoPath, "Opcore init undo metadata", "file"), "utf8"); - const parsed = JSON.parse(raw) as unknown; - const expectedKind = scope === "global" ? "opcore_global_init_undo" : "opcore_init_undo"; - if (!isPlainObject(parsed) || parsed.schemaVersion !== 1 || parsed.kind !== expectedKind || !Array.isArray(parsed.entries)) { - throw new Error(".opcore/init-undo.json is not valid Opcore init undo metadata"); - } - const recordedRoot = scope === "global" ? parsed.homeRoot : parsed.repoRoot; - if (typeof recordedRoot !== "string" || resolve(recordedRoot) !== resolve(root)) { - throw new Error(".opcore/init-undo.json repoRoot does not match this repository"); - } - const allowedPaths = scope === "global" ? allowedGlobalUndoPaths : allowedUndoPaths; - const seenPaths = new Set(); - const entries: UndoEntry[] = []; - for (const entry of parsed.entries) { - if (!isPlainObject(entry) || typeof entry.path !== "string" || typeof entry.existed !== "boolean") { - throw new Error(".opcore/init-undo.json contains an invalid entry"); - } - if (!allowedPaths.has(entry.path)) { - throw new Error(`.opcore/init-undo.json contains unsupported path: ${entry.path}`); - } - if (seenPaths.has(entry.path)) { - throw new Error(`.opcore/init-undo.json contains duplicate path: ${entry.path}`); - } - seenPaths.add(entry.path); - const kind = typeof entry.kind === "string" ? entry.kind : "restore_file"; - if (kind === "append_managed_line") { - if (entry.path !== gitignorePath) { - throw new Error(`.opcore/init-undo.json append-managed-line entry targets unsupported path: ${entry.path}`); - } - if (typeof entry.line !== "string" || entry.line !== opcoreIgnoreLine) { - throw new Error(`.opcore/init-undo.json append-managed-line entry for ${entry.path} has invalid line`); - } - if ( - "appended" in entry && - entry.appended !== undefined && - entry.appended !== `${opcoreIgnoreLine}\n` && - entry.appended !== `\n${opcoreIgnoreLine}\n` - ) { - throw new Error(`.opcore/init-undo.json append-managed-line entry for ${entry.path} has invalid appended text`); - } - resolveRepoPath(root, entry.path); - entries.push({ - kind: "append_managed_line", - path: entry.path, - existed: entry.existed, - line: entry.line, - ...(typeof entry.appended === "string" ? { appended: entry.appended } : {}) - }); - continue; - } - if (entry.path === gitignorePath) { - throw new Error(".opcore/init-undo.json .gitignore entry must use managed-line undo metadata"); - } - if (kind !== "restore_file") { - throw new Error(`.opcore/init-undo.json contains unsupported entry kind: ${kind}`); - } - if (entry.existed && typeof entry.content !== "string") { - throw new Error(`.opcore/init-undo.json restore entry for ${entry.path} is missing string content`); - } - if (!entry.existed && "content" in entry && entry.content !== undefined && typeof entry.content !== "string") { - throw new Error(`.opcore/init-undo.json remove entry for ${entry.path} has invalid content`); - } - resolveRepoPath(root, entry.path); - entries.push({ - kind: "restore_file", - path: entry.path, - existed: entry.existed, - ...(typeof entry.content === "string" ? { content: entry.content } : {}) - }); - } - return { - schemaVersion: 1, - kind: expectedKind, - ...(scope === "global" ? { homeRoot: recordedRoot } : { repoRoot: recordedRoot }), - entries - }; -} - -function readUndoMetadataIfExists(root: string, scope: ParsedInitArgs["scope"]): UndoMetadata | undefined { - if (!repoPathExists(root, undoPathForScope(scope))) return undefined; - return readUndoMetadata(root, scope); -} - -function readJsonObject(repoRoot: string, path: string): Record { - const content = readOptionalRepoFile(repoRoot, path); - if (content === undefined) return {}; - const parsed = JSON.parse(content) as unknown; - if (!isPlainObject(parsed)) throw new Error(`${path} must contain a JSON object`); - return parsed; -} - -function readJsonObjectIfExists(repoRoot: string, path: string): Record { - if (!repoPathExists(repoRoot, path)) return {}; - return readJsonObject(repoRoot, path); -} - -function readOptionalRepoFile(repoRoot: string, path: string): string | undefined { - if (!repoPathExists(repoRoot, path)) return undefined; - return readFileSync(assertExistingRepoPath(repoRoot, path, "Existing repo file", "file"), "utf8"); -} - -function gitignoreIgnoresOpcore(content: string): boolean { - let ignored = false; - for (const rawLine of content.split(/\r\n|\n|\r/u)) { - const line = rawLine.trim(); - if (line.length === 0 || line.startsWith("#")) continue; - const negated = line.startsWith("!"); - const pattern = negated ? line.slice(1).trim() : line; - if (isOpcoreGitignorePattern(pattern)) { - ignored = !negated; - } - } - return ignored; -} - -function isOpcoreGitignorePattern(pattern: string): boolean { - return pattern === ".opcore" || - pattern === ".opcore/" || - pattern === "/.opcore" || - pattern === "/.opcore/" || - pattern === ".opcore/**" || - pattern === "/.opcore/**"; -} - -function appendManagedGitignoreLine(existing: string | undefined): string { - if (existing === undefined || existing.length === 0) return `${opcoreIgnoreLine}\n`; - return `${existing.endsWith("\n") || existing.endsWith("\r") ? "" : "\n"}${opcoreIgnoreLine}\n`; -} - -function removeManagedGitignoreLine(current: string, entry: ManagedLineUndoEntry): { content: string; removed: boolean } { - if (entry.appended !== undefined && current.endsWith(entry.appended)) { - return { content: current.slice(0, -entry.appended.length), removed: true }; - } - const chunks = current.match(/[^\r\n]*(?:\r\n|\n|\r|$)/gu) ?? []; - const meaningfulChunks = chunks.filter((chunk) => chunk.length > 0); - for (let index = 0; index < meaningfulChunks.length; index += 1) { - const chunk = meaningfulChunks[index]; - const chunkLine = chunk.replace(/(?:\r\n|\n|\r)$/u, ""); - if (chunkLine === entry.line) { - meaningfulChunks.splice(index, 1); - return { content: meaningfulChunks.join(""), removed: true }; - } - } - return { content: current, removed: false }; -} - -function resolveRepoPath(repoRoot: string, path: string): string { - if (path.length === 0 || path.includes("\0")) throw new Error(`Invalid repo path: ${path}`); - const absolute = resolve(repoRoot, path); - const normalized = relative(repoRoot, absolute); - if (normalized === "" || normalized.startsWith("..") || normalized.split(sep).includes("..")) { - throw new Error(`Repo-relative path escapes repository: ${path}`); - } - return absolute; -} - -function resolveScopedPath(root: string, path: string): string { - return resolveRepoPath(root, path); -} - -function assertMutationPath(root: string, path: string, label: string): string { - return assertRepoMutationPath(root, path, label); -} - -function undoPathForScope(_scope: ParsedInitArgs["scope"]): string { - return undoPath; -} - -function actionPath(scope: ParsedInitArgs["scope"], path: string): string { - return scope === "global" ? `~/${path}` : path; -} - -function assertRepoMutationPath(repoRoot: string, path: string, label: string): string { - const absolute = resolveRepoPath(repoRoot, path); - assertExistingAncestorInsideRepo(repoRoot, absolute, path, label); - if (lstatIfExists(absolute)) assertExistingRepoPath(repoRoot, path, label, "file"); - return absolute; -} - -function assertExistingRepoPath(repoRoot: string, path: string, label: string, expected: "file" | "directory"): string { - const absolute = resolveRepoPath(repoRoot, path); - assertExistingAncestorInsideRepo(repoRoot, absolute, path, label); - return assertExistingAbsolutePath(repoRoot, absolute, path, label, expected); -} - -function assertExistingAbsolutePath( - repoRoot: string, - absolute: string, - displayPath: string, - label: string, - expected: "file" | "directory" -): string { - const lstat = lstatIfExists(absolute); - if (!lstat) throw new Error(`${label} does not exist: ${displayPath}`); - if (lstat.isSymbolicLink()) throw new Error(`${label} must not be a symlink: ${displayPath}`); - let realPath: string; - try { - realPath = realpathSync(absolute); - } catch (error) { - throw new Error(`${label} symlink cannot be resolved for ${displayPath}: ${errorMessage(error)}`); - } - if (!isInsideRepo(repoRoot, realPath)) { - throw new Error(`${label} resolves outside repository through a symlink: ${displayPath}`); - } - const stat = statSync(absolute); - if (expected === "file" && !stat.isFile()) throw new Error(`${label} is not a file: ${displayPath}`); - if (expected === "directory" && !stat.isDirectory()) throw new Error(`${label} is not a directory: ${displayPath}`); - return absolute; -} - -function assertExistingAncestorInsideRepo(repoRoot: string, absolutePath: string, path: string, label: string): void { - const relativeParent = relative(resolve(repoRoot), dirname(absolutePath)); - if (relativeParent === "") return; - if (relativeParent.startsWith("..") || isAbsolute(relativeParent)) { - throw new Error(`${label} parent cannot be resolved inside repository: ${path}`); - } - - let current = resolve(repoRoot); - for (const segment of relativeParent.split(sep)) { - if (!segment) continue; - current = resolve(current, segment); - if (!lstatIfExists(current)) return; - assertExistingAbsolutePath(repoRoot, current, repoRelativePath(repoRoot, current), `${label} parent`, "directory"); - } -} - -function repoPathExists(repoRoot: string, path: string): boolean { - return lstatIfExists(resolveRepoPath(repoRoot, path)) !== undefined; -} - -function isLinkedGitWorktree(repoRoot: string): boolean { - const gitPath = lstatIfExists(resolveRepoPath(repoRoot, ".git")); - return gitPath?.isFile() === true; -} - -function lstatIfExists(path: string): ReturnType | undefined { - try { - return lstatSync(path); - } catch (error) { - if (errorCode(error) === "ENOENT") return undefined; - throw error; - } -} - -function isInsideRepo(repoRoot: string, path: string): boolean { - const normalized = relative(resolve(repoRoot), resolve(path)); - return normalized === "" || (!normalized.startsWith("..") && !isAbsolute(normalized)); -} - -function repoRelativePath(repoRoot: string, absolutePath: string): string { - const normalized = relative(resolve(repoRoot), resolve(absolutePath)); - return normalized === "" ? "." : normalized; -} - -function isPlainObject(value: unknown): value is Record { - return typeof value === "object" && value !== null && !Array.isArray(value); -} - -function trimRightPreserve(text: string): string { - return text.replace(/\s+$/u, ""); -} - -function trimLeftPreserve(text: string): string { - return text.replace(/^\s+/u, ""); -} - -function removeEmptyOpcoreHookDir(repoRoot: string): void { - const hooksDir = resolveRepoPath(repoRoot, ".opcore/hooks"); - if (!lstatIfExists(hooksDir)) return; - assertExistingRepoPath(repoRoot, ".opcore/hooks", "Opcore hooks directory", "directory"); - if (readdirSync(hooksDir).length === 0) { - rmSync(hooksDir, { recursive: true, force: true }); - } -} - -function uniqueStrings(values: readonly string[]): string[] { - return [...new Set(values)]; -} - -function formatSetupPlan(payload: OpcoreInitPlanPayload, applied: boolean, command: OpcoreSetupCommand): string { - if (command === "install") return formatInstallPlan(payload, applied); - if (command === "uninstall") return formatUninstallPlan(payload, applied); - return formatInitPlan(payload, applied); -} - -function formatInstallPlan(payload: OpcoreInitPlanPayload, applied: boolean): string { - const skillEnabled = payload.actions.some((action) => action.path.endsWith("/skills/opcore/SKILL.md")); - const hooksEnabled = payload.actions.some((action) => action.path.endsWith(claudeSettingsPath)); - const activePreCommitEnabled = payload.actions.some((action) => action.path === activePreCommitHookPath); - const scanSummary = `Analyzed ${payload.scan.totalFiles} files; validation=${payload.scan.validationStatus}; diagnostics=${payload.scan.diagnosticCount}.`; - return [ - "Opcore install:", - ` ${scanSummary}`, - "Setup choices:", - `${skillEnabled ? "[x]" : "[ ]"} Install Opcore agent skill`, - `${hooksEnabled ? "[x]" : "[ ]"} Install Claude Code and Codex write-gate hooks`, - `${activePreCommitEnabled ? "[x]" : "[ ]"} Install Git pre-commit hook`, - "", - formatInitPlan(payload, applied, "--yes") - ].join("\n"); -} - -function formatUninstallPlan(payload: OpcoreInitPlanPayload, applied: boolean): string { - return [ - "Opcore uninstall:", - " Restore or remove only files recorded in .opcore/init-undo.json.", - "", - formatInitPlan(payload, applied, "--yes") - ].join("\n"); -} - -function formatInitPlan(payload: OpcoreInitPlanPayload, applied: boolean, approvalFlag?: string): string { - const heading = payload.mode === "undo" ? "Undo:" : "Setup:"; - const requiredApprovalFlag = approvalFlag ?? (payload.mode === "undo" ? "--undo --approve" : "--approve"); - const actionLines = payload.actions.map((action) => `- ${action.kind} ${action.path}: ${action.summary}`); - const approvalLine = payload.interaction.promptState === "requested" - ? "Approval: awaiting TTY response." - : payload.interaction.promptState === "declined" - ? "Approval: declined; no files written." - : applied - ? "Approval: applied." - : payload.mode === "undo" - ? `Approval: required; rerun with ${requiredApprovalFlag} to restore/remove recorded files.` - : `Approval: required; rerun with ${requiredApprovalFlag} to write this setup.`; - const languages = payload.scan.languages.length === 0 - ? "none" - : payload.scan.languages.map((entry) => `${entry.language} ${entry.files}`).join(", "); - const unsupported = payload.scan.unsupportedStacks.length === 0 - ? "none" - : payload.scan.unsupportedStacks.map((stack) => `${stack.language} ${stack.count}`).join(", "); - const degradedValidationTools = payload.scan.degradedRustTools.length === 0 - ? "none" - : payload.scan.degradedRustTools.map((tool) => `${tool.adapter}:${tool.tool}`).join(", "); - const warningLines = payload.warnings.length === 0 - ? [" none"] - : payload.warnings.map((warning) => ` ${warning}`); - const pythonDependencyManagers = payload.settings.python?.dependencyManagers.length - ? payload.settings.python.dependencyManagers.map((manager) => `${manager.kind}:${manager.path}`).join(", ") - : "none"; - const pythonVirtualEnvironments = payload.settings.python?.virtualEnvironments.length - ? payload.settings.python.virtualEnvironments.map((environment) => environment.path).join(", ") - : "none"; - return [ - "Coverage:", - ` files=${payload.scan.totalFiles}`, - ` graph-supported=${payload.scan.graphSupportedFiles}`, - ` validation-supported=${payload.scan.validationSupportedFiles}`, - ` validation-retained=${payload.scan.validationRetainedFiles}`, - ` unsupported=${unsupported}`, - ` languages=${languages}`, - ` degraded-validation-tools=${degradedValidationTools}`, - ` python-dependency-managers=${pythonDependencyManagers}`, - ` python-virtualenvs=${pythonVirtualEnvironments}`, - "Findings:", - ` diagnostics=${payload.scan.diagnosticCount}`, - ` validation=${payload.scan.validationStatus}`, - ` failed-checks=${payload.scan.failedChecks.length === 0 ? "none" : payload.scan.failedChecks.join(", ")}`, - ` graph=${payload.scan.graphState}`, - ` activation=${payload.scan.activationLevel}`, - "Warnings:", - ...warningLines, - heading, - `Repo: ${payload.repo.root}`, - `Scope: ${payload.options.scope}`, - `Mode: ${payload.mode}`, - `Approved: ${payload.approved ? "yes" : "no"}`, - "Actions:", - ...actionLines, - approvalLine, - "Timing:", - ` first-output-ms=${payload.timings.firstOutputMs} scan-ms=${payload.timings.scanMs} total-ms=${payload.timings.totalMs}` - ].join("\n"); -} - -function formatInteractiveOutcome(payload: OpcoreInitPlanPayload, command: OpcoreSetupCommand): string { - return payload.approved - ? `opcore ${command} applied\nApproval: applied.` - : `opcore ${command} declined\nApproval: declined; no files written.`; -} - -function opcoreSetupHelpMessage(command: OpcoreSetupCommand): string { - if (command === "install") return opcoreInstallHelpMessage(); - if (command === "uninstall") return opcoreUninstallHelpMessage(); - return opcoreInitHelpMessage(); -} - -function opcoreInitHelpMessage(): string { - return [ - "Usage:", - " opcore init [--repo ] [--local|--global] [--approve] [--json]", - " opcore init --undo --approve [--repo ] [--local|--global] [--json]", - "Flags:", - " --repo Repository root to set up.", - " --local Force repo-scoped setup.", - " --global Install the write gate in user-level agent settings.", - " --approve Apply the proposed additive setup.", - " --undo Revert files recorded in .opcore/init-undo.json.", - " --fail-closed-hook Add the optional fail-closed pre-commit hook.", - " --json Emit structured JSON.", - "Defaults:", - " Without --approve, init is plan-only outside an interactive approval prompt.", - " Inside a Git repo on a TTY, init asks whether to install for this repo or globally.", - "Examples:", - " opcore init --repo . --json", - " opcore init --repo . --approve", - " opcore init --global --approve", - "Exit codes: 0 planned or applied, 1 setup error, 64 unsupported." - ].join("\n"); -} - -function opcoreInstallHelpMessage(): string { - return [ - "Usage:", - " opcore install [--repo ] [--local|--global] [--yes] [--json]", - "Flags:", - " --repo Repository root to set up.", - " --local Force repo-scoped setup.", - " --global Install user-level agent skills and write-gate hooks.", - " --yes Apply the proposed setup without prompting.", - " --no-skill Do not install the Opcore agent skill.", - " --no-pre-commit Do not install the repo Git pre-commit hook.", - " --json Emit structured JSON.", - "Defaults:", - " install scans first, then applies on --yes or an interactive default-yes approval prompt.", - "Examples:", - " opcore install", - " opcore install --repo . --yes", - "Exit codes: 0 planned or applied, 1 setup error, 64 unsupported." - ].join("\n"); -} - -function opcoreUninstallHelpMessage(): string { - return [ - "Usage:", - " opcore uninstall [--repo ] [--local|--global] [--yes] [--json]", - "Flags:", - " --repo Repository root to restore/remove recorded setup from.", - " --local Force repo-scoped uninstall.", - " --global Restore/remove user-level recorded setup.", - " --yes Apply the uninstall without prompting.", - " --json Emit structured JSON.", - "Defaults:", - " uninstall restores or removes only files recorded in .opcore/init-undo.json.", - "Examples:", - " opcore uninstall --repo . --yes", - " opcore uninstall --global --yes", - "Exit codes: 0 planned or applied, 1 setup error, 64 unsupported." - ].join("\n"); -} - -function errorMessage(error: unknown): string { - return error instanceof Error ? error.message : String(error); -} - -function errorCode(error: unknown): string | undefined { - return typeof error === "object" && error !== null && "code" in error ? String((error as { code?: unknown }).code) : undefined; -} +export { AGENT_FILE_CANDIDATES } from "./init-constants.js"; +export { + routeOpcoreInit, + routeOpcoreInstall, + routeOpcoreUninstall +} from "./init-router.js"; +export type { OpcoreInitRuntime } from "./init-types.js"; diff --git a/packages/opcore/src/repo-paths.ts b/packages/opcore/src/repo-paths.ts new file mode 100644 index 0000000..f7ce66b --- /dev/null +++ b/packages/opcore/src/repo-paths.ts @@ -0,0 +1,26 @@ +import { isAbsolute, relative, resolve, sep } from "node:path"; + +export function resolveRepoPath(root: string, path: string): string { + if (path.length === 0 || path.includes("\0")) { + throw new Error(`Invalid repo path: ${path}`); + } + const absolute = resolve(root, path); + const normalized = relative(root, absolute); + if ( + normalized === "" || + normalized.startsWith("..") || + normalized.split(sep).includes("..") + ) { + throw new Error(`Repo-relative path escapes repository: ${path}`); + } + return absolute; +} + +export function isMissingPathError(error: unknown): boolean { + return ( + typeof error === "object" && + error !== null && + "code" in error && + (error.code === "ENOENT" || error.code === "ENOTDIR") + ); +} diff --git a/packages/opcore/src/scan.ts b/packages/opcore/src/scan.ts index 0d42333..6238d1e 100644 --- a/packages/opcore/src/scan.ts +++ b/packages/opcore/src/scan.ts @@ -31,6 +31,7 @@ import { opcorePublicValidationRuntimePolicy } from "./validation-composition.js"; import { validationChecksForRepoPolicyAndCoverage } from "./repo-validation-policy.js"; +import { isMissingPathError, resolveRepoPath } from "./repo-paths.js"; const skippedPathSegments = new Set(commonSkippedPathSegments); const cloneDiagnosticsCommand = "opcore check --all --checks clone.duplication --json"; @@ -184,7 +185,7 @@ function createReadOnlyWorkspace(repoRoot: string): ValidationWorkspace { content: await readFile(resolveRepoPath(root, path), "utf8") }; } catch (error) { - if (isMissingFileError(error)) return { status: "missing" }; + if (isMissingPathError(error)) return { status: "missing" }; throw error; } }, @@ -252,24 +253,6 @@ function formatScanMessage(repoState: OpcoreRepoStatePayload, validationResult: ].join("\n"); } -function resolveRepoPath(root: string, path: string): string { - const absolute = resolve(root, path); - const normalized = relative(root, absolute); - if (normalized === "" || normalized.startsWith("..") || normalized.split(sep).includes("..")) { - throw new Error(`Repo-relative path escapes repository: ${path}`); - } - return absolute; -} - function hasSkippedSegment(path: string): boolean { return path.split(/[\\/]+/).some((segment) => skippedPathSegments.has(segment)); } - -function isMissingFileError(error: unknown): boolean { - return ( - typeof error === "object" && - error !== null && - "code" in error && - ((error as { code?: unknown }).code === "ENOENT" || (error as { code?: unknown }).code === "ENOTDIR") - ); -} diff --git a/packages/validation-docs/src/check-definition.ts b/packages/validation-docs/src/check-definition.ts new file mode 100644 index 0000000..d96b4f1 --- /dev/null +++ b/packages/validation-docs/src/check-definition.ts @@ -0,0 +1,33 @@ +import type { ValidationDiagnostic } from "@the-open-engine/opcore-contracts"; +import type { + ValidationCheckContext, + ValidationCheckDefinition, + ValidationCheckResult +} from "@the-open-engine/opcore-validation"; +import { + docsCheckAdapter, + docsCheckOwner, + optInDocsDefaultScopes +} from "./check-constants.js"; + +type DocsCheckRunner = ( + context: ValidationCheckContext +) => Promise | ValidationCheckResult; + +export function docsCheck( + id: string, + defaultSeverity: ValidationDiagnostic["severity"], + supportedScopes: readonly ValidationCheckDefinition["supportedScopes"][number][], + run: DocsCheckRunner +): ValidationCheckDefinition { + return { + id, + owner: docsCheckOwner, + adapter: docsCheckAdapter, + defaultSeverity, + supportedScopes, + defaultScopes: optInDocsDefaultScopes, + requiresGraph: false, + run + }; +} diff --git a/packages/validation-docs/src/check-results.ts b/packages/validation-docs/src/check-results.ts new file mode 100644 index 0000000..c027d1d --- /dev/null +++ b/packages/validation-docs/src/check-results.ts @@ -0,0 +1,17 @@ +import type { ValidationCheckResult } from "@the-open-engine/opcore-validation"; + +export function skippedDocsResult(message: string): ValidationCheckResult { + return { + status: "skipped", + diagnostics: [], + failureMessage: message + }; +} + +export function skippedHistoryUnavailableResult(message: string): ValidationCheckResult { + return skippedDocsResult( + message.startsWith("Git history is unavailable") + ? message + : `Git history is unavailable: ${message}` + ); +} diff --git a/packages/validation-docs/src/checks.ts b/packages/validation-docs/src/checks.ts index ae1382c..763f5d2 100644 --- a/packages/validation-docs/src/checks.ts +++ b/packages/validation-docs/src/checks.ts @@ -1,497 +1,20 @@ -import type { GraphFactEdge, GraphFactNode, RequiredContextDocPolicy, ValidationDiagnostic } from "@the-open-engine/opcore-contracts"; -import type { ValidationCheckContext, ValidationCheckDefinition, ValidationCheckResult } from "@the-open-engine/opcore-validation"; -import { countPhysicalLines, graphFactNodePath, graphFactPathFromEndpoint, splitPhysicalLines } from "@the-open-engine/opcore-validation"; -import { - defaultDocsHistoryThresholds, - defaultDocsHubCoverageThresholds, - docsCheckAdapter, - docsCheckOwner, - optInDocsDefaultScopes, - repoWideDocsValidationScopes, - supportedDocsValidationScopes -} from "./check-constants.js"; -import { - DOCS_CODE_BLOCKS_CHECK_ID, - DOCS_CONTENT_QUALITY_CHECK_ID, - DOCS_DRY_CHECK_ID, - DOCS_EXISTENCE_CHECK_ID, - DOCS_FRESHNESS_CHECK_ID, - DOCS_HUB_COVERAGE_CHECK_ID, - DOCS_LENGTH_CHECK_ID, - DOCS_RULES_WHY_CHECK_ID, - DOCS_SUBTREE_COVERAGE_CHECK_ID, - DOCS_STALENESS_CHECK_ID -} from "./check-ids.js"; -import { diagnostic, sortDiagnostics } from "./diagnostics.js"; -import { assertGitHistoryAvailable, latestCommitIso } from "./history.js"; -import { runDocsFreshnessCheck } from "./freshness.js"; -import { - isDocsPath, - materializeDocsSnapshot, - pathBasename, - type DocsDocument, - type DocsPolicyOptions -} from "./snapshot.js"; - -export interface DocsHistoryOptions { - now?: string | Date; - maxStaleDays?: number; -} - -export interface DocsHubCoverageOptions { - minFanIn?: number; - minFanOut?: number; - requireExplicitMention?: boolean; -} - -export interface DocsSubtreeCoverageOptions { - minLoc?: number; -} - -export interface CreateDocsValidationChecksOptions extends DocsPolicyOptions { - history?: DocsHistoryOptions; - hubCoverage?: DocsHubCoverageOptions; - subtreeCoverage?: DocsSubtreeCoverageOptions; -} - -type DocsCheckRunner = (context: ValidationCheckContext) => Promise | ValidationCheckResult; - -export function createDocsExistenceCheck(options: DocsPolicyOptions = {}): ValidationCheckDefinition { - return docsCheck(DOCS_EXISTENCE_CHECK_ID, "error", repoWideDocsValidationScopes, async (context) => { - const snapshot = await materializeDocsSnapshot(context, options); - const diagnostics = snapshot.requiredLocations - .filter((location) => location.found.length === 0) - .map((location) => - diagnostic({ - path: location.root === "." ? undefined : location.root, - code: "DOCS_REQUIRED_CONTEXT_DOC_MISSING", - message: `Required context doc is missing at ${location.root}: expected ${snapshot.policy.filenames.join(" or ")}.` - }) - ); - return { diagnostics }; - }); -} - -export function createDocsStalenessCheck(options: CreateDocsValidationChecksOptions = {}): ValidationCheckDefinition { - return docsCheck(DOCS_STALENESS_CHECK_ID, "warning", supportedDocsValidationScopes, async (context) => { - const skipped = await skippedHistoryResult(context, options); - if (skipped !== undefined) return skipped; - const snapshot = await materializeDocsSnapshot(context, options); - if (snapshot.docs.length === 0) return skippedDocsResult("No documentation files were selected."); - const repoRoot = context.request.repo.repoRoot; - if (repoRoot === undefined) return skippedHistoryUnavailableResult("Git history is unavailable: request.repo.repoRoot is missing."); - const now = historyNow(options.history); - const maxStaleDays = options.history?.maxStaleDays ?? defaultDocsHistoryThresholds.maxStaleDays; - const diagnostics: ValidationDiagnostic[] = []; - - for (const doc of snapshot.docs) { - const committed = latestCommitIso(repoRoot, doc.path); - if (!committed.ok) return skippedHistoryUnavailableResult(committed.message); - if (committed.value === undefined) continue; - const ageDays = elapsedDays(committed.value, now); - if (ageDays > maxStaleDays) { - diagnostics.push( - diagnostic({ - severity: "warning", - path: doc.path, - code: "DOCS_STALE", - message: `Documentation was last committed ${Math.floor(ageDays)} days ago, over the ${maxStaleDays}-day staleness threshold.` - }) - ); - } - } - return { diagnostics: sortDiagnostics(diagnostics) }; - }); -} - -export function createDocsFreshnessCheck(options: CreateDocsValidationChecksOptions = {}): ValidationCheckDefinition { - return docsCheck(DOCS_FRESHNESS_CHECK_ID, "warning", supportedDocsValidationScopes, (context) => - runDocsFreshnessCheck(context, options) - ); -} - -export function createDocsLengthCheck(options: DocsPolicyOptions = {}): ValidationCheckDefinition { - return docsCheck(DOCS_LENGTH_CHECK_ID, "error", supportedDocsValidationScopes, async (context) => { - const snapshot = await materializeDocsSnapshot(context, options); - if (snapshot.docs.length === 0) return skippedDocsResult("No documentation files were selected."); - return { - diagnostics: sortDiagnostics( - snapshot.docs.flatMap((doc) => lengthDiagnostics(doc, snapshot.policy)) - ) - }; - }); -} - -export function createDocsDryCheck(options: DocsPolicyOptions = {}): ValidationCheckDefinition { - return docsCheck(DOCS_DRY_CHECK_ID, "warning", supportedDocsValidationScopes, async (context) => { - const snapshot = await materializeDocsSnapshot(context, options); - if (snapshot.docs.length === 0) return skippedDocsResult("No documentation files were selected."); - const seen = new Map(); - const diagnostics: ValidationDiagnostic[] = []; - for (const doc of snapshot.docs) { - for (const paragraph of normalizedParagraphs(doc.content)) { - const existing = seen.get(paragraph); - if (existing === undefined) { - seen.set(paragraph, doc.path); - continue; - } - if (existing === doc.path) continue; - diagnostics.push( - diagnostic({ - severity: "warning", - path: doc.path, - code: "DOCS_DRY_DUPLICATE_PARAGRAPH", - message: `Documentation repeats a long paragraph already present in ${existing}.` - }) - ); - break; - } - } - return { diagnostics: sortDiagnostics(diagnostics) }; - }); -} - -export function createDocsContentQualityCheck(options: DocsPolicyOptions = {}): ValidationCheckDefinition { - return docsCheck(DOCS_CONTENT_QUALITY_CHECK_ID, "error", supportedDocsValidationScopes, async (context) => { - const snapshot = await materializeDocsSnapshot(context, options); - if (snapshot.docs.length === 0) return skippedDocsResult("No documentation files were selected."); - return { diagnostics: sortDiagnostics(snapshot.docs.flatMap(contentQualityDiagnostics)) }; - }); -} - -export function createDocsCodeBlocksCheck(options: DocsPolicyOptions = {}): ValidationCheckDefinition { - return docsCheck(DOCS_CODE_BLOCKS_CHECK_ID, "error", supportedDocsValidationScopes, async (context) => { - const snapshot = await materializeDocsSnapshot(context, options); - if (snapshot.docs.length === 0) return skippedDocsResult("No documentation files were selected."); - return { diagnostics: sortDiagnostics(snapshot.docs.flatMap(codeBlockDiagnostics)) }; - }); -} - -export function createDocsRulesWhyCheck(options: DocsPolicyOptions = {}): ValidationCheckDefinition { - return docsCheck(DOCS_RULES_WHY_CHECK_ID, "error", supportedDocsValidationScopes, async (context) => { - const snapshot = await materializeDocsSnapshot(context, options); - if (snapshot.docs.length === 0) return skippedDocsResult("No documentation files were selected."); - return { diagnostics: sortDiagnostics(snapshot.docs.flatMap(ruleWhyDiagnostics)) }; - }); -} - -export function createDocsHubCoverageCheck(options: CreateDocsValidationChecksOptions = {}): ValidationCheckDefinition { - return { - ...docsCheck(DOCS_HUB_COVERAGE_CHECK_ID, "warning", repoWideDocsValidationScopes, async (context) => { - const snapshot = await materializeDocsSnapshot(context, options); - if (snapshot.docs.length === 0) return skippedDocsResult("No documentation files were selected."); - const [nodeResult, importsFrom] = await Promise.all([ - context.graph.facts({ kind: "nodes", nodeKinds: ["File", "file", "Module"] }), - context.graph.importsFrom() - ]); - const hubs = hubPaths(nodeResult.nodes, importsFrom, snapshot.policy, options.hubCoverage); - const documentedHubs = hubs.filter((hub) => - !docsMentionPath(snapshot.docs, hub.path, options.hubCoverage?.requireExplicitMention === true) - ); - return { - diagnostics: documentedHubs.map((hub) => - diagnostic({ - severity: "warning", - category: "graph", - code: "DOCS_HUB_UNDOCUMENTED", - message: `Graph hub ${hub.path} has ${hub.fanIn} incoming and ${hub.fanOut} outgoing IMPORTS_FROM edges but is not mentioned in discovered context docs.` - }) - ) - }; - }), - requiresGraph: true, - graphRequirements: () => [ - { - operation: "factQuery", - selector: { - kind: "nodes", - nodeKinds: ["File", "file", "Module"] - } - }, - { - operation: "factQuery", - selector: { - kind: "edges", - edgeKinds: ["IMPORTS_FROM"] - } - } - ] - }; -} - -export function createDocsSubtreeCoverageCheck(options: CreateDocsValidationChecksOptions = {}): ValidationCheckDefinition { - return docsCheck(DOCS_SUBTREE_COVERAGE_CHECK_ID, "warning", repoWideDocsValidationScopes, async (context) => { - const snapshot = await materializeDocsSnapshot(context, options); - if (snapshot.docs.length === 0) return skippedDocsResult("No documentation files were selected."); - const minLoc = options.subtreeCoverage?.minLoc ?? 10_000; - const subtrees = await sourceSubtreeLoc(context); - return { - diagnostics: sortDiagnostics( - [...subtrees] - .filter(([, loc]) => loc >= minLoc) - .filter(([subtree]) => !docsMentionPath(snapshot.docs, subtree, true)) - .map(([subtree, loc]) => - diagnostic({ - severity: "warning", - path: subtree, - code: "DOCS_SUBTREE_UNDOCUMENTED", - message: `Source subtree ${subtree} has ${loc} lines but is not explicitly mentioned in discovered context docs.` - }) - ) - ) - }; - }); -} - -function docsCheck( - id: string, - defaultSeverity: ValidationDiagnostic["severity"], - supportedScopes: readonly ValidationCheckDefinition["supportedScopes"][number][], - run: DocsCheckRunner -): ValidationCheckDefinition { - return { - id, - owner: docsCheckOwner, - adapter: docsCheckAdapter, - defaultSeverity, - supportedScopes, - defaultScopes: optInDocsDefaultScopes, - requiresGraph: false, - run - }; -} - -async function skippedHistoryResult( - context: ValidationCheckContext, - options: DocsPolicyOptions -): Promise { - const snapshot = await materializeDocsSnapshot(context, options); - if (snapshot.hasOverlays) { - return skippedDocsResult("Documentation Git-history checks require committed state and skip when overlays are present."); - } - const repoRoot = context.request.repo.repoRoot; - if (repoRoot === undefined) { - return skippedHistoryUnavailableResult("Git history is unavailable: request.repo.repoRoot is missing."); - } - const available = assertGitHistoryAvailable(repoRoot); - return available.ok ? undefined : skippedHistoryUnavailableResult(available.message); -} - -function skippedDocsResult(message: string): ValidationCheckResult { - return { - status: "skipped", - diagnostics: [], - failureMessage: message - }; -} - -function skippedHistoryUnavailableResult(message: string): ValidationCheckResult { - return skippedDocsResult(message.startsWith("Git history is unavailable") ? message : `Git history is unavailable: ${message}`); -} - -function lengthDiagnostics(doc: DocsDocument, policy: RequiredContextDocPolicy): readonly ValidationDiagnostic[] { - const diagnostics: ValidationDiagnostic[] = []; - if (doc.content.trim().length < policy.minimumContentLength) { - diagnostics.push( - diagnostic({ - path: doc.path, - code: "DOCS_TOO_SHORT", - message: `Documentation content is shorter than the ${policy.minimumContentLength}-character require-context-doc policy minimum.` - }) - ); - } - const lines = splitPhysicalLines(doc.content); - const totalLines = countPhysicalLines(doc.content); - if (policy.maxLines !== undefined && totalLines > policy.maxLines) { - diagnostics.push( - diagnostic({ - path: doc.path, - code: "DOCS_TOO_LONG", - message: `Documentation has ${totalLines} lines; max is ${policy.maxLines}.` - }) - ); - } - if (policy.maxSectionLines !== undefined) { - const section = longestSectionLineCount(lines); - if (section > policy.maxSectionLines) { - diagnostics.push( - diagnostic({ - path: doc.path, - code: "DOCS_SECTION_TOO_LONG", - message: `Documentation section has ${section} lines; max is ${policy.maxSectionLines}.` - }) - ); - } - } - return diagnostics; -} - -function historyNow(options: DocsHistoryOptions | undefined): Date { - if (options?.now instanceof Date) return options.now; - if (typeof options?.now === "string") return new Date(options.now); - return new Date(); -} - -function elapsedDays(iso: string, now: Date): number { - return (now.getTime() - Date.parse(iso)) / (24 * 60 * 60 * 1000); -} - -function normalizedParagraphs(content: string): readonly string[] { - return content - .split(/\n\s*\n/u) - .map((paragraph) => paragraph.replace(/\s+/gu, " ").trim()) - .filter((paragraph) => paragraph.length >= 80 && !paragraph.startsWith("```")); -} - -function longestSectionLineCount(lines: readonly string[]): number { - let current = 0; - let longest = 0; - for (const line of lines) { - if (/^\s*#{1,6}\s+/u.test(line)) { - longest = Math.max(longest, current); - current = 1; - continue; - } - current += 1; - } - return Math.max(longest, current); -} - -function contentQualityDiagnostics(doc: DocsDocument): readonly ValidationDiagnostic[] { - const diagnostics: ValidationDiagnostic[] = []; - const lines = doc.content.split(/\r?\n/u); - lines.forEach((line, index) => { - if (/\b(TODO|TBD|FIXME|placeholder|lorem ipsum)\b/iu.test(line)) { - diagnostics.push( - diagnostic({ - path: doc.path, - code: "DOCS_CONTENT_PLACEHOLDER", - message: `Documentation contains placeholder text on line ${index + 1}.` - }) - ); - } - if (/^(<{7}|={7}|>{7})/u.test(line)) { - diagnostics.push( - diagnostic({ - path: doc.path, - code: "DOCS_CONTENT_CONFLICT_MARKER", - message: `Documentation contains an unresolved conflict marker on line ${index + 1}.` - }) - ); - } - }); - return diagnostics; -} - -function codeBlockDiagnostics(doc: DocsDocument): readonly ValidationDiagnostic[] { - if (!isMarkdownLike(doc.path)) return []; - let openFenceLine: number | undefined; - const diagnostics: ValidationDiagnostic[] = []; - doc.content.split(/\r?\n/u).forEach((line, index) => { - if (!line.trimStart().startsWith("```")) return; - if (openFenceLine === undefined) { - openFenceLine = index + 1; - } else { - openFenceLine = undefined; - } - }); - if (openFenceLine !== undefined) { - diagnostics.push( - diagnostic({ - path: doc.path, - code: "DOCS_CODE_BLOCK_UNCLOSED", - message: `Markdown code block opened on line ${openFenceLine} is not closed.` - }) - ); - } - return diagnostics; -} - -function ruleWhyDiagnostics(doc: DocsDocument): readonly ValidationDiagnostic[] { - if (!doc.requiredContext) return []; - const diagnostics: ValidationDiagnostic[] = []; - doc.content.split(/\r?\n/u).forEach((line, index) => { - const trimmed = line.trim(); - if (!/^(?:[-*]\s*)?(ALWAYS|NEVER|MUST|SHOULD)\b/u.test(trimmed)) return; - if (/\bWHY:\b/u.test(trimmed) || /\bbecause\b/iu.test(trimmed)) return; - diagnostics.push( - diagnostic({ - path: doc.path, - code: "DOCS_RULE_WITHOUT_WHY", - message: `Context-doc rule on line ${index + 1} is missing a WHY rationale.` - }) - ); - }); - return diagnostics; -} - -function hubPaths( - nodes: readonly GraphFactNode[], - edges: readonly GraphFactEdge[], - policy: RequiredContextDocPolicy, - options: DocsHubCoverageOptions | undefined -): readonly { path: string; fanIn: number; fanOut: number }[] { - const nodePaths = new Set(nodes.map(nodePath).filter((path): path is string => path !== undefined && !isDocsPath(path, policy))); - const incoming = new Map(); - const outgoing = new Map(); - for (const edge of edges) { - if (edge.kind !== "IMPORTS_FROM") continue; - const sourcePath = endpointPath(edge.from); - const targetPath = endpointPath(edge.to); - if (sourcePath !== undefined && nodePaths.has(sourcePath)) { - outgoing.set(sourcePath, (outgoing.get(sourcePath) ?? 0) + 1); - } - if (targetPath !== undefined && nodePaths.has(targetPath)) { - incoming.set(targetPath, (incoming.get(targetPath) ?? 0) + 1); - } - } - const minFanIn = options?.minFanIn ?? defaultDocsHubCoverageThresholds.minFanIn; - const minFanOut = options?.minFanOut; - return [...nodePaths] - .map((path) => ({ path, fanIn: incoming.get(path) ?? 0, fanOut: outgoing.get(path) ?? 0 })) - .filter((hub) => hub.fanIn >= minFanIn || (minFanOut !== undefined && hub.fanOut >= minFanOut)) - .sort((left, right) => right.fanIn - left.fanIn || right.fanOut - left.fanOut || left.path.localeCompare(right.path)); -} - -function docsMentionPath(docs: readonly DocsDocument[], path: string, requireExplicit = false): boolean { - const basename = pathBasename(path).toLowerCase(); - const normalizedPath = path.toLowerCase(); - return docs.some((doc) => { - const content = doc.content.toLowerCase(); - return content.includes(normalizedPath) || (!requireExplicit && content.includes(basename)); - }); -} - -async function sourceSubtreeLoc(context: ValidationCheckContext): Promise> { - const totals = new Map(); - const visibleFiles = await context.fileView.listVisibleFiles(); - for (const path of visibleFiles) { - if (isDocsPath(path) || !isSourceLikePath(path)) continue; - const result = await context.fileView.readAfter(path); - if (result.status !== "found") continue; - const subtree = firstPathSegment(path); - totals.set(subtree, (totals.get(subtree) ?? 0) + countPhysicalLines(result.content)); - } - return totals; -} - -function isSourceLikePath(path: string): boolean { - return /\.(?:[cm]?[jt]sx?|pyi?|rs|toml)$/iu.test(path); -} - -function firstPathSegment(path: string): string { - return path.split("/")[0] ?? path; -} - -function nodePath(node: GraphFactNode): string | undefined { - return graphFactNodePath(node); -} - -function endpointPath(endpoint: string): string | undefined { - return graphFactPathFromEndpoint(endpoint); -} - -function isMarkdownLike(path: string): boolean { - const lower = path.toLowerCase(); - return lower.endsWith(".md") || lower.endsWith(".mdx") || lower.endsWith(".rst") || lower.endsWith(".adoc"); -} +export { createDocsExistenceCheck } from "./existence-check.js"; +export { createDocsStalenessCheck } from "./staleness-check.js"; +export { createDocsFreshnessCheck } from "./freshness.js"; +export { + createDocsCodeBlocksCheck, + createDocsContentQualityCheck, + createDocsDryCheck, + createDocsLengthCheck, + createDocsRulesWhyCheck +} from "./content-checks.js"; +export { + createDocsHubCoverageCheck, + createDocsSubtreeCoverageCheck +} from "./coverage-checks.js"; +export type { + CreateDocsValidationChecksOptions, + DocsHistoryOptions, + DocsHubCoverageOptions, + DocsSubtreeCoverageOptions +} from "./options.js"; diff --git a/packages/validation-docs/src/code-blocks-check.ts b/packages/validation-docs/src/code-blocks-check.ts new file mode 100644 index 0000000..b0ac2c0 --- /dev/null +++ b/packages/validation-docs/src/code-blocks-check.ts @@ -0,0 +1,49 @@ +import type { ValidationDiagnostic } from "@the-open-engine/opcore-contracts"; +import type { ValidationCheckDefinition } from "@the-open-engine/opcore-validation"; +import { DOCS_CODE_BLOCKS_CHECK_ID } from "./check-ids.js"; +import { diagnostic } from "./diagnostics.js"; +import { createDocsDocumentCheck } from "./document-check.js"; +import { + type DocsDocument, + type DocsPolicyOptions +} from "./snapshot.js"; + +export function createDocsCodeBlocksCheck( + options: DocsPolicyOptions = {} +): ValidationCheckDefinition { + return createDocsDocumentCheck( + DOCS_CODE_BLOCKS_CHECK_ID, + "error", + options, + codeBlockDiagnostics + ); +} + +function codeBlockDiagnostics( + doc: DocsDocument +): readonly ValidationDiagnostic[] { + if (!isMarkdownLike(doc.path)) return []; + let openFenceLine: number | undefined; + doc.content.split(/\r?\n/u).forEach((line, index) => { + if (!line.trimStart().startsWith("```")) return; + openFenceLine = openFenceLine === undefined ? index + 1 : undefined; + }); + return openFenceLine === undefined + ? [] + : [ + diagnostic({ + path: doc.path, + code: "DOCS_CODE_BLOCK_UNCLOSED", + message: + `Markdown code block opened on line ${openFenceLine} ` + + "is not closed." + }) + ]; +} + +function isMarkdownLike(path: string): boolean { + const lower = path.toLowerCase(); + return [".md", ".mdx", ".rst", ".adoc"].some((extension) => + lower.endsWith(extension) + ); +} diff --git a/packages/validation-docs/src/content-checks.ts b/packages/validation-docs/src/content-checks.ts new file mode 100644 index 0000000..cfb5e8c --- /dev/null +++ b/packages/validation-docs/src/content-checks.ts @@ -0,0 +1,5 @@ +export { createDocsCodeBlocksCheck } from "./code-blocks-check.js"; +export { createDocsContentQualityCheck } from "./content-quality-check.js"; +export { createDocsDryCheck } from "./dry-check.js"; +export { createDocsLengthCheck } from "./length-check.js"; +export { createDocsRulesWhyCheck } from "./rules-why-check.js"; diff --git a/packages/validation-docs/src/content-quality-check.ts b/packages/validation-docs/src/content-quality-check.ts new file mode 100644 index 0000000..972fb28 --- /dev/null +++ b/packages/validation-docs/src/content-quality-check.ts @@ -0,0 +1,49 @@ +import type { ValidationDiagnostic } from "@the-open-engine/opcore-contracts"; +import type { ValidationCheckDefinition } from "@the-open-engine/opcore-validation"; +import { DOCS_CONTENT_QUALITY_CHECK_ID } from "./check-ids.js"; +import { diagnostic } from "./diagnostics.js"; +import { createDocsDocumentCheck } from "./document-check.js"; +import { + type DocsDocument, + type DocsPolicyOptions +} from "./snapshot.js"; + +export function createDocsContentQualityCheck( + options: DocsPolicyOptions = {} +): ValidationCheckDefinition { + return createDocsDocumentCheck( + DOCS_CONTENT_QUALITY_CHECK_ID, + "error", + options, + contentQualityDiagnostics + ); +} + +function contentQualityDiagnostics( + doc: DocsDocument +): readonly ValidationDiagnostic[] { + const diagnostics: ValidationDiagnostic[] = []; + doc.content.split(/\r?\n/u).forEach((line, index) => { + if (/\b(TODO|TBD|FIXME|placeholder|lorem ipsum)\b/iu.test(line)) { + diagnostics.push( + diagnostic({ + path: doc.path, + code: "DOCS_CONTENT_PLACEHOLDER", + message: `Documentation contains placeholder text on line ${index + 1}.` + }) + ); + } + if (/^(<{7}|={7}|>{7})/u.test(line)) { + diagnostics.push( + diagnostic({ + path: doc.path, + code: "DOCS_CONTENT_CONFLICT_MARKER", + message: + "Documentation contains an unresolved conflict marker on line " + + `${index + 1}.` + }) + ); + } + }); + return diagnostics; +} diff --git a/packages/validation-docs/src/coverage-checks.ts b/packages/validation-docs/src/coverage-checks.ts new file mode 100644 index 0000000..6f8716a --- /dev/null +++ b/packages/validation-docs/src/coverage-checks.ts @@ -0,0 +1,220 @@ +import type { + GraphFactEdge, + GraphFactNode, + RequiredContextDocPolicy +} from "@the-open-engine/opcore-contracts"; +import type { + ValidationCheckContext, + ValidationCheckDefinition +} from "@the-open-engine/opcore-validation"; +import { + countPhysicalLines, + graphFactNodePath, + graphFactPathFromEndpoint +} from "@the-open-engine/opcore-validation"; +import { + defaultDocsHubCoverageThresholds, + repoWideDocsValidationScopes +} from "./check-constants.js"; +import { + DOCS_HUB_COVERAGE_CHECK_ID, + DOCS_SUBTREE_COVERAGE_CHECK_ID +} from "./check-ids.js"; +import { docsCheck } from "./check-definition.js"; +import { withSelectedDocs } from "./document-check.js"; +import { diagnostic } from "./diagnostics.js"; +import type { + CreateDocsValidationChecksOptions, + DocsHubCoverageOptions +} from "./options.js"; +import { + isDocsPath, + pathBasename, + type DocsDocument +} from "./snapshot.js"; + +export function createDocsHubCoverageCheck( + options: CreateDocsValidationChecksOptions = {} +): ValidationCheckDefinition { + return { + ...docsCheck( + DOCS_HUB_COVERAGE_CHECK_ID, + "warning", + repoWideDocsValidationScopes, + async (context) => { + return withSelectedDocs(context, options, async (snapshot) => { + const [nodeResult, importsFrom] = await Promise.all([ + context.graph.facts({ + kind: "nodes", + nodeKinds: ["File", "file", "Module"] + }), + context.graph.importsFrom() + ]); + const hubs = hubPaths( + nodeResult.nodes, + importsFrom, + snapshot.policy, + options.hubCoverage + ); + return hubs + .filter( + (hub) => + !docsMentionPath( + snapshot.docs, + hub.path, + options.hubCoverage?.requireExplicitMention === true + ) + ) + .map((hub) => + diagnostic({ + severity: "warning", + category: "graph", + code: "DOCS_HUB_UNDOCUMENTED", + message: + `Graph hub ${hub.path} has ${hub.fanIn} incoming and ` + + `${hub.fanOut} outgoing IMPORTS_FROM edges but is not ` + + "mentioned in discovered context docs." + }) + ); + }); + } + ), + requiresGraph: true, + graphRequirements: () => [ + { + operation: "factQuery", + selector: { + kind: "nodes", + nodeKinds: ["File", "file", "Module"] + } + }, + { + operation: "factQuery", + selector: { + kind: "edges", + edgeKinds: ["IMPORTS_FROM"] + } + } + ] + }; +} + +export function createDocsSubtreeCoverageCheck( + options: CreateDocsValidationChecksOptions = {} +): ValidationCheckDefinition { + return docsCheck( + DOCS_SUBTREE_COVERAGE_CHECK_ID, + "warning", + repoWideDocsValidationScopes, + async (context) => { + return withSelectedDocs(context, options, async (snapshot) => { + const configuredMinLoc = options.subtreeCoverage?.minLoc; + const minLoc = configuredMinLoc === undefined ? 10_000 : configuredMinLoc; + const subtrees = await sourceSubtreeLoc(context); + return [...subtrees] + .filter(([, loc]) => loc >= minLoc) + .filter(([subtree]) => !docsMentionPath(snapshot.docs, subtree, true)) + .map(([subtree, loc]) => + diagnostic({ + severity: "warning", + path: subtree, + code: "DOCS_SUBTREE_UNDOCUMENTED", + message: + `Source subtree ${subtree} has ${loc} lines but is not ` + + "explicitly mentioned in discovered context docs." + }) + ); + }); + } + ); +} + +function hubPaths( + nodes: readonly GraphFactNode[], + edges: readonly GraphFactEdge[], + policy: RequiredContextDocPolicy, + options: DocsHubCoverageOptions | undefined +): readonly { path: string; fanIn: number; fanOut: number }[] { + const nodePaths = new Set( + nodes + .map(graphFactNodePath) + .filter( + (path): path is string => + path !== undefined && !isDocsPath(path, policy) + ) + ); + const incoming = new Map(); + const outgoing = new Map(); + for (const edge of edges) { + if (edge.kind !== "IMPORTS_FROM") continue; + incrementPathCount(outgoing, graphFactPathFromEndpoint(edge.from), nodePaths); + incrementPathCount(incoming, graphFactPathFromEndpoint(edge.to), nodePaths); + } + const minFanIn = + options?.minFanIn ?? defaultDocsHubCoverageThresholds.minFanIn; + const minFanOut = options?.minFanOut; + return [...nodePaths] + .map((path) => ({ + path, + fanIn: incoming.get(path) ?? 0, + fanOut: outgoing.get(path) ?? 0 + })) + .filter( + (hub) => + hub.fanIn >= minFanIn || + (minFanOut !== undefined && hub.fanOut >= minFanOut) + ) + .sort( + (left, right) => + right.fanIn - left.fanIn || + right.fanOut - left.fanOut || + left.path.localeCompare(right.path) + ); +} + +function incrementPathCount( + counts: Map, + path: string | undefined, + allowed: ReadonlySet +): void { + if (path === undefined || !allowed.has(path)) return; + counts.set(path, (counts.get(path) ?? 0) + 1); +} + +function docsMentionPath( + docs: readonly DocsDocument[], + path: string, + requireExplicit = false +): boolean { + const basename = pathBasename(path).toLowerCase(); + const normalizedPath = path.toLowerCase(); + return docs.some((doc) => { + const content = doc.content.toLowerCase(); + return ( + content.includes(normalizedPath) || + (!requireExplicit && content.includes(basename)) + ); + }); +} + +async function sourceSubtreeLoc( + context: ValidationCheckContext +): Promise> { + const totals = new Map(); + const visibleFiles = await context.fileView.listVisibleFiles(); + for (const path of visibleFiles) { + if (isDocsPath(path) || !isSourceLikePath(path)) continue; + const result = await context.fileView.readAfter(path); + if (result.status !== "found") continue; + const subtree = path.split("/")[0] ?? path; + totals.set( + subtree, + (totals.get(subtree) ?? 0) + countPhysicalLines(result.content) + ); + } + return totals; +} + +function isSourceLikePath(path: string): boolean { + return /\.(?:[cm]?[jt]sx?|pyi?|rs|toml)$/iu.test(path); +} diff --git a/packages/validation-docs/src/document-check.ts b/packages/validation-docs/src/document-check.ts new file mode 100644 index 0000000..8e40d0c --- /dev/null +++ b/packages/validation-docs/src/document-check.ts @@ -0,0 +1,57 @@ +import type { ValidationDiagnostic } from "@the-open-engine/opcore-contracts"; +import type { + ValidationCheckContext, + ValidationCheckDefinition, + ValidationCheckResult +} from "@the-open-engine/opcore-validation"; +import { supportedDocsValidationScopes } from "./check-constants.js"; +import { docsCheck } from "./check-definition.js"; +import { skippedDocsResult } from "./check-results.js"; +import { sortDiagnostics } from "./diagnostics.js"; +import { + materializeDocsSnapshot, + type DocsDocument, + type DocsPolicyOptions, + type DocsSnapshot +} from "./snapshot.js"; + +type SnapshotCollector = ( + snapshot: DocsSnapshot +) => Promise | readonly ValidationDiagnostic[]; + +export function createDocsDocumentCheck( + id: string, + severity: ValidationDiagnostic["severity"], + options: DocsPolicyOptions, + collect: (doc: DocsDocument, snapshot: DocsSnapshot) => readonly ValidationDiagnostic[] +): ValidationCheckDefinition { + return createDocsSnapshotCheck(id, severity, options, (snapshot) => + snapshot.docs.flatMap((doc) => collect(doc, snapshot)) + ); +} + +export function createDocsSnapshotCheck( + id: string, + severity: ValidationDiagnostic["severity"], + options: DocsPolicyOptions, + collect: SnapshotCollector +): ValidationCheckDefinition { + return docsCheck( + id, + severity, + supportedDocsValidationScopes, + (context) => withSelectedDocs(context, options, collect) + ); +} + +export async function withSelectedDocs( + context: ValidationCheckContext, + options: DocsPolicyOptions, + collect: SnapshotCollector +): Promise { + const snapshot = await materializeDocsSnapshot(context, options); + if (snapshot.docs.length === 0) { + return skippedDocsResult("No documentation files were selected."); + } + return { diagnostics: sortDiagnostics(await collect(snapshot)) }; +} diff --git a/packages/validation-docs/src/dry-check.ts b/packages/validation-docs/src/dry-check.ts new file mode 100644 index 0000000..528507a --- /dev/null +++ b/packages/validation-docs/src/dry-check.ts @@ -0,0 +1,64 @@ +import type { ValidationDiagnostic } from "@the-open-engine/opcore-contracts"; +import type { ValidationCheckDefinition } from "@the-open-engine/opcore-validation"; +import { DOCS_DRY_CHECK_ID } from "./check-ids.js"; +import { diagnostic } from "./diagnostics.js"; +import { createDocsSnapshotCheck } from "./document-check.js"; +import { + type DocsDocument, + type DocsPolicyOptions +} from "./snapshot.js"; + +export function createDocsDryCheck( + options: DocsPolicyOptions = {} +): ValidationCheckDefinition { + return createDocsSnapshotCheck( + DOCS_DRY_CHECK_ID, + "warning", + options, + (snapshot) => duplicateParagraphDiagnostics(snapshot.docs) + ); +} + +function duplicateParagraphDiagnostics( + docs: readonly DocsDocument[] +): readonly ValidationDiagnostic[] { + const seen = new Map(); + const diagnostics: ValidationDiagnostic[] = []; + for (const doc of docs) { + const duplicate = firstDuplicateParagraph(doc, seen); + if (duplicate === undefined) continue; + diagnostics.push( + diagnostic({ + severity: "warning", + path: doc.path, + code: "DOCS_DRY_DUPLICATE_PARAGRAPH", + message: + "Documentation repeats a long paragraph already present in " + + `${duplicate}.` + }) + ); + } + return diagnostics; +} + +function firstDuplicateParagraph( + doc: DocsDocument, + seen: Map +): string | undefined { + for (const paragraph of normalizedParagraphs(doc.content)) { + const existing = seen.get(paragraph); + if (existing !== undefined && existing !== doc.path) return existing; + seen.set(paragraph, doc.path); + } + return undefined; +} + +function normalizedParagraphs(content: string): readonly string[] { + return content + .split(/\n\s*\n/u) + .map((paragraph) => paragraph.replace(/\s+/gu, " ").trim()) + .filter( + (paragraph) => + paragraph.length >= 80 && !paragraph.startsWith("```") + ); +} diff --git a/packages/validation-docs/src/existence-check.ts b/packages/validation-docs/src/existence-check.ts new file mode 100644 index 0000000..228c852 --- /dev/null +++ b/packages/validation-docs/src/existence-check.ts @@ -0,0 +1,34 @@ +import type { ValidationCheckDefinition } from "@the-open-engine/opcore-validation"; +import { repoWideDocsValidationScopes } from "./check-constants.js"; +import { DOCS_EXISTENCE_CHECK_ID } from "./check-ids.js"; +import { docsCheck } from "./check-definition.js"; +import { diagnostic } from "./diagnostics.js"; +import { + materializeDocsSnapshot, + type DocsPolicyOptions +} from "./snapshot.js"; + +export function createDocsExistenceCheck( + options: DocsPolicyOptions = {} +): ValidationCheckDefinition { + return docsCheck( + DOCS_EXISTENCE_CHECK_ID, + "error", + repoWideDocsValidationScopes, + async (context) => { + const snapshot = await materializeDocsSnapshot(context, options); + const diagnostics = snapshot.requiredLocations + .filter((location) => location.found.length === 0) + .map((location) => + diagnostic({ + path: location.root === "." ? undefined : location.root, + code: "DOCS_REQUIRED_CONTEXT_DOC_MISSING", + message: + `Required context doc is missing at ${location.root}: expected ` + + `${snapshot.policy.filenames.join(" or ")}.` + }) + ); + return { diagnostics }; + } + ); +} diff --git a/packages/validation-docs/src/freshness.ts b/packages/validation-docs/src/freshness.ts index 47d4c17..c9b50fb 100644 --- a/packages/validation-docs/src/freshness.ts +++ b/packages/validation-docs/src/freshness.ts @@ -1,7 +1,19 @@ import type { ValidationDiagnostic } from "@the-open-engine/opcore-contracts"; -import type { ValidationCheckContext, ValidationCheckResult } from "@the-open-engine/opcore-validation"; +import type { + ValidationCheckContext, + ValidationCheckDefinition, + ValidationCheckResult +} from "@the-open-engine/opcore-validation"; +import { supportedDocsValidationScopes } from "./check-constants.js"; +import { DOCS_FRESHNESS_CHECK_ID } from "./check-ids.js"; +import { docsCheck } from "./check-definition.js"; +import { + skippedDocsResult, + skippedHistoryUnavailableResult +} from "./check-results.js"; import { diagnostic, sortDiagnostics } from "./diagnostics.js"; import { assertGitHistoryAvailable, latestCommitIso } from "./history.js"; +import type { CreateDocsValidationChecksOptions } from "./options.js"; import { isDocsPath, materializeDocsSnapshot, @@ -16,6 +28,17 @@ type FreshnessHistoryOutcome = | { status: "skipped"; result: ValidationCheckResult }; type LatestCommittedPath = { path: string; iso: string }; +export function createDocsFreshnessCheck( + options: CreateDocsValidationChecksOptions = {} +): ValidationCheckDefinition { + return docsCheck( + DOCS_FRESHNESS_CHECK_ID, + "warning", + supportedDocsValidationScopes, + (context) => runDocsFreshnessCheck(context, options) + ); +} + export async function runDocsFreshnessCheck( context: ValidationCheckContext, options: DocsPolicyOptions = {} @@ -116,18 +139,6 @@ function freshnessSkippedOutcome(message: string): FreshnessHistoryOutcome { return { status: "skipped", result: skippedHistoryUnavailableResult(message) }; } -function skippedDocsResult(message: string): ValidationCheckResult { - return { - status: "skipped", - diagnostics: [], - failureMessage: message - }; -} - -function skippedHistoryUnavailableResult(message: string): ValidationCheckResult { - return skippedDocsResult(message.startsWith("Git history is unavailable") ? message : `Git history is unavailable: ${message}`); -} - function latestCommittedPath( repoRoot: string, paths: readonly string[] diff --git a/packages/validation-docs/src/length-check.ts b/packages/validation-docs/src/length-check.ts new file mode 100644 index 0000000..509a7b5 --- /dev/null +++ b/packages/validation-docs/src/length-check.ts @@ -0,0 +1,86 @@ +import type { + RequiredContextDocPolicy, + ValidationDiagnostic +} from "@the-open-engine/opcore-contracts"; +import type { ValidationCheckDefinition } from "@the-open-engine/opcore-validation"; +import { + countPhysicalLines, + splitPhysicalLines +} from "@the-open-engine/opcore-validation"; +import { DOCS_LENGTH_CHECK_ID } from "./check-ids.js"; +import { diagnostic } from "./diagnostics.js"; +import { createDocsDocumentCheck } from "./document-check.js"; +import { + type DocsDocument, + type DocsPolicyOptions +} from "./snapshot.js"; + +export function createDocsLengthCheck( + options: DocsPolicyOptions = {} +): ValidationCheckDefinition { + return createDocsDocumentCheck( + DOCS_LENGTH_CHECK_ID, + "error", + options, + (doc, snapshot) => lengthDiagnostics(doc, snapshot.policy) + ); +} + +function lengthDiagnostics( + doc: DocsDocument, + policy: RequiredContextDocPolicy +): readonly ValidationDiagnostic[] { + const diagnostics: ValidationDiagnostic[] = []; + if (doc.content.trim().length < policy.minimumContentLength) { + diagnostics.push( + diagnostic({ + path: doc.path, + code: "DOCS_TOO_SHORT", + message: + "Documentation content is shorter than the " + + `${policy.minimumContentLength}-character require-context-doc policy minimum.` + }) + ); + } + const lines = splitPhysicalLines(doc.content); + const totalLines = countPhysicalLines(doc.content); + if (policy.maxLines !== undefined && totalLines > policy.maxLines) { + diagnostics.push( + diagnostic({ + path: doc.path, + code: "DOCS_TOO_LONG", + message: `Documentation has ${totalLines} lines; max is ${policy.maxLines}.` + }) + ); + } + const sectionLines = longestSectionLineCount(lines); + if ( + policy.maxSectionLines !== undefined && + sectionLines > policy.maxSectionLines + ) { + diagnostics.push( + diagnostic({ + path: doc.path, + code: "DOCS_SECTION_TOO_LONG", + message: + `Documentation section has ${sectionLines} lines; ` + + `max is ${policy.maxSectionLines}.` + }) + ); + } + return diagnostics; +} + +function longestSectionLineCount(lines: readonly string[]): number { + let current = 0; + let longest = 0; + for (const line of lines) { + if (/^\s*#{1,6}\s+/u.test(line)) { + longest = Math.max(longest, current); + current = 1; + } else { + current += 1; + } + } + return Math.max(longest, current); +} diff --git a/packages/validation-docs/src/options.ts b/packages/validation-docs/src/options.ts new file mode 100644 index 0000000..cd38a1c --- /dev/null +++ b/packages/validation-docs/src/options.ts @@ -0,0 +1,22 @@ +import type { DocsPolicyOptions } from "./snapshot.js"; + +export interface DocsHistoryOptions { + now?: string | Date; + maxStaleDays?: number; +} + +export interface DocsHubCoverageOptions { + minFanIn?: number; + minFanOut?: number; + requireExplicitMention?: boolean; +} + +export interface DocsSubtreeCoverageOptions { + minLoc?: number; +} + +export interface CreateDocsValidationChecksOptions extends DocsPolicyOptions { + history?: DocsHistoryOptions; + hubCoverage?: DocsHubCoverageOptions; + subtreeCoverage?: DocsSubtreeCoverageOptions; +} diff --git a/packages/validation-docs/src/rules-why-check.ts b/packages/validation-docs/src/rules-why-check.ts new file mode 100644 index 0000000..b943fac --- /dev/null +++ b/packages/validation-docs/src/rules-why-check.ts @@ -0,0 +1,42 @@ +import type { ValidationDiagnostic } from "@the-open-engine/opcore-contracts"; +import type { ValidationCheckDefinition } from "@the-open-engine/opcore-validation"; +import { DOCS_RULES_WHY_CHECK_ID } from "./check-ids.js"; +import { diagnostic } from "./diagnostics.js"; +import { createDocsDocumentCheck } from "./document-check.js"; +import { + type DocsDocument, + type DocsPolicyOptions +} from "./snapshot.js"; + +export function createDocsRulesWhyCheck( + options: DocsPolicyOptions = {} +): ValidationCheckDefinition { + return createDocsDocumentCheck( + DOCS_RULES_WHY_CHECK_ID, + "error", + options, + ruleWhyDiagnostics + ); +} + +function ruleWhyDiagnostics( + doc: DocsDocument +): readonly ValidationDiagnostic[] { + if (!doc.requiredContext) return []; + const diagnostics: ValidationDiagnostic[] = []; + doc.content.split(/\r?\n/u).forEach((line, index) => { + const trimmed = line.trim(); + if (!/^(?:[-*]\s*)?(ALWAYS|NEVER|MUST|SHOULD)\b/u.test(trimmed)) return; + if (/\bWHY:/u.test(trimmed) || /\bbecause\b/iu.test(trimmed)) return; + diagnostics.push( + diagnostic({ + path: doc.path, + code: "DOCS_RULE_WITHOUT_WHY", + message: + `Context-doc rule on line ${index + 1} ` + + "is missing a WHY rationale." + }) + ); + }); + return diagnostics; +} diff --git a/packages/validation-docs/src/staleness-check.ts b/packages/validation-docs/src/staleness-check.ts new file mode 100644 index 0000000..fc6d31a --- /dev/null +++ b/packages/validation-docs/src/staleness-check.ts @@ -0,0 +1,109 @@ +import type { ValidationDiagnostic } from "@the-open-engine/opcore-contracts"; +import type { + ValidationCheckContext, + ValidationCheckDefinition, + ValidationCheckResult +} from "@the-open-engine/opcore-validation"; +import { + defaultDocsHistoryThresholds, + supportedDocsValidationScopes +} from "./check-constants.js"; +import { DOCS_STALENESS_CHECK_ID } from "./check-ids.js"; +import { docsCheck } from "./check-definition.js"; +import { + skippedDocsResult, + skippedHistoryUnavailableResult +} from "./check-results.js"; +import { diagnostic, sortDiagnostics } from "./diagnostics.js"; +import { assertGitHistoryAvailable, latestCommitIso } from "./history.js"; +import type { + CreateDocsValidationChecksOptions, + DocsHistoryOptions +} from "./options.js"; +import { materializeDocsSnapshot } from "./snapshot.js"; + +export function createDocsStalenessCheck( + options: CreateDocsValidationChecksOptions = {} +): ValidationCheckDefinition { + return docsCheck( + DOCS_STALENESS_CHECK_ID, + "warning", + supportedDocsValidationScopes, + async (context) => runStalenessCheck(context, options) + ); +} + +async function runStalenessCheck( + context: ValidationCheckContext, + options: CreateDocsValidationChecksOptions +): Promise { + const skipped = await skippedHistoryResult(context, options); + if (skipped !== undefined) return skipped; + const snapshot = await materializeDocsSnapshot(context, options); + if (snapshot.docs.length === 0) { + return skippedDocsResult("No documentation files were selected."); + } + const repoRoot = context.request.repo.repoRoot; + if (repoRoot === undefined) { + return skippedHistoryUnavailableResult( + "Git history is unavailable: request.repo.repoRoot is missing." + ); + } + const now = historyNow(options.history); + const maxStaleDays = + options.history?.maxStaleDays ?? + defaultDocsHistoryThresholds.maxStaleDays; + const diagnostics: ValidationDiagnostic[] = []; + for (const doc of snapshot.docs) { + const committed = latestCommitIso(repoRoot, doc.path); + if (!committed.ok) { + return skippedHistoryUnavailableResult(committed.message); + } + if (committed.value === undefined) continue; + const ageDays = elapsedDays(committed.value, now); + if (ageDays <= maxStaleDays) continue; + diagnostics.push( + diagnostic({ + severity: "warning", + path: doc.path, + code: "DOCS_STALE", + message: + `Documentation was last committed ${Math.floor(ageDays)} days ago, ` + + `over the ${maxStaleDays}-day staleness threshold.` + }) + ); + } + return { diagnostics: sortDiagnostics(diagnostics) }; +} + +async function skippedHistoryResult( + context: ValidationCheckContext, + options: CreateDocsValidationChecksOptions +): Promise { + const snapshot = await materializeDocsSnapshot(context, options); + if (snapshot.hasOverlays) { + return skippedDocsResult( + "Documentation Git-history checks require committed state and skip when overlays are present." + ); + } + const repoRoot = context.request.repo.repoRoot; + if (repoRoot === undefined) { + return skippedHistoryUnavailableResult( + "Git history is unavailable: request.repo.repoRoot is missing." + ); + } + const available = assertGitHistoryAvailable(repoRoot); + return available.ok + ? undefined + : skippedHistoryUnavailableResult(available.message); +} + +function historyNow(options: DocsHistoryOptions | undefined): Date { + if (options?.now instanceof Date) return options.now; + if (typeof options?.now === "string") return new Date(options.now); + return new Date(); +} + +function elapsedDays(iso: string, now: Date): number { + return (now.getTime() - Date.parse(iso)) / (24 * 60 * 60 * 1000); +} diff --git a/packages/validation-typescript/src/dead-code-roots.ts b/packages/validation-typescript/src/dead-code-roots.ts index 21c0105..9de29b9 100644 --- a/packages/validation-typescript/src/dead-code-roots.ts +++ b/packages/validation-typescript/src/dead-code-roots.ts @@ -9,6 +9,7 @@ import { import ts from "typescript"; import type { TypeScriptDeadCodeOptions } from "./dead-code-entrypoints.js"; import { isTypeScriptSourcePath, type TypeScriptMaterializedSourceSet } from "./source-files.js"; +import { isConventionalTypeScriptTestPath } from "./test-paths.js"; interface TypeScriptOutputMapping { readonly rootDir: string; @@ -19,7 +20,6 @@ interface PackageEntrypointResolution { readonly context: ValidationCheckContext; readonly packageRoot: string; readonly target: string; - readonly sourcePaths: ReadonlySet; readonly outputMapping: TypeScriptOutputMapping | undefined; } @@ -31,7 +31,7 @@ export async function discoverTypeScriptDeadCodeRoots( ): Promise { if (options.entrypoints !== undefined) return []; const sourcePaths = knownSourcePaths(sourceSet, nodes); - const testRoots = sourcePaths.filter((path) => isConventionalTestPath(path)); + const testRoots = sourcePaths.filter((path) => isConventionalTypeScriptTestPath(path)); const graphTestRoots = nodes.filter(isGraphTestNode).map(graphFactNodePath).filter(isDefined); const packageRoots = await discoverPackageEntrypoints(context, sourcePaths); return uniqueSortedStrings([...testRoots, ...graphTestRoots, ...packageRoots]); @@ -51,7 +51,6 @@ async function discoverPackageEntrypoints( context: ValidationCheckContext, sourcePaths: readonly string[] ): Promise { - const sourcePathSet = new Set(sourcePaths); const entrypoints: string[] = []; for (const packageRoot of candidatePackageRoots(sourcePaths)) { const manifest = await readPackageManifest(context, packageRoot); @@ -62,7 +61,6 @@ async function discoverPackageEntrypoints( context, packageRoot, target, - sourcePaths: sourcePathSet, outputMapping }); if (entrypoint !== undefined) entrypoints.push(entrypoint); @@ -133,7 +131,7 @@ async function resolvePackageEntrypoint(resolution: PackageEntrypointResolution) if (targetPath === undefined) return undefined; const mappedSource = await resolveOutputSource(resolution.context, targetPath, resolution.outputMapping); if (mappedSource !== undefined) return mappedSource; - if (!resolution.sourcePaths.has(targetPath) || !isTypeScriptSourcePath(targetPath)) return undefined; + if (!isTypeScriptSourcePath(targetPath)) return undefined; return (await resolution.context.fileView.exists(targetPath)) ? targetPath : undefined; } @@ -203,10 +201,6 @@ function isGraphTestNode(node: GraphFactNode): boolean { return node.kind === "Test" || node.attributes?.isTest === true; } -function isConventionalTestPath(path: string): boolean { - return /(?:^|\/)__tests__\//u.test(path) || /\.(?:test|spec)\.[cm]?[jt]sx?$/u.test(path); -} - function isDefined(value: T | undefined): value is T { return value !== undefined; } diff --git a/packages/validation-typescript/src/dead-code-run.ts b/packages/validation-typescript/src/dead-code-run.ts index 2c29d13..8682465 100644 --- a/packages/validation-typescript/src/dead-code-run.ts +++ b/packages/validation-typescript/src/dead-code-run.ts @@ -139,7 +139,9 @@ function deadCodeDiagnostics(state: DeadCodeState): readonly ValidationDiagnosti !hasReachableSymbol(node, state.reachability.reachableSymbolAliases) ); const typeExports = partitionTypeExportsByReferenceSupport({ - nodes: typeCoveredExports.filter((node) => !nodeHasPath(node, state.missingGraphImportTargets)), + nodes: typeCoveredExports + .filter((node) => !hasReachableSymbol(node, state.reachability.reachableSymbolAliases)) + .filter((node) => !nodeHasPath(node, state.missingGraphImportTargets)), typeReferences: state.typeReferences, incomingTypeReferences: new Set(state.typeReferences.map((edge) => edge.to)), importsFrom: state.importsFrom, diff --git a/packages/validation-typescript/src/diagnostics.ts b/packages/validation-typescript/src/diagnostics.ts index 6f95af8..c6c60a0 100644 --- a/packages/validation-typescript/src/diagnostics.ts +++ b/packages/validation-typescript/src/diagnostics.ts @@ -32,13 +32,6 @@ export function sortValidationDiagnostics(diagnostics: readonly ValidationDiagno return [...diagnostics].sort(compareDiagnostics); } -export function scriptKindForPath(path: string): ts.ScriptKind { - if (path.endsWith(".tsx")) return ts.ScriptKind.TSX; - if (path.endsWith(".jsx")) return ts.ScriptKind.JSX; - if (path.endsWith(".js")) return ts.ScriptKind.JS; - return ts.ScriptKind.TS; -} - function compareDiagnostics(left: ValidationDiagnostic, right: ValidationDiagnostic): number { return ( (left.path ?? "").localeCompare(right.path ?? "") || diff --git a/packages/validation-typescript/src/function-metrics-check.ts b/packages/validation-typescript/src/function-metrics-check.ts index 415bf59..abe9ced 100644 --- a/packages/validation-typescript/src/function-metrics-check.ts +++ b/packages/validation-typescript/src/function-metrics-check.ts @@ -8,7 +8,8 @@ import { typeScriptCheckOwner, supportedTypeScriptValidationScopes } from "./check-constants.js"; -import { scriptKindForPath, sortValidationDiagnostics } from "./diagnostics.js"; +import { sortValidationDiagnostics } from "./diagnostics.js"; +import { scriptKindForPath } from "./script-kind.js"; import { materializeTypeScriptSources } from "./source-files.js"; export interface TypeScriptFunctionMetricThresholds { diff --git a/packages/validation-typescript/src/graph-requirements.ts b/packages/validation-typescript/src/graph-requirements.ts index d55e1d6..06675d7 100644 --- a/packages/validation-typescript/src/graph-requirements.ts +++ b/packages/validation-typescript/src/graph-requirements.ts @@ -42,7 +42,7 @@ export async function deadCodeGraphRequirements( export async function relevantTestsGraphRequirements( context: ValidationCheckContext ): Promise { - return edgeAndScopedFileRequirements(context, ["TESTED_BY"]); + return edgeAndScopedFileRequirements(context, ["IMPORTS_FROM", "TESTED_BY"]); } async function edgeAndScopedFileRequirements( diff --git a/packages/validation-typescript/src/import-graph-check.ts b/packages/validation-typescript/src/import-graph-check.ts index 85ef1a0..d17d6a6 100644 --- a/packages/validation-typescript/src/import-graph-check.ts +++ b/packages/validation-typescript/src/import-graph-check.ts @@ -41,6 +41,7 @@ async function retainedMissingEdgeDiagnostics( ) return []; throw error; } + // IMPORTS_FROM represents compiler dependencies, so type-only imports still require graph parity. return relativeImports .filter((relativeImport) => !edges.some((edge) => matchesDirectedFileEdge(edge, relativeImport.fromPath, relativeImport.resolvedPath))) .map((relativeImport): ValidationDiagnostic => ({ @@ -66,7 +67,12 @@ interface ImportGraphEdge { } function cycleDiagnostics(relativeImports: readonly TypeScriptRelativeImport[]): readonly ValidationDiagnostic[] { - return findCycles(relativeImports.map((relativeImport) => ({ from: relativeImport.fromPath, to: relativeImport.resolvedPath }))).map( + const runtimeImports = relativeImports.filter((relativeImport) => relativeImport.kind === "runtime"); + const edges = runtimeImports.map((relativeImport) => ({ + from: relativeImport.fromPath, + to: relativeImport.resolvedPath + })); + return findCycles(edges).map( (cycle): ValidationDiagnostic => ({ category: "graph", severity: "warning", diff --git a/packages/validation-typescript/src/import-layer-rules-check.ts b/packages/validation-typescript/src/import-layer-rules-check.ts index c13a512..61d2e2d 100644 --- a/packages/validation-typescript/src/import-layer-rules-check.ts +++ b/packages/validation-typescript/src/import-layer-rules-check.ts @@ -1,9 +1,8 @@ import type { ValidationDiagnostic } from "@the-open-engine/opcore-contracts"; import type { ValidationCheckDefinition } from "@the-open-engine/opcore-validation"; -import ts from "typescript"; import { TYPE_SCRIPT_IMPORT_LAYER_RULES_CHECK_ID } from "./check-ids.js"; import { typeScriptCheckAdapter, typeScriptCheckOwner, supportedTypeScriptValidationScopes } from "./check-constants.js"; -import { scriptKindForPath, sortValidationDiagnostics } from "./diagnostics.js"; +import { sortValidationDiagnostics } from "./diagnostics.js"; import { materializeTypeScriptSources } from "./source-files.js"; export interface TypeScriptImportLayerRule { @@ -31,10 +30,9 @@ export function createImportLayerRulesCheck(options: TypeScriptImportLayerRulesO const rules = options.layerRules ?? []; if (rules.length === 0) return { diagnostics: [] }; const sourceSet = await materializeTypeScriptSources(context); - const typeOnly = typeOnlyImportSpecifiers(sourceSet.files); const diagnostics: ValidationDiagnostic[] = []; for (const relativeImport of sourceSet.relativeImports) { - if (options.ignoreTypeOnlyImports === true && typeOnly.get(relativeImport.fromPath)?.has(relativeImport.specifier)) continue; + if (options.ignoreTypeOnlyImports === true && relativeImport.kind === "type_only") continue; for (const rule of rules) { if (!layerRuleMatches(rule, relativeImport.fromPath, relativeImport.resolvedPath)) continue; diagnostics.push({ @@ -51,26 +49,6 @@ export function createImportLayerRulesCheck(options: TypeScriptImportLayerRulesO }; } -function typeOnlyImportSpecifiers( - files: readonly { path: string; content: string }[] -): ReadonlyMap> { - const byPath = new Map>(); - for (const file of files) { - const sourceFile = ts.createSourceFile(file.path, file.content, ts.ScriptTarget.Latest, true, scriptKindForPath(file.path)); - const specifiers = new Set(); - sourceFile.forEachChild((node) => { - if (ts.isImportDeclaration(node) && node.importClause?.isTypeOnly === true && ts.isStringLiteral(node.moduleSpecifier)) { - specifiers.add(node.moduleSpecifier.text); - } - if (ts.isExportDeclaration(node) && node.isTypeOnly === true && node.moduleSpecifier !== undefined && ts.isStringLiteral(node.moduleSpecifier)) { - specifiers.add(node.moduleSpecifier.text); - } - }); - if (specifiers.size > 0) byPath.set(file.path, specifiers); - } - return byPath; -} - function layerRuleMatches(rule: TypeScriptImportLayerRule, fromPath: string, toPath: string): boolean { if (!patternMatches(rule.from, fromPath)) return false; if (rule.fromNot?.some((pattern) => patternMatches(pattern, fromPath)) === true) return false; diff --git a/packages/validation-typescript/src/module-dependencies.ts b/packages/validation-typescript/src/module-dependencies.ts new file mode 100644 index 0000000..c71beb0 --- /dev/null +++ b/packages/validation-typescript/src/module-dependencies.ts @@ -0,0 +1,83 @@ +import ts from "typescript"; +import { scriptKindForPath } from "./script-kind.js"; + +export type TypeScriptImportKind = "runtime" | "type_only"; + +export interface TypeScriptModuleDependency { + specifier: string; + kind: TypeScriptImportKind; +} + +export function moduleDependencies(path: string, content: string): readonly TypeScriptModuleDependency[] { + const sourceFile = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true, scriptKindForPath(path)); + const collected = new Map(); + for (const reference of sourceFile.referencedFiles) { + recordDependency(collected, { specifier: reference.fileName, kind: "type_only" }); + } + const visit = (node: ts.Node): void => { + const dependency = dependencyForNode(node); + if (dependency !== undefined) recordDependency(collected, dependency); + ts.forEachChild(node, visit); + }; + sourceFile.forEachChild(visit); + return [...collected.entries()] + .map(([specifier, kind]) => ({ specifier, kind })) + .sort((left, right) => left.specifier.localeCompare(right.specifier)); +} + +function recordDependency( + collected: Map, + dependency: TypeScriptModuleDependency +): void { + const existing = collected.get(dependency.specifier); + if (existing === "runtime" || existing === dependency.kind) return; + collected.set(dependency.specifier, dependency.kind); +} + +function dependencyForNode(node: ts.Node): TypeScriptModuleDependency | undefined { + if (ts.isImportDeclaration(node)) { + return dependency(node.moduleSpecifier, importDeclarationKind(node)); + } + if (ts.isExportDeclaration(node) && node.moduleSpecifier !== undefined) { + return dependency(node.moduleSpecifier, exportDeclarationKind(node)); + } + if (ts.isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference)) { + return dependency(node.moduleReference.expression, node.isTypeOnly ? "type_only" : "runtime"); + } + if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument)) { + return dependency(node.argument.literal, "type_only"); + } + return callDependency(node); +} + +function importDeclarationKind(node: ts.ImportDeclaration): TypeScriptImportKind { + const clause = node.importClause; + if (clause?.isTypeOnly === true) return "type_only"; + if (clause === undefined || clause.name !== undefined || clause.namedBindings === undefined) return "runtime"; + if (!ts.isNamedImports(clause.namedBindings)) return "runtime"; + const elements = clause.namedBindings.elements; + return elements.length > 0 && elements.every((element) => element.isTypeOnly) ? "type_only" : "runtime"; +} + +function exportDeclarationKind(node: ts.ExportDeclaration): TypeScriptImportKind { + if (node.isTypeOnly) return "type_only"; + if (node.exportClause === undefined || !ts.isNamedExports(node.exportClause)) return "runtime"; + const elements = node.exportClause.elements; + return elements.length > 0 && elements.every((element) => element.isTypeOnly) ? "type_only" : "runtime"; +} + +function callDependency(node: ts.Node): TypeScriptModuleDependency | undefined { + if (!ts.isCallExpression(node)) return undefined; + const isLoader = + node.expression.kind === ts.SyntaxKind.ImportKeyword || + (ts.isIdentifier(node.expression) && node.expression.text === "require"); + return isLoader ? dependency(node.arguments[0], "runtime") : undefined; +} + +function dependency( + expression: ts.Expression | undefined, + kind: TypeScriptImportKind +): TypeScriptModuleDependency | undefined { + if (expression === undefined || !ts.isStringLiteralLike(expression)) return undefined; + return { specifier: expression.text, kind }; +} diff --git a/packages/validation-typescript/src/relevant-tests-check.ts b/packages/validation-typescript/src/relevant-tests-check.ts index 9107dd5..1e94cbd 100644 --- a/packages/validation-typescript/src/relevant-tests-check.ts +++ b/packages/validation-typescript/src/relevant-tests-check.ts @@ -1,9 +1,11 @@ -import type { GraphFactEdge, ValidationDiagnostic } from "@the-open-engine/opcore-contracts"; +import type { ValidationDiagnostic } from "@the-open-engine/opcore-contracts"; import type { ValidationCheckDefinition } from "@the-open-engine/opcore-validation"; import { TYPE_SCRIPT_RELEVANT_TESTS_CHECK_ID } from "./check-ids.js"; import { typeScriptCheckAdapter, typeScriptCheckOwner, supportedTypeScriptValidationScopes } from "./check-constants.js"; import { relevantTestsGraphRequirements } from "./graph-requirements.js"; -import { materializeTypeScriptSources, toFileNodeId } from "./source-files.js"; +import { createRelevantTestEvidence } from "./relevant-tests-evidence.js"; +import { materializeTypeScriptSources } from "./source-files.js"; +import { isConventionalTypeScriptTestPath } from "./test-paths.js"; export function createRelevantTestsCheck(): ValidationCheckDefinition { return { @@ -15,51 +17,24 @@ export function createRelevantTestsCheck(): ValidationCheckDefinition { requiresGraph: true, graphRequirements: relevantTestsGraphRequirements, run: async (context) => { - const [sourceSet, testedBy] = await Promise.all([materializeTypeScriptSources(context), context.graph.testedBy()]); - const diagnostics = sourceSet.rootPaths.map((path): ValidationDiagnostic => { - const evidence = testedBy.filter((edge) => edgeReferencesFile(edge, path)); - if (evidence.length > 0) { - return { - category: "test", - severity: "info", - path, - code: "TS_RELEVANT_TESTS_FOUND", - message: `TESTED_BY graph evidence exists for ${path}: ${evidence.map(testEndpoint).sort().join(", ")}` - }; - } - return { + const [sourceSet, importsFrom, testedBy] = await Promise.all([ + materializeTypeScriptSources(context), + context.graph.importsFrom(), + context.graph.testedBy() + ]); + const relevantTestEvidence = createRelevantTestEvidence(importsFrom, testedBy); + const sourcePaths = sourceSet.rootPaths.filter((path) => !isConventionalTypeScriptTestPath(path)); + const diagnostics = sourcePaths.flatMap((path): ValidationDiagnostic[] => { + if (relevantTestEvidence(path).length > 0) return []; + return [{ category: "test", severity: "info", path, code: "TS_RELEVANT_TESTS_ABSENT", message: `No TESTED_BY graph evidence found for ${path}.` - }; + }]; }); return { diagnostics }; } }; } - -function edgeReferencesFile(edge: GraphFactEdge, path: string): boolean { - return endpointReferencesFile(edge.from, path) || endpointReferencesFile(edge.to, path); -} - -function testEndpoint(edge: GraphFactEdge): string { - return endpointFilePath(edge.to) ?? edge.to; -} - -function endpointAliases(path: string): ReadonlySet { - return new Set([path, toFileNodeId(path)]); -} - -function endpointReferencesFile(endpoint: string, path: string): boolean { - const aliases = endpointAliases(path); - if (aliases.has(endpoint)) return true; - const endpointPath = endpointFilePath(endpoint); - return endpointPath !== undefined && aliases.has(endpointPath); -} - -function endpointFilePath(endpoint: string): string | undefined { - const match = /^[^:]+:([^#]+)(?:#.*)?$/.exec(endpoint); - return match?.[1]; -} diff --git a/packages/validation-typescript/src/relevant-tests-evidence.ts b/packages/validation-typescript/src/relevant-tests-evidence.ts new file mode 100644 index 0000000..586c5cb --- /dev/null +++ b/packages/validation-typescript/src/relevant-tests-evidence.ts @@ -0,0 +1,74 @@ +import type { GraphFactEdge } from "@the-open-engine/opcore-contracts"; +import { graphFactPathFromEndpoint } from "@the-open-engine/opcore-validation"; + +const maxRelevantTestTraversalFiles = 10_000; + +export function createRelevantTestEvidence( + importsFrom: readonly GraphFactEdge[], + testedBy: readonly GraphFactEdge[] +): (path: string) => readonly string[] { + const reverseImporters = collectReverseImporters(importsFrom); + return (path) => relevantTestEvidence(path, testedBy, reverseImporters); +} + +function relevantTestEvidence( + path: string, + testedBy: readonly GraphFactEdge[], + reverseImporters: ReadonlyMap +): readonly string[] { + const direct = directTestEndpoints(path, testedBy); + if (direct.length > 0) return direct; + + const discovered = new Set([path]); + const pending: string[] = []; + const initialImporters = reverseImporters.get(path); + if (initialImporters !== undefined) appendUnseenPaths(pending, discovered, initialImporters); + const evidence = new Set(); + for (let cursor = 0; cursor < pending.length; cursor += 1) { + const importer = pending[cursor]; + if (importer === undefined) continue; + for (const endpoint of directTestEndpoints(importer, testedBy)) evidence.add(`${importer} -> ${endpoint}`); + const nextImporters = reverseImporters.get(importer); + if (nextImporters !== undefined) appendUnseenPaths(pending, discovered, nextImporters); + } + return [...evidence].sort(); +} + +function appendUnseenPaths(pending: string[], discovered: Set, paths: readonly string[]): void { + for (const path of paths) { + if (discovered.size >= maxRelevantTestTraversalFiles) return; + if (discovered.has(path)) continue; + discovered.add(path); + pending.push(path); + } +} + +function collectReverseImporters(edges: readonly GraphFactEdge[]): ReadonlyMap { + const collected = new Map>(); + for (const edge of edges) { + if (edge.kind !== "IMPORTS_FROM") continue; + const importer = graphFactPathFromEndpoint(edge.from); + const imported = graphFactPathFromEndpoint(edge.to); + if (importer === undefined || imported === undefined) continue; + const importers = collected.get(imported) ?? new Set(); + importers.add(importer); + collected.set(imported, importers); + } + return new Map( + [...collected.entries()] + .sort(([left], [right]) => left.localeCompare(right)) + .map(([path, importers]) => [path, [...importers].sort()]) + ); +} + +function directTestEndpoints(path: string, testedBy: readonly GraphFactEdge[]): readonly string[] { + const endpoints = new Set(); + for (const edge of testedBy) { + const fromPath = graphFactPathFromEndpoint(edge.from); + const toPath = graphFactPathFromEndpoint(edge.to); + const referencesPath = edge.from === path || edge.from === `file:${path}` || fromPath === path || + edge.to === path || edge.to === `file:${path}` || toPath === path; + if (referencesPath) endpoints.add(toPath ?? edge.to); + } + return [...endpoints].sort(); +} diff --git a/packages/validation-typescript/src/script-kind.ts b/packages/validation-typescript/src/script-kind.ts new file mode 100644 index 0000000..2d27650 --- /dev/null +++ b/packages/validation-typescript/src/script-kind.ts @@ -0,0 +1,8 @@ +import ts from "typescript"; + +export function scriptKindForPath(path: string): ts.ScriptKind { + if (path.endsWith(".tsx")) return ts.ScriptKind.TSX; + if (path.endsWith(".jsx")) return ts.ScriptKind.JSX; + if (path.endsWith(".js")) return ts.ScriptKind.JS; + return ts.ScriptKind.TS; +} diff --git a/packages/validation-typescript/src/source-files.ts b/packages/validation-typescript/src/source-files.ts index d086ad3..3f64dcb 100644 --- a/packages/validation-typescript/src/source-files.ts +++ b/packages/validation-typescript/src/source-files.ts @@ -1,6 +1,7 @@ import type { ValidationCheckContext, ValidationFileView } from "@the-open-engine/opcore-validation"; import { joinRepoRelativePaths, normalizeValidationFileViewPath, uniqueSortedStrings } from "@the-open-engine/opcore-validation"; import ts from "typescript"; +import { moduleDependencies, type TypeScriptImportKind } from "./module-dependencies.js"; export const typeScriptSourceExtensions = [".ts", ".tsx", ".js", ".jsx", ".mts", ".cts"] as const; const jsonModuleExtension = ".json"; @@ -14,6 +15,7 @@ export interface TypeScriptRelativeImport { fromPath: string; specifier: string; resolvedPath: string; + kind: TypeScriptImportKind; } export interface TypeScriptMaterializedSourceSet { @@ -101,10 +103,12 @@ async function materializeTypeScriptSourcesUncached( if (rootPathSet.has(path)) materializedRootPaths.push(path); if (supportPathSet.has(path)) materializedSupportPaths.push(path); - for (const specifier of moduleImportSpecifiers(path, result.content)) { - const resolvedPath = await resolveRepoImport(context, path, specifier, compilerOptions); + for (const dependency of moduleDependencies(path, result.content)) { + const resolvedPath = await resolveRepoImport(context, path, dependency.specifier, compilerOptions); if (resolvedPath === undefined) continue; - if (isRelativeSpecifier(specifier)) relativeImports.push({ fromPath: path, specifier, resolvedPath }); + if (isRelativeSpecifier(dependency.specifier)) { + relativeImports.push({ fromPath: path, specifier: dependency.specifier, resolvedPath, kind: dependency.kind }); + } if (!visited.has(resolvedPath) && !sourceFileByPath.has(resolvedPath)) pending.push(resolvedPath); } } @@ -117,22 +121,13 @@ async function materializeTypeScriptSourcesUncached( files, sourceFileByPath, relativeImports: relativeImports.sort((left, right) => - `${left.fromPath}\0${left.resolvedPath}\0${left.specifier}`.localeCompare( - `${right.fromPath}\0${right.resolvedPath}\0${right.specifier}` + `${left.fromPath}\0${left.resolvedPath}\0${left.specifier}\0${left.kind}`.localeCompare( + `${right.fromPath}\0${right.resolvedPath}\0${right.specifier}\0${right.kind}` ) ) }; } -function moduleImportSpecifiers(path: string, content: string): readonly string[] { - const preprocessed = ts.preProcessFile(content, true, true); - return uniqueSortedStrings( - [...preprocessed.importedFiles, ...preprocessed.referencedFiles] - .map((entry) => entry.fileName) - .filter((specifier) => isRelativeSpecifier(specifier) || isPathMappableSpecifier(specifier)) - ); -} - async function resolveRepoImport( context: ValidationCheckContext, fromPath: string, diff --git a/packages/validation-typescript/src/test-paths.ts b/packages/validation-typescript/src/test-paths.ts new file mode 100644 index 0000000..6bd1387 --- /dev/null +++ b/packages/validation-typescript/src/test-paths.ts @@ -0,0 +1,3 @@ +export function isConventionalTypeScriptTestPath(path: string): boolean { + return /(?:^|\/)__tests__\//u.test(path) || /\.(?:test|spec)\.[cm]?[jt]sx?$/u.test(path); +} diff --git a/scripts/run-opcore-self-check.mjs b/scripts/run-opcore-self-check.mjs index 80e8f2b..f315536 100644 --- a/scripts/run-opcore-self-check.mjs +++ b/scripts/run-opcore-self-check.mjs @@ -1,11 +1,12 @@ #!/usr/bin/env node import { spawnSync } from "node:child_process"; -import { existsSync } from "node:fs"; +import { existsSync, readFileSync } from "node:fs"; import { join } from "node:path"; import { fileURLToPath } from "node:url"; const repoRoot = fileURLToPath(new URL("..", import.meta.url)); const cliPath = join(repoRoot, "packages", "opcore", "dist", "index.js"); +const configPath = join(repoRoot, ".opcore", "config"); const requestedBase = process.env.OPCORE_SELF_CHECK_BASE_REF ?? process.env.OPCORE_LOCAL_CI_BASE_REF ?? @@ -16,35 +17,171 @@ if (!existsSync(cliPath)) { } const baseRef = commitExists(requestedBase) ? requestedBase : "HEAD"; -const command = [ - cliPath, +const manifestRun = runOpcore("validation manifest", [ + "check", + "manifest", + "--repo", + repoRoot, + "--json" +]); +const entries = manifestRun.payload.validationResult?.manifest?.entries; +if (!Array.isArray(entries) || entries.length === 0) { + fail("Opcore self-check manifest did not contain validation check entries."); +} +const availableChecks = entries.map((entry) => entry.checkId); +assertUniqueStrings(availableChecks, "validation manifest check ids"); +assertStrictConfig(entries); + +const changedChecks = entries + .filter((entry) => entry.supportedScopes?.includes("changed")) + .map((entry) => entry.checkId); +const repoChecks = entries + .filter((entry) => !entry.supportedScopes?.includes("changed")) + .map((entry) => entry.checkId); + +for (const entry of entries.filter((candidate) => repoChecks.includes(candidate.checkId))) { + if (!entry.supportedScopes?.includes("all")) { + fail(`Opcore self-check cannot execute ${entry.checkId}: neither changed nor all scope is supported.`); + } +} + +const changedRun = runOpcore(`changed validation against ${baseRef}`, [ "check", "changed", + "--repo", + repoRoot, "--base", baseRef, "--report-mode", "introduced", "--json" -]; -const result = spawnSync(process.execPath, command, { - cwd: repoRoot, - encoding: "utf8", - env: process.env, - maxBuffer: 64 * 1024 * 1024 -}); - -if (result.error) fail(`Unable to run Opcore self-check: ${result.error.message}`); - -let payload; -try { - payload = JSON.parse(result.stdout); -} catch { - fail(`Opcore self-check returned malformed JSON.\n${result.stderr || result.stdout}`); -} - -const diagnostics = payload.validationResult?.diagnostics ?? []; -if (result.status !== 0 || payload.exitCode !== 0 || payload.status !== "ok" || diagnostics.length > 0) { - const summary = diagnostics +]); +assertValidationRun(changedRun.payload, changedChecks, `changed validation against ${baseRef}`); + +if (repoChecks.length > 0) { + if (entries.some((entry) => repoChecks.includes(entry.checkId) && entry.requiresGraph === true)) { + assertGraphBuild(runOpcore("repo-wide graph preparation", [ + "graph", + "build", + "--repo", + repoRoot, + "--json" + ]).payload); + } + const repoRun = runOpcore("repo-wide validation", [ + "check", + "all", + "--repo", + repoRoot, + "--checks", + repoChecks.join(","), + "--json" + ]); + assertValidationRun(repoRun.payload, repoChecks, "repo-wide validation"); +} + +assertSameStringSet([...changedChecks, ...repoChecks], availableChecks, "self-check scope coverage"); +process.stdout.write( + `Opcore self-check passed against ${baseRef}: ${availableChecks.length} checks across ` + + `${repoChecks.length > 0 ? 2 : 1} scopes, 0 diagnostics.\n` +); + +function runOpcore(label, args) { + const result = spawnSync(process.execPath, [cliPath, ...args], { + cwd: repoRoot, + encoding: "utf8", + env: process.env, + maxBuffer: 64 * 1024 * 1024 + }); + if (result.error) fail(`Unable to run Opcore ${label}: ${result.error.message}`); + let payload; + try { + payload = JSON.parse(result.stdout); + } catch { + fail(`Opcore ${label} returned malformed JSON.\n${result.stderr || result.stdout}`); + } + if (result.status !== 0 || payload.exitCode !== 0 || payload.status !== "ok") { + fail(commandFailure(label, result, payload)); + } + return { payload, stderr: result.stderr }; +} + +function commandFailure(label, result, payload) { + return [ + `Opcore self-check ${label} failed.`, + `router status=${String(payload.status)} exit=${String(payload.exitCode)} process=${String(result.status)}`, + diagnosticSummary(payload.validationResult?.diagnostics), + result.stderr.trim() + ] + .filter(Boolean) + .join("\n"); +} + +function assertValidationRun(payload, expectedChecks, label) { + const validationResult = payload.validationResult; + if (validationResult?.status !== "passed" || validationResult.ok !== true) { + fail(`Opcore self-check ${label} returned validation status ${String(validationResult?.status)}.`); + } + assertExactStrings(validationResult.manifest?.checks, expectedChecks, `${label} manifest`); + const diagnostics = validationResult.diagnostics ?? []; + if (diagnostics.length > 0) { + fail(`Opcore self-check ${label} returned diagnostics.\n${diagnosticSummary(diagnostics)}`); + } +} + +function assertGraphBuild(payload) { + if ( + payload.providerStatus?.state !== "available" || + payload.graphPipeline?.summary?.operation !== "build" + ) { + fail("Opcore self-check repo-wide graph preparation did not produce an available build."); + } +} + +function assertStrictConfig(entries) { + let config; + try { + config = JSON.parse(readFileSync(configPath, "utf8")); + } catch (error) { + fail(`Opcore self-check could not read strict policy: ${errorMessage(error)}`); + } + const checks = config?.validation?.checks; + assertExactStrings(checks?.defaults, entries.map((entry) => entry.checkId), "validation.checks.defaults"); + assertExactStrings(checks?.disabled, [], "validation.checks.disabled"); + assertExactStrings( + config?.validation?.adapters, + [...new Set(entries.map((entry) => entry.adapter))], + "validation.adapters" + ); +} + +function assertExactStrings(actual, expected, label) { + if (!Array.isArray(actual) || actual.some((value) => typeof value !== "string")) { + fail(`Opcore self-check ${label} must be a string array.`); + } + if (actual.length !== expected.length || actual.some((value, index) => value !== expected[index])) { + fail( + `Opcore self-check ${label} mismatch.\n` + + `expected=${JSON.stringify(expected)}\nactual=${JSON.stringify(actual)}` + ); + } +} + +function assertUniqueStrings(values, label) { + if (new Set(values).size !== values.length) { + fail(`Opcore self-check ${label} must be unique.`); + } +} + +function assertSameStringSet(actual, expected, label) { + assertUniqueStrings(actual, label); + const actualSorted = [...actual].sort(); + const expectedSorted = [...expected].sort(); + assertExactStrings(actualSorted, expectedSorted, label); +} + +function diagnosticSummary(diagnostics = []) { + return diagnostics .slice(0, 20) .map((diagnostic) => { const location = diagnostic.path ? `${diagnostic.path}: ` : ""; @@ -52,23 +189,8 @@ if (result.status !== 0 || payload.exitCode !== 0 || payload.status !== "ok" || return `${location}${diagnostic.message ?? "validation diagnostic"}${code}`; }) .join("\n"); - fail( - [ - `Opcore self-check failed against ${baseRef}.`, - `router status=${String(payload.status)} exit=${String(payload.exitCode)} process=${String(result.status)}`, - summary, - result.stderr.trim() - ] - .filter(Boolean) - .join("\n") - ); } -const checkCount = payload.validationResult?.manifest?.checks?.length ?? 0; -process.stdout.write( - `Opcore self-check passed against ${baseRef}: ${checkCount} checks, ${diagnostics.length} diagnostics.\n` -); - function commitExists(ref) { const probe = spawnSync("git", ["rev-parse", "--verify", `${ref}^{commit}`], { cwd: repoRoot, @@ -78,6 +200,10 @@ function commitExists(ref) { return probe.status === 0; } +function errorMessage(error) { + return error instanceof Error ? error.message : String(error); +} + function fail(message) { process.stderr.write(`${message}\n`); process.exit(1); diff --git a/scripts/run-test-ci.mjs b/scripts/run-test-ci.mjs index 582224c..0bab42e 100644 --- a/scripts/run-test-ci.mjs +++ b/scripts/run-test-ci.mjs @@ -6,7 +6,7 @@ import { join } from "node:path"; const nativePackagingTest = "tests/native-packaging-policy.test.mjs"; const pythonValidationTest = "tests/validation-python.test.mjs"; const testFiles = readdirSync("tests") - .filter((file) => file.endsWith(".test.mjs")) + .filter((file) => file.endsWith(".test.mjs") || file.endsWith(".test.ts")) .map((file) => join("tests", file)) .sort(); const parallelSafeTests = testFiles.filter((file) => file !== nativePackagingTest && file !== pythonValidationTest); diff --git a/tests/fixtures/package-packlists.json b/tests/fixtures/package-packlists.json index df6cd14..f714c0b 100644 --- a/tests/fixtures/package-packlists.json +++ b/tests/fixtures/package-packlists.json @@ -39,6 +39,9 @@ "dist/advanced/inspect-language-service.d.ts", "dist/advanced/inspect-language-service.d.ts.map", "dist/advanced/inspect-language-service.js", + "dist/advanced/inspect-typescript-project.d.ts", + "dist/advanced/inspect-typescript-project.d.ts.map", + "dist/advanced/inspect-typescript-project.js", "dist/advanced/manifest.d.ts", "dist/advanced/manifest.d.ts.map", "dist/advanced/manifest.js", @@ -70,6 +73,132 @@ "dist/index.d.ts", "dist/index.d.ts.map", "dist/index.js", + "dist/init-action-helpers.d.ts", + "dist/init-action-helpers.d.ts.map", + "dist/init-action-helpers.js", + "dist/init-actions-global.d.ts", + "dist/init-actions-global.d.ts.map", + "dist/init-actions-global.js", + "dist/init-actions-repo.d.ts", + "dist/init-actions-repo.d.ts.map", + "dist/init-actions-repo.js", + "dist/init-actions.d.ts", + "dist/init-actions.d.ts.map", + "dist/init-actions.js", + "dist/init-apply.d.ts", + "dist/init-apply.d.ts.map", + "dist/init-apply.js", + "dist/init-args.d.ts", + "dist/init-args.d.ts.map", + "dist/init-args.js", + "dist/init-config.d.ts", + "dist/init-config.d.ts.map", + "dist/init-config.js", + "dist/init-constants.d.ts", + "dist/init-constants.d.ts.map", + "dist/init-constants.js", + "dist/init-context-payload.d.ts", + "dist/init-context-payload.d.ts.map", + "dist/init-context-payload.js", + "dist/init-data.d.ts", + "dist/init-data.d.ts.map", + "dist/init-data.js", + "dist/init-files.d.ts", + "dist/init-files.d.ts.map", + "dist/init-files.js", + "dist/init-format-summary.d.ts", + "dist/init-format-summary.d.ts.map", + "dist/init-format-summary.js", + "dist/init-format.d.ts", + "dist/init-format.d.ts.map", + "dist/init-format.js", + "dist/init-gitignore.d.ts", + "dist/init-gitignore.d.ts.map", + "dist/init-gitignore.js", + "dist/init-guidance.d.ts", + "dist/init-guidance.d.ts.map", + "dist/init-guidance.js", + "dist/init-help.d.ts", + "dist/init-help.d.ts.map", + "dist/init-help.js", + "dist/init-hooks.d.ts", + "dist/init-hooks.d.ts.map", + "dist/init-hooks.js", + "dist/init-language-settings.d.ts", + "dist/init-language-settings.d.ts.map", + "dist/init-language-settings.js", + "dist/init-messages.d.ts", + "dist/init-messages.d.ts.map", + "dist/init-messages.js", + "dist/init-paths.d.ts", + "dist/init-paths.d.ts.map", + "dist/init-paths.js", + "dist/init-payloads.d.ts", + "dist/init-payloads.d.ts.map", + "dist/init-payloads.js", + "dist/init-plan-flow.d.ts", + "dist/init-plan-flow.d.ts.map", + "dist/init-plan-flow.js", + "dist/init-plan-global.d.ts", + "dist/init-plan-global.d.ts.map", + "dist/init-plan-global.js", + "dist/init-plan-repo.d.ts", + "dist/init-plan-repo.d.ts.map", + "dist/init-plan-repo.js", + "dist/init-plan-writes.d.ts", + "dist/init-plan-writes.d.ts.map", + "dist/init-plan-writes.js", + "dist/init-plan.d.ts", + "dist/init-plan.d.ts.map", + "dist/init-plan.js", + "dist/init-prompts.d.ts", + "dist/init-prompts.d.ts.map", + "dist/init-prompts.js", + "dist/init-python-settings.d.ts", + "dist/init-python-settings.d.ts.map", + "dist/init-python-settings.js", + "dist/init-result.d.ts", + "dist/init-result.d.ts.map", + "dist/init-result.js", + "dist/init-router.d.ts", + "dist/init-router.d.ts.map", + "dist/init-router.js", + "dist/init-session.d.ts", + "dist/init-session.d.ts.map", + "dist/init-session.js", + "dist/init-settings.d.ts", + "dist/init-settings.d.ts.map", + "dist/init-settings.js", + "dist/init-timing.d.ts", + "dist/init-timing.d.ts.map", + "dist/init-timing.js", + "dist/init-types.d.ts", + "dist/init-types.d.ts.map", + "dist/init-types.js", + "dist/init-undo-entry.d.ts", + "dist/init-undo-entry.d.ts.map", + "dist/init-undo-entry.js", + "dist/init-undo-flow.d.ts", + "dist/init-undo-flow.d.ts.map", + "dist/init-undo-flow.js", + "dist/init-undo-metadata.d.ts", + "dist/init-undo-metadata.d.ts.map", + "dist/init-undo-metadata.js", + "dist/init-undo-plan.d.ts", + "dist/init-undo-plan.d.ts.map", + "dist/init-undo-plan.js", + "dist/init-wizard-flow.d.ts", + "dist/init-wizard-flow.d.ts.map", + "dist/init-wizard-flow.js", + "dist/init-wizard-plan.d.ts", + "dist/init-wizard-plan.d.ts.map", + "dist/init-wizard-plan.js", + "dist/init-wizard-render.d.ts", + "dist/init-wizard-render.d.ts.map", + "dist/init-wizard-render.js", + "dist/init-write.d.ts", + "dist/init-write.d.ts.map", + "dist/init-write.js", "dist/init.d.ts", "dist/init.d.ts.map", "dist/init.js", @@ -94,6 +223,9 @@ "dist/repo-check-packs.d.ts", "dist/repo-check-packs.d.ts.map", "dist/repo-check-packs.js", + "dist/repo-paths.d.ts", + "dist/repo-paths.d.ts.map", + "dist/repo-paths.js", "dist/repo-validation-config.d.ts", "dist/repo-validation-config.d.ts.map", "dist/repo-validation-config.js", @@ -198,9 +330,327 @@ "node_modules/@the-open-engine/opcore-asp-provider/dist/workspace.js", "node_modules/@the-open-engine/opcore-asp-provider/package.json", "node_modules/@the-open-engine/opcore-contracts/README.md", + "node_modules/@the-open-engine/opcore-contracts/dist/clone/validators.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/clone/validators.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/clone/validators.js", + "node_modules/@the-open-engine/opcore-contracts/dist/command/adapter-validator.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/command/adapter-validator.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/command/adapter-validator.js", + "node_modules/@the-open-engine/opcore-contracts/dist/command/contracts.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/command/contracts.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/command/contracts.js", + "node_modules/@the-open-engine/opcore-contracts/dist/command/helper-validators.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/command/helper-validators.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/command/helper-validators.js", + "node_modules/@the-open-engine/opcore-contracts/dist/command/manifest.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/command/manifest.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/command/manifest.js", + "node_modules/@the-open-engine/opcore-contracts/dist/command/router-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/command/router-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/command/router-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/command/router-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/command/router-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/command/router-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/command/router-contracts.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/command/router-contracts.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/command/router-contracts.js", + "node_modules/@the-open-engine/opcore-contracts/dist/command/validators.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/command/validators.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/command/validators.js", + "node_modules/@the-open-engine/opcore-contracts/dist/command/vocabulary.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/command/vocabulary.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/command/vocabulary.js", + "node_modules/@the-open-engine/opcore-contracts/dist/edit/contracts.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/edit/contracts.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/edit/contracts.js", + "node_modules/@the-open-engine/opcore-contracts/dist/edit/refusal-validator.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/edit/refusal-validator.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/edit/refusal-validator.js", + "node_modules/@the-open-engine/opcore-contracts/dist/edit/validators.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/edit/validators.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/edit/validators.js", + "node_modules/@the-open-engine/opcore-contracts/dist/edit/vocabulary.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/edit/vocabulary.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/edit/vocabulary.js", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/daemon-validators-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/daemon-validators-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/daemon-validators-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/daemon-validators-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/daemon-validators-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/daemon-validators-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/helper-validators.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/helper-validators.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/helper-validators.js", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/payload-validators.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/payload-validators.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/payload-validators.js", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/pipeline-contracts.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/pipeline-contracts.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/pipeline-contracts.js", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/protocol-validators.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/protocol-validators.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/protocol-validators.js", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/provider-contracts-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/provider-contracts-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/provider-contracts-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/provider-contracts-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/provider-contracts-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/provider-contracts-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/provider-validators.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/provider-validators.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/provider-validators.js", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/query-contracts-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/query-contracts-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/query-contracts-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/query-contracts-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/query-contracts-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/query-contracts-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/query-validators.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/query-validators.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/query-validators.js", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/search-contracts.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/search-contracts.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/search-contracts.js", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/search-validators.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/search-validators.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/search-validators.js", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/vocabulary-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/vocabulary-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/vocabulary-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/vocabulary-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/vocabulary-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/graph/vocabulary-02.js", "node_modules/@the-open-engine/opcore-contracts/dist/index.d.ts", "node_modules/@the-open-engine/opcore-contracts/dist/index.d.ts.map", "node_modules/@the-open-engine/opcore-contracts/dist/index.js", + "node_modules/@the-open-engine/opcore-contracts/dist/inspect/contracts-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/inspect/contracts-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/inspect/contracts-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/inspect/contracts-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/inspect/contracts-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/inspect/contracts-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/inspect/helper-validators-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/inspect/helper-validators-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/inspect/helper-validators-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/inspect/helper-validators-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/inspect/helper-validators-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/inspect/helper-validators-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/inspect/validators.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/inspect/validators.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/inspect/validators.js", + "node_modules/@the-open-engine/opcore-contracts/dist/inspect/warm-contracts.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/inspect/warm-contracts.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/inspect/warm-contracts.js", + "node_modules/@the-open-engine/opcore-contracts/dist/managed/contracts.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/managed/contracts.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/managed/contracts.js", + "node_modules/@the-open-engine/opcore-contracts/dist/managed/helper-validators.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/managed/helper-validators.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/managed/helper-validators.js", + "node_modules/@the-open-engine/opcore-contracts/dist/managed/validators-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/managed/validators-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/managed/validators-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/managed/validators-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/managed/validators-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/managed/validators-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/managed/validators-03.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/managed/validators-03.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/managed/validators-03.js", + "node_modules/@the-open-engine/opcore-contracts/dist/product/init-contracts.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/product/init-contracts.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/product/init-contracts.js", + "node_modules/@the-open-engine/opcore-contracts/dist/product/init-validators-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/product/init-validators-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/product/init-validators-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/product/init-validators-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/product/init-validators-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/product/init-validators-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/product/latency-contracts.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/product/latency-contracts.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/product/latency-contracts.js", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-contracts-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-contracts-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-contracts-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-contracts-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-contracts-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-contracts-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-coverage-validators.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-coverage-validators.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-coverage-validators.js", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-validators-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-validators-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-validators-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-validators-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-validators-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-validators-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-validators-03.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-validators-03.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-validators-03.js", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-validators-04.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-validators-04.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-validators-04.js", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-validators-05.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-validators-05.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/product/metrics-validators-05.js", + "node_modules/@the-open-engine/opcore-contracts/dist/product/status-contracts.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/product/status-contracts.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/product/status-contracts.js", + "node_modules/@the-open-engine/opcore-contracts/dist/product/status-validators.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/product/status-validators.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/product/status-validators.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/asp-contracts-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/asp-contracts-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/asp-contracts-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/asp-validators-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/asp-validators-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/asp-validators-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/asp-validators-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/asp-validators-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/asp-validators-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/cutover-contracts.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/cutover-contracts.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/cutover-contracts.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/cutover-validators-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/cutover-validators-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/cutover-validators-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/cutover-validators-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/cutover-validators-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/cutover-validators-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-contracts.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-contracts.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-contracts.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-optional-validators.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-optional-validators.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-optional-validators.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-validators-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-validators-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-validators-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-validators-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-validators-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-validators-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-validators-03.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-validators-03.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-validators-03.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-vocabulary-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-vocabulary-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-vocabulary-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-vocabulary-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-vocabulary-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/graph-vocabulary-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/public-validators.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/public-validators.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/public-validators.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/receipt-contracts-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/receipt-contracts-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/receipt-contracts-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/receipt-contracts-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/receipt-contracts-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/receipt-contracts-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/receipt-validators-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/receipt-validators-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/receipt-validators-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/receipt-validators-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/receipt-validators-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/receipt-validators-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/receipt-validators-03.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/receipt-validators-03.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/receipt-validators-03.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/vocabulary-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/vocabulary-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/vocabulary-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/vocabulary-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/vocabulary-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/vocabulary-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/vocabulary-03.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/vocabulary-03.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/vocabulary-03.js", + "node_modules/@the-open-engine/opcore-contracts/dist/release/vocabulary-04.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/release/vocabulary-04.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/release/vocabulary-04.js", + "node_modules/@the-open-engine/opcore-contracts/dist/shared/json.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/shared/json.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/shared/json.js", + "node_modules/@the-open-engine/opcore-contracts/dist/shared/path-validators.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/shared/path-validators.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/shared/path-validators.js", + "node_modules/@the-open-engine/opcore-contracts/dist/shared/primitives.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/shared/primitives.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/shared/primitives.js", + "node_modules/@the-open-engine/opcore-contracts/dist/shared/validators-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/shared/validators-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/shared/validators-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/shared/validators-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/shared/validators-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/shared/validators-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/capability-contracts.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/capability-contracts.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/capability-contracts.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/diagnostic-contracts.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/diagnostic-contracts.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/diagnostic-contracts.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/prewrite-status-validators-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/prewrite-status-validators-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/prewrite-status-validators-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/prewrite-status-validators-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/prewrite-status-validators-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/prewrite-status-validators-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-project-contracts-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-project-contracts-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-project-contracts-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-project-contracts-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-project-contracts-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-project-contracts-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-project-validators-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-project-validators-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-project-validators-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-project-validators-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-project-validators-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-project-validators-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-pytest-validators-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-pytest-validators-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-pytest-validators-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-pytest-validators-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-pytest-validators-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-pytest-validators-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-ruff-validators-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-ruff-validators-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-ruff-validators-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-ruff-validators-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-ruff-validators-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-ruff-validators-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-ruff-validators-03.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-ruff-validators-03.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-ruff-validators-03.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-types-validators.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-types-validators.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-types-validators.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-validator-primitives.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-validator-primitives.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/python-validator-primitives.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/request-contracts.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/request-contracts.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/request-contracts.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/request-validators-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/request-validators-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/request-validators-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/request-validators-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/request-validators-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/request-validators-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/result-validator.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/result-validator.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/result-validator.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/status-contracts.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/status-contracts.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/status-contracts.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/vocabulary-01.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/vocabulary-01.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/vocabulary-01.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/vocabulary-02.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/vocabulary-02.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/vocabulary-02.js", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/vocabulary-03.d.ts", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/vocabulary-03.d.ts.map", + "node_modules/@the-open-engine/opcore-contracts/dist/validation/vocabulary-03.js", "node_modules/@the-open-engine/opcore-contracts/package.json", "node_modules/@the-open-engine/opcore-contracts/schemas/opcore-contracts.schema.json", "node_modules/@the-open-engine/opcore-edit/README.md", @@ -261,6 +711,30 @@ "node_modules/@the-open-engine/opcore-edit/dist/tree-planner.d.ts", "node_modules/@the-open-engine/opcore-edit/dist/tree-planner.d.ts.map", "node_modules/@the-open-engine/opcore-edit/dist/tree-planner.js", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/filesystem-discovery.d.ts", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/filesystem-discovery.d.ts.map", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/filesystem-discovery.js", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/import-resolution.d.ts", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/import-resolution.d.ts.map", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/import-resolution.js", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/index.d.ts", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/index.d.ts.map", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/index.js", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/path-policy.d.ts", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/path-policy.d.ts.map", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/path-policy.js", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/project-service.d.ts", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/project-service.d.ts.map", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/project-service.js", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/source-discovery.d.ts", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/source-discovery.d.ts.map", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/source-discovery.js", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/tsconfig.d.ts", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/tsconfig.d.ts.map", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/tsconfig.js", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/types.d.ts", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/types.d.ts.map", + "node_modules/@the-open-engine/opcore-edit/dist/typescript-project/types.js", "node_modules/@the-open-engine/opcore-edit/dist/validated-apply.d.ts", "node_modules/@the-open-engine/opcore-edit/dist/validated-apply.d.ts.map", "node_modules/@the-open-engine/opcore-edit/dist/validated-apply.js", @@ -333,15 +807,42 @@ "node_modules/@the-open-engine/opcore-validation-docs/dist/check-constants.d.ts", "node_modules/@the-open-engine/opcore-validation-docs/dist/check-constants.d.ts.map", "node_modules/@the-open-engine/opcore-validation-docs/dist/check-constants.js", + "node_modules/@the-open-engine/opcore-validation-docs/dist/check-definition.d.ts", + "node_modules/@the-open-engine/opcore-validation-docs/dist/check-definition.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-docs/dist/check-definition.js", "node_modules/@the-open-engine/opcore-validation-docs/dist/check-ids.d.ts", "node_modules/@the-open-engine/opcore-validation-docs/dist/check-ids.d.ts.map", "node_modules/@the-open-engine/opcore-validation-docs/dist/check-ids.js", + "node_modules/@the-open-engine/opcore-validation-docs/dist/check-results.d.ts", + "node_modules/@the-open-engine/opcore-validation-docs/dist/check-results.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-docs/dist/check-results.js", "node_modules/@the-open-engine/opcore-validation-docs/dist/checks.d.ts", "node_modules/@the-open-engine/opcore-validation-docs/dist/checks.d.ts.map", "node_modules/@the-open-engine/opcore-validation-docs/dist/checks.js", + "node_modules/@the-open-engine/opcore-validation-docs/dist/code-blocks-check.d.ts", + "node_modules/@the-open-engine/opcore-validation-docs/dist/code-blocks-check.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-docs/dist/code-blocks-check.js", + "node_modules/@the-open-engine/opcore-validation-docs/dist/content-checks.d.ts", + "node_modules/@the-open-engine/opcore-validation-docs/dist/content-checks.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-docs/dist/content-checks.js", + "node_modules/@the-open-engine/opcore-validation-docs/dist/content-quality-check.d.ts", + "node_modules/@the-open-engine/opcore-validation-docs/dist/content-quality-check.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-docs/dist/content-quality-check.js", + "node_modules/@the-open-engine/opcore-validation-docs/dist/coverage-checks.d.ts", + "node_modules/@the-open-engine/opcore-validation-docs/dist/coverage-checks.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-docs/dist/coverage-checks.js", "node_modules/@the-open-engine/opcore-validation-docs/dist/diagnostics.d.ts", "node_modules/@the-open-engine/opcore-validation-docs/dist/diagnostics.d.ts.map", "node_modules/@the-open-engine/opcore-validation-docs/dist/diagnostics.js", + "node_modules/@the-open-engine/opcore-validation-docs/dist/document-check.d.ts", + "node_modules/@the-open-engine/opcore-validation-docs/dist/document-check.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-docs/dist/document-check.js", + "node_modules/@the-open-engine/opcore-validation-docs/dist/dry-check.d.ts", + "node_modules/@the-open-engine/opcore-validation-docs/dist/dry-check.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-docs/dist/dry-check.js", + "node_modules/@the-open-engine/opcore-validation-docs/dist/existence-check.d.ts", + "node_modules/@the-open-engine/opcore-validation-docs/dist/existence-check.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-docs/dist/existence-check.js", "node_modules/@the-open-engine/opcore-validation-docs/dist/freshness.d.ts", "node_modules/@the-open-engine/opcore-validation-docs/dist/freshness.d.ts.map", "node_modules/@the-open-engine/opcore-validation-docs/dist/freshness.js", @@ -351,9 +852,21 @@ "node_modules/@the-open-engine/opcore-validation-docs/dist/index.d.ts", "node_modules/@the-open-engine/opcore-validation-docs/dist/index.d.ts.map", "node_modules/@the-open-engine/opcore-validation-docs/dist/index.js", + "node_modules/@the-open-engine/opcore-validation-docs/dist/length-check.d.ts", + "node_modules/@the-open-engine/opcore-validation-docs/dist/length-check.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-docs/dist/length-check.js", + "node_modules/@the-open-engine/opcore-validation-docs/dist/options.d.ts", + "node_modules/@the-open-engine/opcore-validation-docs/dist/options.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-docs/dist/options.js", + "node_modules/@the-open-engine/opcore-validation-docs/dist/rules-why-check.d.ts", + "node_modules/@the-open-engine/opcore-validation-docs/dist/rules-why-check.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-docs/dist/rules-why-check.js", "node_modules/@the-open-engine/opcore-validation-docs/dist/snapshot.d.ts", "node_modules/@the-open-engine/opcore-validation-docs/dist/snapshot.d.ts.map", "node_modules/@the-open-engine/opcore-validation-docs/dist/snapshot.js", + "node_modules/@the-open-engine/opcore-validation-docs/dist/staleness-check.d.ts", + "node_modules/@the-open-engine/opcore-validation-docs/dist/staleness-check.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-docs/dist/staleness-check.js", "node_modules/@the-open-engine/opcore-validation-docs/package.json", "node_modules/@the-open-engine/opcore-validation-policy/README.md", "node_modules/@the-open-engine/opcore-validation-policy/dist/check-packs.d.ts", @@ -433,15 +946,6 @@ "node_modules/@the-open-engine/opcore-validation-python/dist/process.d.ts", "node_modules/@the-open-engine/opcore-validation-python/dist/process.d.ts.map", "node_modules/@the-open-engine/opcore-validation-python/dist/process.js", - "node_modules/@the-open-engine/opcore-validation-python/dist/python-check-result.d.ts", - "node_modules/@the-open-engine/opcore-validation-python/dist/python-check-result.d.ts.map", - "node_modules/@the-open-engine/opcore-validation-python/dist/python-check-result.js", - "node_modules/@the-open-engine/opcore-validation-python/dist/python-context-result.d.ts", - "node_modules/@the-open-engine/opcore-validation-python/dist/python-context-result.d.ts.map", - "node_modules/@the-open-engine/opcore-validation-python/dist/python-context-result.js", - "node_modules/@the-open-engine/opcore-validation-python/dist/python-execution-workspace.d.ts", - "node_modules/@the-open-engine/opcore-validation-python/dist/python-execution-workspace.d.ts.map", - "node_modules/@the-open-engine/opcore-validation-python/dist/python-execution-workspace.js", "node_modules/@the-open-engine/opcore-validation-python/dist/project-config-files.d.ts", "node_modules/@the-open-engine/opcore-validation-python/dist/project-config-files.d.ts.map", "node_modules/@the-open-engine/opcore-validation-python/dist/project-config-files.js", @@ -499,6 +1003,15 @@ "node_modules/@the-open-engine/opcore-validation-python/dist/pytest-workspace.d.ts", "node_modules/@the-open-engine/opcore-validation-python/dist/pytest-workspace.d.ts.map", "node_modules/@the-open-engine/opcore-validation-python/dist/pytest-workspace.js", + "node_modules/@the-open-engine/opcore-validation-python/dist/python-check-result.d.ts", + "node_modules/@the-open-engine/opcore-validation-python/dist/python-check-result.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-python/dist/python-check-result.js", + "node_modules/@the-open-engine/opcore-validation-python/dist/python-context-result.d.ts", + "node_modules/@the-open-engine/opcore-validation-python/dist/python-context-result.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-python/dist/python-context-result.js", + "node_modules/@the-open-engine/opcore-validation-python/dist/python-execution-workspace.d.ts", + "node_modules/@the-open-engine/opcore-validation-python/dist/python-execution-workspace.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-python/dist/python-execution-workspace.js", "node_modules/@the-open-engine/opcore-validation-python/dist/relevant-tests-check.d.ts", "node_modules/@the-open-engine/opcore-validation-python/dist/relevant-tests-check.d.ts.map", "node_modules/@the-open-engine/opcore-validation-python/dist/relevant-tests-check.js", @@ -517,12 +1030,12 @@ "node_modules/@the-open-engine/opcore-validation-python/dist/ruff-config-proof.d.ts", "node_modules/@the-open-engine/opcore-validation-python/dist/ruff-config-proof.d.ts.map", "node_modules/@the-open-engine/opcore-validation-python/dist/ruff-config-proof.js", - "node_modules/@the-open-engine/opcore-validation-python/dist/ruff-execution.d.ts", - "node_modules/@the-open-engine/opcore-validation-python/dist/ruff-execution.d.ts.map", - "node_modules/@the-open-engine/opcore-validation-python/dist/ruff-execution.js", "node_modules/@the-open-engine/opcore-validation-python/dist/ruff-execution-workspace.d.ts", "node_modules/@the-open-engine/opcore-validation-python/dist/ruff-execution-workspace.d.ts.map", "node_modules/@the-open-engine/opcore-validation-python/dist/ruff-execution-workspace.js", + "node_modules/@the-open-engine/opcore-validation-python/dist/ruff-execution.d.ts", + "node_modules/@the-open-engine/opcore-validation-python/dist/ruff-execution.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-python/dist/ruff-execution.js", "node_modules/@the-open-engine/opcore-validation-python/dist/ruff-format-check.d.ts", "node_modules/@the-open-engine/opcore-validation-python/dist/ruff-format-check.d.ts.map", "node_modules/@the-open-engine/opcore-validation-python/dist/ruff-format-check.js", @@ -716,15 +1229,27 @@ "node_modules/@the-open-engine/opcore-validation-typescript/dist/lint-rules.d.ts", "node_modules/@the-open-engine/opcore-validation-typescript/dist/lint-rules.d.ts.map", "node_modules/@the-open-engine/opcore-validation-typescript/dist/lint-rules.js", + "node_modules/@the-open-engine/opcore-validation-typescript/dist/module-dependencies.d.ts", + "node_modules/@the-open-engine/opcore-validation-typescript/dist/module-dependencies.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-typescript/dist/module-dependencies.js", "node_modules/@the-open-engine/opcore-validation-typescript/dist/relevant-tests-check.d.ts", "node_modules/@the-open-engine/opcore-validation-typescript/dist/relevant-tests-check.d.ts.map", "node_modules/@the-open-engine/opcore-validation-typescript/dist/relevant-tests-check.js", + "node_modules/@the-open-engine/opcore-validation-typescript/dist/relevant-tests-evidence.d.ts", + "node_modules/@the-open-engine/opcore-validation-typescript/dist/relevant-tests-evidence.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-typescript/dist/relevant-tests-evidence.js", + "node_modules/@the-open-engine/opcore-validation-typescript/dist/script-kind.d.ts", + "node_modules/@the-open-engine/opcore-validation-typescript/dist/script-kind.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-typescript/dist/script-kind.js", "node_modules/@the-open-engine/opcore-validation-typescript/dist/source-files.d.ts", "node_modules/@the-open-engine/opcore-validation-typescript/dist/source-files.d.ts.map", "node_modules/@the-open-engine/opcore-validation-typescript/dist/source-files.js", "node_modules/@the-open-engine/opcore-validation-typescript/dist/syntax-check.d.ts", "node_modules/@the-open-engine/opcore-validation-typescript/dist/syntax-check.d.ts.map", "node_modules/@the-open-engine/opcore-validation-typescript/dist/syntax-check.js", + "node_modules/@the-open-engine/opcore-validation-typescript/dist/test-paths.d.ts", + "node_modules/@the-open-engine/opcore-validation-typescript/dist/test-paths.d.ts.map", + "node_modules/@the-open-engine/opcore-validation-typescript/dist/test-paths.js", "node_modules/@the-open-engine/opcore-validation-typescript/dist/type-check.d.ts", "node_modules/@the-open-engine/opcore-validation-typescript/dist/type-check.d.ts.map", "node_modules/@the-open-engine/opcore-validation-typescript/dist/type-check.js", @@ -935,13 +1460,6 @@ "node_modules/brace-expansion/dist/esm/index.js.map", "node_modules/brace-expansion/dist/esm/package.json", "node_modules/brace-expansion/package.json", - "node_modules/concat-map/.travis.yml", - "node_modules/concat-map/LICENSE", - "node_modules/concat-map/README.markdown", - "node_modules/concat-map/example/map.js", - "node_modules/concat-map/index.js", - "node_modules/concat-map/package.json", - "node_modules/concat-map/test/map.js", "node_modules/code-block-writer/LICENSE", "node_modules/code-block-writer/README.md", "node_modules/code-block-writer/esm/_dnt.test_shims.d.ts.map", @@ -971,6 +1489,13 @@ "node_modules/code-block-writer/script/utils/string_utils.d.ts.map", "node_modules/code-block-writer/script/utils/string_utils.js", "node_modules/code-block-writer/script/utils/string_utils.test.d.ts.map", + "node_modules/concat-map/.travis.yml", + "node_modules/concat-map/LICENSE", + "node_modules/concat-map/README.markdown", + "node_modules/concat-map/example/map.js", + "node_modules/concat-map/index.js", + "node_modules/concat-map/package.json", + "node_modules/concat-map/test/map.js", "node_modules/debug/LICENSE", "node_modules/debug/README.md", "node_modules/debug/package.json", diff --git a/tests/gate-negative-fixtures.test.mjs b/tests/gate-negative-fixtures.test.mjs index d4bda9d..f46d9bc 100644 --- a/tests/gate-negative-fixtures.test.mjs +++ b/tests/gate-negative-fixtures.test.mjs @@ -1,4 +1,4 @@ -import { describe, it } from "node:test"; +import { afterEach, describe, it } from "node:test"; import assert from "node:assert/strict"; import { cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; @@ -19,6 +19,7 @@ import { externalRuntimePackageDir } from "../scripts/stage-opcore-bundle.mjs"; const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const releaseDocsLockTimeoutMs = 900000; +const tempRepoRoots = new Set(); const copiedRepoSkips = new Set([ ".git", "node_modules", @@ -33,6 +34,13 @@ const copiedRepoSkips = new Set([ ]); describe("negative gate fixtures", () => { + afterEach(() => { + for (const tempRoot of tempRepoRoots) { + rmSync(tempRoot, { recursive: true, force: true }); + } + tempRepoRoots.clear(); + }); + it("rejects tracked TypeScript build info", () => { const repo = tempRepo(); writeFileSync(join(repo, "packages/contracts/tsconfig.tsbuildinfo"), "{}\n"); @@ -320,6 +328,7 @@ describe("negative gate fixtures", () => { function tempRepo(options = {}) { const tempRoot = mkdtempSync(join(tmpdir(), "opcore-gate-")); + tempRepoRoots.add(tempRoot); const repo = join(tempRoot, "repo"); withReleaseDocsLock(() => { cpSync(repoRoot, repo, { diff --git a/tests/graph-ephemeral-snapshot.test.mjs b/tests/graph-ephemeral-snapshot.test.mjs index a86190a..a819d15 100644 --- a/tests/graph-ephemeral-snapshot.test.mjs +++ b/tests/graph-ephemeral-snapshot.test.mjs @@ -68,6 +68,50 @@ describe("ephemeral graph snapshots", () => { assert.equal(existsSync(materializedRepo), false); }); + it("materializes root tsconfig so exact snapshots resolve source aliases", async () => { + const logicalRepo = { repoId: "target", repoRoot: "/target/repo" }; + const files = new Map([ + [ + "tsconfig.json", + JSON.stringify({ compilerOptions: { baseUrl: ".", paths: { "@example/pkg": ["src/index.ts"] } } }) + ], + ["src/index.ts", "export const publicValue = 1;\n"], + [ + "tests/package-contract.test.ts", + "import { publicValue } from '@example/pkg'; test('public value', () => publicValue);\n" + ] + ]); + const snapshot = await createEphemeralGraphSnapshot({ + logicalRepo, + sourceUniverse: { paths: [...files.keys()], complete: true }, + readFile: (path) => files.has(path) + ? { status: "found", content: files.get(path) } + : { status: "missing" } + }); + try { + assert.deepEqual(snapshot.materializedPaths, [ + "src/index.ts", + "tests/package-contract.test.ts", + "tsconfig.json" + ]); + const result = snapshot.factQuery({ + requestId: "exact-test-evidence", + repo: logicalRepo, + schemaVersion: 1, + mode: "required", + selector: { kind: "edges", edgeKinds: ["TESTED_BY"] } + }); + assert.equal(result.status.state, "available"); + assert.ok(result.edges.some((edge) => + edge.kind === "TESTED_BY" && + edge.from === "file:src/index.ts" && + edge.to === "file:tests/package-contract.test.ts" + )); + } finally { + snapshot.dispose(); + } + }); + it("rejects incomplete universes and removes materialized state after build errors", async () => { await assert.rejects(createEphemeralGraphSnapshotWithOperations({ logicalRepo: { repoId: "target" }, diff --git a/tests/graph-release-readiness.test.mjs b/tests/graph-release-readiness.test.mjs index 81f07e8..408bb87 100644 --- a/tests/graph-release-readiness.test.mjs +++ b/tests/graph-release-readiness.test.mjs @@ -151,12 +151,12 @@ describe("graph release readiness receipt", () => { function tempGraphPackageInspectionRepo() { const tempRoot = mkdtempSync(join(tmpdir(), "opcore-graph-package-test-")); mkdirSync(join(tempRoot, "scripts"), { recursive: true }); - mkdirSync(join(tempRoot, "packages/contracts/dist"), { recursive: true }); + mkdirSync(join(tempRoot, "packages/contracts"), { recursive: true }); mkdirSync(join(tempRoot, "packages/graph"), { recursive: true }); cpSync(join(repoRoot, "package.json"), join(tempRoot, "package.json")); cpSync(join(repoRoot, ".npmrc"), join(tempRoot, ".npmrc")); cpSync(join(repoRoot, "scripts/generate-graph-release-receipt.mjs"), join(tempRoot, "scripts/generate-graph-release-receipt.mjs")); - cpSync(join(repoRoot, "packages/contracts/dist/index.js"), join(tempRoot, "packages/contracts/dist/index.js")); + cpSync(join(repoRoot, "packages/contracts/dist"), join(tempRoot, "packages/contracts/dist"), { recursive: true }); cpSync(join(repoRoot, "packages/graph/package.json"), join(tempRoot, "packages/graph/package.json")); cpSync(join(repoRoot, "packages/graph/README.md"), join(tempRoot, "packages/graph/README.md")); cpSync(join(repoRoot, "packages/graph/dist"), join(tempRoot, "packages/graph/dist"), { recursive: true }); diff --git a/tests/graph-store-conformance.test.mjs b/tests/graph-store-conformance.test.mjs index 2855963..be42f0c 100644 --- a/tests/graph-store-conformance.test.mjs +++ b/tests/graph-store-conformance.test.mjs @@ -70,7 +70,7 @@ describe("GraphProvider SQLite store conformance", () => { { kind: "IMPLEMENTS", count: 2 }, { kind: "IMPORTS_FROM", count: 11 }, { kind: "INHERITS", count: 2 }, - { kind: "TESTED_BY", count: 4 } + { kind: "TESTED_BY", count: 7 } ] ); assert.ok( diff --git a/tests/opcore-facade.test.mjs b/tests/opcore-facade.test.mjs index f1966da..ca7cfff 100644 --- a/tests/opcore-facade.test.mjs +++ b/tests/opcore-facade.test.mjs @@ -17,6 +17,7 @@ import { compactScanValidationResult } from "../packages/opcore/dist/scan-valida const repoRoot = fileURLToPath(new URL("..", import.meta.url)); const opcoreBin = resolve(repoRoot, "packages/opcore/dist/index.js"); const sourceFixtureRoot = resolve(repoRoot, "packages/fixtures/source-extraction/wave1"); +const systemToolPath = "/usr/bin:/bin:/opt/homebrew/bin"; describe("opcore public facade", () => { it("runs zero-command scan with coverage-first output and only .opcore artifacts", () => { @@ -1469,7 +1470,9 @@ printf '%s\\n' '${JSON.stringify({ if (fixture.gitInit) run("git", ["init"], temp, 0); for (const [path, content] of fixture.files) writeFixtureFile(temp, path, content); - const result = parseJson(runOpcore(["init", "--repo", temp, "--json"], temp, 0).stdout); + const result = parseJson( + runOpcore(["init", "--repo", temp, "--json"], temp, 0, { ...process.env, PATH: systemToolPath }).stdout + ); const languages = result.opcoreInit.settings.languages.map((entry) => entry.language).sort(); assert.equal(result.opcoreInit.scan.totalFiles, fixture.expect.totalFiles, fixture.name); diff --git a/tests/scaffold.test.mjs b/tests/scaffold.test.mjs index 56ef734..ef82da5 100644 --- a/tests/scaffold.test.mjs +++ b/tests/scaffold.test.mjs @@ -3,6 +3,47 @@ import assert from "node:assert/strict"; import { existsSync, readFileSync } from "node:fs"; const readJson = (path) => JSON.parse(readFileSync(path, "utf8")); +const selfValidationCheckIds = [ + "typescript.syntax", + "typescript.types", + "typescript.lint", + "typescript.import-graph", + "typescript.dead-code", + "typescript.function-metrics", + "typescript.relevant-tests", + "typescript.file-length", + "rust.source-hygiene", + "rust.fmt", + "rust.cargo-check", + "rust.clippy", + "rust.rustdoc", + "rust.import-graph", + "rust.dead-code", + "rust.graph-signals", + "rust.unused-deps", + "rust.file-length", + "rust.function-metrics", + "python.syntax", + "python.source-hygiene", + "python.ruff-lint", + "python.ruff-format", + "python.types", + "python.import-graph", + "python.dead-code", + "python.relevant-tests", + "python.pytest", + "docs.existence", + "docs.staleness", + "docs.freshness", + "docs.length", + "docs.dry", + "docs.content-quality", + "docs.code-blocks", + "docs.rules-why", + "docs.hub-coverage", + "docs.subtree-coverage", + "clone.duplication" +]; describe("Opcore scaffold", () => { it("keeps opcore, graph, edit, and validation as separate package tracks", () => { @@ -104,3 +145,20 @@ describe("Opcore scaffold", () => { } }); }); + +describe("Opcore self-validation policy", () => { + it("selects every registered check with strict complexity thresholds", () => { + const policy = readJson(".opcore/config").validation; + assert.deepEqual(policy.adapters, ["typescript", "rust", "python", "docs", "clone"]); + assert.deepEqual(policy.checks.defaults, selfValidationCheckIds); + assert.deepEqual(policy.checks.disabled, []); + assert.deepEqual(policy.checks.typescript, { + fileLength: { maxFileLines: 300 }, + functionMetrics: { maxFunctionLines: 80, maxComplexity: 10, maxParams: 4 } + }); + assert.deepEqual(policy.checks.rust, { + fileLength: { maxFileLines: 500 }, + functionMetrics: { maxFunctionLines: 80, maxComplexity: 10, maxParams: 4 } + }); + }); +}); diff --git a/tests/source-package-contracts.test.ts b/tests/source-package-contracts.test.ts new file mode 100644 index 0000000..29b8481 --- /dev/null +++ b/tests/source-package-contracts.test.ts @@ -0,0 +1,19 @@ +import { commandRouterManifest } from "@the-open-engine/opcore-contracts"; +import { createEditCommandAdapter } from "@the-open-engine/opcore-edit"; +import { createEphemeralGraphSnapshot } from "@the-open-engine/opcore-graph"; +import { createDocsValidationChecks } from "@the-open-engine/opcore-validation-docs"; +import { createTypeScriptValidationChecks } from "@the-open-engine/opcore-validation-typescript"; +import { routeOpcoreCommand } from "opcore"; + +assertRuntimeExport("commandRouterManifest", commandRouterManifest, "object"); +assertRuntimeExport("createEditCommandAdapter", createEditCommandAdapter, "function"); +assertRuntimeExport("createEphemeralGraphSnapshot", createEphemeralGraphSnapshot, "function"); +assertRuntimeExport("createDocsValidationChecks", createDocsValidationChecks, "function"); +assertRuntimeExport("createTypeScriptValidationChecks", createTypeScriptValidationChecks, "function"); +assertRuntimeExport("routeOpcoreCommand", routeOpcoreCommand, "function"); + +function assertRuntimeExport(name: string, value: unknown, expectedType: "function" | "object"): void { + if (typeof value !== expectedType) { + throw new Error(`${name} must be a runtime ${expectedType} export`); + } +} diff --git a/tests/validation-cli.test.mjs b/tests/validation-cli.test.mjs index 482e35f..afb2710 100644 --- a/tests/validation-cli.test.mjs +++ b/tests/validation-cli.test.mjs @@ -1,4 +1,4 @@ -import { describe, it } from "node:test"; +import { it } from "node:test"; import assert from "node:assert/strict"; import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; @@ -70,8 +70,9 @@ const availableCheckIds = [ ...docsCheckIds, ...cloneCheckIds ]; +const repoWideDocsCheckIds = ["docs.existence", "docs.hub-coverage", "docs.subtree-coverage"]; +const rootFileConfiguredCheckIds = availableCheckIds.filter((checkId) => !repoWideDocsCheckIds.includes(checkId)); -describe("validation CLI", () => { it("keeps opcore status separate from validation execution results", async () => { const temp = mkdtempSync(join(tmpdir(), "opcore-validation-status-")); try { @@ -145,7 +146,7 @@ describe("validation CLI", () => { const result = run(args, [0, 1]); assert.equal(result.owner, "validation"); assert.equal(result.exitCode === 0 || result.exitCode === 1, true); - assert.deepEqual(result.validationResult.manifest.checks, executableDefaultCheckIds); + assert.deepEqual(result.validationResult.manifest.checks, rootFileConfiguredCheckIds); assert.equal(Object.hasOwn(result.validationResult.manifest, "entries"), false); assert.equal(Object.hasOwn(result.validationResult.manifest, "runs"), false); assert.equal(Object.hasOwn(result.validationResult.manifest, "skippedChecks"), false); @@ -319,111 +320,7 @@ describe("validation CLI", () => { it("normalizes repo validation config", async () => { const temp = mkdtempSync(join(tmpdir(), "opcore-validation-config-")); try { - writeRepoConfigObject(temp, { - schemaVersion: 1, - kind: "opcore_init_config", - onboarding: { - scan: { - totalFiles: 1 - } - }, - validation: { - adapters: ["typescript", "rust", "docs", "clone"], - timeoutMs: 120000, - pathPolicy: { - include: ["packages/", "scripts/"], - exclude: ["dist/**", ".agents"] - }, - checks: { - packs: ["./checks/policy.cjs"], - disabled: ["typescript.types"], - defaults: ["docs.existence", "docs.freshness"], - typescript: { - fileLength: { - maxFileLines: 600 - }, - functionMetrics: { - maxFunctionLines: 120, - maxComplexity: 10, - maxParams: 4 - }, - lint: { - repoPlugin: "./eslint-local-rules/index.js", - cacheDependencyGlobs: ["CLAUDE.md", "**/CLAUDE.md"] - }, - importGraph: { - ignoreTypeOnlyImports: true, - layerRules: [ - { - name: "no-client-to-server", - from: "%/client/src/%", - to: "%/server/%" - } - ] - }, - deadCode: { - entrypoints: ["scripts/build-package.mjs"] - } - }, - rust: { - fileLength: { - maxFileLines: 500 - }, - functionMetrics: { - maxFunctionLines: 80, - maxComplexity: 10, - maxParams: 4 - }, - commandGates: [ - { - id: "rust-gate.test", - command: "cargo", - args: ["test"], - cwd: ".", - timeoutMs: 120000 - } - ] - }, - docs: { - enabled: { - existence: true, - freshness: true, - staleness: false, - length: true, - hubCoverage: true, - subtreeCoverage: true - }, - policy: { - filenames: ["CLAUDE.md", "AGENTS.md"], - requiredPaths: ["."], - requireRoot: true, - minimumContentLength: 1, - maxLines: 220, - maxSectionLines: 80 - }, - history: { - maxStaleDays: 90 - }, - hubCoverage: { - minFanIn: 5, - minFanOut: 5, - requireExplicitMention: true - }, - subtreeCoverage: { - minLoc: 20000 - } - }, - clone: { - windowSize: 16, - minLines: 16, - threshold: 5, - partitions: [["server", "shared"], ["client"], ["platform-cli"]], - exclude: ["docs/**"], - modes: ["staged", "changed", "files"] - } - } - } - }); + writeRepoConfigObject(temp, normalizedRepoConfigFixture()); const { readOpcoreRepoConfig } = await import("../packages/opcore/dist/repo-validation-config.js"); const config = readOpcoreRepoConfig(temp); @@ -1439,7 +1336,6 @@ describe("validation CLI", () => { rmSync(temp, { recursive: true, force: true }); } }); -}); function validRequest(repoRootPath) { return { @@ -1451,6 +1347,116 @@ function validRequest(repoRootPath) { }; } +function normalizedRepoConfigFixture() { + return { + schemaVersion: 1, + kind: "opcore_init_config", + onboarding: { scan: { totalFiles: 1 } }, + validation: { + adapters: ["typescript", "rust", "docs", "clone"], + timeoutMs: 120000, + pathPolicy: { + include: ["packages/", "scripts/"], + exclude: ["dist/**", ".agents"] + }, + checks: normalizedChecksFixture() + } + }; +} + +function normalizedChecksFixture() { + return { + packs: ["./checks/policy.cjs"], + disabled: ["typescript.types"], + defaults: ["docs.existence", "docs.freshness"], + typescript: normalizedTypeScriptChecksFixture(), + rust: normalizedRustChecksFixture(), + docs: normalizedDocsChecksFixture(), + clone: { + windowSize: 16, + minLines: 16, + threshold: 5, + partitions: [["server", "shared"], ["client"], ["platform-cli"]], + exclude: ["docs/**"], + modes: ["staged", "changed", "files"] + } + }; +} + +function normalizedTypeScriptChecksFixture() { + return { + fileLength: { maxFileLines: 600 }, + functionMetrics: { + maxFunctionLines: 120, + maxComplexity: 10, + maxParams: 4 + }, + lint: { + repoPlugin: "./eslint-local-rules/index.js", + cacheDependencyGlobs: ["CLAUDE.md", "**/CLAUDE.md"] + }, + importGraph: { + ignoreTypeOnlyImports: true, + layerRules: [ + { + name: "no-client-to-server", + from: "%/client/src/%", + to: "%/server/%" + } + ] + }, + deadCode: { entrypoints: ["scripts/build-package.mjs"] } + }; +} + +function normalizedRustChecksFixture() { + return { + fileLength: { maxFileLines: 500 }, + functionMetrics: { + maxFunctionLines: 80, + maxComplexity: 10, + maxParams: 4 + }, + commandGates: [ + { + id: "rust-gate.test", + command: "cargo", + args: ["test"], + cwd: ".", + timeoutMs: 120000 + } + ] + }; +} + +function normalizedDocsChecksFixture() { + return { + enabled: { + existence: true, + freshness: true, + staleness: false, + length: true, + hubCoverage: true, + subtreeCoverage: true + }, + policy: { + filenames: ["CLAUDE.md", "AGENTS.md"], + requiredPaths: ["."], + requireRoot: true, + minimumContentLength: 1, + maxLines: 220, + maxSectionLines: 80 + }, + history: { maxStaleDays: 90 }, + hubCoverage: { + minFanIn: 5, + minFanOut: 5, + requireExplicitMention: true + }, + subtreeCoverage: { minLoc: 20000 } + }; +} + function writeRepoConfig(repoRootPath, packs) { writeRepoConfigObject(repoRootPath, { schemaVersion: 1, diff --git a/tests/validation-docs.test.mjs b/tests/validation-docs.test.mjs index 361a0c4..0a38cd3 100644 --- a/tests/validation-docs.test.mjs +++ b/tests/validation-docs.test.mjs @@ -139,6 +139,22 @@ const expectedDocsCheckIds = [ assert.equal(result.diagnostics.every((diagnostic) => ["AGENTS.md", "CLAUDE.md", "docs/guide.md"].includes(diagnostic.path)), true); }); + it("accepts an explicit WHY rationale in a context-doc rule", async () => { + const result = await runner({ + files: { + "AGENTS.md": validGuidance("rule rationale") + } + }).runValidation( + request({ + checks: [DOCS_RULES_WHY_CHECK_ID], + scope: { kind: "files", files: ["AGENTS.md"] } + }) + ); + + assert.equal(result.status, "passed"); + assert.deepEqual(result.diagnostics, []); + }); + it("applies docs policy maximum line and section limits", async () => { const result = await runner({ files: { diff --git a/tests/validation-typescript.test.mjs b/tests/validation-typescript.test.mjs index 21ff5cc..cc30e0b 100644 --- a/tests/validation-typescript.test.mjs +++ b/tests/validation-typescript.test.mjs @@ -1212,6 +1212,64 @@ describe("validation-typescript adapter", () => { assert.deepEqual(result.diagnostics, []); }); + it("does not report a runtime cycle for pure type-only imports", async () => { + const result = await runner({ + files: { + "src/a.ts": "import type { B } from './b';\nexport interface A { child?: B }\n", + "src/b.ts": "import type { A } from './a';\nexport interface B { parent?: A }\n", + "src/index.ts": "import type { A } from './a';\nexport type Value = A;\n" + } + }).runValidation( + request({ + checks: [TYPE_SCRIPT_IMPORT_GRAPH_CHECK_ID] + }) + ); + + assert.equal(result.status, "passed", JSON.stringify(result.diagnostics, null, 2)); + assert.deepEqual(result.diagnostics, []); + }); + + it("reports runtime cycles when a declaration mixes type and value imports", async () => { + const result = await runner({ + files: { + "src/a.ts": "import { type B, b } from './b';\nexport type A = B;\nexport const a = b;\n", + "src/b.ts": "import { a } from './a';\nexport interface B { value: number }\nexport const b = a;\n", + "src/index.ts": "import { a } from './a';\nexport const value = a;\n" + } + }).runValidation( + request({ + checks: [TYPE_SCRIPT_IMPORT_GRAPH_CHECK_ID] + }) + ); + + assert.equal(result.status, "passed", JSON.stringify(result.diagnostics, null, 2)); + assert.deepEqual(result.diagnostics.map((diagnostic) => diagnostic.code), ["TS_IMPORT_GRAPH_CYCLE"]); + assert.equal(result.diagnostics[0].message, "TypeScript import cycle detected: src/a.ts -> src/b.ts -> src/a.ts"); + }); + + it("retains missing graph-edge diagnostics for type-only imports", async () => { + const result = await runner({ + files: { + "src/index.ts": "import type { Value } from './types';\nexport type Result = Value;\n", + "src/types.ts": "export interface Value { id: string }\n" + }, + graphProviderClient: graphClient({ + factQuery: (query) => availableFactResult(query, [], []) + }) + }).runValidation( + request({ + checks: [TYPE_SCRIPT_IMPORT_GRAPH_CHECK_ID] + }) + ); + + assert.equal(result.status, "passed"); + assert.deepEqual(result.diagnostics.map((diagnostic) => diagnostic.code), ["TS_IMPORT_GRAPH_MISSING_EDGE"]); + assert.equal( + result.diagnostics[0].message, + "Missing IMPORTS_FROM graph edge for src/index.ts -> src/types.ts" + ); + }); + it("reports configured TypeScript import layer rule violations", async () => { const result = await runner({ checks: createTypeScriptValidationChecks({ @@ -1870,105 +1928,46 @@ describe("validation-typescript adapter", () => { ); }); - it("reports unreferenced source files and unused exported types from graph facts", async () => { + it("does not report entrypoint-reachable exported types as unsupported", async () => { const nodes = [ + fileNode("src/index.ts"), + fileNode("src/types.ts"), { - id: "file:src/index.ts", - kind: "File", - path: "src/index.ts", - attributes: { - language: "typescript" - } - }, - { - id: "file:src/used.ts", - kind: "File", - path: "src/used.ts", - attributes: { - language: "typescript" - } - }, - { - id: "file:src/orphan.ts", - kind: "File", - path: "src/orphan.ts", - attributes: { - language: "typescript" - } - }, - { - id: "type:src/orphan.ts#ReferencedShape", - kind: "Type", - path: "src/orphan.ts", - name: "ReferencedShape", - attributes: { - exported: true, - exportKind: "named", - exportName: "ReferencedShape" - } - }, - { - id: "type:src/orphan.ts#LocalExtension", - kind: "Type", - path: "src/orphan.ts", - name: "LocalExtension", - attributes: { - exported: false - } - }, - { - id: "type:src/orphan.ts#UnusedShape", + id: "type:src/types.ts#Shape", kind: "Type", - path: "src/orphan.ts", - name: "UnusedShape", - attributes: { - exported: true, - exportKind: "named", - exportName: "UnusedShape" - } - }, - { - id: "variable:src/used.ts#used", - kind: "Variable", - path: "src/used.ts", - name: "used", - attributes: { - exported: false - } + path: "src/types.ts", + name: "Shape", + attributes: { exported: true, exportKind: "named", exportName: "Shape" } } ]; const edges = [ - { - kind: "IMPORTS_FROM", - from: "file:src/index.ts", - to: "file:src/used.ts" - }, - { - kind: "CONTAINS", - from: "file:src/used.ts", - to: "variable:src/used.ts#used" - }, - { - kind: "CONTAINS", - from: "file:src/orphan.ts", - to: "type:src/orphan.ts#ReferencedShape" - }, - { - kind: "CONTAINS", - from: "file:src/orphan.ts", - to: "type:src/orphan.ts#LocalExtension" - }, - { - kind: "CONTAINS", - from: "file:src/orphan.ts", - to: "type:src/orphan.ts#UnusedShape" - }, - { - kind: "INHERITS", - from: "type:src/orphan.ts#LocalExtension", - to: "type:src/orphan.ts#ReferencedShape" - } + importEdge("src/index.ts", "src/types.ts"), + containsEdge("src/types.ts", "type:src/types.ts#Shape") ]; + const result = await runner({ + files: { + "package.json": JSON.stringify({ main: "./src/index.ts" }), + "src/index.ts": "export type { Shape } from './types';\n", + "src/types.ts": "export interface Shape { width: number }\n" + }, + graphProviderClient: graphClient({ + status: (validationRequest) => ({ + ...availableStatus(validationRequest.graph.mode, validationRequest.repo), + handshake: graphHandshake() + }), + factQuery: (query) => availableFactResult(query, nodes, edges) + }) + }).runValidation(request({ + checks: [TYPE_SCRIPT_DEAD_CODE_CHECK_ID], + scope: { kind: "files", files: ["src/types.ts"] } + })); + + assert.equal(result.status, "passed"); + assert.deepEqual(result.diagnostics, []); + }); + + it("reports unreferenced source files and unused exported types from graph facts", async () => { + const { nodes, edges } = unusedTypeGraphFacts(); const result = await runner({ files: { @@ -2502,46 +2501,9 @@ describe("validation-typescript adapter", () => { it("does not report used direct re-exported callables as dead exports with the real graph provider", () => { const repo = mkdtempSync(join(tmpdir(), "lattice-dead-code-reexport-")); try { - mkdirSync(join(repo, "src"), { recursive: true }); - writeFileSync( - join(repo, "tsconfig.json"), - `${JSON.stringify( - { - compilerOptions: { - target: "ES2022", - module: "ESNext", - moduleResolution: "Bundler", - strict: true - }, - include: ["src/**/*.ts"] - }, - null, - 2 - )}\n` - ); - writeFileSync(join(repo, "src/source.ts"), "export function add() { return 1; }\n"); - writeFileSync(join(repo, "src/barrel.ts"), "export { add as addFromBarrel } from './source';\n"); - writeFileSync( - join(repo, "src/index.ts"), - "import { addFromBarrel } from './barrel';\nfunction run() { return addFromBarrel(); }\nrun();\n" - ); - - const graphBuild = spawnSync(process.execPath, ["packages/opcore/dist/advanced/index.js", "graph", "build", "--repo", repo, "--json"], { - cwd: process.cwd(), - encoding: "utf8" - }); - assert.equal(graphBuild.status, 0, graphBuild.stderr || graphBuild.stdout); - - const edgeQuery = spawnSync( - process.execPath, - ["packages/opcore/dist/advanced/index.js", "graph", "query", "--repo", repo, "--kind", "edges", "--json"], - { - cwd: process.cwd(), - encoding: "utf8" - } - ); - assert.equal(edgeQuery.status, 0, edgeQuery.stderr || edgeQuery.stdout); - const edges = JSON.parse(edgeQuery.stdout).graphQuery.edges; + writeDirectReexportFixture(repo); + runOpcoreJson(repo, ["graph", "build"]); + const edges = runOpcoreJson(repo, ["graph", "query", "--kind", "edges"]).graphQuery.edges; assert.equal( edges.some( (edge) => @@ -2550,16 +2512,8 @@ describe("validation-typescript adapter", () => { true ); - const nodeQuery = spawnSync( - process.execPath, - ["packages/opcore/dist/advanced/index.js", "graph", "query", "--repo", repo, "--kind", "nodes", "--json"], - { - cwd: process.cwd(), - encoding: "utf8" - } - ); - assert.equal(nodeQuery.status, 0, nodeQuery.stderr || nodeQuery.stdout); - const fileNode = JSON.parse(nodeQuery.stdout).graphQuery.nodes.find((node) => node.id === "file:src/barrel.ts"); + const nodes = runOpcoreJson(repo, ["graph", "query", "--kind", "nodes"]).graphQuery.nodes; + const fileNode = nodes.find((node) => node.id === "file:src/barrel.ts"); assert.deepEqual(fileNode?.attributes?.exports, [ { kind: "named", @@ -2571,30 +2525,17 @@ describe("validation-typescript adapter", () => { } ]); - const check = spawnSync( - process.execPath, - [ - "packages/opcore/dist/advanced/index.js", - "check", - "files", - "src/source.ts", - "src/barrel.ts", - "src/index.ts", - "--repo", - repo, - "--checks", - TYPE_SCRIPT_DEAD_CODE_CHECK_ID, - "--graph-mode", - "required", - "--json" - ], - { - cwd: process.cwd(), - encoding: "utf8" - } - ); - assert.equal(check.status, 0, check.stderr || check.stdout); - const payload = JSON.parse(check.stdout); + const payload = runOpcoreJson(repo, [ + "check", + "files", + "src/source.ts", + "src/barrel.ts", + "src/index.ts", + "--checks", + TYPE_SCRIPT_DEAD_CODE_CHECK_ID, + "--graph-mode", + "required" + ]); assert.deepEqual( payload.validationResult.diagnostics.map((diagnostic) => diagnostic.code), ["TS_DEAD_CODE_UNUSED_FILE"] @@ -2608,7 +2549,7 @@ describe("validation-typescript adapter", () => { } }); - it("finds relevant tests from symbol TESTED_BY endpoints", async () => { + it("finds direct relevant tests from symbol TESTED_BY endpoints", async () => { const result = await runner({ files: { "src/index.ts": "export const value = 1;" @@ -2636,7 +2577,131 @@ describe("validation-typescript adapter", () => { ); assert.equal(result.status, "passed"); - assert.deepEqual(result.diagnostics.map((diagnostic) => diagnostic.code), ["TS_RELEVANT_TESTS_FOUND"]); + assert.deepEqual(result.diagnostics, []); + }); + + it("inherits relevant-test evidence through directed reverse importers", async () => { + const edges = [ + importEdge("src/index.ts", "src/implementation.ts"), + { + kind: "TESTED_BY", + from: "function:src/index.ts#value", + to: "test:src/index.test.ts#covers value" + } + ]; + const result = await runner({ + files: { + "src/implementation.ts": "export const value = 1;", + "src/index.ts": "export { value } from './implementation';" + }, + graphProviderClient: graphClient({ + factQuery: (query) => availableFactResult(query, [], query.selector.kind === "edges" ? edges : []) + }) + }).runValidation( + request({ + checks: [TYPE_SCRIPT_RELEVANT_TESTS_CHECK_ID], + scope: { kind: "files", files: ["src/implementation.ts"] } + }) + ); + + assert.equal(result.status, "passed"); + assert.deepEqual(result.diagnostics, []); + }); + + it("does not inherit relevant-test evidence from unrelated tested files", async () => { + const edges = [ + importEdge("src/index.ts", "src/other.ts"), + { + kind: "TESTED_BY", + from: "file:src/index.ts", + to: "test:src/index.test.ts#covers index" + } + ]; + const result = await runner({ + files: { + "src/implementation.ts": "export const value = 1;", + "src/index.ts": "export { other } from './other';", + "src/other.ts": "export const other = 2;" + }, + graphProviderClient: graphClient({ + factQuery: (query) => availableFactResult(query, [], query.selector.kind === "edges" ? edges : []) + }) + }).runValidation( + request({ + checks: [TYPE_SCRIPT_RELEVANT_TESTS_CHECK_ID], + scope: { kind: "files", files: ["src/implementation.ts"] } + }) + ); + + assert.equal(result.status, "passed"); + assert.deepEqual(result.diagnostics.map((diagnostic) => diagnostic.code), ["TS_RELEVANT_TESTS_ABSENT"]); + }); + + it("traverses cyclic reverse importers once while finding relevant tests", async () => { + const edges = [ + importEdge("src/index.ts", "src/implementation.ts"), + importEdge("src/helper.ts", "src/index.ts"), + importEdge("src/index.ts", "src/helper.ts"), + { + kind: "TESTED_BY", + from: "function:src/helper.ts#exercise", + to: "test:src/helper.test.ts#covers exercise" + } + ]; + const result = await runner({ + files: { + "src/implementation.ts": "export const value = 1;", + "src/index.ts": "export { value } from './implementation';\nexport { exercise } from './helper';", + "src/helper.ts": "export { value } from './index';\nexport const exercise = true;" + }, + graphProviderClient: graphClient({ + factQuery: (query) => availableFactResult(query, [], query.selector.kind === "edges" ? edges : []) + }) + }).runValidation( + request({ + checks: [TYPE_SCRIPT_RELEVANT_TESTS_CHECK_ID], + scope: { kind: "files", files: ["src/implementation.ts"] } + }) + ); + + assert.equal(result.status, "passed"); + assert.deepEqual(result.diagnostics, []); + }); + + it("fails closed when required relevant-test graph queries fail", async () => { + const result = await runner({ + graphProviderClient: graphClient({ + status: (validationRequest) => availableStatus(validationRequest.graph.mode, validationRequest.repo), + factQuery: () => ({ + status: graphFailure("error", "query_failed", "required") + }) + }) + }).runValidation( + request({ + checks: [TYPE_SCRIPT_RELEVANT_TESTS_CHECK_ID], + graph: { + mode: "required", + provider: "opcore-graph" + } + }) + ); + + assert.equal(result.status, "provider_failure"); + assert.equal(result.failure.category, "provider_failure"); + }); + + it("does not require relevant-test evidence for test files", async () => { + const result = await runner({ + files: { + "src/index.test.ts": "test('works', () => {});\n" + } + }).runValidation(request({ + checks: [TYPE_SCRIPT_RELEVANT_TESTS_CHECK_ID], + scope: { kind: "files", files: ["src/index.test.ts"] } + })); + + assert.equal(result.status, "skipped"); + assert.deepEqual(result.diagnostics, []); }); it("maps graph query failures to runner provider_failure", async () => { @@ -2725,6 +2790,90 @@ describe("validation-typescript adapter", () => { }); }); +function unusedTypeGraphFacts() { + const nodes = [ + fileNode("src/index.ts"), + fileNode("src/used.ts"), + fileNode("src/orphan.ts"), + { + id: "type:src/orphan.ts#ReferencedShape", + kind: "Type", + path: "src/orphan.ts", + name: "ReferencedShape", + attributes: { exported: true, exportKind: "named", exportName: "ReferencedShape" } + }, + { + id: "type:src/orphan.ts#LocalExtension", + kind: "Type", + path: "src/orphan.ts", + name: "LocalExtension", + attributes: { exported: false } + }, + { + id: "type:src/orphan.ts#UnusedShape", + kind: "Type", + path: "src/orphan.ts", + name: "UnusedShape", + attributes: { exported: true, exportKind: "named", exportName: "UnusedShape" } + }, + { + id: "variable:src/used.ts#used", + kind: "Variable", + path: "src/used.ts", + name: "used", + attributes: { exported: false } + } + ]; + const edges = [ + importEdge("src/index.ts", "src/used.ts"), + containsEdge("src/used.ts", "variable:src/used.ts#used"), + containsEdge("src/orphan.ts", "type:src/orphan.ts#ReferencedShape"), + containsEdge("src/orphan.ts", "type:src/orphan.ts#LocalExtension"), + containsEdge("src/orphan.ts", "type:src/orphan.ts#UnusedShape"), + { + kind: "INHERITS", + from: "type:src/orphan.ts#LocalExtension", + to: "type:src/orphan.ts#ReferencedShape" + } + ]; + return { nodes, edges }; +} + +function writeDirectReexportFixture(repo) { + mkdirSync(join(repo, "src"), { recursive: true }); + writeFileSync( + join(repo, "tsconfig.json"), + `${JSON.stringify({ + compilerOptions: { + target: "ES2022", + module: "ESNext", + moduleResolution: "Bundler", + strict: true + }, + include: ["src/**/*.ts"] + })}\n` + ); + writeFileSync(join(repo, "src/source.ts"), "export function add() { return 1; }\n"); + writeFileSync(join(repo, "src/barrel.ts"), "export { add as addFromBarrel } from './source';\n"); + writeFileSync( + join(repo, "src/index.ts"), + "import { addFromBarrel } from './barrel';\nfunction run() { return addFromBarrel(); }\nrun();\n" + ); +} + +function runOpcoreJson(repo, args) { + const result = spawnSync( + process.execPath, + ["packages/opcore/dist/advanced/index.js", ...args, "--repo", repo, "--json"], + { + cwd: process.cwd(), + encoding: "utf8" + } + ); + assert.equal(result.status, 0, result.stderr || result.stdout); + return JSON.parse(result.stdout); +} + function runner(options = {}) { return createValidationRunner({ workspace: workspace(options), diff --git a/tsconfig.json b/tsconfig.json index 8d6139d..94d7fac 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,28 @@ { + "extends": "./tsconfig.base.json", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@the-open-engine/opcore-contracts": [ + "packages/contracts/src/index.ts" + ], + "@the-open-engine/opcore-edit": [ + "packages/edit/src/index.ts" + ], + "@the-open-engine/opcore-graph": [ + "packages/graph/src/index.ts" + ], + "@the-open-engine/opcore-validation-docs": [ + "packages/validation-docs/src/index.ts" + ], + "@the-open-engine/opcore-validation-typescript": [ + "packages/validation-typescript/src/index.ts" + ], + "opcore": [ + "packages/opcore/src/index.ts" + ] + } + }, "files": [], "references": [ { From 4bd7f8c2f760f95efe9669b5ceedb2b5a4491830 Mon Sep 17 00:00:00 2001 From: Tom Dupuis <60640908+tomdps@users.noreply.github.com> Date: Sat, 8 Aug 2026 10:07:47 +0000 Subject: [PATCH 3/5] fix(ci): provision cargo-udeps for self-check --- .github/workflows/ci.yml | 8 + AGENTS.md | 1 + CLAUDE.md | 1 + .../validation-rust/src/unused-deps-check.ts | 151 +++++++++++------- tests/helpers/validation-rust-fixtures.mjs | 6 +- tests/native-packaging-policy.test.mjs | 5 + tests/validation-rust.test.mjs | 27 ++++ 7 files changed, 137 insertions(+), 62 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c5a0e53..857e693 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,6 +69,12 @@ jobs: with: targets: x86_64-unknown-linux-musl + - name: Provision pinned cargo-udeps proof artifact + run: | + rustup toolchain install nightly-2026-07-27 --profile minimal + cargo +nightly-2026-07-27 install cargo-udeps --version 0.1.61 --locked + test "$(cargo +nightly-2026-07-27 udeps --version)" = "cargo-udeps 0.1.61" + - name: Setup Linux musl toolchain run: sudo apt-get update && sudo apt-get install -y musl-tools @@ -83,6 +89,8 @@ jobs: - name: Check run: npm run ci + env: + OPCORE_RUST_NIGHTLY_TOOLCHAIN: nightly-2026-07-27 - name: Prove authoritative mypy after-state execution run: npm run python:mypy-authority-proof diff --git a/AGENTS.md b/AGENTS.md index a5b1daa..664dd26 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -77,6 +77,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - Zeroshot worktrees run `npm ci` and the same local CI-equivalent command proof - WHY: humans, agents, CI, and ship clusters must exercise one repository-owned validation surface. - Root `.npmrc` sets `loglevel=silent` - WHY: JSON-emitting npm scripts such as `npm run asp-dogfood:check -- --json` must write parseable JSON to redirected stdout without npm lifecycle preambles. - `npm run test:ci` routes through `scripts/run-test-ci.mjs`: it runs the parallel-safe Node test files first, runs `tests/validation-python.test.mjs` separately because its real subprocess fixtures can exhaust CI process slots under the parallel suite, then runs `tests/native-packaging-policy.test.mjs` separately with receipt gates skipped - WHY: Python compiler-truth tests need deterministic process availability, and the native packaging policy test intentionally mutates native package artifacts while exercising aggregate dry-run failures. +- CI provisions `cargo-udeps` 0.1.61 with `nightly-2026-07-27` and selects that exact toolchain for `rust.unused-deps` through `OPCORE_RUST_NIGHTLY_TOOLCHAIN`; the adapter defaults to the conventional `nightly` selector elsewhere - WHY: strict self-validation must execute unused-dependency evidence without replacing stable as the repository's default Rust toolchain or allowing the CI authority to float. - Graph-owned transitional `opcore graph serve --repo ` starts the graph package stdio/MCP bridge over graph-core JSONL; `--repo` defaults to cwd, supports ping/status/query/search/shutdown, injects missing nested query repos, and returns typed startup/frame/provider failures. - #126 ships graph-core through bundled internal Opcore native packages `@the-open-engine/opcore-graph-core-darwin-arm64`, `@the-open-engine/opcore-graph-core-darwin-x64`, and `@the-open-engine/opcore-graph-core-linux-x64`; `packages/graph` resolves only matching package metadata and never probes `packages/graph/dist/native`, sibling checkouts, or PATH. - Release flow is `dev -> main`: CI runs on `dev` and `main`, PRs to `main` must come from `dev`, and `.github/workflows/release.yml` auto-publishes npm package version `0.2.1` with dist-tag `latest` after the `CI` workflow succeeds on `main`. Each release must provide readable notes at `docs/release/v.md`; the workflow uses that file verbatim for the GitHub release. The CI native jobs upload tarred native package directories so `opcore-graph-core` execute bits survive artifact transfer; aggregate CI and release publish must set `OPCORE_REQUIRE_ALL_NATIVE_PACKAGES=1` after extracting all three native artifacts, and `scripts/release-dry-run.mjs` then validates package-local executable binaries/checksums without rebuilding graph-core - WHY: aggregate and publish proof must consume runnable per-target artifacts produced by native jobs, not a local Linux rebuild or non-executable download. diff --git a/CLAUDE.md b/CLAUDE.md index a5b1daa..664dd26 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -77,6 +77,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - Zeroshot worktrees run `npm ci` and the same local CI-equivalent command proof - WHY: humans, agents, CI, and ship clusters must exercise one repository-owned validation surface. - Root `.npmrc` sets `loglevel=silent` - WHY: JSON-emitting npm scripts such as `npm run asp-dogfood:check -- --json` must write parseable JSON to redirected stdout without npm lifecycle preambles. - `npm run test:ci` routes through `scripts/run-test-ci.mjs`: it runs the parallel-safe Node test files first, runs `tests/validation-python.test.mjs` separately because its real subprocess fixtures can exhaust CI process slots under the parallel suite, then runs `tests/native-packaging-policy.test.mjs` separately with receipt gates skipped - WHY: Python compiler-truth tests need deterministic process availability, and the native packaging policy test intentionally mutates native package artifacts while exercising aggregate dry-run failures. +- CI provisions `cargo-udeps` 0.1.61 with `nightly-2026-07-27` and selects that exact toolchain for `rust.unused-deps` through `OPCORE_RUST_NIGHTLY_TOOLCHAIN`; the adapter defaults to the conventional `nightly` selector elsewhere - WHY: strict self-validation must execute unused-dependency evidence without replacing stable as the repository's default Rust toolchain or allowing the CI authority to float. - Graph-owned transitional `opcore graph serve --repo ` starts the graph package stdio/MCP bridge over graph-core JSONL; `--repo` defaults to cwd, supports ping/status/query/search/shutdown, injects missing nested query repos, and returns typed startup/frame/provider failures. - #126 ships graph-core through bundled internal Opcore native packages `@the-open-engine/opcore-graph-core-darwin-arm64`, `@the-open-engine/opcore-graph-core-darwin-x64`, and `@the-open-engine/opcore-graph-core-linux-x64`; `packages/graph` resolves only matching package metadata and never probes `packages/graph/dist/native`, sibling checkouts, or PATH. - Release flow is `dev -> main`: CI runs on `dev` and `main`, PRs to `main` must come from `dev`, and `.github/workflows/release.yml` auto-publishes npm package version `0.2.1` with dist-tag `latest` after the `CI` workflow succeeds on `main`. Each release must provide readable notes at `docs/release/v.md`; the workflow uses that file verbatim for the GitHub release. The CI native jobs upload tarred native package directories so `opcore-graph-core` execute bits survive artifact transfer; aggregate CI and release publish must set `OPCORE_REQUIRE_ALL_NATIVE_PACKAGES=1` after extracting all three native artifacts, and `scripts/release-dry-run.mjs` then validates package-local executable binaries/checksums without rebuilding graph-core - WHY: aggregate and publish proof must consume runnable per-target artifacts produced by native jobs, not a local Linux rebuild or non-executable download. diff --git a/packages/validation-rust/src/unused-deps-check.ts b/packages/validation-rust/src/unused-deps-check.ts index 57b7de3..4d4619f 100644 --- a/packages/validation-rust/src/unused-deps-check.ts +++ b/packages/validation-rust/src/unused-deps-check.ts @@ -16,74 +16,37 @@ import { runTool } from "./process.js"; import { skippedRustInputResult } from "./source-files.js"; import { toolAvailable } from "./toolchain.js"; -export function createUnusedDepsCheck(options: { env?: Record; timeoutMs?: number } = {}): ValidationCheckDefinition { - return { - id: RUST_UNUSED_DEPS_CHECK_ID, - owner: rustCheckOwner, - adapter: rustCheckAdapter, - defaultSeverity: "error", - supportedScopes: supportedRustValidationScopes, - run: async (context) => { - const skipped = skippedRustInputResult(context); - if (skipped !== undefined) return skipped; - if (!toolAvailable("cargo-udeps", { env: options.env })) { - return { - status: "unsupported_request", - diagnostics: [], - failureMessage: "cargo-udeps is unavailable" - }; - } - const materialized = await materializeRustWorkspace(context, { env: options.env }); - const metadata = loadCargoMetadata(materialized.root, { - ...options, - cargoTargetCacheKey: materialized.cargoTargetCacheKey - }); - if (!metadata.ok) return metadataFailureResult(metadata); - const packageScope = resolveCargoPackageScope(metadata.metadata, context.scope); - if (!packageScope.ok) return metadataFailureResult(packageScope); - const result = runTool("cargo", unusedDepsArgs(packageScope.member, options), { - cwd: materialized.root, - cargoTargetCacheKey: materialized.cargoTargetCacheKey, - env: options.env, - timeoutMs: options.timeoutMs, - allowedExitCodes: [0, 1, 101] - }); - const infrastructureFailure = commandInfrastructureFailure(result); - if (infrastructureFailure !== undefined) return infrastructureFailure; - const unsupportedFailure = requiredToolUnsupportedFailure(result, "udeps") ?? requiredToolUnsupportedFailure(result, "cargo-udeps"); - if (unsupportedFailure !== undefined) return unsupportedFailure; - if (result.status !== 0) { - const diagnostics = parseUnusedDependencyDiagnostics(result.stderr || result.stdout, packageScope.member); - if (diagnostics.length > 0) return { diagnostics }; - const toolchainFailure = cargoUdepsToolchainFailure(result); - if (toolchainFailure !== undefined) return toolchainFailure; - return singleStderrPolicyFailure({ - path: packageScope.member?.manifestPath ?? "Cargo.toml", - code: "RUST_UNUSED_DEPS", - stderr: result.stderr || result.stdout, - fallback: "Unused Rust dependencies found" - }); - } - return { diagnostics: [] }; - } - }; +interface UnusedDepsCheckOptions { + env?: Record; + timeoutMs?: number; } -function unusedDepsArgs(member: CargoMetadataPackage | undefined, options: { env?: Record; timeoutMs?: number }): readonly string[] { - const args = member === undefined - ? ["udeps", "--workspace", "--all-targets", "--all-features"] - : ["udeps", "-p", member.name, "--all-targets", "--all-features"]; - return nightlyCargoUdepsAvailable(options) ? ["+nightly", ...args] : args; -} +const defaultNightlyToolchain = "nightly"; +const nightlyToolchainEnvName = "OPCORE_RUST_NIGHTLY_TOOLCHAIN"; -function nightlyCargoUdepsAvailable(options: { env?: Record; timeoutMs?: number }): boolean { - return runTool("cargo", ["+nightly", "udeps", "--version"], { +function nightlyCargoUdepsAvailable(options: UnusedDepsCheckOptions): boolean { + return runTool("cargo", [nightlyToolchainSelector(options), "udeps", "--version"], { env: options.env, timeoutMs: options.timeoutMs, allowedExitCodes: [0] }).ok; } +function nightlyToolchainSelector(options: UnusedDepsCheckOptions): string { + const configured = (options.env ?? process.env)[nightlyToolchainEnvName]?.trim(); + return `+${configured || defaultNightlyToolchain}`; +} + +function unusedDepsArgs( + member: CargoMetadataPackage | undefined, + options: UnusedDepsCheckOptions +): readonly string[] { + const args = member === undefined + ? ["udeps", "--workspace", "--all-targets", "--all-features"] + : ["udeps", "-p", member.name, "--all-targets", "--all-features"]; + return nightlyCargoUdepsAvailable(options) ? [nightlyToolchainSelector(options), ...args] : args; +} + function cargoUdepsToolchainFailure(result: ReturnType): ValidationCheckResult | undefined { const output = [result.stderr, result.stdout, result.failureMessage].filter(Boolean).join("\n"); if (!isNightlyRustToolchainFailure(output)) return undefined; @@ -102,7 +65,10 @@ function isNightlyRustToolchainFailure(output: string): boolean { ].some((pattern) => pattern.test(output)); } -function parseUnusedDependencyDiagnostics(output: string, member: CargoMetadataPackage | undefined): readonly ValidationDiagnostic[] { +function parseUnusedDependencyDiagnostics( + output: string, + member: CargoMetadataPackage | undefined +): readonly ValidationDiagnostic[] { const names = new Set(); for (const line of output.split(/\r?\n/)) { const trimmed = line.trim(); @@ -122,3 +88,68 @@ function parseUnusedDependencyDiagnostics(output: string, member: CargoMetadataP ) ); } + +async function runUnusedDepsCheck( + context: Parameters[0], + options: UnusedDepsCheckOptions +): Promise { + const skipped = skippedRustInputResult(context); + if (skipped !== undefined) return skipped; + if (!toolAvailable("cargo-udeps", { env: options.env })) { + return { + status: "unsupported_request", + diagnostics: [], + failureMessage: "cargo-udeps is unavailable" + }; + } + const materialized = await materializeRustWorkspace(context, { env: options.env }); + const metadata = loadCargoMetadata(materialized.root, { + ...options, + cargoTargetCacheKey: materialized.cargoTargetCacheKey + }); + if (!metadata.ok) return metadataFailureResult(metadata); + const packageScope = resolveCargoPackageScope(metadata.metadata, context.scope); + if (!packageScope.ok) return metadataFailureResult(packageScope); + const result = runTool("cargo", unusedDepsArgs(packageScope.member, options), { + cwd: materialized.root, + cargoTargetCacheKey: materialized.cargoTargetCacheKey, + env: options.env, + timeoutMs: options.timeoutMs, + allowedExitCodes: [0, 1, 101] + }); + const infrastructureFailure = commandInfrastructureFailure(result); + if (infrastructureFailure !== undefined) return infrastructureFailure; + const unsupportedFailure = + requiredToolUnsupportedFailure(result, "udeps") ?? + requiredToolUnsupportedFailure(result, "cargo-udeps"); + if (unsupportedFailure !== undefined) return unsupportedFailure; + return unusedDepsResult(result, packageScope.member); +} + +function unusedDepsResult( + result: ReturnType, + member: CargoMetadataPackage | undefined +): ValidationCheckResult { + if (result.status === 0) return { diagnostics: [] }; + const diagnostics = parseUnusedDependencyDiagnostics(result.stderr || result.stdout, member); + if (diagnostics.length > 0) return { diagnostics }; + const toolchainFailure = cargoUdepsToolchainFailure(result); + if (toolchainFailure !== undefined) return toolchainFailure; + return singleStderrPolicyFailure({ + path: member?.manifestPath ?? "Cargo.toml", + code: "RUST_UNUSED_DEPS", + stderr: result.stderr || result.stdout, + fallback: "Unused Rust dependencies found" + }); +} + +export function createUnusedDepsCheck(options: UnusedDepsCheckOptions = {}): ValidationCheckDefinition { + return { + id: RUST_UNUSED_DEPS_CHECK_ID, + owner: rustCheckOwner, + adapter: rustCheckAdapter, + defaultSeverity: "error", + supportedScopes: supportedRustValidationScopes, + run: (context) => runUnusedDepsCheck(context, options) + }; +} diff --git a/tests/helpers/validation-rust-fixtures.mjs b/tests/helpers/validation-rust-fixtures.mjs index 2d8dbd6..804e33d 100644 --- a/tests/helpers/validation-rust-fixtures.mjs +++ b/tests/helpers/validation-rust-fixtures.mjs @@ -113,9 +113,11 @@ export function fakeCargoScript({ return [ "#!/bin/sh", logLine, - 'if [ "$1" = "+nightly" ]; then', + 'case "$1" in', + "+nightly*)", " shift", - "fi", + " ;;", + "esac", 'if [ "$1" = "--version" ]; then', " printf '%s\\n' 'cargo 1.93.0'", " exit 0", diff --git a/tests/native-packaging-policy.test.mjs b/tests/native-packaging-policy.test.mjs index 65ab6a6..0139bab 100644 --- a/tests/native-packaging-policy.test.mjs +++ b/tests/native-packaging-policy.test.mjs @@ -90,6 +90,11 @@ describe("native graph-core packaging policy", () => { assert.match(triggerBlock, /merge_group:/); assert.match(triggerBlock, /dev/); assert.match(triggerBlock, /main/); + const checkJob = workflow.slice(workflow.indexOf(" check:"), workflow.indexOf("native-artifact:")); + assert.match(checkJob, /rustup toolchain install nightly-2026-07-27 --profile minimal/); + assert.match(checkJob, /cargo \+nightly-2026-07-27 install cargo-udeps --version 0\.1\.61 --locked/); + assert.match(checkJob, /test "\$\(cargo \+nightly-2026-07-27 udeps --version\)" = "cargo-udeps 0\.1\.61"/); + assert.match(checkJob, /OPCORE_RUST_NIGHTLY_TOOLCHAIN:\s*nightly-2026-07-27/); const nativeJob = workflow.slice(workflow.indexOf("native-artifact:"), workflow.indexOf("aggregate:")); for (const [target, expected] of Object.entries(nativeTargets)) { assert.match(nativeJob, new RegExp(`target: ${target}[\\s\\S]*?rust_target: ${expected.rustTarget}`)); diff --git a/tests/validation-rust.test.mjs b/tests/validation-rust.test.mjs index bac1aa4..14ac086 100644 --- a/tests/validation-rust.test.mjs +++ b/tests/validation-rust.test.mjs @@ -1759,6 +1759,33 @@ const rustCheckIds = [ } }); + it("runs cargo-udeps through the configured pinned nightly toolchain", async () => { + const temp = mkdtempSync(join(tmpdir(), "opcore-validation-rust-udeps-pinned-nightly-run-")); + try { + const logPath = join(temp, "cargo.log"); + const { env } = writeFakeRustToolchain(join(temp, "bin"), { + cargo: { + logPath + } + }); + const result = await runner({ + files: rustCrate(), + env: { + ...env, + OPCORE_RUST_NIGHTLY_TOOLCHAIN: "nightly-2026-07-27" + } + }).runValidation(request({ checks: [RUST_UNUSED_DEPS_CHECK_ID] })); + + assert.equal(result.status, "passed", JSON.stringify(result, null, 2)); + assert.match( + readFileSync(logPath, "utf8"), + /\+nightly-2026-07-27 udeps --workspace --all-targets --all-features/ + ); + } finally { + rmSync(temp, { recursive: true, force: true }); + } + }); + it("classifies cargo-udeps nightly toolchain failures as unsupported instead of unused dependencies", async () => { const temp = mkdtempSync(join(tmpdir(), "lattice-validation-rust-udeps-nightly-")); try { From f4f832df7c0ad60037da802aea423ae559cfba2d Mon Sep 17 00:00:00 2001 From: Tom Dupuis <60640908+tomdps@users.noreply.github.com> Date: Sat, 8 Aug 2026 10:32:13 +0000 Subject: [PATCH 4/5] test(rust): isolate nightly selector fixtures --- tests/helpers/validation-rust-fixtures.mjs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/helpers/validation-rust-fixtures.mjs b/tests/helpers/validation-rust-fixtures.mjs index 804e33d..b3cfefa 100644 --- a/tests/helpers/validation-rust-fixtures.mjs +++ b/tests/helpers/validation-rust-fixtures.mjs @@ -177,10 +177,12 @@ export function writeFakeRustToolchain(bin, options = {}) { join(bin, "rust-code-analysis-cli"), fakeVersionedToolScript("rust-code-analysis-cli 0.0.25", options.rustCodeAnalysis) ); + const env = { ...process.env }; + delete env.OPCORE_RUST_NIGHTLY_TOOLCHAIN; return { bin, env: { - ...process.env, + ...env, PATH: bin } }; From 30954a87ebe6063b29d1616b409fc3217178b456 Mon Sep 17 00:00:00 2001 From: Tom Dupuis <60640908+tomdps@users.noreply.github.com> Date: Sat, 8 Aug 2026 12:40:46 +0000 Subject: [PATCH 5/5] fix(ci): provision rust function metrics tool --- .github/workflows/ci.yml | 5 +++++ AGENTS.md | 2 +- CLAUDE.md | 2 +- tests/native-packaging-policy.test.mjs | 5 +++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 857e693..e453b8e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,6 +75,11 @@ jobs: cargo +nightly-2026-07-27 install cargo-udeps --version 0.1.61 --locked test "$(cargo +nightly-2026-07-27 udeps --version)" = "cargo-udeps 0.1.61" + - name: Provision pinned Rust function-metrics proof artifact + run: | + cargo install rust-code-analysis-cli --version 0.0.25 --locked + test "$(rust-code-analysis-cli --version)" = "rust-code-analysis-cli 0.0.25" + - name: Setup Linux musl toolchain run: sudo apt-get update && sudo apt-get install -y musl-tools diff --git a/AGENTS.md b/AGENTS.md index 664dd26..4227771 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -77,7 +77,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - Zeroshot worktrees run `npm ci` and the same local CI-equivalent command proof - WHY: humans, agents, CI, and ship clusters must exercise one repository-owned validation surface. - Root `.npmrc` sets `loglevel=silent` - WHY: JSON-emitting npm scripts such as `npm run asp-dogfood:check -- --json` must write parseable JSON to redirected stdout without npm lifecycle preambles. - `npm run test:ci` routes through `scripts/run-test-ci.mjs`: it runs the parallel-safe Node test files first, runs `tests/validation-python.test.mjs` separately because its real subprocess fixtures can exhaust CI process slots under the parallel suite, then runs `tests/native-packaging-policy.test.mjs` separately with receipt gates skipped - WHY: Python compiler-truth tests need deterministic process availability, and the native packaging policy test intentionally mutates native package artifacts while exercising aggregate dry-run failures. -- CI provisions `cargo-udeps` 0.1.61 with `nightly-2026-07-27` and selects that exact toolchain for `rust.unused-deps` through `OPCORE_RUST_NIGHTLY_TOOLCHAIN`; the adapter defaults to the conventional `nightly` selector elsewhere - WHY: strict self-validation must execute unused-dependency evidence without replacing stable as the repository's default Rust toolchain or allowing the CI authority to float. +- CI provisions `cargo-udeps` 0.1.61 with `nightly-2026-07-27`, selects that exact toolchain for `rust.unused-deps` through `OPCORE_RUST_NIGHTLY_TOOLCHAIN`, and provisions `rust-code-analysis-cli` 0.0.25 for `rust.function-metrics`; the unused-deps adapter defaults to the conventional `nightly` selector elsewhere - WHY: strict self-validation must execute both retained Rust tool authorities without replacing stable as the repository's default Rust toolchain or allowing either CI authority to float. - Graph-owned transitional `opcore graph serve --repo ` starts the graph package stdio/MCP bridge over graph-core JSONL; `--repo` defaults to cwd, supports ping/status/query/search/shutdown, injects missing nested query repos, and returns typed startup/frame/provider failures. - #126 ships graph-core through bundled internal Opcore native packages `@the-open-engine/opcore-graph-core-darwin-arm64`, `@the-open-engine/opcore-graph-core-darwin-x64`, and `@the-open-engine/opcore-graph-core-linux-x64`; `packages/graph` resolves only matching package metadata and never probes `packages/graph/dist/native`, sibling checkouts, or PATH. - Release flow is `dev -> main`: CI runs on `dev` and `main`, PRs to `main` must come from `dev`, and `.github/workflows/release.yml` auto-publishes npm package version `0.2.1` with dist-tag `latest` after the `CI` workflow succeeds on `main`. Each release must provide readable notes at `docs/release/v.md`; the workflow uses that file verbatim for the GitHub release. The CI native jobs upload tarred native package directories so `opcore-graph-core` execute bits survive artifact transfer; aggregate CI and release publish must set `OPCORE_REQUIRE_ALL_NATIVE_PACKAGES=1` after extracting all three native artifacts, and `scripts/release-dry-run.mjs` then validates package-local executable binaries/checksums without rebuilding graph-core - WHY: aggregate and publish proof must consume runnable per-target artifacts produced by native jobs, not a local Linux rebuild or non-executable download. diff --git a/CLAUDE.md b/CLAUDE.md index 664dd26..4227771 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -77,7 +77,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit - Zeroshot worktrees run `npm ci` and the same local CI-equivalent command proof - WHY: humans, agents, CI, and ship clusters must exercise one repository-owned validation surface. - Root `.npmrc` sets `loglevel=silent` - WHY: JSON-emitting npm scripts such as `npm run asp-dogfood:check -- --json` must write parseable JSON to redirected stdout without npm lifecycle preambles. - `npm run test:ci` routes through `scripts/run-test-ci.mjs`: it runs the parallel-safe Node test files first, runs `tests/validation-python.test.mjs` separately because its real subprocess fixtures can exhaust CI process slots under the parallel suite, then runs `tests/native-packaging-policy.test.mjs` separately with receipt gates skipped - WHY: Python compiler-truth tests need deterministic process availability, and the native packaging policy test intentionally mutates native package artifacts while exercising aggregate dry-run failures. -- CI provisions `cargo-udeps` 0.1.61 with `nightly-2026-07-27` and selects that exact toolchain for `rust.unused-deps` through `OPCORE_RUST_NIGHTLY_TOOLCHAIN`; the adapter defaults to the conventional `nightly` selector elsewhere - WHY: strict self-validation must execute unused-dependency evidence without replacing stable as the repository's default Rust toolchain or allowing the CI authority to float. +- CI provisions `cargo-udeps` 0.1.61 with `nightly-2026-07-27`, selects that exact toolchain for `rust.unused-deps` through `OPCORE_RUST_NIGHTLY_TOOLCHAIN`, and provisions `rust-code-analysis-cli` 0.0.25 for `rust.function-metrics`; the unused-deps adapter defaults to the conventional `nightly` selector elsewhere - WHY: strict self-validation must execute both retained Rust tool authorities without replacing stable as the repository's default Rust toolchain or allowing either CI authority to float. - Graph-owned transitional `opcore graph serve --repo ` starts the graph package stdio/MCP bridge over graph-core JSONL; `--repo` defaults to cwd, supports ping/status/query/search/shutdown, injects missing nested query repos, and returns typed startup/frame/provider failures. - #126 ships graph-core through bundled internal Opcore native packages `@the-open-engine/opcore-graph-core-darwin-arm64`, `@the-open-engine/opcore-graph-core-darwin-x64`, and `@the-open-engine/opcore-graph-core-linux-x64`; `packages/graph` resolves only matching package metadata and never probes `packages/graph/dist/native`, sibling checkouts, or PATH. - Release flow is `dev -> main`: CI runs on `dev` and `main`, PRs to `main` must come from `dev`, and `.github/workflows/release.yml` auto-publishes npm package version `0.2.1` with dist-tag `latest` after the `CI` workflow succeeds on `main`. Each release must provide readable notes at `docs/release/v.md`; the workflow uses that file verbatim for the GitHub release. The CI native jobs upload tarred native package directories so `opcore-graph-core` execute bits survive artifact transfer; aggregate CI and release publish must set `OPCORE_REQUIRE_ALL_NATIVE_PACKAGES=1` after extracting all three native artifacts, and `scripts/release-dry-run.mjs` then validates package-local executable binaries/checksums without rebuilding graph-core - WHY: aggregate and publish proof must consume runnable per-target artifacts produced by native jobs, not a local Linux rebuild or non-executable download. diff --git a/tests/native-packaging-policy.test.mjs b/tests/native-packaging-policy.test.mjs index 0139bab..c6aafc3 100644 --- a/tests/native-packaging-policy.test.mjs +++ b/tests/native-packaging-policy.test.mjs @@ -94,6 +94,11 @@ describe("native graph-core packaging policy", () => { assert.match(checkJob, /rustup toolchain install nightly-2026-07-27 --profile minimal/); assert.match(checkJob, /cargo \+nightly-2026-07-27 install cargo-udeps --version 0\.1\.61 --locked/); assert.match(checkJob, /test "\$\(cargo \+nightly-2026-07-27 udeps --version\)" = "cargo-udeps 0\.1\.61"/); + assert.match(checkJob, /cargo install rust-code-analysis-cli --version 0\.0\.25 --locked/); + assert.match( + checkJob, + /test "\$\(rust-code-analysis-cli --version\)" = "rust-code-analysis-cli 0\.0\.25"/ + ); assert.match(checkJob, /OPCORE_RUST_NIGHTLY_TOOLCHAIN:\s*nightly-2026-07-27/); const nativeJob = workflow.slice(workflow.indexOf("native-artifact:"), workflow.indexOf("aggregate:")); for (const [target, expected] of Object.entries(nativeTargets)) {

    quPp~f5>TR<0#oS8SdAGk>5I?+;J2^ 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�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�l(!tC{6JJ= z=M%_Fq!U%w0Z(yR0~wI=w+^@!UX|kGxu=6H`^`NuY)GOp)eVO|?5R@agO25iA!;I1 zt|*_WkNQ!bye#nqJDP_NbmZi+7D+}wp`Dzp)D<~O%FEiLeAAtd{{W;c0uiMtWjCd% z6ykB+0m{BfGB$T>SHlK3D7Z0K8{^SlkvleoX<-h(%TCbZ>_T+%L+mn2{{M5VXrG&Zkx@2 zW+SpD2Ibed6-5}7SHZ{I=4oh0Y?niC@5ktpE+n=pkzJi}6I2xMuulgjST23K_>zdk-sr zlX0XLMj{BA)>$gWPE6%66boBezH~~9F!YD5v)CTJSy$)rIa|Zz%I0|L{Su`%ymaE; zpbMo-Wy_uQIK`evYfs;~Ev%g4E zzY9Dnh*Mfb&+Gk$}qQQRn5;mvO;si{y;V_;HK{1W)f;ED`6o71i1U7o;S z5K7jUeH+7)ZpLM({?0eCVSy;WNyNaUBw#6wLxTld@D{Xv~IzoGt4d1oMq*=fnowj%(QRJ-kq(v!x9hx8IRZ7WIcvfy!A? z2|oP)Z|F)_6iE{L`ehc>(i}OuIhZO9l`&MSZQ0HHfjZDYlq=}IeNV2TJ`wW&153uxn@*`UOeda1Hx;Z&B6d`9hfq@85?;a&JHN~%~f_Y(;QW|(Rwm# z6qKTp%W#pn2HL%~xueS#6DDyzeDnqtJ#hS_{U?CS)ZO-a8rYBa$l49@mSQEHc{k2W zbs(jRExa3-<}%9`exJg-BV6ikG`K|C!n@sz0|FGC7?Yu;==Ff@bu+y(dgW-6Bk=^^ zThS!Dn{t_dRh+@a&q|@HOs-;uRdFe*Jt?^G^tnOnoWc%gnG-x;OQA9DL8>~;J=?5n z*a`vEC8KXq&?Gl%+3gH8=1bAFRL6A|b~a@jvdxK}rA^^=TE5%xJICQ+&h2QQr6~(( zhlSgyrmRgnbE@iQvU~;1?Qk-!tLX$BW{FEseSqNK@5dQD2U4(t7^!Y=)P_T+FNSGJ?z~8v_T4bd?$6q*!8*K_WdlL#=o= zy|X#A?pA}W0U(j5;@E&(?2a4cE6pt?8oAv%>J3I+0e=#PqyUSDOd$B-_c=d0f<)@A zLi`fBr>5ws2D*@KtD()JUWQ-T zR1F_1L$`XY%lH7J6nvO?Te2aaL%8 zmx|taFU)}>+tMp_yQ;tlbgDm@x~L9>E69;dc@Tz%%gB)_;Y3Wxk*U zapXwV>M#rk!V?UtH}K%^9v{RWMC$9Q+SYjU!`s8ZfopjqR6&nyOTd!WmswGOww#5- zOtOX7qx4?v+wSII<{OQtKk2CrJ*o4tgHsr-udg}~q9|J(4P^{(5097(E<;1CGJJcR zy@ZqymeR|tV{aTa)pK#R#jQGO@)CXvX0v+3O)MBJc3jD!^veCz=9tOLC8g%8tL=3a zVujyyY*rsYYp%wTGMTCNaTCBObujL@$)IYhJh=GT2o`|Ayd{l>$%c{a*9c3Sb zkLX|HBHh2A^Aa?%#h}I;&fl5n@hm z0;isiiwP|b5mv_k3=4!DSMQuNw0CjRpGxF{`O4VR~9B;xm9)mle(lBAH(md0KN#n z+BE^H@5ft~(z)IG22c@b)X(N&@dQA<&&M^8$_DU31LR=sE&q0W>v^!D03X)#@Ncoe zpPwBd*VXn#MB_i=8fc^Nk@-hl&CnNqLLh|4$4{X7-?7Us_+eT5kGQJtzHdwHJj8qa z2}JMXLwJ0Q7$k>i!x5VJCwwPs@p1f3kg57bUm+n0%1l4OO1{)o?eCoN+79nhd_FGl zZO6-909AvKOIY&|zgMvL-k~ZPaGe0><`1}%26>B2GbD1Uu1u2%Xe#5?Kz7Z;A{ zNVmo5#q8Ubh=p?u47L^IKFi_4v56YFmX;({7migtDBF3MEpNGSRB|8I@w*Ee#Ynq- z__`r-K>2CFMp&094i?Mh`XKKvxHZrO`;;9_fiiq)cI|+2$@INWarr3K^5F?X& zldF41Oe?WFEAjPT!~5aRZym<2@c1|$IiO!g;*jwqI4?u59BFku1p0;xS7xJt9M7Uu zg5D^K^YdIV)rqeK@C`#p@_WR`6vc(txZ$M~y>L1Wx_5x~Z5ze0Rvk3;5#NstmFFXa zZJTi4wp4*8QA6YT4e?JOqKTk*2Epd_k=x8h}Ss2YVE9sb_J%VL;kNtDe?I9UwS zB8ty%5^buHt5+y&oJ$mbg5uw%&|z7mR8;IuDNfHDjD-Cc15i9O2!z6CN3o}p-sYSR zyvzT{Y^}JC^x2MCq}%4w?Jd>#>J$LEn~qIbn5QV*ZrFvtDD;1Ay-6z0$UzED9DPyn z2!kdJ-?mVJr?A@yR&1QU7k(yC4`F8FH^bTz5!Lb*xVHZrNQohMUp}gV} z0#}_W_S?_@z8)7~G{Ya51H~kA(8L6a2?E6!2a0+siiSo);jn0PV7rsWb!;lJHW*p{ zwU!qvDCIXj;#orO>>p589{MgUdEf-A@K?0Vzgwu2R3wpu2npHuwJ{VrDBrk~SS7Ia z7TDgesIp4U7XQqnr0kvKcX9`Io53nz1FP`&Kgz~N%Sag53reBrPN&Vg2^3!n6!ZQY zMT$TX8Am8+3S{3ah!^eLo``=Sxg3}ikxLp_ftSmK9QP8bT^*?bBDGst1*h2>PremZDZUR%@J%yh>*hV8EHRy! zK#%aO6sqAb&KD!K`eC5NtJ(NVawZNYy*odcR1W=qQwz+V`nfCpK4>xt=Wr!c zsZ$P)eh-gQV`NhVp#R5Usk@EXf!2z`ekeY=SiRQp$>U@{_qzax(^7Zr;k2j|#=SDo zO0(S5R%%3hY4=SU~_M3*hGX`yU0r za#ebCV{2JabZ_fgICB)N8Vgn_jt-Y#MFRzhv2PBNZJ&xIj$cr&-Eg=Zob|V=Q&mzi zH628lRB~Qnytma5(1QhZU;Oloewk4IUxL;S*H$ID^S+gt@>6Rg=JgDPL!tkhabZc(2a^8LqwJG zV<_Ts`loabpG*F91`02h8KpiN_c*eYL1Vh`cwg(IBt!>9?E|>$o;)==&b8nZL~WZI zT}wM1QGZX3X6~&JMJE`par?Czf!<-Sh!G_R@%C@vnx(b<_?TR|iuZ+=BGI{9`OyZx zELU#mJy(gxH7DA&OR(12z;92B4)=IZEH{)o9ifeTnXWi@e7cF8JNob;InmYJFU)qF ztJ^azIaqpisMEnfUt+{*nrFu0%R)_Mx-Y z-9?W+(G}8?6~s>ZZDjrC>=*iF7&(AKfE@7zsu%fwe3C@+&@#>XyBtNT;<^ognF1I-fC9t{QgmSvtnG|L|aWV~gU! zG6#Slu4LyIO@PxUh$Tr3A6i44$ket4;!_5A@4x&|9Ope^trgS|KthH`&Qq4IMH|LZ z8&-k6N%>a{A%dR)5N-g)-`hZRpGCP{bZKwMsdBpXX}Ws46dxUCM#sBen1!^@ zXGY5r-#h_9rH|UoepdQW3*-g65r9z+l6HBLE>84oo8hD8tmrUJ5Fa%|4(s&nu^<_m z7$m=Q9P}d6i01i&iRQn642C2A{!i19y-7`1Ji{T%bVSrNi*P&KWqzjAzn;J~L*U9L z@~pjlWNkmx>ZVp7E#YAzXbl4&)Y~VZ0%BD`P#tFe@Q4VqTl)+mf;IyEyQje{FYSg^ zFimC$G>RyTNiaxN{AGqAKy`UIkg~RT%oAtJ3GNr}lcYEf2x^Z!OjrD6F2%=**>W3g z!u^Nhr6g@l#5Bv7>*@*y5CV5lf=eEc(s{iP8#XC>XrwU!6z!bhfd2tNAElYJ%4 zV*bmA4a22-Le&NFqP%F=8V>~DIh#2eg)#=A10&(&LD)`JPYZ7=3&{Li#CkDQo3? zZJF4*Zg<{dfZx4zUT#RRNqhfpI>tqZ1ESU zQsQ{Q%125E(Fq?vgiVywxRNC{ZzVZo=cw;@m9jO|<8uIJ;ry zKip0-(DHBYH(#z=rLL0Mh~o_r)-TAcq?5LndQpzr|M1T9<-~?Qm^CP#{VcyLPKx}5 zYJMe*`UA<9-_6rcXBZ&qK$c>Gf40BuhP&nY$@y}dN_SViMz1-LY}cv&CWjk%%;$0w zZDkCNqdu1dqzU(U{^zn$pVgBJ{k2#~83uP=WqhpBHQzH{%ddVe8#HzKQ-I<1(A*pp zZ&tmzGs)!GFXS3#DYI$&9M$QpU#BB7*O>F# zx|s)`CT7My$=Ml8yIK;{KAZj1$aDzK|Cgr`!UgbN2h2EkPz+)c8By zWzRj7tNPDMbK^a^>?^k-DEM`ndVmtU?N>NP-*~kgB1R4-g)5MrG$Dp69^0MjeFQJA zT;Z~RbiF|@?YJk~Tgt3TUZc!rOLM(WpJa;DVQ;0m9}Z**^f?DccH7Uj3*`6tlm&7? z^!$PUui2`6iENJs{J&<~{iQres&bb{d+BX{+VC?N}i@k;eEcA8)Ri3 zNkx@TUWFvXZ*BaDHw`fk8O9-YcZgs_cg)Kn}TqI>2LQ;fCN<H-vs+}ygSg%pN`xx|i0^hvH(L(lnS>&K^BcKSPspGm zXFwcdT{Z{Iz_2fv5o4WC!ANItIt2qn5UxRh&N>f<9T6L29gH-R(a$;(->zRH6>{ks z|8f!T;4Z)v^Y9`$GPE12Kpq(2d}5PnJP2pj znY8~T6Fk5^vHFaFZq5%+L`EF@>2wm;8S_`0x`2uSeBNRi8#6`x*kU$ zyxbDGI_AQ)m&it4>ad642Kk@Fxdm73Pb`sRH8Fh75@@jvm-uP~rM1KOl_hd5>52>2 zEtNm0Ocy*yDh;GwKD8UpUF0{G%Dt1rr-Hk5s~pNA4szk#xahIpDa#|Ri^%DBHNfxA2ll+bPK|js;Rt~E;?2?nEQ?M#j_^n(iE2$)|2h=#&Je{l~)j1>D;q^sl zrp5vwBe*0ngmNow5TYb@DoH$0l2a0inT<*kFDQwj)ObyBtGdn`;F}mdk53K?lv0tjmSRn$k4jZ2(OC@8?vl z#1mdb8~BbD@&VnY^N=gF8Y%(refxQ#TbK|3_ItUOw)^FDe&%~QrhHcdLErI8Dezn= zPu5hKeAda$?=jtmW?+I6s^^S_1uK7$(c(F>3@U3)5(HBNItV-O54FDIsZUHr8 z9MRCg+pLy7T?=vMfKT1q)KHLtpeFXhnYYKIK$vT!Bc`K9X9ZU?Oh(~y2X-fXG5^Xp z{3P$lD%eb_`sfK{b_0~yz_S}hopUOXou&xz!^($23ab{QM&hC{ofx%_!6~cax~gOv ztx1?ukdLcqNGSY{=sw>IA2xQ~{6e?FOyM;q0o<}SK_+5LgBB#eZnI94PMLO^KUyO< z)*nxMX@_qg7hqSsc)eOLM@ea}yxCg0Sc9X5>*TN&$0kIBjP52scu}b3SO*Zg1oAxV zWJBOvgqflcFfh56l0>cr?*L#R0I=1<=d6QGF@UdFCs+0P4qKPzyoILo#HXB>@B`LC z;ypIXLHy}Dxv6F!Z@6Agtdi6N-5;IUPauS$qadtz0tnsK@-NoQRW;T4*7b5kz-j>s zXCvfa#B6T#R5yJvop+wJ=%e_WMJt2Gw;1gPN>oD zS8CYebZRtco;1qQjhu^hqQcU!xB|1LI2ng#kMm=jWMliz6b`W)vOuxg`qX&_4Ohvt z?#9VQYR|GwYdD01*l7LhQ;NT3-8}(9^0VF01PJVgIQ-=ao3RDm;(B_)2b<*?nhM#1 z>o4H8XDxrYMUM8=QuDutqQ@ZTgZo!u|*QT5IEF|@5QH( zb1@>me~900zs#Vk)X@}w>1s!7yzr&nW&nKY z9$^RJ>X52vC+p*H6c)r;1N$mfJ(H=h;-yTYoqB{S+zNk7q?EQM7ukdLyv;V*GmC6c z;flsut^))Qf=}Jb4Ck?wLIO#SaZ>VD3x`LFgSzXKVwr79z^_i9E5=)0WUh&x0m_?i znk6#MHqBPSPJtP~Hy=S{qK2VNODMA!zq?JYoJHZiBAjc=Mh7J~v^3CKd`vknBvL_a#{96QQF+mPc5ZHgR( z4^c(@+6`9@p@C_7p_)4(u*^gQ2O0Sn+vU)30$qo6#gmPr_mcpQpM>s7ve3z$pWH6{ z`VTmWyy&kZRf_YW~^0>JaZIAc@3+#Df^2h{tjv z9;udF$%S{JP<9H;)-pI9lhYX2evRh5q46f1i@R%;reRTqBuBkSdKX9J_hXnSyk$(X z6s5R711H?T34OAqrIQ<51NmorpnjTZ_^Lf}6G_tW;yrTp@{TI*#pxEcit9wxW}B9% zRiryesG=SviV>BnMh=)MLak!kW}=EmkRqz6!`>Fjh2R2yS0W(^^0E%UsQD&=0gftw z*}Cj!M-@}@CS{SF+O114_>@#pEIXy{qmgj9s8|cj0KY^_&mc!rcgN6*LE-m8r=Z#~ zPsXUx%F!g`pzFfSC*-e<*bXgqnree47p5-Sm`Kys9&d{dG5oozZLenB(+68BJ$&I=x~5q1zkWo@OfMG=+6oL3SI4p2%4NA3|_ZX5(D z5z7IVEc8kuJFgX%MHFnwdA`Y527vnC@CT`XN9Wrl%2J1cDf2NuK-ETqOi^c4i`YdC z>88pLWhG^4Ej+A;2MdnT)bm{oFXd+45Y7u#F{aOij_<<+PLg46;C2?0lt zN;N~FD~epv3J3~CK3`>p5^J`=qdA%vsl*|Kwkjb)j8N82YI~%ToDwj}fnk zu2i7}G{Q?L41!3^bm0Ch`~sSABXv1K0;hnQZz8o0<}RS^R(?9yI8-W1Xmb%V+ z2~iAXLA)L{Bz0?Mvz1Y7dQH*y7easW$-CrGF*LaWngo-Ch+2a%6ZZ%yRYG8+W~FqH zOCzZ0gDO0gLhybR4`DKj-`QfKOX5I%mGTkmD4*!ek+Vtox>ElnwgAjZl|fM93gn&Y z19S;Q24%x9l1cpon&ge3=OE~iITDp@91=1B2hmdc_a?PTl)ea2Q!3cWFWEAd z#wE8zOPe5@Uau74Xucpqp|m23FsTF_r;-pRAwVi!XFw>6WP#`tvVn*qsLjejS#_-+z_gJY8dh%LeEOTIPlzlCB#>axC@=Ei7ib815YV`iWFTBb z=n#H|WC+ePiNBua5s;;J6)Kcmfe9q}3k7FVELda_xz%|1#k5i(m}KO-fT50=P{?+g z3Dy6rWF{1^CLtq=#V~+|e7+5{mnC0O!>tz4L?oVz7Dk$=38~cS@I0m4CmXYBp2yq{ zZrStT^^lm0Ow??`D#I_0)TEX%Qt!kMIs4V+u0&gjr6AD-h>v7%BN=-N1+-$Us$Yw0 z(BO9@ppq=hgcb+D#XMVs(+quta>i$1fk21BVoEf_MkQDPrO#4Q$$O;*B4YTO!ZFH*|yj1ox2q zOj3738U%x=riRR@LN(MQMzIW%;BF8+C8}(m_yemwQ8)hpzj|2q%ld&3E%*)vIv&dD z`+t==g+PauDl-YZ7llFtiK0-r8LvwDWrU*{o<| zGt_<-{G8e)-Tb{t#v;(F&v}l3FiG=BAo6Kd$SJa>jj#c>B$>L*LJKyAGw2*4jjw53 zM$=;32U8mwR%opTL3sI0F?G$;u zyU94KyQxcy+#9-Fp#hL*?iUK%(@qQn%GJP5y3xpHPSv7Z@}aFD2n^dA8# z*b&DFBP68|B5vuVwNxZ$$?)~L+_Vxg#3eXeL0=XDh(-!J{#FFQD`T5k7!&YKHbxgw zL!d<%K@Uh6Mv7BvYHy?>_?wa#q=e2Rl#<`LT(bSJ7-4z*aoM+8+*;cIxKw; zyE2NAl#n_l6*PwrZ{{166$xua#LQRbeUX?+P!8l=LM4M%8ssTVwV^PnX~NK(;=w*6 z(X9?N)@sdYa7C3VM>cT=!#at(E;j{!WSo|bl6g~>0+Vm3ybfLb?>W@P-(EMN5nfP0 z>%G$(<1jG|1}h;IV%R3>C`ALsTXPah26zV<)B@C?BrA|tjA;nywhT*w%#w$42bI$w z;yjFP*?r7+fYzaZi>M+{=-K$Smcga0cl#R+po54n$^h`zi1@_z6*zYYii20378Bg6 z6kdU^R;LohM&vp)I`KLURA#*IP~@b6jE(IXL5-yhKVJB&w@+5sT8u}etE`PuAAxzZ zMHVxZU7;1d0DXxxpsM;-yrP35XdIM6{Rsv55l`Ns2)8J)+D~0T)E{GwqVJVVL<}h( zG8)ZUC_%5w9fb-yO-C{rNghrK)MbWt!NWGEi2r1`(L#VSfj~y8AsOb>SI8U{8>m(d zmW!|^OyyJcQ!LMvx+vy^x>k<)JfY7*IfNFDVOFO*wH44(-$W}@-`u6>9Iyfb8ZxQQ z0HHo)hSPWq#M*6LU)r-Ai~oyY)2ONo zT1(MimZL%yLEj7I1um0_m@VUQP(U#~bYA74BdNoIfueIQ5DwNRrx?2-=@`--4FYOO z0}@|E1EyMTQ<`W)V)S@hB>e1Wsa?XHhkfk~fAk9j`GeFuJ;%r4Ra;1g(nV&!56L1{ zIXj2#i|2OQ$x=j8nF5n`$VLUh&skJY&_~*emg52=sH>_fG3VJEUFh^8mDRK87g@wA z0GijKC71A1zstTMT~HD0LL5J|%>~W}BmiQ)ED||TMSeTJrN0jakV>o1Br&?k`KD7m z_L9LT3n@iv%02v2B@(E#1O)I+pJs|DUZW&z*zHz%jS$*TtgowMo?y5DT83>JGKmQ# zt!xaUfS@2kr62+nAhEkH0#gPBs%IB~b~58nn>fl8;h4uDFk=qtLJT^EUo2NF#}7j1 z@q@LG)oLOUjYO(963x8A?{a;t{!utlSE;3u5F~(|SVb70Y+00zc&vHQAd)9mRF~k( z_6h*h3BSiLag&;e$Qe%oC-b2iNGB?dqb$)RTiT2yz}_MOd_Y8+jxTQ6)Hf9Iof7az ztnIcmQckM01oB*69<{=zFj7-J8BoDc>XMy9nPflBm6@!gfQ4AKsn< z&ZoH*%}+LqnxTts>McM^w%VqxP(*{qcgKP$$s#eu?Oh3=$a=()q~x4~cTz32u()kZ zwM>aX8;J(9w2a~UL$V=D-Po{EHONjki{FJ}-sb2-G%RGJ_3Vb-=|(Ym>8ohgwY5lN~`)k7Igg(Pm>o~#Uvq;eAV=uFV@5$FcAa?8H&a)R2<2$Z3o__K~>C_ zVk*iAVYgOqt(2QBIOnC(CprMh)sw3#egDu0GEoZlp|cKvz&{bE?8X{JY!XI=;+9Gb zX>Pp6MKSeC@7NkCCbV=+MT$}1f~27c&m#N?MODEzS||>jmH0=O`#xEIr5d{y&99W! zZulKYVMrG{&5GS&Jv~^w(gGK0CYovSrAr$6WW|Yb!)|!G9IcQ~R@CJ7P|?9?GkBB+ z8ep)MFTq5*(fU4toB~~hp2TAxX_>dh1A`q<+Gtn-l$hEntHph`|ts~nCH5;Ya4QZ5Zp2(J|tBBbhJBcCirI=pih-L8BH*FWVINupIopm5{UZDGo zKAc@3pU_Pa1Z@DT}KKzyzSf5%$D+{-8wvQEf zVjb~SRK6LNMw+1!eKYiwYrE%WtK@pHYs8;F9mXEA%C|0-%iUXpNMv=8?uyo zES5ot7H{g2l|}I(O7WCL#ah4buV8R?it|g9jG`-d1AYQ60*|e=Rs+jseAx}hfZc95 zh`%Z`1g`jTrI^vXG0w~2^@Szx0QspvG@@Exwm}Z42i_U4~sjW3PLAeO=XC*qb5?ul+>M?&T>{4JAquER<6amqIt(z(Vq1ru@ z?C~Ngo9de&4$mtFqxIqUr3$Nt!iwi&p%%Qd8}{O_dVqu1E|LQ){j^9q^yVkl>&PxX z#d{RVJq+>&h~hfnCS8gZ5a>DGb*-s-9x zAx&S;M_rZurKnwe_Eot>h3nYADsE^uR9KwOvw2Kl*e;6j!U2^Ocgk(@gXt zo*ZAq1Fp%nHCo>Kn!HG#@g52S&_X>W-uZ?e-jFMMCIsRr1QxuMlW!Am%At7mO^ba5yDf{4 zxgj6YK3I=SJ~wf!HFIdP{SdCWsF#*=-Uv^)Ye|}t?I*0=7Ep({iyHztV~&*cTuPpk}rk zTz}%hH)Mb94_njePGF-ztbmx+AQf!_gxle)TJm_|06vds*ew$k|`u{0hb?7wg!W;>>w-tm=-b!Yi1vEVR{N2otd7Mpdmh^VsAphDf+h8-e zSihP<1=A`J&f)Yk4T9uQ+2U0D8cZ5-gjo$+d-2^i`Td$LQG{5x!^N6H<@nfqU_#;1 z&ceYoaP5ip8E^5o+*dMv&VT${4wNJpUiDdICI08%@>proHr}on2a^`g<#UU1Ty*bR zzPlI)dppnN=ZfXp<;QjbH{Ibyc|OUVmJQ%i0jPwB0v@5)h{<-G8={GdJ6DFtFoCZ;)msL7au zQ|3ifk$fyA>+pPobB}h`XX3Q#u97|4cTcMVz*=#%IIS8@gDRG8M$#@r24qQ`Kka}A z|JJO9!pc*I8)eV+2v5Hw*O4rr^67Wv=5=-;C%wS=48K@0r3D|c=0Zq7DfLCWA>>oC z74Yiz9cxkue{n|+Dpzw3ir0+d!FS~l&0HRT7q4Rd5W@T4mHU;gKo;B#{`#&wvErCI ziuN_aP4lzRc$< z8)6ob5QEQ&%BK;Zy_8QZY?Vn`$@A{ZjiraP_|f}l=9!=H$M@wh-_c})ra?{ap~~&1 zA~5oO&1*l912nO`^#j?cG4de~r);^zzu3219Jq8;CLNa1u17${&kjB^z zI)cQp*M@*}9(zr*8e`OYTE{L>ijj6#i8}P)3s7|%nJnNzSMhea2S&!t2()DTlIgQDB!z1p8i-4 ztrYVybxX{sfJ^ip>tR0QvD~@xKFd1^)N9_Sv{Dn?j}T*MsgC_DBk)RhR5O= zoIjCAYkKhVPjQ*SjW>QOx2;6J5@;EMN=eRn0}K8TU-T3gB%00OXP?SL%6@|prTBe5 z?wQ=FO(Kmh(9Xm}U2Z1M%?e|3D19i*yh8J)?i5x|9Se51CU&<_A$9?x7)xq5XY#=3 za$xuZ3ge0Uy)#8a5tPwdVXn%N7lI=*i6aK1844-c@`P>~<{M&WC|uB&X3ulEmL`sG zelFK7W19?U{^YsbK;N~D;76Rwk9z$4OF5!E#zBl9cAHk>OWDb z;#M-SqSN$*b*Wq$qw0to1(RRN{^jc;p7_!&moIrG2S!$;F!<6S*U1;Xb?PSyUyeQ@ zz62sk7gBKdm3%~_`J8WkBS*K7v!F_14J6;NHQOnmze32dE!?E!MDIdMBn@vjY!Zo? zRZ9}5mL&4k1r6 z%LVgbiFs9?tcc4JU{rRHp!))P@+653(u~9_xNJ<|@yTynb{-2+MUr&AEAM?Y+K0E* zvf3VtZoYwega7`rw&x#fSpX#NJ1v_nRa(mHy0V78A33?+1#3p&`Y<|>`-)F>WzlG+ zHLlEGy4jW=b!D*{Z~oGiy(g8Oz|Cb?3(Z>oNf|cA=e_YTP;nA7!;`djUIwmbnYp1X zo94NB97qBO@jB(`*?f6fW|Z^`_@T0_y=QaGCe);RdAwRVR;BtJNTjVB-s~-HCDoWh z3n7@405?_QA>%{JvEZx}65kRe#q&JOv?|Z^@izJW)yCGcx5hTKv z&l%)GE9N>N&pIOqK?8!$=_7I8`e_@*rm3E-*xhT3siic)=L$1wJg?x!>Umx&a&ma? zA3Vj4MQR4}v2H9yYQ2;naAQ%LD*Ub+3pI>Qcd=MW?+!*Mg=s|+<;$zNv(c_3*?gfp zt0Pqm;HTW#8YO00dG?pocozS#0=q7an84e5u+{-*mP2g7)CB7vRY7$#yzY=e)>!)t zzR`n)hK?agzhXW0R}_B5h0cS&JQjks3sD$BGWb&uRx@U_v*8zEhCy`YfeOfdFQkwv zr@>bU9}!xNFX9Pc1k=1T6QthVJw*0UCd;iUi^NIu64 z46K!I*H{uOWgpGDH<}OkVq-Lc{F)cb3moAE4+MEoDr4P*Dgix-!^q5O`6Zv_&8n0| zz$&%ntG!tx>70(=@@6|Fe;xm>63f*L;ZZ)Uv9!gT5AtEnm71^hVcVqNZu1WfY(V&= zLFkSNpx3%@IxR7ZyDr4S?G!1ld~_Ye9~;<|$ZgdXB1aPR!clg6>_5eA?1qUS3LQn{ z$56_&QG8)#)}U!?J-~7EHpY%=b#9W$LwV}@8CYYjh1?0b|0pqOFtn}FgdFX^{312> zlfk&myiOm)Yx*+J5arcgBQC2>9@NlfH}OtzBGq!wZZP!cU47X&>BcAgv@Z+OROe58 zS*SF06c6xY>751=(>s`O9|5qB`l8n%=Kv^D%ijWEVqgBqkJSy4GJ|wmeuch<)o0E*(52)GqU}!% z*u0iM^9^l^jKXRYVk+{#`T$I%CqfAS;sJc1KdawyGPOX?er&&XH$_n2i6D}vop}S2 zCu8}rA(r}yD>wU>=8BUfpe*)r+~`QQTuio~)a6d(Hh)%2(~|oIFjH2S*;KCH(_!KGNP38p0g)OHLI z;6We6m+;h#BPrPZK$8anM&VXfm0P_C(kVkKw;~OXdXu$&y|@e=Rl#c9722^3!nV1a zlU&Fh^j&r!Yc2i!F+U#2Vx@t@xIK`ib#B*Jk+TF*=-poL%Gt18PB}CGubjCdx5&7K zGCJj~OJBYsh}HEz@#D@IRcU}R~FCXwoBdb|s4Y*?u z*F${l;rmcr$)z*1<$)Pb*5r-DNrS`o@D?-qR3i(m_rK&yoPgPlx{z4CBoq67` zqz-TPKmtF>*8G;%UQE!A704#7@*y7i8TJ#M7ny~?fufU^1D6VqqrrM zHIO`eknfc={SGjcZuX2hMW_$<;8nxeVy#E_bmfV-k2Lzg3TL~KI)dkl{F7W`VlmRj z?!2yv<+z_cL75@|ymntufTYdf7fdWbIyZ$E%dASvTQHVs3{jr%!6E=ovYtU4*|Hmf zlhUB%??DxkO=}j|zCwcXvY&VeNSDdGg|mQw&M=`=MBS*qIS1Te$lJq5tz0H<;J>3I%R5jK{>d=)>ti}?&yt}GZK6;$s z1K;PzsTp^M_@YH4*=wS4(5gz}bk-!Xo?I;$()?7}q>u$T&Z@wO2xT1p+*}S5VeLBb`O&PqPd~_KO5PZKYNI!q?t;=Ad&!#=7vFuOGX(FBzdusjusWR4HIY46w z(&Gu-J%&}UU#}zV3pemVcrj@LS;ehSXgCZqckG7BNHtsj5c619oQ}Zh-z(P0{k%W2 zX1VMa^-V*EGNj-yz2np|v2$Xl#Eyv_+MB1|J_5AKm?U7uWr-*O_prmz3u&mN;mL0J z1;#5zce6dCDf#>`>+~t6F1kn*1;cv~9lJ8>Z~{{gO#>b9(O{^8zOGZvnj`k2JYFG| zRgzkE;l@}tSd+j%j%5f_Ir+GQoPsh&V)bFeAbviURSR8|sg|V6oduYGDUBQJFr&_L zKBq{Nls7cjr}$fXv!-nw)-GPkEJsM|6-PneCgIGcYZ8|HaGz?D2}>&1#CVJ~`q^l5 zs*RMYOTzJdm?MdKE#3I#I(VJ#rQApjNgLqN{W|b!O<+U0zUx~Nots1gRQkRarS{#e4laFzgTDATTqGhI; zT5~G?VyQhBh;W<%ElO@fQu4Df&9Pf&^QmzxOp0j5SH!VuJ}#}4ZoGASlw)_pg`@k{ z(hmOutCqdoqomJTQXj3%AGR|DYAYgkYA^Sokftpuquu&+8x@=kG~%U6MaT5Pdph*k zSUECY6>as_V`CvB)A86?oPUdd)iD?wgpR4aVT3}QEXtUT5=lt;x=sxYft?3CQAdFR zCHiYbIxp(fz*k#e#S+m-#ZtJHD6Ir6C4-IfIwm<)xjsnf*lG`lm)5aL*S~@|(scs` zhHaplx9wBGUrI{nZL{M2rNK$OKV#uilU{r_V~Nt0bbgYty4ul0YV$IU*$&C4H{aKo zHPL2#Si3+Q&t#3XwmEOwgvE6^SoS129HJ@ubZl6WtrZ$c1I_FN)Gt3iQv`fT8|9Zd zQY}?kHwWXfaTI$RFROrg0+KCHlP#~{cPvp=6Y6m`WAB1D zO;{a`w5c1flfd4W?xpZg5?Gia5T%Mio=*2ciD0jzOHY0XNmBE8UYx+{yLAsE@@6m| zk;sNiZ<_E0iENN>zvd)|qJzX(LvWg`I0~LnnsDD{EGp=|^-*+dSF3KIps!7cC-5uQ z*!8?eGqysW-i6ErLsV6#W1Xg$6QKUyT}8quN>~bU#=LAGB^bsb2D9#T9^ah#2aN^3 z)af_I$M2%cKzrzqsD|CJx*H$e9BneV8{gcV$M-BOcQdQ=|T^_zx}F zhzQf06V9gmMJy-APc;&e#SKMOeiMPQ9eF}4=3h0;MPc(tN2f?U<0|5b69tEpSyfH-qRtIniYq2sjwBXZ-BYw>>cLcLq1)n;SXfjh=dY$InnR%F z)81V79;>CV4aTSB8*-5+B|kX5cR}iVOsi=*X$om4IvkWA-*0qHv_ytUZ#Q^CK+y$_ zFd=%7vE-fxV{xG1;{yyzh>4<@ZX%|r2mc}k)>xP3{7DLH-=Nat6ZYaWBt01XTi#Zb zYpoU3TeIs@pI0>5TZ7Yiv5pqZ7p7vJ;fL6Q@HWh(fzz$lZCR%DIjoAdY`kF}4bAon znH3$@g59vLD{qs=Mrii%-D%9ISp+|tEY$T_Ep+M6ruFo3!$~n(yzS1brnAb@PqlfY zbXHCJrVHzu%5k(OlyC_RJX4c)Zd|*wiv?uczWde-op2 z({SFlJm^t^~N#yTBJrd}Di-95T`gB(MOlwXT!KF(w|^fkg!DGdFZe zw0H8u+RpTCXzFHpN%5Fjdj_}SJvu-Fhx4osth)Qzv6%E$q2c+{4k*6?f+SL8c7s5m zu2O>fhcJG#1IvpVT8`KyfW)pjOp6kB4Ik_zG8)7J7Ya>cxw$*5rhnsxIU6lS zydiy~kKj|fv;LBMIKSDQZSx$SUCX7=<}pB5TxLu*U)6*CCRI)3Q+u-f%5xJ)CBr-- z33U~CVRICida=10?TYIs3byrTk2U(SQYu&{8VmEV>{!101JasqqmsePVW&cCT)>4WD$Jly*Uz zXE&~msIxviayYBPvj;IZ{>cFLqrMG*cGJGKK(-=)n+LKN0mXRKLd8;qPc`@N_~cUu zG85(hSej_!9)npIth4tY%wjxly*h~%(2TO!qpir74rXSlZ#7;#n01$a@#M)L!dBcJ z&gXo{5;~^T0}p5czqX#t@q>pFq`m_VyYw4jKcgTB^Q5M~Y0fz)>1KuD(qBdc99c17 zT}BzrXk5Xh!fucpnD7jE_z>1Y>wWPAA2x)gMen3LhFF(@NnC&9i<9<5``JCvAj-T0 zQ~F+rv!4-EOu`dKY>Os}FB!@@XuE_#Za-u(y!tRUL7#}ih50Jj(*zHU3}ZohavEO3 zG;g1Ri^G^fXI-w4)U z+YvANM2%!ohO3zH;F?s~eDilX&qK$B0;VvU4<5;S!dK{#k*r145rBYaBuz$z#Ox^= zv}HaVGb?rtiNRhyuQdvb<{fkx@*^A-#e1jF)S{NDeC{YVNNQJwzZ%7+>lXuEN$wsx z{@G}Z``tYY%8z0B8tHK*{$&PB)Q%1m-07*fKHFJ$dG5p0i)?4!#?>3&j9~kW}Mn3p+ z1(|y)08q8Onpfke#_mrrC-iFxI* zenq$Hl|3VBVLU5Ok-B#x`%vTyq$_=e`Zg#m&vhR$qgT7)6r2MA-B`FZ=a;Eb%o9Fh zKGkcY!nQ?}2V&tOy#C^%fj1&86uBHoa8S7%|L7wYR{x<5I2OUK+Kvy0ji=h~hrzI7 z2Jzoc<>K&hvgIF?ll?b8^AR&i6TSHJk61#@Sz4f;1W8dXB=asb$Y6x+hUu=v85ms3 zVu_7pVQxY3PvFRdqn3fNp%o04W??9BRh%##Vj>)WwWYzEslUdys?eQK# z>_4{e1n%8}y#2QMqT0gjt1Hr-75NPp_lrM5k5nK@K=jtNy!{krD%)BEwk7hr9~i5& zu7+VA z0aG!wa$GpSoW*K&+bC8%!GhVGbKz zY0gzO;Rz*x=*2GlZVvO;-guqHE9A0Z|4ELl0s>jvyHHE~ZY)U5Wur7&`BfU-@g%QSADfeFeqd<>hxqRY2`nI@P&rV3v+16dy;Yw>#} z^{NEGi!)e=oj8i3TM38@^gyiKDxA$5PrEN15V4JGF@h} zLE7jSX$AXdvacj5Bu#=+EUm7y)h+<%JPd#D@Cw zM|tc+?I(}Z3i`}u8jV!Phxf~8b$rgA6Ah1rIF*MtpQIK1m=7~pTlHZYKk*6sNW1Jo z8b_|GeAuV#f|TM_(0UHrpz*wSmr4~v>{*Gs&t>1}TT+yl(4nBEw>RH1m!)cLchY#- zc^HW2;-kkrOmi!j=S$`>ru9OEz-*rf%-cN*F3x8Yw9@x`c;^MIvu6_<&{Y&Q=;l$d zV*xAF7~-^$FWbuuRVm2Z*5p=iuu+d?~ByxuOU; zl;+v5Gw0tShux#=1%^F7qFjK_2#Q=Cziy?)qkL8jyfB6hFbPvkskdHx0`I4;pA3~DP%$b znQF&hJjXYz#=78VNTT}ua4rp21(BQ+@eOVLOVu$faAO)+#0gVTkFr3t$OG`cO`h1bgD6hU{L zf|Bg}ssV4jmJN@3zks-oz5;O&6OCU1jFp$sq(vn<2N7i)(v?n`O>x3$bz{@Je} zsRG`qc+B6dWfpyZ3VTos5y4rHIA6y$=$D;BEP4ZNq~d|Ghy2BQ7R{%xXZ`ikTS-A8 zX>yk5Mn`AFNnJQNfwgj4sEUgl)M2@Vg*Ft4HvS4(imT#d8$gsQkb&k$Ts@?K+(L>V z;tlrbgr59KNthuLsMeIl9Gita^OU5ajsk^U@fTs6pro`@%2D~$Pl=3o7C~ABbf+ls z;U#FE5{~*xbVUJum0w_n=m8kdlWu4+4$;~W2IA%L6ay8m>DgxUI^+_ED!94 z14jrroPWBRRq-!4DLA@yttzf+FFtaDAKJ|NmR)uf2>tNhW6er@U3~2_<)$M7iz#p) z>+Tc$p-6?0mw0vRj3}_rs*C4YTUeBL7{b`UbWE!|1d3_3Njj27^Zf|-BLx1`I;k=D zE?|qaL5SaTwW%L3ZrdoR{NXgreXtkz4O8~w_8;M4TUj;jh+7SmMeKpa1fiuPL~ku( zXWdT|YXAmd_?MlsicmyT0~!YS+*eBfN+wg|Hh^j6BP7e+UpkZ~){nJc;-kSfR!eJL zeUcB{#&W8E+lYW49ZLJpRe)FAYdZ_mE;yLRzmH?#QF}e9)4>tt6hHI;+@40MGn#0Inu!lF;&tiO5ToSPQ*nbkm{1-l$H}Q%4vB{PfglU#@g=r3L5y48s z+0RKPSL+ERV#CQ*%G4d!Dr)>Z7E$%x_GA0Z!_pkiBa%$6W(y8~tMRrW!EfQ~DAtj1 zw?QGGeC$7H;7!nt6%RM3$q6?e!0P3<7r6NV`?b+V)PK)dF>zQ)pF73pH}sh)KIhWs zF7Y{&K9#A=q+Pt-L6+hD3A72g;hLb^^4PKuTL72&`Gc&ls|Lma?-Ld2%R3!n9XvyK zI`R4L4Op^HDh^NF z2|E<3z^SD?w(!s+EWP~E*hrGPC+8dRf0|VE)y(3bA7NS2pvAoMQ5GAVx|l|QbHFTg z+nY1g&BoCA1Zz|j?{^d@7-ya1Gmf%fDr%r;Nn~it`QTCB=V#WdYVK> ztFez1vVoub83#yDq`9orWZ-CbnOna$aIE=a9|wnqfpqRs9(0UFRvsvHUN-6j<n5pIbn#PCv*S-_@ z4OvajIiz=#C60Qv#JWgo{_qGQXBEgwwcIKG3lsl!q)wXQFP5I%q>M0}EPO`J7y^>> zrrZ;=Q|MigR>~VV@bQhOSVL*w8h-l}OC7M)REtcIi(ioNV;evn`tW3eJG2jUgx`Lr zhX?|IP5~5F+JB}T77uK<*UtbB#6-jrDhG~4@-G4iU}}1pFaDLq`rrE*LKZG!F-tvU z4?ngKH3+}7kK2A_QC`DN3WZ$fBigB|Sz-Q3ZaR&5#<+8Q;A!?=x$=;0Tc%xkk?WV! ztWw;ovqa(kQp`G@;gypAQ3R>p5V(O)vAisXwe-~bfsq<<(@(^(H9v8`GuXbGdy+Rj zgW=}QD!%Rv3pVKfq72uS40A&$!;RJa+8MUheOkEaw0Dp5WxuiSD*McXy;g?K@AxF+ z@D5Jhp=jjt3y>in*(^mHENms_%k99zmfumDS*aM0%e3_ z8Fm=mhpqF1_=U4LE?s#DcRR~0Z9NVMycx5dct=422)u3r)jo_*z`OBNLKXbIz}pkh zc0&v_trPE zdCkS$wHA0G%Pl$Quu;*Q$&m*j0m&b@@Gm+xXs;u5Is2hwupRaEXd0i@rgms7jPH^I zN_Z9sX?KZD3D^uq2K9i_E9{OI*R&gUu1MqWb!re^{yW8Psqc?_9^U$DQoj}agFi8H z)!WTK{u2}ahrRi#KUu1@X*qxKCkrgM78%Gu4Dr?*%a2@6x$V{nKi={Ji_&)ZE{!ki z)F3p_7hPI4+}=7OP#Zc;91(cDGff;c2!0ObND?{Kmjvt#5SC_4!aa|UWtP2TqsoYI z+NdES6;e@Rqk15Us5(%Y|9FwL(d6>y7uhgPWj^o{tBw`;PcPxlz@eqwdI{S&+rQ-= zmtnwtv=kKMpnY8fXO~%hO$VOd#^A%JUuJ>99-vB`d8dBV>xH5R3w(%R_iy>G%Pc}_ zyOrO%%xcJAp=<>-huJf!iLAG$ z%d748@Ksk>Qm>9+yXsY*_CDL-rW)ZMP>G1xx&*Z6k8{mws>$?#z|P9W!q3-_?RLd1 zhe^2w?SawV)r^yZFl!(i$G>#Qgxz5MlD8?sX|D$vd|?r*s~N^m6)_*>OtrO$)$h7^ z1_}MB%~VYM2O@C%8|1g^s9(i;M=2*z%=x_p4Y1teJLFKmzpNe(u}uawqTJ;RO3K~4 zfXemfpI&8pY1dx9^eP1HuLJzvRrW#B`>0e(-W2_wEn-#oJp_yW_RzH)Y@|`3!kz;n z5Q_j_DyXC`q&|2C2@ipvhefcB?%ne?_-jBaF>9N?#SbZBaEAtgJ2)jJeV(*ANOuabHO!EortmQw;MWxIl@*H znX$0CAIae&8G|Sii~aB`D4~=J!Vbkek$4K)j_9Z%1CTyoPDc`pIEY0~6HR8UnP0ii zm?SUZK{ueh!guq*H&`oa=SIHa2D_W}a%U+64<}BbMB5e6N=bYQeu}HXFhGM}rsLW@ z6MLanp?j$T2(GpQ7l|Lm=K{&ag*C!BY*pMHK*~N=i&(7uh@Az|g4+~`@M0MdvA}b4 zi)t&O&=w zqr&COtaJDZM8x`T7X!+2#P9w|8K^LzE8Uh(23r68Md`M4D^P3SSiq(KkFhs_ zkMa8c$1_ia1j!_1vLKN#A)6(3^4v&jn~+4*URAqNJ4x*ll3+SQv^c1?wyK@hGPX)A zrCMuI+S;``k*a+y=J!7LnM|VJe*Uk&*Gryxo_p^(=br7JyW3)aP~yL&lQwkIs60Kj zVP(l-q8f#ZK-&fxHgkIi` z&38?NXPy3O3t#%FD7I1J%yNwB;sS2yf5P7q=bDP8zWSZ+M=+W7T$heZ**eSvtvd@$ z_5zdH?LACy^x)+rxl`Em`{Yr_zhbKgdzH=;TAU|U=BbfKbN-5L7k-Q?uh_tv;rN1BY5ru4=j6KGz(ek8;KB6ER;K#G zHfnk&R(1Vl!YS*AcVed~uJ3$!iXPv^Dwc`!sO!DhXgyO7QU^|8QiJDE-o4l!#^1kT zMzPU4d!xGD_*BvbS(--`?_>UV!d&WhKUOuhn9IpmZ4Y$3;0JNqI@!=1Ni<(5#5NYY z-4M^Q1txvo1ls_;A}+TF<#1olJo@#1Y^;wp_YGtdwo|PKK=xiPjeZc@sb&S4eARZy z#er4()mv`>oR5 zLgOFBHYy)#(dhW)HSKtWP~?S${(2NU(sk@dspON`-;c&Uj&1G}@)2@brk=K4cohM1 zBP{goQtL~)@Fcd8@{5@~pT;J+H+;!u zbIjE5X>1Sov;Xj0n3)bdMT=TEi~e{T+g7=MhUz@SD6nNDb$*8BU)#^oyl1i1l)Q5k z@*I(c_eeVV3_GlZ<9m?f`)4C4;dyLJW$#()@DdAbj3a2z^Vk-(O=sD)?3e)Nn2xr| zl+Gi!am{o5HGQ_p+%*q6P~$9BeF1klb2#;Q5v$hTf^JQzLTE6cDUGuvC}$yDDkKZt z8b;5!P=)_2bW?tH7P@gpE7W!vE&eCAnKI;zR=uP%TJ=tRKt3<={r+jK8ofTSX1$E9 z=9;nQg!B?uhQdpX@i66f zy|BNDgwY)^ijv?LfBpc&&vv$-MzHsy=1JxqqnTxv9BfHO8@w=B4fHZdRmbGo&p42o z7Pe9AEB+4`j#rKD_aCqbI{+89cysD9=fRv>|o?c-!=_a$VnsCKFHd_Kkxr)KK( z*+ysvtYWswlAZ^u7Sjk%^={>f+oM69;dw7+b$TW?g)XB}UTOoS711g$)%>=3o6O+j z%+T~xSsxvwf`ZN8amx707*6o?e;1(4&oBv*>$$ggk`}ebfKj~X0ggJCrs-AxULmr) zXET(CsCP^73sWJ#{ZW!T1ZYJ)fx!<+nzYlh@u@9%x%peh?RlxEEc-1UP?IeVHF<9g z-P5bS!KP^-hi6xz07e+OY@`zH<=Br6ze&RbRAV{$l5mrfz114Z{oORiTkSF+lj99+ z4({QsS?|%Z$}41o=P4wBF%Vb z*(fjq-Ec8UCV8$!5-2j`0gC=~P!x$=`U*5XWuRaAxf1YGTifeos-!lr{STBJJGIj4 zU=2Q|ZxFYQ1TAvV#4&P$py9(gw6>DkS2?|l%2!sWD0PWuS5_ywTduMJT^>#zzG_yj zdlUWzs^0Mb3u+jBRY`3?zx%3@%G?jg+n~m~t5+Dz++o%p2K7^=+1op0H9zX)tY*ET zlI`PUg69e(pprB2Kqap_Dp_+VMFpregI~i?ilzN)hEv*QS=u#zV`)byPk*&ifGU(` zW4@87)}=U6r3{v+K9&id1xNs@6?l|F_10jjSXHeW6gI8|m2T>rs1j*dfEwu4^!^K47OLzJKyO|{c5UVRgZj7LhZ^lrdHbAQQMtU zw4$b(SbOCu7W<=jIUCgbTWmG8+dR**^z+9I% zjR{bzP+o{yhBnkzx3&0lwWPOtf|K6E$Xe7L^p^dSlRO(C3G^E9K!uBa#LgbHI9v^@ zHg8Y~vBRXf6fbw*@dN!5uBMh*)eWQoYjsMBP|Jrk`GJX_-MyH2UcY41oz#dAl!?Q- z)8q(saEpPfByG!DI%!KlF=@70GRgC6B!RY%@hBzshTW-Zq#9VI$1z5=9{306Wo1-! zNh^HIdw5nTInh>de(EUX?S|JNOjV0Lz-+r%XKfFBN(rvbUobrp-52o$x zkHa(L{E#Nw>fnHpa)$=zY42_S^wpE5Mr|D7ZH0Ajhq>EPnQKo!BucOuL z%K6W&*XyWb6{YbqYuEbfRz=yegvvEk&1KXiFf6sH8f^Whq58U zsv4j?>rb^*HBQ;pp9ZUH6Q!UFeXgoCm0<~VNL3p||LJ!U=eJD#KFN}Px@fDma*9{n zAFzEqF%@I z*k<2W{I^o)(k1>?VBt`TiBr=vg4w}gFvVdQi*IsxJEp628QK70+V%(xXaV^4Tx7m`#$q1#;VUL(01nLgfr%hIEield?^ow1m=3V67#tTC+0DrPhwt+ zZxZwS-~pVKhI#!r*5BjRmP)f4AIXyM)NxTHkooc;zP6qb@^bvA> zBr;efQqNN%k~OJ}A$KAB9!IcL7qk_92&r!Zy;`tTp#we*K-o50*>PIgE_jr3gpZqY z!0{!A&q?NQWyizPX1U2sqY~8$^?qty!tBi{fag^*33dx=?r4eHpuSgaf2@-+mAm$~ zdmWumRHJIDASfxfw+#z@Tj%iTMC0=K0t2goH*m<9(?r|5Yi&j?+pArM>;_-5rrLCA zjo{SonJitO!941-O?n;y?HovAqUuw&)m+bk;DnQ-v+zyt-R7P?qRna4`H?mNPN?T-ve%G!k0mhX2P)v4iiq-3a`=%XX61A{>l+}h1I2q&T42qC04Rr_IIwp za7!DjUyFYkt6{k`)=HYO7Jh-2vv@|e<*P0R>*3C7v@T=L;}gyf?rp6_2=rxFRXI{J zscvy} z+KKCoPO(Uv(1h=}Hw2cz+Zly4UXXtWcxC5&BU&ce=KNOtWhuUJ$m!*1c7FW~^LSbd zbP7MI)9YTrbhWG6SlRj^`E^s9DT_K%YB#lEc1wO!jP5Q6mV?902hl^l32Yp^^_zPw78;Hs2>s|Ro(el2oo@+~WzN+vZKckE7$}(wPJV*pb_XkQ zzjPYTRd``Uw~KEYE459cNh`ATQu`^xQmDhb>QF@;O*`LJ*D8M}P~YCFpEA;9ozPny zq-4~tz_K`24UjJ)#DZ%L@^n@($ua^Cu?t%1LV0)|CMC;50P^c1F7hQ8G5?I!Zzz}q zP(|3mXXsE7b2LRv54U+pxq%N@sxQlxWoRQLD#OR+Gm!J3G%t8=&sE6h;H?}o_zm?h zd`sqSvsUkoBW!c-Y1&w01T)#Qg@?4Ny`Tz4#c9&bYnT>>pfs-=hf>WLE`_qk9SAg& z@58CLG9-b9VN7})^3~Gvjl;V`7kacoKCQX-tj6>ex8p6DxHVdN&X3ZzVNJAR8UaCE zG+WCtAC$82wUi?Gx1td!(L78d^z016;7=Rj0SlicEj-DxgIjtW6I~s-V+V;<<)BV(X${)d`>#qj6kDSlVSwS$>>aW%cUL4Hj zoV~Cy^D(jHf&Et6XAjLa$QQ zRLa|9@WoV-HY)=h;_Oh#eRq)W;Hn!ML{w;6xnh&JRNoQf%Y`;xkyvM39wW8c1Jq@0 z?+uZ~1q)9z&>9^1=X##yFRb`%i(ZK9fXiBz6}o4Dgg*%{n#oRhF%u6i_5ay5B>q(?T3Hnho0JWLiDpvgHU zo{wAa$4zRH5>r59?1{b|M45pSyXpd5ns#6Jm&^#037(yiz}kfew5xbn^*r)m0v+C? zSc5jyCCgAXw0sBn5tft z(f_-@?R~$u#qPe6p8GF56w|ZFSJLyVOz?Dn)q&|LM0zQDK9YI9HA$VH8d^eD&nUV) zTn#M$b0v+exidjlJ*D?diW#9cQamNDZ8ZYunKTI*7x8job7NMC8<0xfIe=i^nLI?Z>BpIvNE!3df`R$$EZQ&gB_@Uo=&^Qs4+?_Zz{sG{#H*I9@8loGsqQ`pWzd0QeCV+L#fgn z_$iu_#;P%u^w81LRlHD%=8aVY6OI2DnvVloC{J{ouh{0JH7N468+-s7ODY^+C*v!0 z*0w5><@VT}XA4E_w1?FGnuO&sQ);%mixKqV38r%gYw3yO)GvToi&2+MrBfI%uf|6<77YI zT6BAy8dsg27yK~~M|iNB=cdXnM&WX1d0rK2m!{SViSES;?)m*Qsm0In;0eWeN7v>+ zMaoT6gOqog(8e@1viu^3g8}73c~p>wHot8cJxhbV>1s7a@uH-3HAJaji$S0CbaUvwj|$j9U%!YkoGk(1R2L6Q}N+W1A591Vmv;fbCG~I)Us7iM+Y&v zBCY#SZ6XGFVd)vBz7b-$O}`x_e}^FB3Hdz)6(ap!jT$DP=xVvQP3gw#>B@oH_a zp6v3%9-<+$hkaI&I*nIr2AL3t!rAexQV@Q!o@m1|$WmFZp{!b+odJ(4aA)mp)S`qR zw0ve0|7IV_k)`TSK)?BVNoQy-TA`k302`k`2IuFnqt%MR(Y!T2{!OlcQ}(cU5B~cA zMM|J!4W>P;JG`+2yX+aJLeXrC+tRxe)N0MDwF6E|GvK(`8Pzra8dG3tt<1+tAhtYLx~*w3Ve3^;@n0!VD*b&l$o3K#&JD7h`>BYuiU+tvW?3 zhccedGF`Y#dtq%jQ5~!(dmGTFlQDH58c^UA^?l`B49%FLwo)=;=;#!6g5nlK2~*V> z%JlmDe9u~Qni{24&tHcb<6Jj;9f-s-xv#u-*$y|=sxQVavHN{Qruxhyxg*Qfa59-j zPFJf{SGCu@$@r>&>-2sX1O$A*5PBa4wlXuU>!zzsT$Qt5e04or)%o>IjCPS(kiOgg z8lgjhZRb>anxigNEPq-*nW_5dl<_87GfS^_Dbtrwt$Cn&b1*HRhnb8? z!PevRR9~GkJcw@OskIa}h|15$(-=ez=Bo{ryVdA@e(tGeT{d6!)G6Dl(uR*Q-jG+7 zPJRrx*t@D#_lbH_0?{p3o;CFnuSL@c@pYAR~=cutiMG!`B5BX6RsgsoMeq<+%n>6yH$i-?( zz$F8dQREAUQMim_avpVc+i9@oEmqenepLoUy0po8GQkHbvZzrsZh(7go7sm_KUGip z3;3nt+Qi_utW9_&YO+kNWq9U;W`ZZcHIi_~hTC}`>zHNgc15YDx7PkbZKCr$84(S+ zC3H@B+tzA;^@HW=6`eAr9<^Vo4pWxQAnQss-V|0R+GTLoGaTE@w-M>B1>&{c#U;Hd zIOl;RIc}%7#)sqmMRal2g3?**BI!yrwOpmX<8wPa+GVY`KIb>441J_1TE9vi>v3Na zVw>HE!d64sw|UU2)oM?VeB_2+t$mN&zEm5Cq}nJPlAeHp`tJv4O$mA3dHDW*xp>eOFi6Cz$2=yp8!@YfJK0)bpqA*55X$ zgLO)lky>q1-&S^yC)%Vgt5Fyl?UFDZTf5O^wd`1oDy22YyA%ZU()0UJ>bhBN>fwRt z7jOc)S{H6so9ik!s`4)k$2(iEZ&BwdK5tcX(o_*N)vjip{*BtGOwhci9ic~w=5pN9 zqo0W;9upzEFz#c!HP-smPPMXYU{gN_J8Hbaj_Q8&dN-QKa6j_fqnebN25a9vYO>Db zcP~lR({|RKdsU^367NlW^40cL1H8Fbh*EJVLQDK`R_C>zsu!rDVndH;mo{b_YSgBc zbx48QN2$`)lX0W|E%nw@)1S*CzTklBN!Jgm4g5W1`U`tn!|AVOVg*R|VMU5Oq&Dzi zcmQUKr$L9{Q#RhFrH9aNzZ^vS4yjYCRJg6x9Vhs;`QBt1UEh*wf_JkG8f5K!SY53v zx20mVi|v$Zef^!>nN`0f`$}$T21!&_?3K(Ye=I`t4*tZ zXph3#0yv00EeeMZ9MQ~~jo@87#SIiy>C$PnqQ{Vzve3Q;*88VbMW^(;WPN-_{i2LM z@L?2dZA?LH^I2Q@I@VP`tGC>gAJ14Vm()8t<@hO@bVdC{(cdTiRRl-(?opkqYB!JC zMG{`9k>*@gdn>)L?dU3I#(hpw@-;QD+_C#nxUDCYe!r%g%Qfeho}rX>UHw7vJ5KR8 z)X9qZN9(sY)CG#S>oMjnoUHVe*cQ?64>hySBf#H}gEER&4E|!fSdH62QSmP%@aZa! zrG&rO@~e^8Efwd&a}hX)rQ~gZYd4!h#sb(nWZ&IjDO|J8P;lC;D zP(@Oh!3l3r*q{o1Y6FE8IOPorT~Xu>3V#lu-)w4KH0W>lf5LN(9llRryi zv*`lqcp(PHa8u7Hi`RX#4kAI-O|Y z(`v4wtqlFW`1@9T=Tmxh>4E!MrW*EKvHA~=e1Eb1=0^{T)Nb`HSE3NJPK8=Cv`B`Q zH5C&AJ@(@^UuUVqH?>k-{b<5pXvVKrrd5BbarH(!zTYq~wrM7&F%;j@jBR4>49;t% z-|t%wlwk-Rz{gf5?>lO>@|7GUkb#=qQQLZ7EGVtndkSdI9W^MRE(j~k#hQFeyKtz27fY(X9K?m-rJ3JR%XS>46w(o6rAIkb$ZJ~U4gYy4YgKJE`q0xE^3&4x~WCW@) z=anpD%km-n-)cP%U(gAvI`~l2yXxrZcffR$Ie8a8oy2PBgeQk}X@UDaOEKXj$}{U; zRHCzY(Lri>ReL{DiOStmYu0?*k-h|rzW3usRwwr=I}$Q0QSv=COc6emc270F>+i!_ zX~hBjVB2^=(*lkSDC%rt;*it}Nremf>(1|r$~sH5ah8aHu{cW@Q6l0hJ-VlM)eq*H z==ZBe?$P%2rXKgz+RDsdY5ILNI?8Zaqu&o?BxW7f6@8JHUmI9B?=394&adHk`9)6` z@2hb&Y!C_9Gr$Y=R406*MAryr^{B2Iz+H{HZhpx_Y! zjzp7C2F;k=d#jETdiz$Ux{nav9okKwK2m!c`tD|%=4ecYt{f+d-Q@9DovQr2%R2k9 zs?&M@Xf2I(la-b{Q9o1~Tdh7%)uyh>%Wc&Dg__`fY+FYM6h^yYoAuKds+Xc{_?kAn zRFjo^(e&aahSHyJrQWa9^Gc1a)<&<@W;(^TnMT^xk;>@Jbj1#>*cP$SP2>gSZ<1Jj z6P_H=r8g^Tr}_IOn-*>o$Ca}iX|hh#QVij=N+-G~$s6gpPOMe-ZlG0)Xr_49wq8~Q z=>l!*97OTS((Z`rv1$Xi^MT4ja1qSReO_1%T9 z60nBax(ihq9BR#W7hPSIqVMRp3SyK}CeWHxQ8ZVSmWODTm#D4;AELEhpzO&(I_)KD zbHM-HOLS0NzM!^xF$-l#6n>*|zCb3;WSd4-D6-apSN zExTP<791-2Dud_Hy-;ya-+MFb7u?bfsPiY6>23{Cy@KZ^sA3Jc8n;apSW|@5{c{t$ zoW|d+g@??oGkF7&L$H}C|6PY!bi+C{D0EA8BwM<|*BsQPpHHy76@9>_;HX%P_ywVX=<_8CywRjAy@C^& zMVN1;jYtVB4XWQ28g3Nxl;}0|$|wSqVZV`oml62E{Wl$HCQHx7XQLQ-r8hM!A(q zqw9!{-U~BJ%W8ZkovMQ(eKW1M>WD=;<=QlwR!{had_T=$mRM|5P~5@XhL3h{@Ou3; z>-Ku$lup5!SvfI4ed-IVe*=8*Z?Y`PMQAaz7P_I)^$b}?uNsOtkEx5LV;XvjS~n8? zlr0mjUo{fBiV`xOnlu%UUB|46w$^PX9w{LW#>pB~!>na%?uz+d1{ZOnf@Wo9#?f0i zNKr}t+d3>(gzJ=JW2_5RF>3rAL@y;_5CtWQ9?I2$*71p=rA}Ehz`CowXsYwD&SY&4J2sV`b$}!2W~J^) z$XIPBCdt7vzkb&EWD#CQzik@Sy%u=9fqw*1>N}!JYbE9wMotPRLxp&l1S;bGO$5B` zUE3MLxM*(J$L}IwN;}(J3e1q4}Lfj56aL zI^0>bRa(456}yN;cY9A}Ug0v**;M~pebKNb=6+iMh(K&hcOY(UzTVoBgUd|pAffUW zTG~al@;NpYsm`d;c6F2W?=B)or=00d>D|D-(6dC{M2gaMn zYW3;~M-=!N@MVV=y(~w6-vF8~r&IhpLiMnXlUf$Oj&j}+3FWShN4V~?)_UR{u}7)A zU~H7jlS8^RAN|g!JhSYXJ(iaC77;!@kWh%NByvzG*RB0nI@eoF_BuWWpS3GbJr9hb z-tUQKKFvou>W4-N6)X28t$9y;shl27$$dnqvUxO(?jusYdX0ueBIF_1ZmFZ`WFKMl zy)>Ld#wSuNTIsJ=QQ5v?xspDL*7g;mLX%PKzmjr2>yDy(eMM|YlQ*%ASqX2pQ&;$n zj)arA5FmQ5ws9b|Gvms z$aiId8(s0cXdr$hji8_Uqv7xwL7oFdhr~l59HRYvDDpl7YIMlH);?MI(;xU`>E`a* ztxWTSANras-Ap*+{~!vC;MAi?ZG$i{*YMs@S}{O0s9-`_uBBW2aIU3q#WJUhT(vB^ zq4auyXk5WI%#r2VFz)yTD?bdO_Xdh1N@M}m8YKQ!whpuU4Hj2jz5BE-ZO93&t!^I( zv#!y!@viWxeAcJ=8b2`x42KyY!>V$j-!LaL|xZ+U!0(a z!$fk`IhElBE#tzmm$g1__f2br(^+xOVVy_MPf)MnqD^o#GN2PZn)hf|#<1Q5(}7!M z9EW9iSEl{L(d6E(L|2E4D#}^hbu?VmR5n+lkP)K3>&s^+sPhPDc>PNB`3T|Xx)>j< zBcS?6eW=_>5n1=su#PT=N`fxF*#K=$#e2LTsH{C~c;h#csb!~@HI%0(Xw}MA-E`jc z>qybWRmFS5>2JM__raq?SfvCb$^cL?jOj-DY?P>4^^qr6#8rOANNmZG*uRC-t7)RD z_x^C%AlMdVg=5s;=1s3hi8{&(J=Ga4su>0#wb!BI1F^A?)!Wv$JiRws)KHRY(u~of zR;;n62H?^R22j_#BQHsTbJxxow^RpGyWy%Aw@z1WP0{p$od~!a561yBH%ANAwGMvV zXctwZqDewnst1HT%3KuWU$sre|3Q6pZApEQzP5(^&*_Z3If@q3YCG&od&Y<;}nEJWC^7A21rb(Kz@G zS_nFzFCoyEaMqUuhrV=rc!FM!6>qyvso0Uc)eJj$)aiV6`4(?za=Fofdb^s>> zal#NX9`6(ts^EFIQ&bIo1&yi&8C-!|6&Iz67}uk{8&j7rL0mSDv1yi8`)p%6Fv1HF4pgFpd5rsM?E#2gW!ln8N za_P{a zl#Zd4kuE|jBGHt)*Mx@4wt_aMi*UugBVCP)s_y!}s$55xtXL(@mszv2 z0=v4sec|zJi|{*ZZvmPk1zVl9386!#>4*3W@;bg0jzTujSTv0ZbrkJ|Z$%^Q1`F`h zinO56swu!yOvy^wj_wWNA&fO;vD*Fbgz6cX8EfR?&8L+Lo+56qAOFYe)C;3)T))0VYe<*3v0k17uQWB!eB5Re-MV zV11ff=AdXAHXhtgtwKx3!(gjL(*E(HR{2K}9dUs-<}S<8-{VDCi}Qj10aJq9fSg?{i*4Dj%$9utI7=^IGXCWuaz^+?&98-YQGmCx{lVaj#S8 z{sb7-sQ}VXM11^ZIL(_Rf|QxzWSS@S=l4B(lMI6&%ih?_PByOQ(pk(_oz zzLuwO7T#D1VyNvTuxqHlMq*b02b#BfQ1ZJzvc)%L!Gx7)QNCS8MSyN<_gVkzT;0$qFI2YV9NmiUqSA->ea zkB$?As=1cIkK9JBtGnLNb)-!n28Ag3I;uWJ_?I75jwQalZ?p@kQ((2@4D|jKh#aSS zTc%){vOgLemde#Mu0)I?6)+q1hMUH*)2Iwxn1UGX4_|sd1!A%q$$zQ{30R0$h0FDp z<+kWLI%YDd&liz36_<|GenC_fCB=pErV2j?74`doij7l6oglkxM6Dewc60K9imi&&DO?_YsN8X%BvI#Uhq~Gezw3f4tWr6q;j}{C~I;L08-?PGcFsNg2Y~LgbOW8 zR$|!0dIQFcOi{mLB=Q}CV_~_{>(@TGjL|^9WP;3_FH`7pFR!Y#ii!L7rAAZwXh{=$ z8)xLO!kff&`1VQOnhv!MkvilP>}UaQjbes31J-)WBWt@p`6or{YxQ6M;X1VH!J$k` zG?b~O4+z=xF@??$ZCpP@?q8Niu6Awd-f_{yp!%-MUZjw|dFw#eO#F~q-VZ-UP7JE< z+7dtV@FOzl1w-C;8uelZ`I#(xFY@nV9^n?O=Q4+l?!9W}=X&{h3O$$@6yq9*cH(|} ztV7$qGP0J}2+owdH-OuReg|l3wupDFj;vo#3aaX=!;e#wf@)UiP*&56PlkunlvRi{N7wjgDU>lwv~rzvpWr95ZO-MbvMvAy>@PZ{pD@$UyW==f{|tOuV!iFnJiw^?{8BcD@< zS=0!e^rWQJ$(tpm`cp5n2<;P(WL_ilY$yw=Jy2j(P>FnXkDw;SEzRK~6wf-;w^W;W z-=^J3sC~8Fz$@F?dI{|%2>Q(|B3ySpPN8xi!STnmqdFgnW`W&-HNQTHcdVTvUl<@% zp)aLw460Hl*HGzqTK16$Y5!rn#;CLB*Eid9XX6JvaNW-SD3r7t1_DTtx3*%4Vc88I zK=$&~lA<`af1KQu2R;?GvKvl6rm_|hQ6m@iDYkU&W|m$LH0^FUgc*?Ym^xWRedBL! z6u4#8XiuM-ggt2p_iG>j{MZEM;&jB|dSF;vZs5yHxROo%~ zRD|r|6!i4q+!6EwAv!^|Z_-nX2v4fqR@N}(hOFWHc}U8qtqi)A2bXs_u8l zl^;?f0yp-7pt!rqVe;1dnDC!z9eRW2%@ZMA?zEP)hF_PoCg4#bjo$#T2tDw3 zjSi78Y@B%ujnY@Rhqc=2ZCG!Z1Ku(my;#)9ZV(S?Nv??Sd~u&?z6c)KwhW?Exd^ZO zv?6=1s9z};{3=c832#c6i!gk}b?QA=gm&59N)mPVswB$&T4|zIqU?VVwF;W^Z=x=6 zG>Vm$Agas*NtEHhJtt8g4h(m=eA+q($Q!&x(ep&(y1hVmY2rRbr{aHc@#-~=i&<|; zg7;sM1Yf|Tgo|SU?>`70`}qG7yoN(wjo`EQG%n`dbrRfW05#787azyd&^%G^t?WCj z=G%=NZ{S)l)GVKW?$@N$jeA@W{s2u~Wl-W!*A6HM6)1C!e$5lcltu}X_)(W7@w4$L zt*#+}3w33n`!!6ppun2CCa_8J$s@M)S2TtF=&n@QN^SpUvaj0Sd!{MulYTUNK1TL7 zwWP1-LuFI`Vp9vQbQ1aU*&{o=hL`7}k42z|>tB+>=&MxYV^OQ;?Us_luuGD{I6O*I z_%mSe!u4hHM19S9Fi5-M9CXE@6m5$&)gAtwdE6Msqy9}RJ{FOl_Zgd}scZVu#gE0? z;WG;H31?|EV^MP{)aJ9*_xeHV%v3Dafs-r6{X=^4%R z=(BnFmFua`ZpOcI#U@o^cY}oXYyP``5t<)))+HTZD54_lX;ZVByQCXTmYJgw30v~` zX4}}jH2h5;keIc<2LgU9`Wat_ZY~zaxW$;c#c7z{K+zeUEAZGC=mQeWGn?Tx!MsSt zL$V^uqF`4z1)I| zsR$bM4-1m7^w?hhqTr4myJ6WMAV;QHVoQah zhT*?!4X?3>99*D`Pep*cJq@SFL*f*=N>uhrPc=D(eA1QXelDVGdSj{^4KrlSCqMHY zuw@GQ044Wyh7 zos@DKj!fJ#_AO!Vd`Jab?vED07BCz`|5CObLgNiMiUdF9PYwJKlbP>96zw(WgHEr)WP6gb80<=uSBYx4IkH3wE0>F&xTBn<5FYw3DcpxT z|I2nDh5I^+TOp!7gBv@3! zEhZ=DI&%;94|!x8!!^@sP*;j1`3ETyFGz+>hZ9Q}7XUd*)5$-#m&eM;5Wicr_>4^pVr zS`nGCg#+IMH(e3*;`lY`3-_H*alx7R-ng15#DO1*A;BeFNO3XWHeW1kA)6!5P)O84 zvZQbwcNNTs{#B|1N^z`ms6BhF($}44(O+wYcVznT%w1U!qYHv5%P#4*Eg6{PFt$u? zetld=Q`W8_NJTg>o9eC;HM_0)4UFKc5)-ptD(PvgwY&)0eHX(pyB1-&x2)}Cgqy2t`osk;$!Z}L+K&2!#A1VFA}rtuKKLIE;MSL zF!V@0mx7f^18|w?WOwX)a4lM$Xnt%m<4S5i4YpYl&zB_ey>d8-_~#UtZ=D(f4&yc` z1f?7_=0diKx!sDjW81wHid!#?8QH9-#Wm9c69_K+|I&_;^5JOZ4%~p`UZcS{TTIn3 zt_uDY%Ruft2!copf0Y4@XW;j5YMPn*Rt}j(LcL^xJE$d#>`Rkl{G!w~fY^7Pg_Fob zH;5gI*Co2R0m~)2KB2^oqKPu-H=4c?Q+a1oX!SD1(VgAUA9lz)=y6;v{5d|!TT;3_ruLgejK@zJWsz+udz1K} z(${cciMbv7;x4cW$n7@x?xM}2Hg4Gq-z*}0o0%*NjQ5}xCd(Y-ay)HI=F_0f!l(FJil$D$D2j_azI#UyEu*7ZbAS1#z`8!Mbu8M0UuU;nzm)1(T0_6 zw#Jva#qsniXzx;Z2KF=d|=|QMJly z<1PrD*9NsLoqPHA6O^A5IB{A5V1g!*()m7H@&pMA+eUWmz(>YbSN3NglX>idldOI(kXm?wpSRb5<; zWE;Onl@xSo%jxGXs?9OT9Qi5pxsujGAD(U8DrMw&f(2X~j10Qy=e|!rej|b*sN3Jb zUp2!j$5~lOoDG(!N`ijD;bhCGkVH$La9~#w?d~$Nui&qkw z2^28P^f?11)@4Q$3UgI>{Dm3J^y$Or7*qM_lWh#(?>=*ka+n2V^mS0%4BtVU$+FB? z0Dw5U+(9Fx0oq){xN>uh*L2i)yAT?UNd-(}c4-gXtDC23M&g1Wz(=)OZhITmfd^8#0)^j|T4)@m4{-9pE*t_r@)O z{KX^@rtze;W^1;Kr#Y}$VEmr>vvWoc9;%{WaKQb*lLJuK2L}EOhfObbVn))tCiz>1 z=)$xn<-(cUrX1$l;TL-^SOk-qM{Hw0X0GAMqx2nZ3k;Tn;PNiWI8Z+1f{(&)!ah!QINO1pOB*)*Rn?H1p_ z_08KOYPQvL9D+W2?x2g#X;mCZfot@*V*HPPb7et;MgYp^TD6 zCpFR=_JbmN?>WX0`CMSEPqBN&NX2C&ZP+VzS2jPB-TI_Z?iIo{n!RxA-EL8n*6qWp z3-1FI@~!v~O9^1QiKyOM;CT`skM40O;5~^~5g7R67fv`gWj$2rRKBo8{E-iB@_1OR z#t>fs8vCjsiVeX$$JmPPAlt5^G5bYs#d^qIoQKNii|~x?#sHXkl6jxBepXDLl4SN4 zD`vP%Uv1oq+Z6db*H+eAzO$+TTLE6!{ow&Av^{{)nA4c;lbt$T=73&=3>aVGBqtd# zYQAUBX*&6%aIciMpKStn9^|^+-;OrMSbftWEYnUD3|XWSJG8gjQA zuc39?;|@ev?zeLY7QWaofI_Z1Y?rGJMGZidQk7ic1EdCxgNLWq1d7mf(?) zxj!3;z`W78SW*DY`s}IrnP{1*37bVcoRtb_m`YRDJ;hX^brsxnpWPMjPk;fczU0|!N}zisz_f}#UEL^b;0ut@I| zc5EPvVD`GI0L-cj#;c)(ql9}KJ6SVXB==+?(2b{UMc*A-vDFCp9qZoc?BV*Na`ZG$-7ggf_KO9)=*&C$? z!w%q`Z#uBv+)v;huhpIMe-!>LI&5=#tbh&N(ZHn!M_sYY!JfHK3j*~y%kHA(+lD`~ zy#_UfC;!Ivrg7g3v(bGYN7S0oZNqjrTS^Fl%>+(=%pEHFUPRXZ^{Cd`<>528cKV!# zceJwn1uHqw>nsepL%QQ4q#^>`JWz&Med0>0b6iALPXS6quxYI|e&up--T3<@2(ITqyK}7+5nF6P#|`RShxe?whg>)W^l~F;51|e~zIH z`uv253iu5pFHOzZsz>C?Xk=}qB0#N6QIE&o>yr7)oWws^XH zQdG^@ewf*~&sf0U#;@kkN{bW=*YjluFasGp<0;!9r+r=8%!={U<|_s4dEyH+XAdx> z!(q;Cv@r*+dF!oOx60$F?Q=G~T+gS=k?S~eF-8}EU~Ibsqkl+AS%H*ovix>qA<8?u zWc#&rF8PV0r0lC5KOhrwxZouE_7uimD&zW7z}omZJ|%G8{~@zos4G+%#*1A)6m(it zsn0eBTaq>fTat|u-la|9UD_DlIYwYxGf|(@VyS!BCq1M!lkSZ0cZJ1KKTBI*iasMk z+%%)_goNO8P6oeT0jkUwouLszQ5RZxMv!;d@a}Io22y_#VY+kF|0n3J!vjd{ph8kt z%HoSXKz)ZtC~icW$#-{=+L}mE$H{u-R!3Y@+s9(?4tZ9-X;;FroC54r3#or72;wA~!Fy^{; z8V1KwcEbtQVGBEFEs-Xj6`tj`^4F1NY0+7%Y+Ls^6`U3RVY9%%1YXD7FR}T(H1we^ zBt@Lk&v*iei546_l7p6J0KGgb2Ki+%Hyr7s@jE%^m_8ee+|P-6-e)?yy1=2ai^T=e zi*>Tq4?4WIl>NLBKoovZMrLVU<$q==t|fOLSU_3`RVwh%=hTOrJD*Gd{IOTXYk71U=}?~S(Ex|DRsNy_E!+{y^>%y@`yvo)#%hk5l}7Q%l{+N zSD!+p1@}Qo$S=Z@F=qAu;uk9(H@YR=7=FVtSHxAERyv?7tOe6WoI7l>9N3aKLbg1F zabRyvN0>y+ns3ksm2ljIBYMkfwG8a^s^!KkK zFk=Y>QiwHP5<99?(vJ0n^f|ZIj*S7(!nXK?BcYk2=0kE^sE{+k>0M;X99qH+g>0E{ zqXSr5_?;}(Lo0<75T52)3mgFoTWb+9j*wT{H0M8JdqGbgLbo z@MY)68dWT9+r*{p{I1#({IU2;%fvB&t=~!%YmP76o-AM?UXjGGI^k>KT&C6ml;OlU z;5_$p!tqw(k>J7jgSg1&B}F8$ch@5Y8$3|6QvK=5%!5?PrYlG2@Fgq+JhYSUUczL} z(@#-f95!cNC23&FvOfb1AG=|7$)_=&QOspw4C;k2ifdLG6xj`fkO3?3@K>&|qmV$V`~ zc?Dxw)gMT3jpjPRb^3w2Ud38{9E2MBJ0?OdE}<(|MLpln_hrr@xz3z9$0+m~wp7jb zkkdzh=gV2By9c@6U5xJgBZ1v+sJ)!JU&98Q{I&GOH6fIXi|NWWQN?%2Jqd5>94EY` z-&4iw*pgFyEw#KZ>V~&l3^u4nm-N%%LKffdmMgB_m8Bk8oTYrer-j!=YsK=J#=K7o z{x|a)YNbsjr3W3Qgd5P42cP{n^H$stI~C7m8q@0LYfM{pM-s8i>?Gp+cXZ|^nAYY~ zjcG~yHKtwoOXmD*wlk;4cN)_wx=E&u-7A?kZvUH1+wdu!{T)o}uvBAO(zhDZ{wk92 zbS3bD57X>Fz_blZG^XuEU-#ck8=#e%Fw05BLfn6PON>z(9Hs1AqGg@w=h-)3#S9nb zmFGXWf?Amd$jQ+kG&_C6DS~5q?UyCI8Ja@uwK8xE-Od{}g>A8}k1261W4m zOSsJoCES!8C){BNDAp!$(8PDN%qH4K_QxE&RxXL@`9$^NY-H#OT?ZfOB!J{5+L z&s`)p%UC5h-S%+l8x93*Nf=t|G{@aQ-3mpLQt2?QE)=aId&?PSXO-uoi-0N*_*0^o zkmW?NFrR#GV@sRq5RJSoYBV~1OXm1vhBJqP!6~Vs?%0h0CH0(t<_+o(eocpOV`JXf zgA`o^%!5ull24k<2keHjh=YLnw?8D@d()k8J@?a+BJr;B)d6~1BtD27cS@E^GNT-r z;=5IrEBsxSd!6YlSM^)6{3SXm1qF2FFOeF#hUbM@2v}N&a_!KI0KRHkxtNl2Df=Wd zgWrD15IuK&Bbm8-2bV5p=EIN4atDI?CZGPgBico-;K^c!t<~i%1U-Q5e^b&Fo#CV@ zaWD1$8~gnJ+)viOMMTrg8#2ejY0ezqV05R1i&z41=6DI{gGtP(QJcvBF1FFu*-sPi ziuSc5v>Zu%u=0+L67xsbCEPEjI^piyO}Fnteto{BcK5`{+D+w5?p1KnZa9RX1LQ8g zCJSwv;w<#TF1mD2q$(5kQQiBZQ|%F$NJTXzrso_A;}qDdve3=R&O$D`H2#%&#wng3 zY?k~R_H}9geL0tYxi8*RM(m~L4@64sLB~n}-(M#IAGsm{|2D}9_}Na{{6M^;EZaln zABs9L5n7J8k{sM%l`s#;KbEtI8o|NqXvjk`Nx89+et#&Y`r0o^1illU2x4|n`Xdn$ z+7|YsX$mfuMfeCnRZD$3`W59r!j7V`mj5>W(8pq$61IU>JQlMX?Uj0iS@*X&Ow0BB zr1gpqw-!dNoK31jCby7!M*7CL$hXM6C4%y#sg%ywzCGu!ixTC??1HfWyx zS4n;jSc7VmYU@AEqMI+p_S%D>`sg-F>aTw;LGC=OF=dn!q-_K3e7Q^X$b?{Bp4_cn|hGxgf??K;4%kay*g*kEWWt^G7HO2}+UO*CpK=7L51HyF76XU~O#|OF8 zAs!!IJ3io0r6eO_PlG26pJDV!!k@nQP|fjSNRs(o<7j+{a(tLz!a#E-laTA#l)q7b zU7UaOSeZP>D0^K;%NY5j76>KUl>=W-fmFU**IVx2NZ2J)SvXUs(l@%e*ow10#?qXu z{r9+YPKBv)y&JxUh7V5Ak1ET%coHvR7qtFDfwtQ|mbbUX@wgopK0y$2>4|=M7K9o`%-3Rp>`0u6zcsH^o*K>^O(#sb1TB@%w02(rc3wG?_A4E zWN@nO9SIYc6D682B$_kO%ru?|697o>B>V}$1c#CiVPG65A+wpH&&mh5l2wFB%&N*$ z*l0kSJpBCy79rRT)txkE_qOxSbMy>~0_DJCe8_k4Prk!)TYO3a$tKXObsAFZls?FK z?n!BcW!bvIfz@tkDE*IU4NJ$OrU~TZ8|Rm?lRX_sZ6-;@g}1GZ@g?-mNC0+It{jF! zozR&ndjmTQc;p*i>c!P*phl<@zZ3W zMX#&;(Z6duHZ#$@GI3#5HiEI>r`(XVwHaOy;`l$j>QkQPB^-sp_~SbY>jX)qU4ip{bc6%RojfeYD389pp2!L*g1B>n8;Q( z&eu<$qeEfnmc_CazQ@F4YBhD_RW7b)ISjp>V`?{*iwi2N&o1EK$IHcyqEY4I>WRyk zFpoKwwO0y4pA!w%#{87IcNd^wA}O9_o+~c=Bo*)uhkcp?uKx_=e=)JsYMz35^SDzq)c21wRx zY>36`?VKj8Nzeo-Acql%l0fXEI!z(sb-hm;NA#Cv9;X1RO#koYqp=({1LG z9#!~|g>l&j}QVAui){|sSYk~{{S zg9AXDl4k4yCsFY?%}Mk{7MBasN$4=#$WEdMHh!b|FRI4{63{I1Bu%c{Z@+3K;!)z% z&$7hnrCNz*2>4NgSD`_4?2IrV%7V>xYkYxxg5rE*GrFP#zJ;K)*HKxzQGcy87FrI~ z!j0QnB|aU7N@$lgd4toqy*19aeBLDfic53RqR3sR zu|Gv&ny_MAfUYIIQ!y^4lIIVg8)g^+Atsssw!J!L{j6f#VqN!!nSeO`a3;RL!OEA1 zBrA}eiggf;(u{?Wn?L+| zhA{+(&|sMHp*{n1iaco50qqbETs3nvL98`pw5sldBpeQ4-6Fuu6)Oj_);t={2(A^& za>vh_2B47<>wc0lT))UE!+Fqu)&R65sxhN9ohL_0I&Cv=)^@~>a(yHpb8X7ZclZ!( zjQVV%`3a5}C>G{|lcm`fs5x3JyG1ojve$uYnzw$$@k<yy}2dr>Z49HbF{A=sAFS{K+ty>m64$;|Mbzbkvu0EN7D7U5bgB(MNi0Zs73^ zExVf^7TgybRQWSf7!?+3!oU$D_b^!J5C%MINiDEBaHJ-TTi1()!6#!RP*>vz1ADD^pez+CBr?S}UefFjy+N4q3Xz+s8V`P)*_9yGYH3C}%P5)cJ~OM34F zcfEIFfZhi)y?P%o=4fMh9N4mp6Fo1NC`Nzt$I|GtPasL!-({joakVnwL5`<4#9{+2 zY>bv^n~@3IcS;48`kd`R;K)_D9=|cg$<=^^#L?i%Jf;K3ejEEWKG4L89a{+3q)d}p zoTD|}`V62;q>_}uQD!Ksqt-0F<|Qeqav^UN`cQK8S)X&5+w=lvg(ny*8--VrM1*l| zvhV0~_-x`t;0tjC%2-Q)FD`{w@K>V~*Nba`Fyi19Lo^a`geRszy(NupnZ_O(jl9C9 z6pc(`#xN&|TDu3IW63KPU$zkh7WQIIh5P~>cA?L?2^va;!i7910R4B}FAZbzd+l8^ z9NuRwDvOlc-^!HsNO2_0(-NL*34xrzCm>?TUbCey`mik>cwZKuBwx1ki#ZzoQ`sh= z$Aa;;WDecSb2tw+_24S&b2h{}wB83RxiZvRFXH-AmA8H+V99`r^2Ki1wn zKIZHDAJ4o-2oV!xh&2%j5nF;FVqUMrI$?&`wUnrqP^~SgwI-27M2w}?UbVESuJ5+A zR8S>FYb~mKwU$ycjoMm8i}^m!xvyEG?fd=tJsv-Q$lU$hbI(27J@>voMnSsIAA?B1h1 z(7t7IAV|tLR`A9~P<&P3IMY=V?Xk?_%85pGOp?}Et9Kxu1!wKe69^Ay$})c>HJ*dV z{<|JuI`Mz$@jaK2iv54o;{y)Lo%ip0{5C-NH$8rRBy#_Y9>1cN)Z;C+{*QY6FHusD zKTKr*U-kH9pd3Fv{`xqe*s1IBUp__&@YJ$C6Y&dpjXFh2Ko)xS*W=wWkHQWa*G2?| zt1*tO$2b&|RQTc?Hkpc35T8<-mh`D`WxxN#pM@2y01uGL zu&wA9m)!b2C38&0H`s#kpIv8r_UPAtVTJzZROmlH4~0@+e%2^nuBSG|D>xqh z*wL?(>J>VvSD}-Fd%X3JxVn2ekmQv3cF>|AJkW)Yv57^KIyeLE$M`*u(eVAR$q;)J{-JD z&N)R9%fr>MfgxnXVf(U?;%694VZ)#v*1!iP-f(#glOl28(aB&J94YeI1@E9f+XaQPkqvBg)xMWluH%O!6KHy~JQ3)Fru9lK1|NH^7-nB!#talOM2P--%3tA2J7EF}| zr)0rj)I##)nNDtzJB)bpE2BBlgA*?x0j}zhY!#M5;E+R-+ht&gv`?7Z^}#`&Zilo* zPcAZi(wv2Km~Abp^Z-A1E{+Dj#5ix0eSn2xrk-tkke^TDs0W>&J3xeiKOFQi^b`Ae zfy z2s3vYbUY8*PKuEca@3K&QBzU>pRA~l_p=q16xeUxwOB88M5JKF{Aov zxggCS0vOVYTJxxgY^;U{*B^rlU|A=>Y%c7L)mHy%L><|q8&S&fVlkq+8eeN|39tnw z=&_=lCDMv=?0XV@{(}{@_&r9(Rv*126wv5CE2`>XT7WZU15OeHB|7mj&}9{FP{HxZ znq^0?YWD?*fmOS%@DvUBr~+doB1FIVY*!wr=VwrdM{AtouQg_vYKojDYFNy3p1#o4 z?Opy|FAu%<9we?8#nDF_Vj@5p81MTfOR4*?V~GlIYPYm5d(wu#2t*u#6?r2 z3OBIA5NyKyInpx<>6lN?yVjDmt>S+Lqya8?%dkp&Z}Kr)g`3%JEKY$&(6 z)-yR4yQk_detss-GOHzi+4x7z%qBETb(gN-S6gE@DUo zE_k2RH89h67&+(Cyqw}bZ4OUj-T7^Z$4%N_G;Hch-c>kR3p(C#p(`m)B5LIzbluB*hNYdF_D zHhO_~q(;~)lPe6BPh=zQ@a5u4v>NG%_h<_(WHx=KQ8&+Z>)cdOfc`zz=IOqAqH!z$ z3=G&&rNiXn_&Mt}ulhV~o|n0j<2wsMHpCsnf#J+u4yJ(#I8dk6hUdFqKB%0W*IvVf zoox`5&zF`zDF?^6^yNWz4~!cshsIqRk6F&{feg8~+M_4m_T|0RgNTyvgu+JwBF)YM z)rEP7+l$G98|Xd)zoj5I!T0}Rk{D?7?iYsx<2x-M%Yr7pf^o9oged)*^8-wN~uLSwUD@&{~7fHEN9RjiN#Xv!_&L86!8G3WGAo0c6ga-C1Ty?we!Wj zrfRr)iWV0n{@f8VO%RFYJyDE#o4gN!wXoUgL=i^__zMH{A*k7&6GgGdpth;r308B( zJ$eDFan`dti||%5Mqhf z(QLssg8sKd1;#PZxya(Jels%V)2CXk&RkN0sO@HMOYfotN`*<@0HZf=i^@$A zJ-`ld*+_pGrs(bHg#i@yikl(J(9oWA#Z>q;I>aG|!J71msqj!GQ6o-m-jG4Udg4MK z`p&~SS^iYgpGoG40dZ>GHfzYQ1fZ={Bi)Lx-kI{70S9zzuz)OnThd?CQ|QjDDZiaj zyb!1Mbl3qj%g-Rz;r%@=bcDB3Kf*i3c*Mo=w4yK5ijL#i!vQ))G99vEJ=%uJFT@=5 z&521ousJUbLL;)=-~`4~?+|?0p>$-UpET~G1q1AS(wJ7%D=4+7pMKQ%3KzlA6uA$< z(58YH0B%}QWjiXRp^|F>%HP1{$Wn~YLy(Uj-$cX`6H0LMwa_yTnawF#!qpE=VySRA ztr`JNA;WSJu9^yQgoi~KhDCsN@|Ad{rP`{&I_jO)fL1Uw70+TQbdEN~l>e&ur6pv7 z)q}EB{~9E@WVK9vhJY-mf>Wy$J?m{<+wePqF>WPB9LiGxzOeWDORQT7>sVp9GbR$& zbI^jY-p!O30mM9j_6`Fj+O3|BSmRJ}as#iHH6W`7;e0p{k>Rr67w@*kJXi}E%OnB( z0Yj&O8*-#lGkb{^lgnxQarn6E{+W5VwN2ldL4@xA-MAMDTq*o(PS85JoBB=EpH>s@y5ozU}MazXq zWs%TYRXeN$mU#70d*dtI&0JqMY19o7U&WTMw8jaLf6KRJ%b&UffaU1XZFb{Wk}d=0 zAZ2w*QlfFNdVzDcNG_aC1zZ6IWOeOL087U23;;9W6=u&fRLP#LFr~Q5XtuPh@J;^3 zLJ__*ShMv&$OI^>wb=uh6w)XZ%WwD1{|wLh(B%S2))2lJGGm)hI1hfGhaB>>91^m* z3x;&@tElKZo%e<70`*c9?EoCRxGR?9(AsQRKj?Hz8E5*8i5efVA^e!O5N|Rjdfo{B zL~5E(FSihIgbxx|gsA-=coAjU{vHFjBU{G@xWr31q?IZi{C9E)Yq1Qt;GtZ}5>y>y zMfcN+PUt|E{RA^vyYUPk}_<=guir&$ET|LKf=-P=+Y{kv~(C}8}XZ!a|xx&Ott z*Ws2Jlb{YU{`g0%_%2XQ{@;i6i>gTH-b6d0*0Z+g3p=D$XZBkk0Im9r+2$4NMX)Vg+92hd3L7>Ek~ylliZ z`TTi(-trgZ1rwl4us`n#A0XTXoUZU-k<>v=!mHWSJE%`7ld6gH9q`!p_lU6ar~`f9 z^y`HvE!~tqKUl;hsxd~qs4e;@s*$Dz7&y(@6XIl_sc=@Xn3t$_3WR`uy{g!ms5Wjl zlAUpoRMf#tDZJ^u0T$lp@WY;T-;`&sie6ZNKUP&#>!>Cr1tG`J71$g}-~8_43n$*# z9#cUlpaqsI&TL(|iukFcI??d3uy;~p8nvKix^~l}LZkeOf^ zR%drZ8s0%GtXWN5>7;g`dAwCcH1Di7P8>}R4om^WJBj82_kKQo1OmdjfCP#SyY>z2 zB74%Y>D~0HR1vc~s}BcVf{sbg9*8EYT3cN0tk!pIMV7UAYji1Exaj5xg+QRkbI6x7 z(e(*3RjhdS2+3O~Z1Pi&KKbZ2kLd+?L2x)_(EEVVk24mC#wh`T z*==0$mBqS_YP0J3RU~-CQN_9Os#)YEskNT?1~fwu<{;Y>Qm_wsI-vLP^B(~{21ln4 z&=fDAq&+gAO@b-{`bJ6eCOoNhpF)dJRX|Kiy)0H81FJ8hE-tEul!?Aw7Da;bx~W_2u8WpnF<1d@_Juf@1k1TVZ}C5Gi*>lt%vw; zh81x_G_a|F+@Bs5sENo(RugMcBne^0nzu)WGs!pOg0oUs_cGVV)Fqqs$z${bp*F2cs_MYrANydHq}WmF;^q8 z^ZbwsgVi$x#CS$)N~7t><3(Se;o_) zDFhct8n0fl<_(~-#_52RcM})8sxdK*(HY6VaCRvndclz&or9&JccNyJa(x2{(MVNW zH8JJwr2-$_rxkTL=6aX%h%8^hyBf)A2jzZC(YC9a<}h}X)R=FBl!n4HXa#Ful{!PD zO8@9AAPxpn?*4Erc}1V@lJ=zjo#;w4ungeDKACvuS?=cXc3xd@AkE2X>Dz&ttVJ!G zb8Gas;u}@1(QN2zq+#UC`=L%t?+vVESaG^;MJ3t}Zgbp35v-{@%qy33&r^IoTORKx zYHDgdaYIx4DaGqWw-hxqELzrkIKit7&d3zIQ`Gv(Z|lVO`0MBtfnjvj43OkJBCG&tl5hd|n6nvmOVz6@CAc zgM2Z=vz1mamH|!3ASnDCX5Ct1A6DNAA^^LH*MgJYC z^@OpP+NS#m=8GORSh_4Y_87a@>UCL|d35Qr$QAyB=Ra0SOzEYzjrY1EBCQbDaJ`Yt z)=e2((oIug7W%M4ZDDRczLGfDOO2|_LxG%9IOgXjFdRf3knE@<^aQ9xd^`H`B;` z`ceep0f@iq6^)@J8keEOZT=SiV3)!JjfCWa|7fO1ZhGRvZAsTzfS3x;FvzJ;`1YC# zPl`yZ+T7tq97}%o;haOY2jn6G>P!U`mF9_ZDZp=1r$Sk&kPsHH@!DBdQ+^Ku3Tpt5 zAi^G?3E^v}q)B`5lZ_|xp;SlW;k^zXB)FCbh}EDiF}bg5c1Y(d%vA7yNG@7Du!d>v zP#c}VVDg;n^(Chx$Iv_g2;ej2-31^%oU=<-P1Hw}`W2$SY!3sEh{l!A6;A^OQXF)0 zOb>?I;DDqhu;H+yP$L``^$fBV^$aa~B%J61RRUbeL@7?oGqJ?6_9UQxrzM3G@5{uR zLf+Yfn|pV5D*5uAmM$%*$zs$ZXX#90vzT*W-@dCQpM_~|(FF6^lo688rn}8CI2m(E z3Ih|>cZn2^{E#z0hT*hgmwaX_+$g?DQ=4}1tu?;!w}8PpI z1wwD)3G0}q)N^W)>7K*H3?0igm6S|Kl6IbKx$u^vYqa6Nry+ zx-R$UmIdF_?kXmKV(xxw?bA=(F$2ilEuILTJq#af>ifOI7K7DGkI$hn!S9Te71zt;nG_T zZ?MO7o3_j!?)*onqR%nS6j|KEfq8g6)5O@|nNBOZJkDocllB=)Vm+Qj61=rwEOyfU z@dO%l|K8FuJq8JSPmOjYfrGOHou0W_XKI#01({R;GXrO&#PW>!{qTsHU2V_`?5LoR zBoKU8t<+R-4PDujexHtLy}#h3ZcKQ0LmC4UV!Is&T9NP+{s0x;R1gOikWlK{s}IgS z=ilKJho5?)PfdtrC5EcIvf(YJr1W1#!mCD%4*Ug2y|-IQP_T@oDUr^TNb94=qMiXT zelfMo+P-c}dl76Fw1G1#oQd{@Yond+qn<$E%fqJf#?&3cf|(X{&-8HIWW)5!mCc5~ z5f+_Cg*K&)Igr4@!`x9wILtj|s_lH<6t5#w*Id4oM@>I-n+Sa>a{UPX;kKkQQ{jBV zkGmA-3NvP-PggGY^0BX%CDaS1U$Nl~yA{mMn)KPo89jU}PIsqZ888QvD^3IXuQ)Eh z$Pwac1j)Yad_X7FT?$hVrAAZ0tB#6M{Sky48VXM1)0fxeGUM6XP%@q=Zu70-1R_p! znV_silI_4imGOQ!OMgT^HJl=#tO06d+@>)GZoGh45S>|#s?b4m{aa+{N9-AZyEcpH8@bzrv5M%-*g9<{yE$E(K zCUMqK04g~v4fQJ+7!5VW!$+ab;j{yM7CtVN&jRpZpOT5=B|9_*qh)NKEb^NR^#PaP z$-saR)L(`paPBr!T%V(w>zb^^25WH|phTP$R*cR8@w%KCH3)ZjhWI~HEgz@hHm$0V^=b?-iUV`+lk z*6B5&ipYabR@f=DkIqC0KJTok*R-OqtWN1zLNIgB?vmeNElDbpCZAu9TEsP38hBto ztB(R%k{?}0iDbYdwQO7Ru4s}Rrb7B!0~i!th6;e>!V{!)z=CZZNxzuY023tFkVIDq zy6#LrS&d2oUQO^r@dG;^%Qsj)-Gevtm@YxxJiP`?ygnxy)JEvdZu1oz%||~2=GjDJ zHn!r8jC7vWCc+4bw}P7Y9rP3ofCUwJ!%M8mlcF=HC&?;Tr4AGd_UAT7#bB4vbxXRI zTQMp=yS_)j)h3sgHSGZ0ZBUdK<3B0uU^#gYZ(Y$k)*U438130m>m}Io^tL88+5>w0 z{^rCDKzrWPTXIMA%DbEnulW3`8ksHr*biD{?lD=jX+)8+-4Q8z>y%t=F=nAgr z$H;=26g}LOPo{(JF`I6{n?7m91%58c>@FTWwofRPm;~2>CFQm!T{0E+BC00*+Ka~F zaQdlG;29$axSEqT!bi6L*vW6Ivx|FIp1K+9wIf^8^DV&y$vygO} z9V5d?#Cg15OZcGHvUw(}1+n$K%Eb^OYo|rXf=j-FuCk!>EN<}<6_DZTHg}g@ZA1>1 zFp8Ux=foJ9s82I6U zll;jO%E5bF@+VB>_zZ@3yu&w>ENE{34#|1SVkm6&vtgnKa-rl0Ne zBA=xhVUah}U+}MREMak>R8b;*t515WGw@^}t*wmo88d?ywy+khD-1C_Y()osMbXeg zc17o)JG?sVJxs>IYBO3v>>Mg$op&gS6~ZW;Df7nBiHsS;{Hxe>4?_0nA}?f?q0-D8 z*2fD#R{%iQojr0yE|H5ma$qgK&*rtbS`(Ov!LV*~^}j((r@4IgJ&RY#585W=g(PVI zX}7yx;A34`!Q;l^imp{uQq&dtoMhrYUgtLdOf(6VZY>^u_wnV5AA-oeY!x*|K!QI8 zUJR#H#jDr|u)0cn7POK9{R_+iH$1M&AE+!(ZZBrQ@hR6dt<2p5T$5M zuF+pCETRJQqi-W&Y5^%m7qt&iPox|=ShZ@JE7cKs2%NeLFzMH|$5QW%pCWu9;tR|S zqjIE@chud;Bnid42)tk!8dIxfI1|JpUhf@=4cIxH;Dg`>Y<&KQH<*#LGlMuNz1>Il z9ms^HV+sXT(zoS0(`)VFl!MpXgY=2U^WUwE7IZ#@I{%hd{VQsR5J+rkQ|MrX)KDYf zjyvJME23lZ*GsDyu5%T-HiW&N!UaGS0b!-uv7{ZKi*zLDS!YHmz-N@wBr&<2Bu4=~ zR_P6hJ3<zZH`Qs5P(5< zS`!#$X}DNSpGa*X^I@>d>+%iLKE7o1J_H*CF5ou*8sT>qpI+84f_C>VUY@dqMwGzo zcxA6g(QY|Y=6H_W;Gajr64384Rp-yeLixwZL3DW5A;EKdzid2z9amRyC4kz`3QeEzMkbf;$HNO-y)d`Mgi~MD>-g+SAgnT6he6% z_$_&lNa>)4i^vQ$#6$?A#UY*8wBRw(HA5Zfup*u*4Ud?@Y)B!qp$vu6Y`|y73ckRi z5EW7rPrP6$|CW--Yd@F)sGt21i`jbYho|QHK~38S_QQu<9p-~h%qb#tj2a&HkH$m% z7&XGtA7Amr@`RGXKu&DZG7$cL2HszLQ5jhbDX2zm}B)mNSbFfb zeE5s!b}5VP0}6T|?z;{-Y#-QwdwRCrR1gIn(A6EEKds5C~6c2pp496`V^ru8HA+KcYPRHmgR3N0Vx z;W$90PV-DBpkST}QM*p{R!}|d2c~*ELQVCBB+*KJ;Q-u}kDD(6RtF5_a4V>I{xD+7 zMDae=Q8#U2D5sbT9RS(pd;)ed+@RBt@R7W}r68v>yu_19v^0gNibFWo;a6Qv$g~3F~HhJ;>}CnZy@EN?;hB8 zAaEaW;a=d-iB9Mc+B%_22z`WplbMiy{OhIj+nh^u?jSqVNVE87LT^RKxftC?=_8u8^R_c^nK{tr)}AiVP%=}o+E1GI_1(5wgrS*X; zbKnrB8R{YOhnP%>XXw0vPXy;<41Z0pSzSO0avILD8uAg5c7Pm|GaR0Px6p~JDK==r zA>vcSVu(C$Q{Rvo!4Z>g8yq*I&`JD!AOsWsodS3J$uXgCXnM6Cd0ky(M0YPiN zI1AlJKAiSrh8#S99_oXkPvKajwizZ+*8p zHwjQJ2IcV_>RZ7;76NzV?}vM01;g$-Ok@C37u_eQjiEc}+F)wlN37@L^Dt-vHv@m& zbm0`#PF~1gH_fuiV`gtx9uvj&87&Zbqdc=S6#9PssUjNddp$jt7e&-6m5KNFz_h>VWy1iiY4_#+cn&qK&$ z{-68(TL44*-Ri7V%j&E?gs@^ux;?!*QQHIC|Bh+?!|2rY8tU*CrAua14@@4OCOW>u z{d1UN_E1x(cy}WB}=Wq}HW2v)q-$d9c^vk32=mV6|v~*Gf_U4aayw)eY#-a#M|LCgyKh0`J zCw)dU+R&g53=RzZkLkGbQHv%+h$fn1L}S7@8Vj5@Rl2C)NP}Mm74YRF>JwP~$NA3~ z;c@s4Jb)8(B4*V7|32?ta^Aliy!14H8M03PO!g0edT@##s9ZtZpR87Kn646ev&+x& zQU+7Y{=bN}v&gcz1BOFXMw2F!3nvnRzw&+|z0_$rH zm8Oj5RVqNYx-V`6DoL_yL`bEe10ADH{J~r))=yCz)+?b60YAayoQj^a9^%*(wdKEt z@{M@H6UsNblMLl+Jr+2CJSIzmUJPqARuAPHR1YKM{{6+UhL14hJ$!rcV+6GLhw{DX z#tA22JNFFvFxm(EpJrAJC6h&m7OFbU3b6O2K#j?DIOFB z!pE|<;xT>)mC|m);U$F9f@S1N!%{*1FIibljJDz8Rb+(&O!y(}MQ6zm0V?%BzNEmA zW~>q56yED_XaE>V6OlTK3=3RFg!|8jn2P@_Xe$gy#IKq9@Eu}?Q@8-OF>PnlHogpr z4_W%3X%g|^=tH;M)*)pt`k5`ajk>0c51Jyke6Z&gT70n>@->o_VP;+sSs6UjYc>ao zN#8MNB>M6@=F~fc;zb?eaw98nINh`+{b-s$ic@qt1jPaDZW4SCN%u^J^y;e3nGMtJ zcT?dzNRYn|?d6uH-GTXSMg|yxgB2li7?vV~l>CGA7dT}+qT&gp*?X~phJ#fQj()uJ zVUxfqZ#dw6S8~7suaTr?aYTtJe+jCyl6(mr(U}y$-Kc*QqenCyU`xG(dAki`*plJB zL|s#%iWJykB>q5eniL&PeFJ*11mf^K@6OE9C@;kaHM|_UT0N0~#%$K13vyr3;+gWF zb-BQjaQ@SnaA{6NwQ+O`?UP0bH_$-=|Dc1uXR6N+W z;ZQSt`dlm5QRLZH`k!goh>sfeLrwZlo7F*t;d{YhK|QH>Me?BR?;)itmSQ~RyO8wJ zHh&AILuK0axC{rCMZOZ-4S=&k4Xp%<5sp{zBJ9{8YtbP5>>KWSA2Uh)W4|FQ2E`64 zKO^Uj*$wx2I^5>FapTc%723W{$#F7 zhL1^lAuQW~xL2kUOfpW%24}zw-R2IJFkpK$B*BMA1Si8%_kZO=WbSbb;vyE6-R1ZSDhb(Y!k+EHZJL6Y-p2JK1xJ7kGIhG$YUEMYA8rF`%arcTOtQ z8w^n?STe;a@+M)vfPfF)T&y#@OVg6}n+gdQ^ss9vgEyGKlM@<)W&eBQ8F&SkjsuT6 zW;|4Zn?Ttjo}hD+2bhWOi+O;VVAueOu6GHK5-Y)gn%0fi%gNKm#>d($;&(4u382*^ z%@E5OkCM+XNc8|Vk0oO$V`9N|xnz-W&Qq8Y1pE-5U;zp|0-ocSIIbzvedk<^`^B;|0R3ZnBY>g(Y^M8;*D{Ixtye0(dUHf9A*!U6@H2s#3$M28Z(ge1uc zRuqW?l+y?uWuGQA9A-PQRM-Fzra4C=vJbBE z8c2Ya*P$Z1A>Swm_(@sDqkAZuVBL-?M4+Wq)sLbNBF*A4hDP^@c|sd*Fj$?QOJ<4n z<+hTHJ)b^;&4T16Dw3Zwmcmpa(6&lf1kIU7=k1UTM3@Tc`5{}fw5m*npCIKCjhH1% z#&nu5`}4umu}4TN{vYCz-f^MX&`LN+KAy!7Q~r0Hy&^gXDW2Rp%E06xALwvHO2|`s zl*Vu=Auppc$@dJNSTI_o;Z<2v0l(1rDUvK9D|$S`=}IJ3a^G`1h>Hh+>EgceU3&mu zwOGr5RETe_hlh9h0L>fXS_@&Z#TeXM;0<>5%EZtAvSXgCm=KC^5Ru)Qq9gTZCttE()|9kAR+$9UP~AhO)% zXa5AooR+z={qNVj?VHH<)40|oSu2L;GMW-3cRWf2aV0jNGW6iJ4Kyzb$Nkt;_yYZQ zS{ld!wp>peFo9q8vV4-&&B!i9YQUIa~TO%D?-?D{^XZr>u{6<>-@jm?FY- z9iGbpPjeWWt~$|SSix$zTIsar4X){-$N3}J_wNVMu1CEPSvHU2f!~Yj1N`BH1P_un zW08;7?E3Mdjsi@&w3~yXC_RjKxWBmjkO&XrZu(@pFd&xZA{6--Y(ATFBtpBLfo*=L zc_j`5>O7yINFA6uC^a3guVQB)bT*P6#pywWdM(Ehu4J20-8!#j0Dts>LVaVp!BGgN zM94^NhZ1yqiSDrHx9$DF%k4{*nH2TyUw+zb}$=c`eT(yHQ%ZZYT4*d61< ze0*#PVJ{*yU#%PEK?c6$*04WCGbbh!OwPn$r0^8+)PBN|r#3Lg-?NKOd1_44Aioyd zuo7rtmMvc8bqNX9TO7V?7i;s>7ENEmzF~x844Z6m1X&m(S+-bj=P{~h0C=FhGes+l zv-8zDO;x|9m+d@Qv21z@DS(%%Hx2k()6>`oo>3RerhR48IkM?Fo1EFYzwF{bK4v!4 zuSGN2B2cz4%NBF=7G2Pyb%ENpX^>xwZ8iq)17u-luObETg7p@M{{$`y)Ul2pR7{!+ zxe_8#W00N!<0p$0zK}=tjLyWlQ;;!L&#>y>H3`YlGa&R~Vxz`VJtI^9u1UyCdd5sT zCd&bZpwe5E$KN&S@-7$u?Ykx~&uK{&H>30)ziV>!rd^y^phmQ2tQ1uzf*;zK=h*;F zWg8PlOWp1z{DCIXuuzR}y8Vi8HRAjDW9kl47j?z6v(XSvB~F(O8_vc?LF|TaR6M#pY@#uB@Q~CY6Dtn_LmP87Eg(I0(8S94 zeK$>hnD8EWeR}G^i9yI41xHmn{vM({1ms=;8H&8sU20nNa2RZ#IBX@~_Q8I?rf>OQ z(IIxS!Z`JNPz!hXUtOrSPnc9rGk=Wc(R!kl=GxkYaqt1f=g|_=`4A~!^392i{rM9` z)grZ7t^PWk?|{nzri<6?;vBxm=x8p7n@^3vVQSIh5{H=pyVCQ17jmvy^R$4hW*)w@ zwl@PgNpA&bkZjeSTVZq?D(tMPXU9wm@}KOuWxnZ-P27PwV7}e-t~_nbE?E3N04jcx zD3L2O!Q?RtLVnBk5Del>d`k!9SMMh*pJ@>eIgFwYgA-nTp8mu5;7V*R>)*gqS9sSf z>0j#KlNk9)ua8(F*?(2Nz9?U$7Ak+(MOLvo0v~%nQLLU;js=R%i`C}Fo>-0V7pqam z6#Q^6R$~o4MXXa5hIdLzooWw*G5m^MR9T7&wXfJqS}j$dG#I`RtCp#Q3=VN+nc7a7 zTSY`IS7VeRRYclywVBeoikM1&1FMw0xLn<4Fs{997XzPDI~bRox0e(>r~YZ^k^3{L zEUU3(q}gy{2@*Zvg{lr)A~Y>;UM$pFI@m;oL6@lZld|?GPGnT-Ck{NXKH=DKfyypp zs;D@G%g)F|04Gi&k>>6No`g?eKiu5=`&qx=3;cf1#_w|AZ=d%os-zZ{J%_a|uj4jv zxkOSJM@2GFqfET_f*L+x`bEk;Q`QoV-R5Ud$1`L-1J@knSM%yaKH$!a z`a->Cc(f#VrFzp~m?R9V)WJ&KJ>u#zyy`J&l^Sjgzi$_XtJDbNACP8muEP3!jvvS9 z$J_VpCBLmwKQRP7c9xJc?w%-JqYmsmpHtQE0amA_H7DGVH6Bop4Dtc6$M5%+l0IwI z1_q_dhb7}*QpXsK(P!-?M_yKQ4Xw7HqW(|b<{p3J#3x92dR*S$*JFX0xn7Mk*57+n zyt-bUV{CZJE^55lvYs&jKeP>MgyCH=dV`u^eEWO5_;m4+p zYLn1O-x55x%A&dY)8C1+8`W0EcodsnRqIs=LoswM(RN_VC}Uyk4x&SrrB|H|-vEH( z(Xf%>F`EIgiEzc6MzJi*(zNLWDl0m}UxaW~C--B;ib<&Wb(W=xG3Fb)sQ(%U`{Qf7 zXpwDcZaVt4geAWZDTXP+olv!2@GzTmY$Vup!;g-NS6)-67+ZX8FRAmo`X7UG>ZI8B zhWbz?ST+G7@=f(h#Fbt&>69;_Z<)K`e?;vS-L@9vCcq7P=0~)COC6vbOAtA4srwDF zqUV3qg@%{KvHz$)8+M4TZzC07VtGfk8Vr}jf_K#s#^BHG;=;RXqVbCpcF}N?x=mU2 zrr73Dv{7B!dPB*+M_HR~Gl}pD&mG{)n$~(tI>U(OG^5#kL z_FT5G$$r1b`u!f}_q(s(Z_V#_d*AO$WzD-b7c;l2V?@#isx_t@4+>h{cz6|x zJAX#F)&T-(@ddy~+2Cixlf`Qv03UT5i8CLlT@3?7!)@wwhL6PNZK};MMcmt_4vJdy zg&im9yKwK<8FI8?hk)WrxjA!c8?tL4vlMv&jl{GP)k3XG)VB<^#E9+c2g>hPN^0#; zXB&(`zuLvpooYA3YH@NWl9c2_3vq?TQj_Wai@PJ9A+^2ZKw`xDi!bUG?}f2vLmy>PTDq_=)c*=gx1 z4t}b(RNnhk-27C1r|PN?>;WzW1-r#dC)Gzp&_VT6!xeG-pxWAyBkmnkJDEB!Fk+>5 zMXv*?lm(v^-49`qFSdv$4ymIJ^Tm-v=&je5lG}&WwubN_pU_}$N0TtH;x9FV!ZS)} zN7Oxv`rZpi)kcQVqVZAnN}c%k>;b9tz}KdGW0*9>~po1A+yAMQvF&nNLs&eDi65BWG zG{rIR6(XFxWtf1=EIsf~-D!^z%`4V>eFf(Z0c+VjM~nFtqPMoCydQEn5hWAdIblWu z+EcF$X#wR`@M8#mkWb|eH2+jlu!+v!rv5`NtV92ZR3}%`ckYNcQKxTFAbB;Y353C;(*TF{l#8 zxs|Df1<7BzgIl|iY=dMSnoYF1jM5QE&*0hyPzgRD?=Z z?+M!v5a`c~%pdT+YX2}FE9;Kmp0PUl{ch>^JHqdGZNJ}D{C@vc+t<%kzu)Ka+rMNCM>RS= z1zBlD2RyaOPfBxcQ8AlfV11&txcs9!(6C!*r`5ca-VGEeh< zvaH&8lu>sPwZxgzY8|CYEph9#I#ap%x|n)KtzBa~wdF7+9MkO*&z@1+DyLS9eP`5O z%3p^?@LBbG(6ZG;vQaxkrE}P>qt=U==Tyz`f!K~e%FVUn&N(o|9cxA6PwFwpp_Np* zK?%BbS{k`|8yA#dn?+#0_{7(0I~^(E$#1LREE}N3J4hza>Iij(TqMd1UwuM!ak&=ATy^DfTsD{dqMktUnJj0fTsG zCox1EI0-h~b*SMTQF;*qVtYyAC6wZ8ttA;{_?#oh%hhD1Tup2`b2;kf?a%acT1y8wUKl_T zBAC{SOXVP&BQJ@jm({4+x0ay+`iw7IyB#Vf6wp`2@XKmrH>4v&rE>5mVyAuY2r2;m)XVSpZbJ{ zk^pp@XD^Yxwo^)uUQv%59zO(J$rGF6u;}s^K&gS}xsMbl`jDa`a0>xz`SB+%xV6~Z z`B5hqER+SGQbAW9=8N|$(5c9HI4n$rUB^dfJ7+?fA_juv4vN3Nk9T09zUKHsZ4%GkR+Eh1ZM2I$@8Q*?vfI#+e_Bvd{f=5{aGc1YP6iSuMXXgAd1%Z%UdAj^ zc@}rx#$f$XG-W>3i<9+?oM~2)3!I1MiZ=B3N{&dUzejV#w7Y6^<&_+<`Yx2t zQ90u5UDX_3<@aV7{==2nfYy&MD(tyN+(3>9xrbJ!9MSfk8f*OO6}#x~u+%dia@fTa z4ojr*HT+oOutb?tf1?iOM548kkEd|I*db2bQ|s3m=#aKrHB|9&RI&NxxQ1}2G`g=g zP%1e}y4;7I)n?Q@xYp>d6uu|Efp)snJQi8loi^ezOJ!C%vcT|{Ki^85sBHW4`IXvZ z%EB4X-`8gK`rtIhlhFktrzQBy7Sur;mQX)~=>HmbX{8+fyWf?V#LaeO*@}+yr@*f- z6(1QiOK7*bc0X-4f0d}FXwA&6zK_Gr0~|Mpw8Jm_HFxlLlG z(s`lSrf7?sExM!xq!w+O0^An-n*A&Q>vWRoX#E_}Q3s|(b&kk1YRwFd#ZseY?v#QO zw^;!PDQ}(TK1!ie@GV?pZbA=>7AM*i&8mQ#t=+$%i4@2L`400Wr;_S(Kmy;yshrHsYI4 zsaX=54M2QD{wZR+k+Z@~+e(4Oou8JtP1Zt!y_R;TR{x2RTy(CwJBXoMx=^@l zYxQbHKal^}LJ=3HXXs}67Y66BK3iwV_<4~Xh=TtLi$OnWHCKKi(mC|PPF61-N(53XGkRAdUny*jNqkc zkCDod3q=gyiv+zQeGO?)Hywf_Drlh0%$tBrnUf-OUgXXau&QlkMkq3%jLF0x)OVZB zGBIuLFn!|(rjs#vWb(_m&`Yxr5A;CHG>p9|Ev(}-;aIS?10{0^DdXA`H zN9!CM$9c!y<^glWBXzX!I^9a!Bbc#LkTvPX^m?Xk)$3v|ZAwLP9j#vQH3FX)oO`y| zR7ZPV*;Xiq*VUd@)Le0*uGUhiSt#z+)y5g$%(08gy;Fa8pg{tp{WV4whJ84fQTSmd zcZ6KGxi@E7+VAFF)#P#BRfnkpc9m)OOeTi-BeoUi}UrhMQvY(=;(^m)z;9NP$_vAL(yU^4{&mvgrbTL zMbucarhzs=sq&1d(omZdvIuLTzwmMXo;bH4B|^O0P zTOysyLw53D-N*1?l`wOfvS+ZaW9;(m5>XG8rs2dCmvMOePoE=JH`01L;<&f$U9Lsk zUC1wwK7j0xnN&iefGwg#LiW2d%R~q)y2V+mIm;4;ES!*xAY280gZbk#b%a8~V8(RL z{g|)<52DO9n)^%_k2Th!V(TEER#{e#M}M9ZAHR^Oyvwm-(+iqeJY#B8O?=i^d(7B! z&=JugLhGPho+&0qXp5`%dIaM@1-viZ5n6H}1y5v`q(o|p!O{1_I9yy08BBj#;M^R) zhwH!7@q48HI|aWp`1j&ydX)yrF?upUCLh(4*RbFy9jhnL%H(5u@`Ox2t|vw#CHo3KS|ZIn~f#oTCZp5dza zD_U!3jJF>V@iEB1HBDs1Xno=GdOJpIrsPd4`7%a>U$*gJk=tDRI`k>*^#yOY$47y7 zUxlA$oER6Y9aK8KBjQ_VZzyx065qE#v#=pTiPKIib7zU4;jXYzDW`TETCAB&%d>D{YoRvAte$y0w;P zRFeCMu?Z+C=_?B9?{j^{e-a>>#`i7xCPAxbP&&LKZnV?xDUb9Q*V}6iJJ#6H0)v^i z+c6l}d?eNwpE?J*x>5iJMN0))x6`>M{^ih%S%BuwdeOau*4lB|as&YtI8DI{P-I#t z{`Pft-JO ziX+C!<(p4I3F+h6#ruXJ)fMxuW+T9|Mv{htrgP9&c@5T6;JI-I4$}LM$HO`ZPNKfz z-R7RCZVE0JPbFe8OReICM6IndqqjJas5PzA?FqRBt9{rEJ&&}KEjQHqLPxE^Lk%{t zwmTnVlD11&Wnr*mnBDKpvRHG+m^5MmIj9ie#{qKr9biOW*@`>bisuF4t7G9n(24aT ztD|-^?lR`>9vmH>Uz*j*RgIs*Nc`HD7w-ClqwfLFq^Ti!eaVmjD{z}5RdKqL z)}>kdms)tTzO&c^^6_fo5H#9}g=KLFpI2g|btP(NZNEWj{gSBEMT<9V7l~c8mLca? zd)qEg7EgB3+Q6y3ri<1kWTLO6L9#gCMQfqNtQ9qqwNW8wR(Ts0bP=*$+HLy;skLe)STzK6T1diIE)1cv7ApKVtI8C60q{)vB4dn z?a_jT&{HUcHv2mM_-nI;DYTLucq#o1pH#fIjoQv^FM%qW(To5N$h|%^`M* zq-i>s{{O6-SgvY)l$;mEkE+(K-A56aEeIVUtc`8OPh)GEJM@wWfZhUA14_}3j`o0* zpil5tyED=Bq8OlQ3AHcue4x^Vj^cSu8`^x1UV!LUh&G@)0Il;&volk3FVWL^S=euh z{Z@)vDOzHip8VoDzaC_+{b0kDiPSKwKH~3)M9YN8PSJWP8wEjN7AI4*Mus27wG_y% zvJRqIs)wU}0`iq=y$bVy0$+m9VBZi0TX@vIRp|l2;-&IQ7r~MmByV+?z z%sE-W6C5$_Ji;^_QW+>L+w?Z9xr$k9I7=2UD|xbqw#s107D2tVzV#2O==2kEr_m)v z;)d(c7w+jMh$nk#^NpW1JSuLWqT)^zT5oLqZxY4$-dYDkf>_fVliS%|T24TobLWTCCE$y%=WIo;F^se^i{XYW*b8PwBZ?<9(l-bMpF)mHdd{e-o-^{4Qm-I>mZsFWZG|Zf13}T0Hm#>A8BpjyB|HzzrOQz& zjz59Z(^g^fl-QWj+Adyx(qc9qtS!%pm0PVu?{uudrdDEUx>l!Z-P%XpI$JO!pF?qTrN@)-)+7HlL8%Nf{8FE7EP(w?xbAZ;Q&X{gE`?VHp^huF`osfUn zboQ3wy}?>rZ{jmmgcG)7x=&U&!gcabk3VCEWOxY8SH$EV0Ii5fb)l zfhD14vsCFeIK7sp8Q;<_E)`g!8>E8RY{k<t?QUj!Mz()z;+9I^bQp4B=#Z8J(+$Bm>8liO#4TvI` z-DTUKC+Mm*6}AytGvnbzyZCdirGarxPs9T}V~Gfz4dssJ){X0}h!T58Xe|NORM0zVdWqo0c_K0#O zR*W8_-D=#c-oID?T@H)>?_E@i6~4Mu}D9wN}cursCjuNQY)ovX`z+MYl|(-)nuuRBA&IJY5nU!|^OXGBB|9f*M`4IWh=or8kHw9}mM6fZ4>uM+Jb?;7H4-By zX&oZALZG{vBSz&zvU}2!@d?{W(VeqH*zt0!k=Qgz8&G9U4W6qwGft~x=vvbFN$sjZ zSz2Af-MtIJ-68DZ38eOzE1TgBvlFK@Pwy5+&!OykGkj~Q%LtsncV3Jl`l( z?Wk4WdUrJOAp}Lqrq^ndelV@QJ;32QK~pUHq}&MY9rZ_{2tF&f+0q1kZjsGyjpq>; zp^STSn>(Q``cq_oMO4s~=hqkoc<0nw1RcTr9lgmdqQz8A zRlW)q6Q+V(UJn*xs@5iUMlfxBP%rI#yvswE7>S+H8soc;D5afQ(;f=09LEUx2|(ye&>wpkqS;<>AL^(y2zzqyA@SWGzH3nFyR3Ae*$Df*+V%A?rGdHWC&O!_IVY@G#+ zGIZ*)Xfj>fXlN!rovvBxy;z0X&5h2$DURDb7CRlreHGJ^P|+|;Yi=kLJ+icil$RTd zm$S6ajT@)3%+8q|u4L7+7Awqzt76@8?XRI-G&$5XOjOI(+764pfOqX6Gs3F!q)0bP zhukT<`!n*RSp&hCWzLI8n)PIS*}J$>L+ocLvT3&S<`DZifvLeb!2jdK(rm3k4J@ZS zXFJ{Fgv<6Vu{~RhG!%*NvbENYmFya+ACX3px+fqY6i2$Y;+dVKq_h(*S)lp-12znCJ>bUwa9*2%XJL1 zI;Vu;ZK0$aI5h~ZVKv!~Sql!KRo)##b{8U-AZuP}O*X=LvE7TRhsE^yU5@C}b6&wzs&u5yoQ%9waenh@1 zh>zvboZMpF46SMZE@veY+s%T$qEELh@CP6~L`Qgd+0*A_of@bE_gQ7q9r6jU{`N4U zd-GJcs5w)6Q&|-ucFoj!M;!{L{a@Q)5AgFYAZm|@t?2u*E_V)#m|0riS}aSO(Fhl`Z!)#{1U zv$b>lCJGIj4n3X$09K~JCdAY5jcNIJu_ud`I{-DHCARrb^MIYCfTg;>BzUf&6%|vX zouvQ{90NtdJXdRJ=ps_*YWriJF`}zt`2h%kteBRJML08ceuAG0z){urwM5)AT3o{E z+V+4g2J}((`ziXECSDuE0THg}pdvl@z8kk<{602xF+lX1r>$%LDf<_u)btWvuugP% z%!`UZc&38&fg;4Ag*W{T%*i#MTkMJss3YO00d}6iWZdQ&_iQ3(j;4|0kXPjI$eG>X z@5uQ-V1-t6Av(3-Vr4*9rLIc0Qr-yj)&5s^jkAU*53(1t|0EyjoD3I8#h6z%YbsRPf?B<#nyKx1fw27{kt^F1rLP9Im zAhb>z@gQ2$<^!#9ytw~}*7I0uiPk_xqLpqgx-L>Sh9+tuvY-KUwC=iN6W4Pzs7o{h z&|D;b@Na!KcwGqcfm#zWQR@@v%^$4+HD!JVn)wr(BUdvw0M$|3cwSh)k%=1L$<<=( zP9^06vxhSs>}(@bHeQ($mvgm-#+Po}MDTpANvry(g>&R+V(8h`=<0iri{tksYv&A8 z{yogWWe}D;*k^k%vB`N_OXG6S~4%dwgc(3q4DpCojAm$s1YV)Pet*>UBG`Vd6j zWp2a_P;h_50}$OpZE8xKrK}GU38%^%_&5BN8cxQ64OR;I6(+tjXox;@@p*w3*5fQO z$h_NkXSAa(DOJ{o0Bo9nDl39#OIhE1hAc#g^ZXetk24V9`DMw+#J3Byh{j!@E}S`UovZ|jNBLM>7;8b!N8?UQ!AE3re*V!1s#G?LNZy%u_LZj8PLVM~O!3^i;{ z%N5aLp|&~l74!;Ssj_Jae_99-Q!B(G$dAZW5qB19aW$&4UbZVb2W+aWp-3pg=EX`- zRpLdb2Kujk!D+F$NLx1o?-u<7AIh4|I)zGC%5M`$d8IWxYQ@~x@+nd`>zUxjegd@d z1P!1f=y#T4W_U2?x=lD2X^oAw@Z;S@S{*TIhE~JAwK~rbTF86W;$79LZ(R|Ydczh_ zz6K6Id7pzP@eHs{s@;>n(cas~E9#j}>VP=$>mr<4e123a7Ka{}$_G^_8+iyV znPuP8iesJjQVGcG2FBCN38b>a$R|Z`KDt5t080JW5tjNjYq7~HFQ=`uv)f<$(~5iV zaQ32W^o2v3>vwvv{@2ig=q{9B{|>(Wlp!1R z)*C=NNPu4Zdjqcj)ByHYzkEjmgk3G`Km9ND*V=f-ZT0%}b{kK)mjN*}4?x5NY?lp$ z-T(|xHmLJ21MHCX59sx=BC`HPNa26#f3d88=5|dv1MhXQZ2`D3rL|3HbQ=Y@jSGKD z;wya#ddn}jtfXocajNw$q~OS|&#!=lOzkTcI^hu-Q$@Vv)EY9(Rflu){sLSpc%WpPOYl*f+866-FEYV_&Q+~IJt4p*EJzx0^LsMYSF>BFb zYthGUb4{}FoPirqK!F$D{5xcRIW0@9Y_zRLv3?ghAY6RCR2v!c6EH~8bMfF`iqMv6 z9UXPYR|1rLANwr*g@Zn@`>|Ngi$rH9H98(JcfLV0hLr$mR#$e?*;-Ty*DWo%JY2}v zsDVR_{x@mXKkbgfD!9$x{c0meJblclYqXyu)tqvfw$PcfO1DSDrJFfii>PYGa!Ci; z*+mWOi!RHxCzXP7vHP4BA3UfW)zL*+wbA0ja;>v5_OeZ!>D8@~@{3K}Z{MSS@-I|j zdS{QfE5*;-wL_Mr>-O-R;oh`py|4}_QrAe!S|T@;K$F;YGaHST{e;Soc5~n zhwX(~jpB{fkv1jusAs%ZW)uBqHEL-596z3#)hNQa2|t$MN1U^a;Mj74Xb_wV-$MNG zTl(#bKYK#fC==K2b*^Kyqv200w03aPe|%P}Ew*onYi|1S5;}2yNGeX*=bIF9WkZ~1 zTzbhS-kaU1fpIE+9G~5&Y4C@?+dvq%%8VnLiMg-D#Z_&CuA#A^%ZNc*!&=w>w2=e5 z3MnkvmEAF)R}=OZw3d;B56b0*nqtZA&ddAaHShAOHO0yow6UfE$fAz`#G66`6R%lI zstIk4q29%x02J&Asw2=iy)q&dS86?iT*HV1CjTsgU)1sxwTD>tqSnLs?JtK* zzIjorp(vM9OD?U3!EQW#(I#rH)e;RuOM0)>8Y{|pRg8aG+hIIoh0YbrF{~XnTyk&dSn2ya>8)Wyz^mv@oOc&hz5>YucQ!#P&Rw>u2R$@Yd?k0Fm>$ zR^Ryk;lpC>>u>=t#g9__a7;RU*qv6~E7sNimfO7pQcu6{$^N9@@m1)XLN1^cz}thfFuLEr z<%ZG;DzSN-c?AB+Q^vU~sPjpTiRr)o12~zCCH{YWeRn{V$MZM$oG7RWB1i8}Tccan7?y0e07rTi)u{RKFOze8^XLpa9@9+KR?%CPd z-PzgMHak1p9YyE%<{sC0o_9bRPVR9_orf-^3QA?t>N4qof~1vr(kg|Enp3DKt;CNi z!2k4zdF;aS$#!@L)|ZF+y* zv|ur1tho+GN1!20x~A`wXgV;o>yMnFpSS5_Qg0&vjHthYM3a=cCPw=bNFW4Lm|Nh< z*YlhRv;dmpHR8B5$q#%!ZSq>3|AhHR+DsKxsIZ>~;?S zJhp(}c-KTU$SRJ~3fC*oQOOCc_|E{c^o&~KN%g);RaW$x0qY2<$azD3ZTxJgZ%lk8f2fd124 z3~C-%c-6%GI}hv2_>9!#BBPejXT3gfhC1)oM{0NBW9e>vjCK${?(vUG_z2jeuU%qx z9n7~w{tT%=DSPzO9oGAa?7Ljf&;I0VvNP)40%yvYgn}4)%_TtBj6=kF1kdZU3>L6+xbaGQ+0 zC_|YtbXkV@)QyCH$k086uy?S$f*@StO?!t9vr>k2j+ z>f`m{`rg|GPJL`^^Kmggl$J+Q7#~*Nqtr}&6}v&`C%oU+s>bEdaL{-#Q}3gJcYwP` zxQQ2iJc1Looo-b3sJ^kX?t*c~QGGj&ViihHj_Cs&_Z>^J2z!BbZkv<5>3;CX1;9|; zZ9ERqWS42gaed{`XUBPii>3KnC(+I`qxcpe$=^;ms43)HWAr-XAPI8+1l z;BlIJLa(!ql%%aZL0eDgos=hg==2FFw)#7g+ev+Xp2ZQoJ?S4%Xc24|hL$SH!iXc?tE>`=8#LxH=gj^6Tn5B8=q6f#?^4o4Ll^Z?F+d;- z&W}KH)Yi84|G{|}FC?AO2c#M!#O|zyxfs5(5yi9+#h@DVfFKTSI5=SDslw9B?~A|2 zTL;3UP@f`*-GGRltnet*LYDFRkgQ668Ly6F6&%QPbz}xEMKY*RCNOl|U${#Nv~vMBb4AYJdj^L=8q4!5Rjvyc%?q1tb?Qz;uKc zKo8IAeLDI}9JM%3roaIars{=>_;{ia5&y#%Qu%Fsvo~x9{|z`wUC!x!%ZA3STv`y@v7=#xpq&+EJRR}>M2+yOJO3E1--kX9-^oV`T);50@3Wo4>aIRNIIwlUD1aq?E<=~B}zw-9ikN%&`sa} zLdFaFMM|T6)bt{(VjFLvj~8KEYxxTWUxIbp<;^tw5(co5TgZ4xA743kv(z=>N`|jE zb;O$Y^gb*{hoQ%+2l!}dw%bs)Q02?|UiOQ(Bv~9z2K*3@#Fq37t-q}ASAOz2K6=0V z36pvX=3I~MET3;-ESi25C@O!$J^3)t;tPGfN_R0ce87F z@_~Hn<|G;tWaCQ@f7f^RIKX?FaG9FtYkacwNQavm2aKKnfK9jGTZk48xZ`oWJ>_?} zToF|6*~C{TP!ix&;?pUbd_~_#`ECuJzoPG{wB0}vSM?v2lc%ZLHT`Uj2W4J^sim@t z+^_4esn3vK&KGpEzb;O!PoUyCuRtwiB3&q^_#F`OLHr0L=&!V_*z zJPP-tc0(p`T5EX(nEVFLbe=FDM?NrU(*3y$Xw|qYEnYeVu!cqmV4{kAu@pWhYAgLB znuTy5IBC|!K;0wu8jxI>0bKa3LWazw3t3HWxAiW1J7DvR#t_+DN1?ze;V;tQ%ma$bzty zdfmx7m<;!@IvlW!Ye1UWxh)eCoBf9^&kv@-KKJ!YmBCAeBB*rqQk2UTLI2W+`}#H^B(9sDfN~} zmCO0E-d1GuQ19Sa2ARMd0c?eBC2oP_*2n&ADb;$Yuj{#gv0Ua4wc%tP2WDX%FWL1i zvnY7Y$8a!3cXxCCE~*3YVAMA{q(RstbQ)oVJ7)v*H+{=gaoUP3)dPh(6$~$OjY;It zuS`en(nU#>(yxrScIJn0qJCvOwfEIP+S{*8bL~ug*z_;cOY4h|k^RdI(r#mcl+(XV ziZ&7-$pgyt)1HM1(uDzK=7xE`6Ev0E2%5O+6_>y<=|B#6Ja`Wp6|=0j}w z{ACuKob9ymnZ6FZ%UpV9oG!teCD?#J zz67ump4imAIN0az+FER)GB5N#{Ho)f|Zff`{cOAC_et03EEFJP7SY##N0q4(`{ z;u8Y$UzT8+E4(Zjx{;p0z+JbibIIkU zeuvU!7M*{I%aT=QEqbMA+WskLNqe;zJX-O{pz^OVN}Qc3M~O)myj`wl(2&nZ6_iAuxM@1PF+Ysgvp;|+ER zR_RpnZ!qrHgEL-$|nQWc&qpJzF~kMrB4b%lo&b7!yJ|eAWu*{FIht=Z=pt*WT4G& z!R%fJI`vi`A9QXJ@1A((l3R6Fg1s27O`DZpOuWah% z1Y;?kiiYB#mmE|pCzxiw)B76h{Hw30aXk6-KN!cJ1{;AfSmVf^{3kN%iAe5ato}i- z*EpVe^dF2$YDdMw!iPCWbUH%MnU9V~6^+oUK5cVJlqNHoHhW7!X z3dz_^@n2w@oAiMCegWs>AJE({u*IK}Zrt}pKSt9k`Mwza*38G~7bHf%LF+NERpKg2 zj0{ma5pR=#c=YG__~XNcm3hXjo5q_$s)H~FeEA1jU~slO8NT5bRs22U?r-2oc|V>^ z+z7-04Rm007b>iOs^h#OP_MZVh723$qEKrI4KE6PJBQ<1&Ya70xF+rl-V-33Hv+^j zABv*RNa*~c&|MtbGQ;NIqTm%0`F&C7baTdYicmXLLf;mJc9=sk8urer#5BIFxiFHL z!d$e~M<>yXPkMJ6rLdveu7l3d3xx$L)fSSYmW@_+FEGy4vZ@;YnJXcE>D(?6Cs1~= zhI&87nQ%M-58Qm|Qyct`zD18pu-ZzATjXiUZYnKT&__$=uT)(@UL{!vrTlOjSCUm$ z)-I>bC7G|1x|}YSL{XJ)&I zyJDY0?$&Hj>RY@~3<+OiKx1I3x(bDGj=_xLIc_>(EyeZ!mg)RObu1lw<9jPyHt=ug zM51EOiPR|~wYVq!5i8H!7&()2vQ4_{qX4OfGI|0l*M%FxW$2nu$2ZLFoP=P(I|1Sf z${jb2d`dG1Rh|;W`V+Yi_!DN%|2iipgEB0n>470=r}RC1NzU;m#HSx>#=i?r%a#a?srh559iB$!0kID0 zh;flm&Jm9TP_=18>2Mj=RaXW@7xCJK(XtWMEX%(0X*r%R$4n_utMiPDV_lnV+*p=5 zD9Yf0bf_Gg>XAJFgFH7~JjORJ!bml{#6vy`n6hV57h6_SX*rk{+cG!phZbkaXv>&O z1k&KtZzFheh_8Ija_2dK?Cc<`|MsUyJ61(G(VzO*F;DCD{pFb4k>()UF=7x;UEfED-*+Ovj=e_twHfd$%q?jv*Ad7I~A*^kr; z%u~~tk}Dw5Pkm@w1?KHO1xT38YJ>NJHhxEg2TZ!eiF6E@+Soogpd4!N>cl0@e2MKY zymy#$zfA59ta9k1-eP9unwZyd2BA($J}Z_Nl~VZ-bbo*TO~^i@X}ANcs`#Cy4GwI- zW*K$SG1aE=89t`t(QlJZpG;eIsPR8p^hgKhUdSRFN7hPb=*-o4!+qmT$CDx-4*vyf zuH-Bl<;Ym7*Gb+t>K zTG|M+e(Nw03iMn1#uQR5No$fxky6FAs0iX5AcC=C;gu%Kxcx)1GTcWbX}ON;q!aT@ zjg?48iX+{_KqnM!UPbbE`7a?*j>Bpz#q!%RKCfTDGyzAY78XMN_7E$Df=b*TT|nD; z#Gw9tdlWCXk0>`!87k~%CPkW3D&|#cp-kAkc)GAWdIuLNvoFf*S%u>6+IptO$fRDy zlitDjEUM~JB&idU7NH}*qDTK*QLK6jRTM*P9=+!ggNmLR8`n+9c799u5v3L>AiqhA zNb7z6&Ya<9+SN9Psk3x>g?(W1exOtX}?UK+Pev(Vca$sk$!+B${+Qjw^(r=t!>(1AIGEKs9WN#KzOMtC2jIq9~gW(^4;%RAJ!+Ma<|y{DKpv ze;fE9`Q*jwYDd;TOSQdOw6fzcjq+y1eG`}WVIelI-DPPQ#@2sN@jfg;*>{2#`mjLP zk-LH{at=cM7METZbCnfdrpGaA#E^TzbPY)>oJ}iMn8P7*@I_%A)w9&xmo-*G>(Odo z=B+f}Pse=ONafW&s^^E1{^>p%;K%%`E^CX581C_f4IgD~`1N&sGg06$EZlXi%=`g) zRh!dcKV;UpE#1ez&1W;85Owc6D(BB!6ie9S`?Hqic5KhDRJZSvXu{GuI&H+v-)Nmb ztL)u@GZm`|hjRgDtt`|6-q{P!O5?t-^xB^_)Q*TgOMSM-wb%5Zg#oO;P58F_R=L-d zH=ULwcOu^Ki zhmS`d#9IN-Vu)rFbfbX01*S1 zC`2m@NiJT%(;w?wkbWm(FhalwMo}W0iu68Vqh!3Uc)UYTb0u07 z$+~f0dUHnaS|hj!)UQy16=a5&iW9uG2i?VyQrMO2f}qR&iV-vzizzXlYz0CAFE;g| zDM74#ND}b*u_(Wd8)y^*f(y?g!=s!CMYL!qj$O)L52iA~%sc!@Ukm~8>u(9UJ-0^= z3mF2|T)=jkVQo1KS120`F|0s?gIR@gqq~apAgq{h9@LuV1~X@u3Vajl1up&_m);0{ zZKRzzxa&Tajs~*`r+@qN3gM(RDWee@PoB17C0MQ!^Qf#5+~9W}Gk7xo%3Ox7tN?zG z%}PdVZ+@hMeME%Q0BT){c`1$qXjCPv8E5q;?+~W54hOjyF6Q*7=n&>qruBCaW%;re zZCmQ^MTaK_SJVU=H-xZ08fE%1WBE|FL!-?(mP93|29JSzT(v42qgBJL>D$!c`r6z| z))YG}*hf1u)S8;62fNXvX~7lR58EzE@#@2wZMm&CXYd05Ospf#JMoAn;-&?MaDTM* zCH^ZQ0>Afv@hyQ5C1B0oar9U!bt0KTbCQlnvJ1-5F0`NyOHxw0(1SW`xbijG*tRY+ zYLsOy$Ucf~QKIWmW)uYGWk>2A&3;kz9mz@0W-8k|7}x9B_Znq>9s11JRi$!G`d!7w z`&I<`$FR1FZ6u|}uvleoTRIWLepVj0p&|9z2krdoNp!@)s{*CQvRKVY;dD4UiH)hWjJn+$}37f6-YD}-1u!V}{730*V z5b0)ktV$@$qfXD^BB~3G%h#iyX~mo0raB;eFJX9z*T>-rEMDlC1>}x62y0hKZm)K% z46x{F9JIIoHh4=(qRVrG-EChri6=*!&Tg@L&ZI56K`)L&7eoEHL`ulhL| zWxUaX#b}i9SaNU4#wu4MXmLvx(x}=@L8vAcga%{(Y0}+kEHb#*O_~ei#PnP&hv@#T zFGv?xH&@r?Aeb16Gm850RHhaC9T&3iv|^h)0xCjD@)?<>z^9Sr-55?EreiFwHoKjn zb*ON5%I{|1{JF!DHs&92q=r4z7~H6XxJH89ZqYX3F3xbMTT3o;C;X$zYZ__}ZeY zDCvwjY=Kb^1)nts?PTzrIp{5e=gmQ`gaPsebCAj4MRSmASFn+>xtK;PJFrdK61p># z*pY=Pr|Z!8j;vQmBX1n^$XyCt-58?5?J1eIlka0w4i1B{ytTNTkM|(EPSC-9@}wG_ zFvxZEq|2S4%F=t%t4`1stt@et>^fsWSt=1DM|$v|aVs|#wHrF{H6ZMAmaSznR+mQzL zV0DxOl{k_=UFgBuhFccFYy{E;hgO1Vd}4q~0d^A4aElv4t3#w=3$^dXDjHLJGAE6H zlL*n}FgXOH&UKIoK0h5%Hn<{{T7N~;{$8w(-RW>tGQ;D$>ijR%wYsrHZ`N9)gxOQa zKF}Ynu0R=mn5#0Sg5Y~pbwBxkzleWE8LLWd9E`uY1FS#OtgzPzu*u zax6#)^2?co&v?UWTsA_x^+#d3x_H9}9z_2xjZc7g1I%{ghN&Ff;32O}Ad+&eAP{pO z0W0?q@H+D3*N_{&Bl$NR%UZt12gZ_AVTF-0GTcHq4&?(1R#ykWlpGE6IV?pQMi6hj z0D6fEBdsM^oO4lr`-I{982`p=pu7TDSp3Ti!7U}r1Ng|5Gpm9M=UO zJNbtlKIZWcZaXjpAAJ18&45JlXYU&YI&C+@IA@F4|G&{>Gx+aR4i*tz5`h! zVIDUgGG#k+_mr3ylPP03bE&h)MrtF$6ie3uoM3*JCnIWO?;y_ni$R=0Ld{tsyzpWZ zPH{3F^^3GqgOaGNlYY3ioG;cSPWnpP`+>MpY0nX`sL1m>`plB2i)tp3`b#9sVn`;IIe)PtPHvL&hYD;A^t zZ%;w_SrnZ|`CS2uis~;YZ|f^4zr_&>QNBYFr8`Tc8WQPzF{BKEWZ6n&&ev}+_`+z` zi%$da#|DFZ;|s zSsR-Y5w>&`>GGGLf%c*X)d0l>g1>MwwBwey`|>PnF$pQ+pUNQYIiNQEvfb^%)55*sQpnFT4A zT&c=r)>Y~6K=UTE&Po{vdOMlbjQRs>8eUy3zf}YW@Lff9zdDQRj(0Cw-BurY$x%%K zhS$q~5~=4D_Ls8XnPR81IZBW{U7O1KDZiDc`qNl!>TpzzXZ;(0wkjiPQnJW;rK8BY zwrkO>KZr`%WH}Y%PqvU1nvk^Uz?9erUAM9` z;xv~{_hZwOOW@5Wh9TRgR14}ihc(l>JWixTb69`v&yNy~p>vsvmEQL>cpi)KebFKu ze!s0OY^SvUf_A|g4_Y8-hHdx6T4|8Q`4)6#9(M3?EvW5$R!gyMVVpOgMQM~v-_hj- zELrPzJBw;8Wbd>;-!+rjco9~$mXD2wMXZ*_t$Wh{LENAu`jbH1rrU{B!a6DOiPUB(+o3E>pt8$YJ*9O5B`m{w-0Ln4TE^-s z|HjjnWz0jl7EfoFu}W4y$A_c!=iZ^O%h*8e{VQ2Ca5*dn*55YfEa!y%82f(^wl-D} zro>;PjFoJFa_g$`%Stu`o8bnMteLApR(veASp%|i>f>4^$cm^h$$B3n$vS+UF0KVx z3u7c%BVr_32{D|ku9@^{kghSzVXZpIAF3@&Yaw zLZfl#Jbm~HTcR~}jP4uRc#UEiMawp^8%jt$8u2r0sa&Z`=YM896|cH9aWj@z*Xq#O z&1kV17pcS+rYh~~P@^p@LRX>lW5mTm3{9x&Oj?C}l3tq+eKv z)Rloj5OUqizZ>Cim;Y(8=(;C+ybFL>N1x*z%VwDfYm>GZ>iML02-*B}2FRO`}v4Q1_t-?Ggo4k%5siPq=s#VVhSX zJM{UAB571<)CxH`1AM5XpgB~~tecvb@bLqltIZRU5axO>az!b#5H{)JB$01kr-Dkp z+cN3)2z=e$*AfDv*GWhNk%DBv0@1>xcbW{(d1Vfdl;Ld%^Wgy*_K=V#1&|gJ^1J|| zwvwQ(0GV_(WMGPjPvOc8UkS-AfY?chO9ABDeNnMq1(4_WjXSoojT&Xlaq6(0eNonh zQ@b5(h+=(=-t1>S#+N%-ca7VIup&KUaQ=9*9qvhYK}g+>&~LlgKxK2LF=RJO)hPc~ zrCEp2VYBwIlZstcnrlSQ*j9z!7}>9iMHO1T7d_!vDBanM4x4_IwEI}Jcc)PE&@;IS zj&*YmeB|Q~d}{A0Q4Q_GLaEC>7G&iJENp0x(A<3(Bbx5bTC^X1((4Ef+RxII$NP;S ze4Iwv5k!LzFh}K35KTV-Mc|jcoJ4Q>bbvKg()St@5Asp3LJ+4@RW1e6Wzef^52V+J zSgKMjkcJ$Fy~T%$#!ZJ=q((UyK#ww6XRY=2ENXZJhe#Lq(cvR(i86Mlwd6Ca4aLU_@CY%DN275|QRri#f z`n-eYp9ZHcc>Mo3war6vYAUecl46JC)ZvX;G$#wJn!Vj<%3_x_N~SaYewG=P5NAq1 zhaH(MM%IZd;z0t31?b(ph67=dzm3`QWH2hJ<6 zh~B#aK$t+B;(;h@5%|18h`WQ#h<9EX;=J1@&}Re4$Cp%*KzC0hXm>~?SSu62dz(3Y zj%OWpC{^SgiF#l>83~|yJn0}P(CyAk<{*;|0ET#krGyNJ@~|Z0?}MTsH$Zs(9?GEm zSw2EQY4C>(*UJkZli}2JXUqsYC7?zg!de;bpBJ7l!Q= z9^7I_0LRF&78b%@lN+3#D&Y@aQ|BveuI3B9xq|8c(oq_5l?_%p9i{B6><`6ogbrR~ zac-yW1bfo5z#d=GK;wSmm9_NTLf+Szqr=s2ft-J*fMDJ!&b&kk*O{v}dr21cx{h;^ zU+}T!I!PBL>m}~jp*A|Vpr+&9s#lY$t3+E`#RB+Fx81Bge{bP50W#mNG-tfc z=Od7j49iC+c+I}Y!!{wJ5mMGroxh;JV`Zt;U(7f4`-yqXNQdhj;l6v5=v}bmh6u+3Og`{lYl!2F$ za5UG8U+K*qR?%gtHLttnT>fX<;E32=oiqh}+dM@;_wuRctq&v8Z7T;wxJdEfSNjL?D&*_sT_y_lTGw~?TD{3b_yUPMJyU6Yy z^GN;KDo@%tX+or8kvL!4u0zrVOGQ-MWoDLo?<$ZsDZ1SDntP1QuuK{sh`Mr(6%4ZR zkTSYYT)zA}n-GaE2KkR&m}o&;?!nX#4+7l3$ATSxm!sAnAe5JB2RCXb$@4yQF7s|F zFZgcuueJfCsi}AM@%a+O`5ME?17GdT{MYr_Vis#UP*dmWC;F+>E;cSD*yk)N6cK_D zWG=K+INe@f-c=d5f^Obtp?=YYQY5Sv`p7ae#oLl1#lw;m{D8S7+Hv8Sbi6k2Z_qa8 zT~Ez`@09|JgJWB{G)U`^L{2gk22?Um|wm=r#^*AoL>)D=ke}sdYTP5h%N6gRr^Jtl8A?&UN z*k9++n@3EiG%GBJLO*{9!+koYbB&Cn*3zM6)6#bv-}ECM)04f`Dq5%3{4`rD? z&Wtan`1vBvhIY~OZ07FV9mW1PcKmMg&SCzs$r#WJh_an>T0CJSw|aO2aJEbhV`@KS zC20J08(qp}-qy=RIhIrQ(#KqECid+l3l^mtB7)HH z@MRrNM?RT!vsP2Jm#nu#EQX`p*uy;eAx^tV=emHlykySq77Gvq_aV~WUSiA<7*FQY zy_abFklEz+ig~(~K?PwT&8sPO@kax2m(&Li;bzhIub7Y3NDV(H$pX=@pt0MQPIq3h zj?KPHM*?`bsf|p<2S1br-c3+jn2#_=sFU>5Zs&$n>u*@B*D{D4U+z}Y4e9EoNmnU(Osl3@?c+bJQLSV`3NdOBCU9P8K?)&VvK45NnTo#x? z9p5rfpDfe_j#_wqB=^&Rz=xtXU;PpB^9)+{7H1`Mrb(`cO#|Zp;QBt0UzqD3`D&P- zasO|wKby*{XZgog$@RzECD&u73Le>SL+uO8^Xm;X@D1|_x<5rE>$~m$%l5Im3$cCF z6q)4{5MG$=6*o}rznO=%lg#qcuafUKCi4;s@%{aebm|@WJ{NNGKlna|ga6C-ZU7hM zd!&ctd)8DM_a1!T1P+*VYdIez-;;sB`Cj`Y=lhJQbl^So)os%0$$KoAt*esl2i64- zq<8to>T505CQ|=}A+?p^tLe=L+&<7xC9jV#aPpi=>?2#QO`VcRmlt6Z`~D-YQ6%Jr zDu2SL5juzBKVk8GX$1}Z#KtSzexR41U?4Ijjn^H{HzB-y_V>nRDaES8UhS&i*aVgnRF4nD;LdY{|4If86(iZamGuZxt?Ltcq;Q1w_ci#C;eCE zryY}#NIpwKf@#)QW@q2zE$6{1(35BAPnqvAy7!(Ux#1ElxS>6_K9PbJhNxP*B{FjE*){aU`G2}J%BN5mj z15bbCF^eaWWjQrd8M#1`QgQ@=lt5#kt=ddc9uJW8G@2{vxi^PO+Jm0y6mPHolzicu zpy%rR|4q-hA%cW!3wS|k<3(Vx2n>^fbt2GH27W>=xX_P4zO6c4>QK%{X?BJx+$r1G zu797z0&^0WoKxn|C_lGIK=EdcH=e5r;&-Z4JU{{~+0fNZ&(OD_QO&jIdM8SUiretP zU)m|Rw0q%_uqiD6)4^eZ9SVWB7JYpZX&vQ8+dzYp7o$ zEYqNc8BKc_zMUCM-@B+ky2T+=OogIPTed&|j~<9qGa5;yT-60uSs5JR^9bWwSGA4e ze?OfEm!^PJSmkIk<1*^aiHm$cR1X4eX7aey6!P*=ovf}2%;Xe`_E5c3?;ylok3u=0 z$cIyI$791Y_OJZ$)Y)5P?_L2;4aM$l~!)y8U~K;ANp{`OG+`2MiO7NtKN%X5qPryJi!*({vS z3wa>@1gzrCm!XRUO!}EyIaB}`04Ci$8Aud?DMQJ{OPyWj(KIuWxkG5Dms+v%F#`vn zmcNUES63ehGP6yx=lz_QV(}28rMEg$6V`qzc4A!jq6`+rxyf)6*NwdzBtYJIARLwa zHHeP-s6kd|kP!@Y2GQR>>g^z%2-RvW(I=a!G7c2sdq^%=`O`pp@2jqJ?lOs|{X9U# z`sc;w4xpWWs;{CSOV|C>jPgHD5rhdZvanT9N6>J8RaK(L&|ZHv*r9r%Xz6_R$Z-1C zU+wL15#^&sh1}0J7*0b1R2Oaduta)sJ;qtnpEd+wP_^$zGXqrTvbCFn6c|-cI&Rlq zSdSZRE2_;knz7V1P_3@KZ$T7@qIb2RTY>618#8Yj}-=nyaMczkH|Apyy) zfHkSEf_Gl9ARCR{4U!~--0T7Pp7a}csI3f#wnaS4bqo#&I0U$6NKQkb!6ioSc<%Dh zoHQJ;+yQu%sR@)Wt5ZCD+^!}S(6UeP@;7h%WiKyjL33ZKUe%Uz1W-^NP;;p%wgshS zZ-&MQ%*n2v$rlJy8q<+#YDMb?$%%!%F)Rx|Lz4zZwN#ELQr|GuQ|UgOW`wEU%EUz4 z5~lh`cSz(-`aav?us=Vmhw?t(x2@V4SrrOT;9=-Wp;7tFFGpotC{!*SJ!;P(PJvYt zMTe`kO2Zsn&^;)HQo~gjWkCWh30JErT@&bBxLVP(4v#l`geiy9cV7CFgFJ`YpoK0~ zQ2kxwLDc`42fN_8?{W@m3w|mt{0a}o2cHIg+czjY4T>9Fd_))5YT>50a&jWIz^icD zmK_uM^KrpS=S2Q`T(}+T#0ltx-5XX}$c{fAr#;dkk)qS;J1SMqh{xi5wQ-zi-r^n1 zcb8A{w%PJo(!cIEe_A>`n4X?o#oUbF5Xu)DIHSlODa9MEF1UE* ziBA_HP28!KIU>8Ml&|dT`#F)paf$SDxJRJ&X+t^9YtQ0CuwyGenvL*q(ay(*@TQ!C zkBl0zUfSmP_+^BLx3(%iE{*UA(|&G{NC^uS9rW`aH*IurGBDx8(j?_ zhfBd{HU96Eit$!r5*Jy;cxz&4LT%OCYI6+Fe5Z5_tz^W|k=kmwHmhkOy-BdHK%1;$ z0&O^o*)eqa(i9b`GVMh*QTm16jt}7%`ZM}1uSNac{?ae>KqNEiddDORztFlc45Wqo zN_k9)$LLafy_=V&Sk&d{f^L+3{5>VqQ-iF>;N=Hdw{|PulAyKM_?eSWiXeecH_?|to6t#}Rz}K$_-P5Z9npm=qR%4ZwAE{}y>KeHg z!&z>6F8ZX|DNoEh963XHZ*BL_16Iop|PSyV7mse&);W90$wO*a3 z4D2S#R2tQz($RPca?N{DXx$Cy%0&uo07oCZ(A!%J635)4>TZIB$sL?ZP;g zH@b}(E3F-UQ&o?OLlBGkDy9!q2&E7mZ}`Wg`%<@Pmi0)DQQp0)6&2t>a zk+_oSkT(j)E~G7;jlrlkw>Eu^QN04JdLv64#B(xZZTIxVI|*Ov{3ql3YOr^mUO+vJ zTtw-+IFcRD7*hl?{-+JisE^BCm(au@^?olpQD3cOAH~svfMG@(*M>B)YN)cO7gdW@ z+ojflR7sT(uQn8v2N1Yo14k}2*5IdBNVb{tt2`~RH{VUC6S_v zAz2C}%ah%B=G-h#+Oyupe1Y@LdjR9PvTBKBGny^cF8Cg=i;rfZ+V1%Hb2OW*+-^+w z3Q_-en4qAMMEXG@ zSr$V&FOV!}p>Bn#|A~*`upjL{(TJ=!oke z=-Ag;(vfE^yBL*kHI5C@4o^s=GvnAc?NQ9i7W{Ike4P-_6E=Db^fiw$ANnlVdV%1nL8@QZ-zP<=dZljJ2GQS>l8us^O1Hjd+)naB|=Ao~hIx3qNk@B74D&G@ueipXf9fSH6=`G)Zezbva+k!TT zmLOYz_$r3mriKVmlmkE}-G~6e{_Du9(4D;37_-qR_D47e`Q<;^8*kN1B+JS0L}Ukf zs5+T-(Qd(9KYudoq)o)fKa*K-{6)MP2$CcCYlIcBcI4bJ+w$5i65MF;z2L^4)TNaTz46hub#Nmd^ zhKnZMZq$yKX*TlRF<+GV^gB`J;Fd+ptX)wsYyv1M!pI=85V83Pc%G53PJ*5Y&|Seu zEO5Z3+EKZH!TXqx;MuPNAzzSBVfX|me*{$WU#Kg#13HIZw^8dVE_w=Wt9GnWXQePF1!#@z_pEQzr`SjOF@{cl1MNHCXAEPCs>4Ybc@Vbi1?q zgVRoo`Z?K{i{jGdS_ZegvhTd2!O3c4C2aMoeD4!-9*Y!~Q!mw@<+4WMVD}R;4yRqQd%5rqQ zm-@Y;c}bzY)j(y}L~7L=H5osVX7*M`JC%Ywc4u6aFhVRv zJ3~QJ7GZEe2C5<;?xl{G;Zh=Uhz$7Zc_bbe?IOeW2%B`@%RuA2$XFSU5|OoJ08d29 zl!5-KqR`rT04IsCE!vDwN`^1xh2Q%L{80#liK6sJ5_F;v=&}THf&`yUy2BE}^D*gm z$Uq`b5z7xOagZQEeK<%$W=V*x$o8%oI9dYR6#;hlqdEiCfz=eepKWMiW%e(h8$9WQ zX1Is27*G4bzl6o`N{B(@gK!`j67A^5Kvl0Sj3k#q>J>%rL$3y@{VnIF`J)Fm`xX@x z{#O*ZV0Mb~Wh@^qLN^JJ$K8qCYRh9RLQuC|5^*+1CkD~a-=YHSe@3_&`nCt6aT%|o zbs~MYxqf9DFj%!$`i`XugVA$>$I_+2YIx&HefXkE++B+GFUh~0ku(L`4`~%59*~7x zwCx^7csS=6Y%afm)_SK{sUf51)C9Xt7Y#KvNhp1sud4@#L>T)Zh2K7Lt^o`Vc zj2QH~;ZOR?=KfdIy?iJUGWa(}*8!%esPO%DdCfWqn;1KR9aRe1V6FRpG*26PQy@e< zLI_1{OADgEOn6hiobRGx>Yysad8{n5wZv#8l5xW&_%uP0{8tot)HhMO=k^sCmN6n} zAuAwjSGqb(4G-^Fr+}nKqc{;!m4FM9E{ag-7l9D58zGQn92G{J%18Su+r6n{idxmL zwm_D4E~z3#ge5O4Bcg{)(QOoMO;O#H8D;2PiW(Z|CfZsi=p++lb5_DiNG7O{zy0=< zc9%IR9%U$UxZ1U4-;ta$%cGH;0ry2^MKyz+i<^=egujvPy@ZX<4l{nfjm}4>Lim*2 zEZQ-!lfZG{25UC5lyiW{cFeb#y6jA$BUE<}H%?-@!x=1k82{{6V|JY88z&{;v>Wvt zp?dqR5UFMT&ImFh`tc$%BDReXENnP}HjPl-%FeMyr#GAN(3}a;uBl^c^Ma8SRegzX zq-l#Z`^{vi&D9nvB)_gq=*{mO#-hE8I!o1w`UvZ`wZlb$rWu?WTu69Eg`QVXyaWYs z1Yd<8uua@c3U>dLy$gl7Y5R25+YcBIAl@FdG@LvwHv3T@oRxfZj5 zP*4klOQGYcLN-->iePmER$dia$O5u!6=TyY2VQ`5`R^cc;vd~quwSG-r9**+4!6U3QY?=!I z_i_NAEwurdP@Q8Wakr7e?XgWWGe-Vv8J*35OhvvGPh-Vbmj2B!nxpg=Yk&^ zK7=O?-G}nvC##&u$RnMXo)M8Vl1~seM@I%xX+v~?{b}HeDFNyEl<+Nq?thE&qRjmf zWohPleL_GvGubc({2mO+u*oo29VV}^7YCp$X#9L$@2f3^5AO?lH?1{3qA%$Ey%dr7 ze?LG_5=av+=p9Oj3>H^nPhD${*Kopf%&vWbZTgDguw&!(~LZZ5{}Y=s-LEQn-Jw zUBO1*@#=C-{Q+MS%p~C8A)ea+!zZRqe0nRi9y}K`K{USrTyh9b8|6Tft}pbaUO?#A z1X9w}I^|yjv+z>68Z}N+UF)4F2DWrZ8$**BdD21?XK0hJ?!_9c80MdDv^GukbvJRT zz|MaF=b&yhS_ay5u87%#qVK1v$)214O|ZZ|;|#`0XN*0%>Z0{7ijzV;E~8ISd(|(= zCn$b=_89#K4LX(Q!-p+?tbbX+bUV0!@zW*j$2QnU9F@Y`)>m6nyz-XcL9S6w@up$6kt%Bmzfg6 zNrl%ScPpA`Q1$lxP>sTfo9*cjgWA9Ri?>DL@KrHG^$p(tH^kV`5zD>?ygC*+_wfrA zWHIcAM@te`<+zhpeb_S^QNLLdo^{Cd!;;{5ZhO+}OwVr4Lo7=(p zO(nnqwlp^#TI%531Ui|n*0!&Ld<#=O(2ne8f)lenY4%LjS=-=Q0{uKw{Ufz%7s0gC z74v1t@(pSLpG{z+xoEV#osAHY?Sc8_LRDE-$ioRF6&$;QVHSNx+0Wz#JJ~EyB{j(>M;|Ze>cmE?TKrikyWl%dqbPdATV%(1EUu z<$GHU>$YXgYD*yrwa|*vtN>4vC5nM3_CrGql$Jd{HAJ%S^PNrq`-)dzcPBsyK75)h zqIAC7j4OO$wed@yG)GO>Xk8yC(ABvRzR>3w80M*o zWw-kB%)y_ArUYs`Pi6M=bBm^hjrityYN+$~&rrB&T&=D{6NwK2W^Pu3hFP5sg zHC3LkGUv>kf>e;Mf>e{|tF7#tBhSLJG0%#g&sRg`4&Gyd+CduvLVsAG4%VK2kwC8& zsFi)&ays}B%O2n(y+5BL8(`bgvLNrMg=(O7`uzmzwNP!P7<1_GLN%o2-|?bx-r8^> zc>-6>*?*G#61Bb7^tq~W@DjCk3GLNAr|HZJ^^N_IJ4JtXVOLnXZ0b0Yr83-6r9$QzTqNjCwBYi@vo#qB1w-=L#*>vr!F?4BJz%D#;8Cw0(;o@kKoxfg zV@1>&YedOa$6Z9B+Xj_K|KN+RLwv?c;y(Bvi@`K%sLX8;s&&X9#^dK-M3UQfoSEoN z2Lz%`M_wM5APGXv34+#3_yyn<&Kef}>2jR@9%ST+XYv!CsqPIj5z1iP7;O9mx(?MP z^(f}UoGUN+(V?!KNFrO%RVLXilfa0^q$|ZiT#fB`9cfm{H1@W6{WY79Eg8D|R-zAx zdyH2lsEW*2fR0MgAHd?mlFBW#wo2GWkrIkmfR;(nLIHXuKn4j~l3(B`0CJJbvBpy# zrBo=Gf`l<#?j=s_-R!lo+!Vt?oH?j1)$d!xO?Z9-oQ^kqPD&r`Oue_L<=vwKEG*K4 zr@Tnw0~~Zw84Z)u#*YF^JZ|O~7j98ID&kFjeU`P6n&T{^H$t*Cipe7FtEFwF|8x68Q&u=m zf9_BlR)68nmqV6ki+0)ud`RNP-deIW6e>ooa-A8pw!CPw&OW<@e603dC(Tc9`QA2FIxFBv)=3Bhet+k!n zkIEa>rpnC2)Rq6&Jxn`|YH-OLXCNmh=z&qKSLV(aK7^uWhu@^&y=tvWo3IisLiRQs zM2BVa^O%yz9MgnJSB+=jK{NKM-fh5F?Ct1M%1NmUbbD}6C=ktM> zSD=NXg~dq=ZLA@=M698=Rn9uxBv->Uos1Ft)Ug^(MdSAUY8#Da50yTk#-?WCNg|W( z#*w0#Io~s9cEi4)Rgz_^RMhbl8;YfPa@U+<15#i+BO36t14kWO3>A~Mq;#ykML9`o zZ;+a7m2-~%JOIws<*%<6Zt#uBM{?o=zJ&GJcvL^9AJsbuU4AD=^1i1!Dxr>~$y6O_ z=0UY=a6K&Ygl71ON%uhvQ|6btu&cF;H~h=@5Gyp}Om?-WhkDZXK_u${by)J)Zh zoHA9NZpQzJ@51A~%C$lizZHF)Plk~+FjKAK6b%h3_ujV+8O23z?2UD!?bQ2asmw{$ z-|i?{IX*qEc6!+fQ?hKQ|3yzT)s^KMVzf>&jJJ!AylXq@JS{i^6aDXK-x2j0+_A1Z zszzz-=)qAnRMXICcTDY}(KNzyq-uzNGF~iv5s~&T;>XNJKhNl!kP;cZMTD{MKfxBLJdh81$C_G z)7-0?o$Zu`*x`p|<*Wc0hduodh;y`ZVmDrjC;JDr#>wXUqbL>*4eJ3fEK8)taB$&- z8eHKma^SWHgY%j4hDx7Q1MK?Z>T4mMJSY80wUee2tvIPhl=}#^3};Q_$m_O~=F@#J zQB#6So>D(*&M!KRLC!!vr`0aXH-#pjR%6P2M#1qJ4K?wRr);Oaq${V@B+Uu($x@xX znuvyJSUav;<>`2aWK_e(k?(CMrr`KBXB--dJWoJ$Q-7gBS+Ixywud%m!5q;ARLdBR zh;gD4XH;iRBzc@s8<*cRSn=I-NM9`gmCF;r!zbF&L7ZcVTd&oWC^oR_PCY&$Ytz-nzVIbKx5?1OHb zOog|IgLYEei)x2dALN6%l}l(4{4!wJN&=Zs+D-P)1h32rB+GeroF}+VX08r*i=JWm z*Q|;HBMu9teingzrQq@ioga(2YIyGqD+)xAry#f*BFrnO0l)H|!S#dbRO6CbyK>ju zhDEnVk(eW}#=6DRNF9yX8vQ@Ut^+K}ml~u@B8iU9mVATJm2#u_s;BWpPk*Eot+H= zny#PG`h;cvsn7f?G4lYnK#P{3{HS4907 z(HsgntF0l;TSJ}C!ZhpyO*yL#QJ>m`RIS{k7mp^VkhZj+JDokN^(-HEmF3Dkz;5Mhxyi?!p~_d*?Lo}bJ|f-z-l^kPU|V}yM-+0-eZl4?Hy*ruq*$O z5MP~MUu7u*4*D?Maa;-sGIbe6oQK^olv?Mt4Kuox=WTm9w}2V`*}xlpx(Im10y5E$ zX0`1F7NFj6i}g1o^G=8oz`H;JhT*%nwF0%yioz`Noj|1{7H>SP7$*qSD4;U<Y8J`wZ-M&4QBQeocL`KJP;3A&1;sj`xJ{w)%LVN+Noul;mR;7? zk`}F_YnOqjwSmT7(Uxmk-jqE83-HjMrEpP~*lsj7_gKC^+n{wTA{v!Ru_+5u9dT~ziqY`RjGQv2J`3H-T}7Bl$361sO=d&BkDMaftd z|AXhftw*zT;f^-cefLtvT#!QgOc^OkJVnR5+HC3akF@MA_WyaMIcp`IDPS#mA!==p ztS#?Qbz8z&ZG|8#QAk)0gc!Zam%bX+fxmP7pQnqD>e3t?Pa>TZmQJQk2IJYXfAYQ! z0*dn}y6DKy9JTHgYiV%fb^&kUFj{`CfQ=We%s5{lhFb}y3b;Pf#FWbQQbPo)1t$@E z|1PxzF0BYIMW7sul%}RY{fZh{3mEn@jl8ebM3ytd@wW9s8XT`3p8T%th*p{V(J&{< z#MmTe)tUHG>%8YEV99~b+}C=zdyEf5nYdp96pX!*)p;p>xUUUUrI(5!RoMnn-Jan& zA0$=^Vf&DZ<&ZJa0iQ(<8Mi?QFjIA$AUqSgk5~t^F7$sAO3Kdb3f^C?xi-1b%Z530 z^eWsF*LIXX{XrM=uo!}$JCnB7w~{k^kkWEa9?ibIApR>27v&9rl;8aT-v)KFlSp5v zB|Y|`r!coAFmhbfk3VP6ILdK0NKlf?e=|y^iHzj(Y2SQmlBe}575Y7*VUKr%((+(d zvv5zbf-H0VuYw#;N9M>S0J*fik8j-ZInI*j`qAH*$S7KnLErF#Xio5gr2kdCARWKw z1quFho+!xZl11-Xp-I^w#lB?b;$$*}YCRop85p7EhDp<3OS)4?zIIuS-1&?tVEYt} zCPsZM{k?g_}$Mi}bD()6mh zJJNc6vba0a613L4BUj3ayCVT}lKI^c>C0@&exeOXy*@j+@al*^+x}VZf4JfM|8aHX z5w?Y2Umdvu9J@O50}3Fnj&wjS)}rKK>xgfDb>v_aNzgP`jW8tB4+$E*G-f5;Nzg>A-p_)Ptt%|%BeWVX$!8@g zT1`;cx>>CG%%(0jESX=xCWBgd>PR4)T%O~N%D)Fw%yYcH zT_THSzR*_u=Gc0+0r-YytK{<1?=$J@3vFoW*)v(}x2F1E$o{1^O4Vv9E~GfaP0)@s z+U^W@|CzM#C2q!C%%q(!wIOa>G6j9F&#Zu)DJV_Rc&d_6g_(-RM`fIpOzRbmzsvkd zg7otzR6b)J zIhBl$P2c!>mMPoFFbKx{W>eGxdJ2+Y^YmmJym)ST>yB@a9s$KFvic*-?ad6@`dZt@ zb1JY1-kWBn#XG2jrzg|tgXKM?Hq)v48*RNn4-kQ`_Fl0P@dRxw3GVn1OrG^M-96?_ zLk?L(+p=8lV+_I$#@rtc)#{@%$MA8qz9tkJmjQ2KdHr^3GL@|xPN3{tbw;O1J z?2o-;b@rY{v)*aLD*rP@RA)W*R2CQ1IoLY`%St{Lw1s)$Zf!ypZCq&tB0E#mW*eG- zYe}|KsMdSf?*~nx{_k=B@WW(;TW%#awmeF@_ASfw9gA6bD{AhvWE!Z~M5qRQh^9?L zoP5acgSJwstl3O}ne*?{f!EpqYX3poUb-@gwtqk#YbTNEgEriDBbEKA4R&d3;To4W zEctGeDem>tcQ}kzC8ve8AaccA#mkOO`Wwtm)D^4OZeYA_z$V>XrG z?rwd3L3_QCrV`%w)jCUEGAQbc)~BQl!o6wqOFgxRV;H_sN0?eI{`NSzG}Z(?VMz}t!u4%93?n-?D@2z2NIb7fv z&oas}X)Hm}g+XmCK`t+-u2~yb>dJH0UKKNvsnZ}$K#)1h2CLJj`FNq>6Q?|wtvCOb zUNB^HvZzTDjbF%?aYctL@2kZ?u}oX?6xWVVrnyZtzN*%9G5(uq0#(yqMbpJ5n#!tj zCCE zKq?Yr4Cn<6p8h~|adizh{@@~ROfqd88XqX(_cSiuIgb{^+nzAul4EJPB*&=SUPRLc zNvCg_{2 zIjn{cs8$Eu|1^zErU&gb6{Rw>Va==w@}ZGPKWk_juA7Zy>D0EQWA1#Mnzq-ZsG<>L zS$hof68PX(#csCOl$9b4^c?Ync8-7=KN(lrkq+8dZ~i-7tv758xy9S{fvMbVitV5Y zbRP~noncIZ$?+jmm5fO>rRPy!6v?|jLRdq8dC1zXDbj%_M*M}enot`Xs7}Y}__X*2 z(w^Zo$w96o-8A4b87AVC;e3L0C(n)=Piee?qC0AW{5}k0shLf`=CRaDAnjuE@79uv zJ}nMlTI07j|8)(KnJuF%UtL4YwT!}o0MluX$uU*uxn!aw{2F3|VaeRvfJ%c8ehtwX zAN(5P%k*S%4e-zb_~MA9oGoOH*!~ zHc3?y)DhLSYov_i1*3eURh5P$i@Sy{_~4f+pAW_qFv{0obs8Vp_>eXYra)Xfw9gvM z%4)rDIAAaxSRhxGFzh)G?Oa(g%;|F)q zuR(Ay{s_Js%<7{PI9*E*g@FwHU-OCtpE2ID9BkJ;?bK=creh- zYs`&*$A;LOs-2<@&hld2+I}q2Z6WN!DlY5}L<(CAQy7V*8BQ$H6Lf}cAv=KI#R9o% zg}o$%xNIje@lbDD8l0$gOJSLt)#Ffud>66zX7pxtKVaBm-UzcSi2?MVy!W6fYH6Iya49>eQ2Yryt(YWXBdZ2mU=Ob zV1UVO2K95prBU0yWN?#1Z6DHaZgQaH^^7jL$rYvV$B^VMe_LuzljCMAh34Hdrn$?_ zZKa?|bVx1lDE+J_tI$)E{TQ0&A@7sgbT>Bels#>w*<&fyOOB9K-x{;L7beT>Zx-ccpeYLpTnE+gM4Q&jPsz->;o0Z*;{@q z#r4EvoN|!uucYyjo0oO&UR+1hgl7B59u>|kV!J&!DdR)+h^a;|cC3D`8;IFj$11==%+%3BGb*Mt&E@C%4eoHK2Dd{%}t9iEL~5Q%Cfr#d(+u@AW3%0#ils z0V^#miWsKY?L-a3IB^vDr(m^^Hwa1|{Ic5bw+Y4I%FKf@a%UlywTWpP6vcU~ zfyC8@R7&!bTX??ejJ;^%ujuOCqb+{2pLDqkoo2u0Vf55bj>t%7#D_TZPec`wp)ec7 zIj8fKzfld$Bv{A4HFfCE77h3Fp=E3<2$>qSscG^96o9?pgQ)r3dp;P*ZwTP5ap8sR zj%#$tNjLd$v^D*|)61q9?1w=;@zm607%lUcLu(!D1RB=e$k@~mLm{sWFWNE0#Zr)^ zkZHX}j1saXizwYP`rmJ)~jZq+~V3%Hj&M&`r>1KG#itFOe=tuH?(Z z`r(=PYdxI@p1^;+BQ-DqHj=u@pCy_bBzIH&w6g-e36iU-f_GP-O2Kl16fn>@Fj$5| z=mCFGZiqZ5!~vQ*=FTQfNK&YbA%X;tvB8YRvv*JWK2*LU1@xnVVRF@^Yw75CrqY;- zC9wX$-4YEA)p}T}hSmFDsBK)*E4i4 z)App{W^CWW>@5nQYkjG91$l#NU7uvy>rmI54o`P=Cy#Ksy5!%AnuKFr<=u+X!{xvV zZ+j#2`Rv65gIbxxbTTMW^FGCg)TC~-CtQv!y$ZSKYjD%K6}=3XbyBn56dxgnRfu4v z0@LIk4HmWxjIr%aLC3{r;{?#KxNJo;BVy zJ5RljhpAwp)r<)a_zz-kQ(@f8ea8>zMG+O{@_wF-Ys7H{biUzv6A?XYJkG@4K#S3l zx>uAts86*>w$bveEcrnImZtL+<=RdIN3mg-!*4x(=t<=@a+p)CBDk=A)Knu!N^^Qs zhDJUd@eYg)XC$`6n&1{DZu#h7?7&7$VF`Ex;)HcTCE4584n^M^jlQz>c3yQZRiI7l z_ARZhB*%CdL89e~EsJ&wg?l-O@+-+n(w@dtB@#S1x-q3kVp6)*oEAjNvB6gxvqk1% zwAV1j;*tf`?SO(X!H2}thS8HqIbQ11ogypC;T};U!ZGf^H@pR3rc=Mlaz*>M zNDQW&?P*D6xu&$eFv&Jk3U`w&f}xxAp5%q7$?Lvp|mJS&@YWBJ4$vhZED2w$}ufJK%1lF${|}V zaBol}MYfD}JSJGF6;EJYCHELP#CD3YW(>UINL5Er ziz;%Eiem64eHA$&;6gI8F??&{_UE18B9=dYH5{X5TRpd6RjZ^B= zlT{5oN*L6?OrZf)<@j>*reG??@i&vLy3EBPKd@gz?4l`ju&P{3%9ujtsmjm=XR`$qH7lzCgA=B|A zZoa^b1UFw`ugP-5)!weiN!XOf)@J2k;RWUBkL-%>B7{bdY3OxXrJT3pg~(XjqLoQ8 z=xueAX?Qi+sXv;|>LPbOXrR%#Kbb8|Vyphmj!_+>@x$+(6x_pM*LwPvbiD{eT^#Hc0nS98_nk99KlLG<T4$};gD<|Uk zg9df+^Ot5rKO84V<0Tt<5hpjwXw;zi%FKWj3tJO+Ny&_6CNvPe?I*FufY8KgnrU1^ zwnXz}v5G9w+E`-M=8IkAyNd<#Zc9l-f^5cn0Y=*jthi!fIlIDqg+bR9ZD?Imi=|W( zV1!S~2sReD$bPvW6Sy}de0`SU!Do82(=b>CqF`oq1IEZ%B|`YbU5d+@mTc<7;Hk8e zjqCAjB*7!Q2ZsySMSkj?Mt!YIKIVf-;1iCr2DoD(sH&CiVri784Ika>;x?4j|lzJo@ zE7p{gZEdg6uv!qOEltKfwPd`Y{6I?;>c};uI!mZ)9XSQAVK<6Q!^qBiZevD*Bs zrH^$W5oheA;kA(3_xD)rzP7e0BJ$WDS>%S&RxOKN*O~DsnquKEEOLHb+FehMwVg%J z>&erk$hI^pL2l-Yxsv(ES9tl2mIexoc&O^tdl+wQ_y;8zcE# zT*U9!n09b$0=G5}ZE2TQ4wsTg(Os=9JJfH)DD%*^9UD;Dael+KhTy-z5N?wd%Kdzf^`M>1-}^m<4^^3g%Bc*e&PLfV1yuII~7jUmJ^93 zs4lCXIkb8$u#r%TbGS=mS|`gP+MP*k+O8HNRFB|Su=%~=Y|O-N?;nc9?xVT1=1V|7 z0h%veEEskVXLK`iP_R>;-YaNql=>>_2pPq zOk6S)m36)C-Q&12BAad@0oz!LZy@_f7h18kdqiKTH2{yvs|Y#k`{zh^ZapFn}Ar2qDQANFPk2DjcIr$#NZ4EqtgO$_XB{hEJSgNRPItd?y+EHI(n! zI)<=mFiU|t7}miwcvxAyirz?`AgxZK7mcuAYY|UAjZx-m&1hz0c}(++{)_{}bF`}% zlN;2p`mw%%2?5O0en7(jb)2IB_m(-F2N+8d1_;>_ZgPUrH5(xdjBT38D(t`q7&|tV z@np@xuGR6E5=Rr|z+QTFTqW%o^=*APeZfI!@oVNAENUCh8#9r^pVnhwsD+7Re1EY;v&p$@zXhKRvUd zt?lL5a)h)nyA|xvb_)lsfpbw(FUy?WJU z{po1muDA_DQ;t^aK_)IbREVV!9c4qtu~2pe)v*tYkJ-@$p~tb2M2YiTA4v>xW)I8H zTF9s0HJz9=uyf+1?DdZTEmJyMbFEm7Re{ZjSyh=f z(3uv`bS+bAsXvoFZ%wU`s6zO|@hXL+kMp*%Ttkz1=wuE(fX7kP~;0Uyq(vZvJ3 zhr(0kjw#1FvSP=d;v+B}&-@zflCp4^oLL_eU{%b>%tk&jM8^)_5vblO=yO4ze+qCK zV9_3@qUlVkT-7b2JZnGbmrV7z6;zJtGI?~B8>)Ps9itvyp~WBcz42gInN&S@wMKn~ zG=?ouN-S^qO>GJqp8Nfd73h*cvx|IT0-Rn*1&@Lpr#=rH=jMl+qEjU8UtijtE_^Gu zm;CBdbWb@}{d5i{1ZjIz2zN})ffCf&;pM`*2#NgZN8Q>wnT|o$YV*z~6l4Dy%Y)^U% z)Nv~c#xku0s&b3^8j!(SCP^S>a~i<00$#vjVN4ZNfyVZggQS15jLZAVnYPlEnMO^2 zxw38Ys>bLawu6d!!yh4t6dly0po6&0UMxqe!)gU8C=A;ZV;_3b=x z5nJCDJ}_Oj(DaKkMrqZKJmvFhmj1XBbat#fTbdt8UB}6lRZGlznl}!D>|FqT87D`p zzI@ReW5&z=wyN?D$#mYkL0#$YG5Rh8gK)%)qjV)hF7NOzjV=C@zv$ViU36}}7>b!7 zcTg?=q^Etk^`aaGe$u07>x7s>(}L+kZoTTx%4g7WHxp<8oYQ1)4h&S?K4zwnDy5pL z)_>H~^oerhH@$J0#A?Q(Y6a{+FJSRs`J*1=1cn&{aVW|-Z0W`%k5aH3ht6eE?lEC< z(hwiqu!09M!g zh#HfmX|EdmUQe@=;54@hKDH)lJgdiokn^hLHu%RBLk4CTOj;^OXwh~bV_lwJnyGhc zL2H+f6{*>jR+9F1(|9yAz0+fG9Le{?9}DR)PFfE}_=5B`)D~Zvul$NwtZaSzv$E~s z1xUb}U(>-uWu| zKrt&9hmn0*O-``%vT6BHUtiTAXN&pSfH%n4;=0(@S(u;Ie4`hxi#_qdTo*?_eXTFF zKa*XUd=;*XcHhTRePuWUOby}cu`^x=9^kYNY?O0@H;-mC#$ zzor$Za4>)Hn`A1{SJO;2{iUAT_Js+>Km=v> z)zpsmdMPqL(~~6@grvXf9cGDO=z^0JnZD&)u%d=KV0>4ET1|1{@YW}5kiDS94SZzt-=;WgeTtak%)so2 z*>3v)nBs)R>I~k`F+NA4bMnVP*UEhn^BsTQ^SAUze~n&s>6xAmq-Z=;f8Zl%fTpeL z_N8MqZh)q%>dR9-J&%J?9ey3F>%7%Z^##T{C%EH4->RU6u}+N2eDN5i4uqZBO?<%q zCM=^Huiw|^H#vRTd}VRZ%FJ(OzZJDS0(u75<#$bTOxp!q)Ewu|6VXYFEJ1Ad!ui3h zK4uA;fFL%$y9n4zdTDiote70GTi8w_~Q0X&n(&L5D|~!pr)|j(Wv_4 zBWsYRN{BxaF0`pbT(jE2lGmZGEQ$vxa|da@RV{d|r*<`UA<>Wd$lxa7h1}X6SeCOZ zA{Ds5fPqX!+hkgjh6Z#(^xO@)AnB2iBc(1p5;0~`vus)IxgDU@X6GMPCD^_0LY(-) znh>8V^!WO! zdHH%W%$4Ju+U4`6=4!$`2RaL!i<;r!!&@MazJwQJNb~y-^%OBIzJkl1hoZdcoh{qU z{bMvS($BBy)w zw%7u+?8F*qEmAP6m!pOc#%laZtb@{>vd3vW{Zk5~h}LMxoO&C^@EK|l{1c4BNN~>6 zQ|dTPV3~ZAXqigZV{ z6tU|kavTpk(E27c{CG`(Di|Lr<298tUOZsQ3Jo|ap3k?vi180o@W4v4=uz{Xg@>49F@`}p94+xwXB>*?7lJRgEz zZXk0AAKct$B|aLh)&#PUwAEOR;FtRxuZ)iat2G*ic(NK%@9C-Z8jZLAJyiRDxg6gJ zB5u$#93MRA_V{=`!Z)Nu@Yv60@*U|LuX=G8!+xaia@D@)dRo2~YzqW?)I?>2kEd%j zzS14UUZ)9QztQ-OxWn@N?F=Lk(<*rAvUaocn(83@_1(Z02u zAKgX*7L%S^8_YClv~NAt(%V>XF`J$V_+U0Y4ksXT`FgNy>tOma+P9KNEu@QguJPA8 z>DaKD-(?X_vicr7MzM?JrrukSI(J?){$0^dpOCAkWs77V`=C~=*VAv)pNr%}Wf$Z^ z#$ka4hQQI_6|8G%v8???6m~vUz>9D#vx1OLAI)&4;`bsOXVt3 z$sF=pCdaA9LyR~Jx#pgP@bZ?_yq=hf(R=b$Kgq_IFTZbL(w>X{Vj1S zBai*X^H|{#N&~z21*m4{pv)`e)My`!ZJZ8sZ!!E(ei7@~%!_7qv+MeQy+&jBK)EaA zindyESSfqEZO{LNIh)@K92=5=y^Hf5s<~25FWuy(nC2=pCgV!%2+m_5d|D~{c-US0 zccR+&6uC;ST=DsHIDce^+E5$fY*y?|4R#erF@;l+Ob+Ix~!M`dAK4jN&&doIN3kTubgVtu4D9RJu1)wA1Ob{ z15|f*9-}=!$rD5Bv0*#)TDqb;a7UG5sMG;0)9_9)7VA%4H^@<{6jYwGWtSYqKbQ^Y zucBSMT`byldd{NlK}C7AEgNLN5dZ&(*ROcI6cMjdv3PSZCaw9w9)uV3V{Wnh_E6c4 z$ZzolYcV&F9vE(-SnXUBYlJVW|at}$h{}2|zXKQmFdCRLu|--W)={ryz)EN-5paaXTp+$(8Ga^^zcA!PeAob!HwSF0(P=77~ z{TMUxGpkp!rH4ViTVQoNc<=Qh!@#Iw_5q=6NzW9q5aBI+W zI1+H-5OwyaLA&L9-aD*$MPUTD#qqCt^EWJmfQvM6k9@L>3gO%bQSt>!{6miSIWKVp zzXEAK&&!t$6dnaVPmBMMyGiza7VMShW?b{FV54=8n3fHNl5@D-bqly$fHy4QRsr6$ zfcpiQV*$?y@RkL7lgI6#2+EMT+%?_0nG0Y0#R zO$BJOfT;q^vw&#=d}skD0Ms9ymW>~b{nzK*tAPw=ONBiW%rD`8OYDU>s z`xnqp-=^jY)D3 zTcTLjDa;1#DwRJd*Kxn=1(VqQ%p~?9zVNGl;YIxq$`N(^;y5Eme@?R58^sy`AE+#ACtgJbGPYXe~O3M zbk6m^fF+M}Y?;4+t<0>a;1a-`freWMJWmm)7YC~HCT;y5g%HPA`8$Mcz)1>dLqVGl z^|-p)$0Ry=SPm!^V-!pCJbHCl?jpVan_3-_TL+|ygetz{eRKYU$ijYaHD0sxMml*! z{#mMgfkqycw`HtHemMTcGZe>p54kXU=EDc5M1lGpBvw?6K%E21tPT;tCKk?4P@$F( znA$v17y?KF?!d`kANzM#($B}_JZZ*1wBWeBM9RBLu_xqy84j%MY%OvVSCWqKa{cm_ zp|G5REdN`ewgbiW?BK6m0=1h{iHPe2>RFM9-wV`X5s~xPM4*b?0%5*>_2hq2?kCN= zLbFcFH!?iib0#<)$(i8Bnh!lfFvFJ9L0-dH?sq1p4eCp;Suv3 z^Nh@A&fA@l_je$-955_rpG6KB)*r-E8;YOfA6RsSzC@1Z&?Ubz-CK;bRYMm}$@lzK zb3&0iE+6xbeS-AM4?l{YxHEfHqTtki1b#j|o3 zTj|FN6n9?k<2Xes2&=M^HlBw)-?K3K`#e?(KZlXq1@K4HFq&{d4hSqivq&vq7Qjw( z=WPW})WdfL9k?KulX64p`USa#Teg=aWl?GP_F7KzMLF87^Vb3GmebUWa=6=6o3CQ{ zEvNk#`1OA@QjY*SHt9l5wM*+eQop^r;uVD+JPe?A%jH$KcR;m(d+Cn$ zOHc^Qz4U{W`>CuyMPHE@OIz2`sVj0o^Ue8eFx#(SMU7VnvGmn_fkR5H4#rb@n@p3w z=7HgG)vWHriTgpA14!ZJg1I=3%W!ZAq|n4sLbG}SsyZ`|QTMBIE2+mS+I3Z~=@YRE zBK|)ftDBp7Og!4UXQf43u^b=lwK^$lsd3dmazoqBH%+Vwuxf-{nD;x+{UnNFb@h=U zP-g^+Q}q|9$2=lhrlWvg1I7(x0qo#_{5z#wm)B>EG>2i$;y982sAuvY-|zTO=wkj8 zxrYD5ZstGnM*JjVC9zNZIvy3j>eJ%a_p z)5SRShFr>4YPyle-;{swD7g%?KcDz5vp@E$@*h+w2PTwPb{IS7$ac1s`fSkKi$Sh z4H#UM>NAv!T7F^1qP4D_O?Mv2QJ#HgGrr8pyUwl}Wgcym`3fwx<}yV*mZPNW8>szb zxq{20?4p&pv6<#RM*W9wpo5R)YM~bwqxuhUbdp{h&vt>dh&?--l2UP(YKO7>6ZwsO zsOuR_N(S}I)8HC|?*;t8VfgPHvRlBR*b%3V)!)ivZL9Q|!nSc{+*LgAK;o6d1kSA~ znjl1%RV}1I=fzD)YEGeh?{Mj5?`QLEuJ4iM&W>gu6cq&KSKKwf0EjLl>IXfp`G(|B1$w% zXDx^-pb7Jr;KK=#^1sK&Jx0b-W>rcB;t@lP;M0Zr2lo9D$3nPxU=3f)aBUFI2NQQP zlm}jC787b6D_R1N>!WVTfJ6LDNuA2RcZl!)C&=}aoGNXYKh@SE&U{W1@%N< z=BuFO))~ajHB*TjY`go`YOy~)H=I4~G zy1J~+_Aq$F)D|m!iK5ug@)!TiRCF!oxNtt5-$Z#oeu_fVrGx~)TlolT{zdNA%l>l| zTeEm!&61Yaj-5_|yO1B7S6_hK64o*=zDO8|q0WmvQy5+?&sVH%win_8@=6KZ+D}pB zW0qt6`*-=eM2|ielxWa!8f2EIIchF}NEGt=u-_%+n62u^_fhn>t@5qo^YaB^%Z5@t zNvWWE`7Vn3NJ=HuVSM}`DHLevP$U=3W^C1?np6;R_7G~SQUX;k-$qfoN~sp_{vY%+ zVd-uugn9^DZVYRpHy=)^nLlif<+=!Kpb<#oo=R!uKdHSXOJw!ojipMW+3`m0U}{%F z33b!vS<wwB>wAD^2@7C?>0P`Tqu~VwK#e5yGV-SUvRKg@X6E!ZWcuFG% zQLmCpa!B6?mOORvt%e1A4j&}yLA=@2Xr}(JqUd5tB_d!%PgH|_7G3Uh zTAc2OAlv_VTjRhqwr0Eb9s9dx60P_~391!=nZX=-5HZc614b2H&2j7dA2W-hP-lz# zpkK4aGEI7xY4osH2HU#MPuJV*V?&Bx%YrZVdB%mMlmk-PKL-~Nkc|BtlybIi)7d;) z;MglK5kiu3%P4B&Mn`3n%Du@$#$D>W+p(kz<;~b`0SzdtY|8jC4KZsz(K$C@+m&h{ z;ImnW4BpC#Mh0U$+EBn_0ShC}S_07m2yF8>O$C7p=W)a`&0U~ABUygLQbNFg|IT2{ z_@^@PhUR#l)jvFIEbVdy{DH$*w4D|3IS!x3eJlb02^eoz2;gr)%!{!?pjH+^%@(M2 zJjz4?{0JZ=GxUL3Jy;-ya0GN9T?O1tB!JV8@y2*JCC@cu_G(spz6l+Q4#FCIEXQ!C z!CeKWuLXmOG!d9IVBq`-3(^$k6tE`8#>ZXcJ#unIL60O%eY8ppns86qCIP^*9zP!)&aaeS9LxFlFNci%*hCt;CDziGA!B~>j(AE3`hGUAP zd&^lbIt#+IB5^F075<<1|lp2*_+W{);&lE5q}D3(7&AbbONk5$J-Of?%CBUB3T1E|4#P z%9HY{TC@>I1q;*#pde6qL>GZ7#gi9O>~kC501M`J zVPpdLP~aBvm``!*TEJNx#(Z%?z}-2FQtuLQ3&7|zvwDMoI|!J!Gqo|kt*K0~bzLJD zpU>~5Qf6&so~!Lwcx|dNvW~Jrm2q}AXPBNd3K-@Z_?X+?^QV}w14mTw@YqZXpHFVa z;U0*=CFxKBcjVy@;D1`ci98(ai56`tP|ZLBQz`+>0)WdNVBV?%!7{M&NI+o(A&H#@ zBAlln3BU&cUj@8qYFwP44ZkC>W*E3vCeLh0u{%JI2ZT` zOdVkOlvswt@%Ec}LoXR!(9o{!d1@?ul#Wm4IVl{a<8hyIYzF4|;~X9PPc^PSSFu9k zG${a0g9|@X;&`0TV=ehD=EUlIpfamRC@fGNzymQxdkOd!uP5L(02R0Ev0taLP_;4Ur0B?Al zZ~^>+Vpt`hTA-G46gZ_XSVa;zjTHyfQz1LOfa2VgBj7Zi>^T9r6`|iJP&QWjEdstW zlI4u_mI0<;YPys+IyP0}?K8XwTLNmI%{`Tc8FD6gE==9t9Xq-vr<=gaJJT;0b_NzO{hA0gN%sP;qKS;9l`|Kmn=>_%2{h z15|)OofRl(x@*w*c1mrh^8;DBV)i;(-FUQ};$)ja6QO*KfUkP!Qy1~?DIv?TTLHM( z5)^>%<(BWJ_+D)x%NAs7EMO}3P6%CV0hZml7{-{l( zIw~XLu17-Ibq%V0i&~dymR!;Y=-^LkBvOSB4=`F$->;aNboD5#lM*CV^D`!QQqHQR zW;ko=s>DgL_E&{gq~ev6>p-4wXt zilArRl=Z3$;d+Ydp?J}6-IXdOnpDFOn9yDElKO>HubzrO1@%zcNzW_LxE_k1s#)Ms zTG|7Z7&^+B-$SWrE42wD&z?$S>6&MaWwTb#`RJLNYY&R-tMDxlDyqu+pbiQp1V>ce!Z?Z zp-xB8#X^y7(M$j8O8xsPO{7s}>5sljIj=YVpn8pMaxtoCS9;P{iIFaAXg$*#U5tyv%{8s_dq5Bn+QYb~t|n!}tX zcHpisG>c>R|CL_15YR5d7^Ae!Od2p)@pi8{Gs-644a`-MeU!#W=uBEPSc$QHMt=`hLL3u-XBtTy zJKj>!m%&OopGXLnDf^wL=Hi%#YbDtZ8Gce?J!VpsAxebwD3dx3QAP*e%MU?`6GNI> zRvIcVqab}e*h~p{I(VmFLN5M@9_mPzFq1%+N~Ypjv3*x5SuYR{SvN(<;kI)qZgVM?f1laV6f-pGtGVGZ67 zt!-$=Fz83i6%Az_^!7OOt}XaK ztR==YYmDE^@s&qqz?h&pY9&X-o-EA;SS1z$0j3kn*@!~1*R}G zD5X?&)e&%4yYV>mSd5Rk>zbFRW#e&@asBR5nmjPRm8t~LJ{d|SyNjQ7aQ1OMGd^6k z{&e?%xtYTJjL4U z;ob%ly8g~l3Y!++Qu?H$jT4mel6xARm;gDua17O+s0@`t#?aP@N}B4^2OR}ZQtC$S z{-_gL??#zlpd&V`Kd>&I3jb$Im{}iNGveP(?*HuRI~`?D!op)9V*fHpsUkfcWxO{@ z@m582N@gSJ?MpNiCKUkKJn+GiUdFOr<|apLOHlYUWwPtww>mt-3s$oz+>iUyAJdd2 z(myWLak^5?vkrQRnYD7qSoLt6jROl)^**$2y0X({&udGOd3nFUYsd`6OH~PF%9^3n zRM|h((a{;oE!SbMkVzV2QhUzRzq``TOy!u*$7ebm`#ws6Q(Q5B3EL4IQTKz>1|`t7 z3#zd%ft4kT=VaQf`scPWkZMO2gYQ+N`&7P|? zP_@CwmAOiFl`lSA=P4v-H#m9+xV39<8d6N zeYnp;b4>8Cx+z?XFY;DYwi*a{xle){aKrh zO753!P%l1j49AZ!X~N@VMZK@hG1I;UN?7Ee!YJiMl;HmrrH+|gN%>P6nNOEUNvgl1 zI-7Mcp%r*Z8o{SoyX6RA9awzh zEEW6KK*KP{C~Unb`ZLM}sk4)p+1JUmQ1LD8i9X{qKsa@sP%)y^T}#6+Chd<(cm)lT zuzHo-i6X(N!aGz1dHl!==(r3&yBsLBb{S7;$vyh%Mb;Vf*23t zP~=*Xguh54q&xX7!U)}sNiuPf(kSL2DxG5J&+JMf|LDdtjc4ru8=J*|AkIecpV;sC zkK=@HxP_%els*m`%)hQTb(H%h-kY2kD=v|-Kw6k1R*=8zYGn=>`+zxMB_Nta`amg* zmB^55v4wU1lM~09fI^+G0s_O$=^90!bK<-u!yVeWSgBF5I}h%NEtny;6OR#}#uEvx z!(+tu5)oaxlFt&-%5*DWC7-fJFU-h-V=?-*8ZoZ@}qE>m{C(9xx(N`kc6 z0XNK)q2Z&dS`+56N{d*lqKn14{#-}DEyH~9x(wwmQ);MH1+lCy!bV|ss=QnYlm?Wc zX3Ld2?(3>p6Sk7reP9)HqixHv<9J<~&MsG!j2izU8~ESJazFVQxQk_0Gsb@{ci!*i%bgagU=V&< zt(32Rs9_K{IRJydzk#aB9gfwrtwKDZ9Ogc`I(;Vlc8-(DGauT$#T+M4L&deAMtO5Q&y zx8wHT)8WM;ZphR!b+P^{Y+IhDB(RZW#orpf;*3@ ziWw=@LeRT@5JL%=J63_an~;;6Vvc!)Ew*M4xqL3j`&Kv=RMV- zf17@Z53kY^VJQX}!lf7@jV+jrhz#T?-$6QN-Rt`@oXd|a9_H9w7e*Vt#D}_m3%DuL zo{wNO^!8IaJhrUV4SWoN01Hg+1al%6BskX+j%Q|uDjY_yrrRJ=F%q1t5ZjgV>N|R3s zEk4nzjY@1x4jf#1=lPDvdXLu& zog6mak?BuOG2d8CltatkHA>>x9)Y%-tla#>Vj5z*~pf4rpSN@y69_XPbOXDHixPxt$q81}jxU2%h zbU??k$1bBAIc4S~&PWXLPh#g_@h6N6e^dIZ8jU){!k@uM?v;Ye`0$f!P&f5s6~?Lp zYKb2Ifa?tERzPvMrGUrzTHr*$%=(CK*}4Qu8;=s^iRRK_Yz~Y{bh*&OkU-oxsqp!n z=YO&dS?Y^=aV0LH1oSXAA;9ied>u6UXGd(YRUJX>vvVWLJ9qJ6<+#M>4COLp&)kUl z6|%e;65AeuUyRXyiNRGo(GR;EesWcsih>YG1h3fQAel0L-J>OEL8pO2-J>~~` zlK)WJNVWIVqCb>!DFOQ-01iR`nAK|b`49$`@HfpqOX3Ts>-+E-#6MrL&${@;i08gm zgVcoHD+7K$y9cJfST0% zm2pl%Xw*VENw=I5diZ+n(OEph@(pGV_J*cc+#>>e_a*RvQd!k#cZLy+5n*h1xElu+Y26p~y#^AofEzmlS_gi=s>era$co9c?z$^Uc_f z0F2Iar5S_e?qb_nE{d+AZE>qc=roH=?`?Rpc9U4Lpcm_2tY&{IN?)K!Ndh%WAA_H% z$92w0FLVa?Mr_f?@(4YAL`UsUD$_LS$ep>HXL^Q~Rk^&=vH}@=YjY@eM+IK|PgTRH zOhiIJmAq1raB!V$2bO+cMM%BhIj%X!-Ll{&G8_~SKj~=VqFUI_MJ#nTU=!eUSBv`W68G%HKW#2Ot^PbPK=@01sRV7~aGlY3pY!0N3TU zhaA&oRtw=b?#lx(t50nyJ`+E{Vb3Yf$?1o{pDCG-*(BpE3__uBhXKt-wjK!y(c^7H z7tZk$(Omkh#$J=*Y74&D)P3z)W7&=2KaR8TlTW{&g8KH>CaQZH_L)02QP0!ZFupyY zqa}~EfvWDCbaddcwtT6?O-%SbFnyw2MlkEUqvB4-wvE*Hj1s5%G|#%^+Gt&J0mGJD z4nMPDWpTAjgHHvPr%h*-ij`jPK*jq&$grE-RbkByCqrg+6e=NZ3Gn3wwwF-0ogmdY z#oz&7E=6xbaGR`S$7$_3rMk3iJ>{HJyj_Ap!!JAVP`rj!>b#1>w-S8l!o-n9 zMBPdu$XYjed|8p?$NoFAEidXo?pefh`GC9*XSSFF{*;K(PO&xj}oo=YYoO*fxC-JMdC0Q8H4$ZhmGItYA_VQ!#~2XKQ!au8Gis^nN9o9QrIpldANk(E zI#^yxb#5p#B>PQtP@Uv~PHc6q0H!;{M zt+ccWcbYH$hda<1D`%O#YkTb92^mOKv>YQ;cJ4EEQtaD;_HVvAC-R2WzaSx0J2+k6$xU+U5sZ zmkXKlz%OdSwS*wm3&`NmYY9sw{dQV&TdArWxDqEuSOvqT<^4R=YVx6k4hNlG5<>!* zIKyHa>5XWGe&fgIW&0*6tmk>f-0_K(0(3o z<$)qGFxrYaXuPBzgf zI>Kopu{`c9WPj)XB$nuTYZ){d-DeJsTuf!}DLQGR4|TYQRYjfGH1Qr(6@z_f)jeqV z?X&69J>{w6#HFHT1K{Xu4xPKN)ONf%y8v})HhDi#no1kWQU3>uS9JB4AvVJjQ)<3r z2X8v(sjvBNFLro2Eb}pmF=-+WnAllzY?X4f?SZmgs;%gt0ePsx)q=hEvu z<()KWBRzcxGXejNR4-rYD#6F@%6w&ns)T|@rG$QpdxpAnULuY-nX!vO?SXHYJFLWB zu^hmsCKkl3m2q1I-&kWIh+npO1dvhB5jj7R*xPg|y?f64K*`GUz>VIP?emEOIy$x|nffBAV=98q#yOS>d^c zG@ivWI40reKQDbcV=(Q%k>FcvM`=cgwcSp(O=R7mE`fof%_<4W1#gDx`YciKxdRXL zn#QV$_Q+X6^hOD*)ukZ5*sUS{>bXVZ-_I=?f5%iy{0tsHl=jH7vsRB7UwgPuLOif@ z{T<9bUZIEk#+KyRw99+X^b?nyCa4dDz?;aqK z?T2x1e?0Aas{||cO0m|#Dcw$1b}qc9Wb16QRFejax&vcczQ4UIB#0E*NR$*dgb2|Ek#_IwhD3RKd3x{t=|QxJ=p4O=W`(Rn)i`^?-;^S-}-cJG-vXXebAGiT0pH%ByXtKVg4fYtBHX01uZ&*4zXMf~^N z6h1RnLI1sHhUYT@Q|UPHYIlDeAF-tl3@KUkMjT}DKaRSFgXrhLkl*yZDLCFv zaZY|v&F*)Er{{}`KZEP=WI$kgix?!&uL0h+f|I!D59;5zP^~=x589M#0_~Y{u(-T35 zF}zOq#ML5MLG^9xs%yo_`;*1%r7u$J4+I=8#V;;)pi6q$P^%h%>@%rD(ZTW(lovCp zVG}+M&RsFJC0-!$%ieagC?EpLZmQb{heR^E6m1 z`ZK{y_C>(qZ7+O5sJGL4?zKV4m+706ia`YBFIT&}bty$rriH7=r^BjD54vB_%0K)h? z1IE!{1hI^uACm>je1ZQ)1Hv;R?{PT(Ge6JL-ydfJhSER9!yO%N3gqO;W_n*d{I8~k zHFS~v`x!jBs=0@WrZoBj&-j71gJc_IiJW~Ah5+0HvX%EqIGT(A+yJ5hl?5ODLWEAvV+;#WEYDnyZ-a7ln(5Kjt4uyL?+H9oL z&a#(Or8hl?$6Vt4U6{$k4>Py1iKWOdC*+q7<@2Mp>j@r1&-vi{cw&HSAi&8}k7>6W^YH)49qDJzMQR529 zKlIY~PBgx-{7CvZM(yq)U)M>sJE##wWS!2wE~=BJwN~GG%g(y8p7#Ymol-LS*(NmGu?IrX zX<^Dy>}eS>oXYviVSdi-AoT?1!{UwWaELf2zOTR4u(*wFr-^Wr{ux2Dd}U9!zgoiD zCya59sa0o)gK%v=52X}eIkZ{ndm_`52a#z>A%uBwP#~R(TEqz+!SDXD8b%>P%!dmt znBQF(>Wnpi5%yOrttzKd;}UWWy-WBBnqERa=yD0+hxik{w*JJ0+LV-gIJ|Ca>jh|g zwF-p_)&i4Uuil^a<)*j6U8lm~uDAWrh1$J7z$I{hf98tDmL9fMgO|7bvb}uu6q=p= zJ*7WdkjW_5b=a7UO2lJl$+XBQx2UoRIx2ksaD+opVl(_i-eh3jL=Y?@I^}^}m(jV! zpRlNEDL;9VE_UXAu2MIlv%p;pKup=4*5E+`My>DH;E@^d1`Td3;A^-{S%X_=z*9B2 z9$+yP838zR?3gQS>t{m>C@lw@35zQ#C3oC!V`?i7() zKQZ^A=tQ^4P7&u4a!-y3&V48z!=j5(h)T#6ACViFN5T_w(WcV!Z`CI|EAc;<m8i zyxbY&Cjg`a<&GSDu5MzRtvu{VbYhdq&I$5lXGETh2$r|1)%@i}I{l<3SYMbK;HADU zBlpz#fBQ`c=EH8$NX6+HpROY46oMc~5#-oa9Ty8%9!tjjjtTdmFcM^;V>*%{0TWe zz%mw}*+glM`>FP=Ah*(2pH@%P!=v2PAq_M%bNo#l)G%vld^pGzGAvi-UHl2vF%Qv= ztd-??(s z=zdkXtlq_h?L2r+T>me#v2}y}|7;y(8e1!Y%x^T7#xsqr_KZ@h@njhu3x_dmML(*^ zwe45$<8Ix*jKZtS_3dK>w44!Dm(BJ+_VTy}jGl66vw*A$Rj(m;)3u=GHE=U=XW7P_ z86QGs<>ddLIcHg8?n9)Fxhs(xb2}q7nazsSWY#ZIlUYopCbNJ@O=b>}n#`U?Xfit% zp~-A@1dXgIm-ZSFfz0DF@1EwvkJ1=)a8u{K$CobV!6UObYszlAW*Yl$5jOTehuhe{ z9sd8bpBk>Qp9Ewf+lk>C`<=q+KrOj+v6`GXXZD|L+25s~wd4f*#=E#k*M!ro+7PK; zKuyExer*VU^G+UT4WsHIi2GeYTf=B_h}_IIGz?}v41I%bOJrpzbqRSlYsDXo4QR7f~gu~sHBKFNny2`;e8rWwgwG0FD?G+;SN@pT~4nfWlFWD6p$fB%5f$!+Ubkz|>&Q5jHMc#2xG|7%78CjBmISG{((HTnSol<|5u) zlj0-gCa$4CI5d_g#nZDIVq4JbNO^`s`K6+)p-XA9EN^lczC?gimr!j*KIpJ>u>g-Q zCW*^`KvEQ^C5?6g9f^`pO0z4` zw7RmVbS;SftSh&6Em9f$`D973RU>k%C;JpI2W80V&U^~1CkINV>eR2ET+~g1Wx()H zh=&&pI|oB)8DauERn){|wm7`TSP$aqQ7KbAA75$W`I*GU)36dfsV6sgxeJm*sYt~p z9&f52EuVFmJy$TjdM=fWkv}-hn@EJ`FfHO9N+-PeTsQ)WDhK z(};sJG;jv_{KmoQ8aSPN8gnpF0~1xBCbEyN`0dh~&aFW4xXyLY(mCH!^m|izh-+Pc zupE{pv8jw6HHE~6`DI9~+B9-*28sPqjzXKsrQ4K(mS#wdm(^-$Qbp^cj;EKgX-~u> zNDSLxR)GNorEt8=c~!&UQkrDDTZ#aWYgvg!T#SQD(~)L!TbJ*M&%T?vwpy{dTu|rG zCteh9OuTAtAqVOls!tKHd5SuwrCeR-@M)5O?I)`#tvIYs6!1yB3D}y$e@qbYlnH8` z-#J`8PNR=g$Fvpn<23s5YDzmnKUSk3tNv^+55pscMQB(@xtL_}p!pr;87`lSfY0_> zva3$jI?28T{5mp_&|b&><~7`-7fg|_aJ13Op^CjzqsmWFFH3+1PcFb@;}nS1vKOdahN;uI3|)(mv9sLHbs5E*Bj+Us%(%?NN88-}}qO3rYD=m%10+`bNj& zk}f-kft1)^4y^yx1(jGg)6O3I{D;+$;)AeiIm0&auo9Ym2}5<^o_^F!M*Go1j(d{* z0NGD^ou4WUkc(Hg6yQ@v5AcgAEn&260~xI5?E*u*>V2RPmgc9RCGNQ3umHslklo!( zv0Mcff^epIN+i+l-?6lQfLunB3eevJPhBcQC*|dJAw$Z>lo{;Gew&xVIM-918GSPXStb(j5-w)j*G) zwB-+g)q99oa}OFbOuj1}(o?(P@>`d)`kZVPpbjIz)<0bZ_N%UReT2N*!CM1^y3+iS z=nL9)5wZQdP_t10mv@1A{uz{4t!iqek12n#FS34jOF- zavv*S(^vRzqBmn@(*OI#ME$0O8ff`AIoh@2cT=uI{D`bgM+Ow3ALHa=g*tsP&n9SC>sP%Z+D^DRORCxro8!s0r^}b-1LW~s(;cK6z5L$?nJslb^`{`?ZHPO@Y zvUjKQ-%O}jfBbTd;7C~-@fEpqjo^mJvQ*VH;>TBkni+1lOWUQG*oSM#UBuc(qBcFL z+E3_-J_0$;i<8H=IshemR~F?#eiP8se{LfrKtyg z67cg@>g`D!KGjmdH(RPNCUbaS3jv>Qp?;sj;q}c0yuZ0xU@C_fHWToMW@@nn4#zhY z@S>(_xoL8wPQUrJNgbF7f7EsU>zo{3b)*H;!QsT;1jYQ{C}@WK&0$<40nTnjRc4}# z9M@0-8&Z{79BiP04XDa&4%XMe`c!2O2V*oahN{fvV6+BCQnBDQx&zyW{%&wvVeP6R$Fc1aM4NvE>}riw3WlRf&}~`NWHmDKB3c(JZ(~w zcF6T~QsF`LcBgD|d2|Yl@@wO5s$`_7UFe?NPjH=i%J2Bpx~F;NX*j$Lso;59w@VJr zv+*_*qZRi&DwP_yLkU94|LupRqgpA$ zcqyC4YNgy=5NimU~8SI?Sb#Rk{~~@Y{u!={UsX*=l~O6`pghLqD^keb1J$I; zDe@qxT1j=Eo)5T##uA*=M991A$$FQfFSqV&7D;^ruQtS((@K;@dqj| z{n+xn@P8c;g?IKAJMgqeDe;;>kgf0sDCvki%rzIVvYXDZ8&p0`9wk-sQa7c^yCf-j z4|O~)AJg3>{}Xau*A08Pu(AsO{BN3ohYei*-l-MdK03Z4)ZpKuv=7131y8zrLatb9 z6xaF^+L1p_jfHP;o>6wqBrG^W)lSOZ^4;B9NpqTn?Yp#+`heP!yT4Y_HvYV%TR?z5 z0$W3D9fVz3t&py}>F7zhw^Z9el}^ct(q=b0d`f;HU8}BcJuQ#XN!_Zc70$}*b<)`? z>dkX<51lliGAS42pHi8Eq+FCwOTRm+E|=umI_ZxfwZUJUCT|7x!ex1zPU;e!$nKhKmAdDlJlEwE)kBHQ=6HMyL1qVN{#nm-pzT>OQK|Lr!TBPd}bI5?l;Il+N!1n0d(4pTIOR}LbF9bQ@vG4tsQCrHRf&{iX8p%IMH2qJ6* z|IDKv&*f6(6&|Z?-^m4hFr8kU=Y!*q;wVJ;UTupGmK0`hG|>;GOo>247R@#wgJ$vn zM-#D;lItkOa$hoYX|GY#`~N83ZJ+|7IVhYpif_gKAHG&?piSv=6MgS@M$*5K7f8?C zY3U1jiN5k%Bh`M1m9EzY)%%saOeeK-qeHLdd(vZ9TKPs!mvXtP)8As%KuU5}zrK^( z>ZB%4YO4?OV4d{JQN8%Dyi+HwFQ88RBsa(NL;2J@pXF9Sc2H}6T*{F z?43Mqq(2KOZ}hKU1)u4OU;O{X7(3 zUEYt+0&^V=523>bu(8Qg@zF_P&(-rq6o~GPMJ->9f2??_PA{%B(dk<~$za$E3`<`g zsk?m?NHXl9TCjwIMtbkQI;Nyz(n){bQ%@L`MmnkUUA0Il1&z$-j*7EK`E>fDyN&8b zf5ih|H#A=Rc2la5e_7>+-n7%G`UfaObkf!v>hf}2s-f4(yS$RF@3_@Sjun*7lKbE4 zpb84i%bUN|J3)fK%W9oU{G;Jt>axn5(R`OQMk!xa<*<}|PCZvu$)l4NUQn-8;|$+B zrg?O2Zg6b!T5lVfXH28>GQ&u{n(hjT3 z6a`J>??dWWrgYIsqf^ulW+h4|;Z1-|QA(;bGg<9hSMk@CY`ZAS7#2FnIjI_eAXv{O zdyHIcUy^C-I;E)8zY1Ngr}&ig%0{p=SrD9ACHpARD)(CoO z1h;pK9D;2G+bYxiXr*My#n}kn91uC!YXrkJf~yBKmSQT?muRK2K4g!kk)vajB&qvu zwPbz8PbVE!)w&J1ehuEEZfM8_RAsmNwGkK4y`5^?##~>A?@+Ha;gSm6u2yWO)J0FW zRUO@2>7|qAZlUKbl=u40>oc3?ZE3|i^+hWs8U|{kTC)vLJog55(eFI*qwCb?ZF%DT z|5WR@=ZQP4RgZPxi62Z-eLL~Q$E{ZTbmoa0SJBNb%7v2Yv01Xow@MUsFM=TbPb-B2 zE}uqcyF&WEd(yM6N)vtkC4w6jjnANy>=&yOyDNCH&25?bwFhVC=n}P6FV4<{#p;IM zoSmRWRIsnISb8v*HuhC4(!K@iyndXkKBT_w&$+5NUkw?kbk#{W=BjH3DFg*_$9$7g zQF>{h0Yj8#u1)RX@?=}LpI3@*3{m2BNz`d5ELs!w@lcpOm(zAY8wOw3w!EK@It@e4 z_FLu;SB6M=CHiByGG6NBLf?if)ulX})hZ*Dp*q)rQckiRK9oFCxvyJH%SI_?X=p*R zj8e)4_*i+pjbM5DwUidDipL|y_j^LClX3F*K4g#UF?SX(pYvKYWel3nO zk2@mbLP|~Lay9q?C8a8Y@c5Y#u}S#UwyJUdt%jF+1kTV%{56sioWws@u2@7L0!{j0 zWK|~zFSxF?C?Z;GDRF^eBO6jmkHSWZZ`~pR{__M{K05Qtis$o{J?v3JX;Yb7QE^s?ozDM zu==;}h|8oCaTWyeySLi&YpPs?OO0@Gx6V04@sY)(7~G-I67P#Wz(_s1!hTzh^4-U9Gv(**1VKRRA%qi^$>QOP(((YxWJSDez_ zJ3g-|S5#t^7v{L*HR>hS{(!r8&Ea@>QS7YlotNIlDe11EUvefFTbN!?P*zE2>}bwJ zrIfVVjt)#zzQ+z}uT{LJ91wOP)Z%VYi;ga~IDV@ZSBk?tPP_ChZ|QCm0~qi zMyn)V3aeo?3WPd&s3HEt5upl*7y-oerrb`pi{v-qUbSf^E(b>j>9w^)Y6#qRS|WQv zjjbQ=P$pi5DOwH@h{S6ki6OxvdL*LxW`~f4T9!L`mYN#aBTGS)r2&2Z<=C`)vf@*~ zgl|3uvUc~uC6tSeUaEDn(o532zGc*Bs#0D*?j6(C?7QHDFN=FeA+!H)D?uQT#5=z( z_z=$!ze7Ue8RCoh*jh#Q)^EYbCH^rDAKQZQ(Gwqrs`xPDqdNa6g^!l}BR4)q@{b2^ z7%k-=srWdA-387|-!Q&oIdi;975*q*Og{k8Vvj*ndLB2E6>!;b`e85Py>1L@R3GIQ*_xHbxfULohPx z)&I{(dmsr$WPAul{O}#9=?6V$ zv}v{?OVcd$Znjchy4{3I%u!V5$mg(BFk)Pb@i37qJ)5Ikl_ut;!*i8QdM9Mqf1Xl7 zKl&-7Rr3^CKOYG_o2Ph7gCA4A`S3LhKBoTjmAd)|XOGdr`AU=6gocqg25;g?qIVb= zm@C~4`>_*6Xmc&J4?@xHj!QRcd)sim*h{<+Dh!0U4+=y2WDxM91?|lVP55g;MsBJ{ zWNn3O@*Z$c^e&x%Xxmtj3pg~Bl*-1L19%ROI~wpV-|id!_~G)I+yZEi9`1dD%`Bu; z(_0@g@>!r%cY@jDMtDpM9~g{_kE!bd<+2p{K$BnELq=^DDssNt_c>ZJUdExXvz5}i z`t;XAMRl6a!G5ycLyAiraeGr1*~rwMp?HLLtRVk20k=>B+BBEKoP66bEyuWs_nO z6j4ZVCsMZc9}R({N%3A!Aw>yskYYXj{USws6o{5ViVf~Cy0}cKuE4l)DV}e{rRZ}P zmeMvMgkv;#F#3&n#v1(&j*2*zF>~hltwY*5l4zVGNvs;zGmyMDMY>C~v52=^@hSHi zy1`GapsrtraSDD)1=oJwLL1;8HBcpJj~;mWK8@~O!ySJ+1Vv|YI&v*YaP;vt-B}KE zd=U>xy8oBC?@W(@=R=qz&BrV#}GF2*xh7A?01W%zNE zqShdXo!JPQYXoC8f`e@YL70so@h0u#1f3$X%J8a_$RP+pU@1x?_`8!f04jcy%B%%J zp=<C9{UWdM->T?y2d-DjNQXmc>3YS#XhEd#KQCEvx6(ZzmW&haQ!CJ|b|Slm zT6QM^K@&UP(8hK@u29qlR7&M+1idtZduVDXPz#OV-F1y1@e1wRpv-3DxGl>+xW1S< z#GNF&KeKp&5x1_s!-56|Iu~fxMrEpAcZuOL8Kq3I zD;F8x1`}mHhu5h7z+|@7@(vRw+=7mvPMefc`Z*V|YjYDU*MmLB=tpEg75zqh?D`O1 zii&K;76v+g`bGZ_kW5+5g6fS>Lj{n)Db>pz`kmh*Gw}m{vq0mpVAOQ|?OUqU60@ z@hWr|8pb7zW5E!>GtkYX{!sy?_2XIu(&m8$i_*O9%2<8gGmJcTD8K9fJk4m>4kfx+ zi_;n(&UH9pM7t zE<$5)P?F1Gx*Y}83vh2(3SoSyWkp7*5Q%ZL2)%1LS=;B=SPZD%okZ2d9T({ww_TX9 z4478Kn^VMgDNXg`PBL1yOYzaS#mC`YO7X&Z&^JdVCy>C6L%{;^yTq!I69y(56HJiX{e9be2jkIqjb=>J3#uS^dHk0MfDFT zS?yw))-rPH{C~M4?LMW7J_3pTvrj3jH{c`R zer2Q{`$lQXer2M&(-CAJmnN>fc=!Peay_huLTOa;fMS$h9-*iMO08lG4r`e;tjf!@ z3KZFt^6R(oU*b zPi=Te*{qY)DAk&(!hULSMEUAo_eo{EagCa_o}r{DZM3W$aY$PMv+}B>_9&;4E))A?tT}^ zdPeCl?LI;S&MIl4<}EDOlqJUQU_J_yMm3^pwK4h+$sfhHHRCpWD4My)w|k(E(vRZT zD`;1d?&l#m!?uM6om2M44rpP7p>-@dQoZU@o58Blv$f0yA{aUCc8^#Xz4}8Q`eAGAiIYA5Kka>RmF`;Tv9ri9E$LQkAxLX577$F6SNvUw<9DVdq`d$!lwwIhjK^4pn7IbN8P?e zr!T>*JoMs4tGbDFeMo$@kh# zovtX8rP*uf#T6x7TE2@aTvfa)pZ}BLt`x5IVqA~5jFbDY)oA=q6ZqX;cn{U)n4tT> zEk!?)xL^`175tSDM}+EfL&^z1f>2OZ!4dK2!kej+L}AyIg;L}ix^)dRH$H3V=QUWU zuSsOQuC#Snl!VdfC-6ay^0Fe*A4&A*b)Yp#qSM!v#*%e4dEY>LU$&aY-cSlx^>gJ_ z(hcS(JpkhVT3v|ylS`(!U!X_KP*W}*yam3F&fb7YEx(%H+)z9`${|OTPRs__=IhW` z{#->qH?jOOm!fVeRSGR%#gTAR%xg;%cV2O8SJTX!N*zOeM3}9LPjo^#%WdTkX~i+0q$$fZ*;Ia|BrSeC+fP%qZVFs^RtuAIA&6`VV4#KTr4Bw^`tOWQ|%PQ-ezpu%^Q z1Fm(KL&EzQu!ZD% zN^;v3im)w@^KsjP*C;f@QXUOd7?#BRoZY*7vucdzWi^b3AjO6gF1-GhNte;NmB70? zOCWk&_yOs2OB+jV?<*cSC1AhF-^sR@_I+8=+zH7I)%2SJ%51 zCy_*lC3NLJ)?ITd;cZ89xTt3OnJ)76~= zJhXVX2vO9Dd9|YAon~%Bm#szF_!M$eg!5<1Gizp)v7x2LHO(f5rMJQ^#)WkGkrM7~ zkMwvwrRL}5a$QKq$4VvpNdmgQfZ9G*j@H=#6~-__HuIN&$6$Q%jL@8uGn+FCWi|Nm z9tnNh^BMd4o#<)5c_AeDzLg9Ayz{G3}lowCJdC8 zPxR!8a-?7tMBp%~IsOKA<1V5-PnB|p2e~=2+iXE;Cn)pIrcY0m;QR#yI?igYUP!ep z$_SslL-~DH{3eIQkOuima;Z%Nxwy6fPl=|`6^k;^<(Qs#iu|?i==d+V;&1`Ac&5yc zxC4b9To*@~qvLbqBByhB1Q~{S?thEj6rCn7_VJv>u0M-{o-1dhaWm=1bEU3yWfn!H zV<~LG40@8T^pFB)P@@+Jzds#&Bow2^f$6*#X*vrH;CfaAn75T~yik0kxasr_Sd!fi z@_nham%b%Z+)J1%HiNFeRLaEuSwm>UCTj$Wha2IsKS)!EvP+>5H6eiEh{8oR8v(+Q z8GZwpQW+T`>RQP_M2+2-)hsv%c(@a#MYA|DL8J#$iwB7fI!+j)5e@^P`NXlGRvP;X zHuTmsTK!7tSg0_HB3eJzgm~B}O_i ziPR6svG63d+rLUUR)3e$hL1{s)L;Q!`v}8XehoGJq!e-8F)617dzVC`J}E=X=Mz@o zSXBHK{(^ZbmtXu`<4OGE>DFIp>f_e2&q@{PhHxY1)1%MI{Mbr?LKQlF&{TnErK!Sp=vMk5)OQe~Fk*y)r*{U}17Lby ze3(Ue#8`w!Yt9DG&T6nsL5OX^QZtxWc?e&y49uV{AZYW9WiFY$p@zC?3g!C>HuFuO za$lk6k5HIjdP5k_J>#kSS7nT2wehIA>Y|T{jHCBol}P_5Utf z;NwWL|Eb*4S9~3zKKZGn>!f;%c3);2u*YZt#Y${_z=si;^D*#WszKp}|1trS4jE?S ziCFIp7)Fit>~|^pIqlH1O-?DpvlC*UZEiR%{|=@2+(2@FMwZ-cwO=??<5@-Qesr2p zAF?wibccx^H7q-2%6z=V%{--@L+FehE9D%JBeu_c(&b^kQmY}VF%L88a&I4k+4lQg zYq+U#d08P{VAsdcCbUZ*a{_Kdv>KWXfCBK_v_>O>E01BCz(C>7OrbP;7U)oJFbavz z?veV%o>h_@m*xq~cyJ}pV5*Uy9e1qCgLo6iA?-nwzW|HS9isXLSUE}Rr;aMX^5}9u zK<`Aa+?YFVzZug%XWL4dPTL&WB*%q%B!ki;nQ#3mvLLHevndaP5kycjsD!Y`0)*=D zqu?#B^N#S`koW0*QHj&C=x6k&tp!;%$EQ+elF|L?T|w47IBq05gz6|?U+7GB!-Q#( zys%Zs!<GSWsXuK1v=h&I2sBzS}pL)}Yz0~Di3Im%_xZvJ73paeK z`mYPKNYdn4>RmThMOVk68*15|Ppd%e&Z3dmzs?)^V55)L!ktiSD9`n4Tq*Z=62MLY zZ0@8sa%Vw0sd6tht}v^ki+$e%H7J`C@X&;?t=~|iSWb!lI|98S#s|1y0z<@qyYoa0 zM;t|V-L7ahB@A_@L`wYW?bDQTldT#5Nw`Cn64N5vmsPiAT7JhzT7T_7tOJQwAHIAjLR= z;W+v{1d?dC2Z;PWC#Muwf!$YN<1_uDleb!{7*tnk(1&7+vrdj)ufa{m^bw6M#r&zT z7xSq2vfVG518Y-e4EW?j89#Kd*-QZpj@R$h-is}iF888uUM$_cTHF5`tAV5FyEiK; zb!knWK5VHyJIAX&tfiXb!;NcZK?ay#Q73$J*%a zcOT=#)QGzIvr|&1u~exH>nFX7Raci`cXcK5wZ|03DYyaIr!W!^Oo0&aUZf?x-BC3u zfMJK(+EMhn9Bd8VYs(kN%2u>A1moGqerPeqeztP+d!aH~44#Zbt$Nm1y9BaHI{S*L zoS#Cy=zV!ssn(yZg%4UiCZQj9KFrB`4JJ!c+7?d#q;jUQ-_QYP%&#)%U{IviA&2C*wTuOclWlOj2% zbuc6|Lmt)^M`>my7VR8XFVhgC;NyGHi%JmAA7;v1nWY3YubVw~A#ZP79p;OkfzQ=r zibxb=x=DRgnYGjTj&2G&@DJ?W;F>WB-5`r(bM_>DcBTGRnV0lRQD;_#?bZ)aBdA{! z=0nG;F=wfiO4qBg!Maq6sm|(1ZzI*j>TH;<{HHLq*Rm*EojqLYZ`y+qyz~9CB%iEt zMHsg;;vGU%$!}CSn6+?jq~?%l1T79`mV#w=16iz*C)y3WuFk5-jC$!r2&p0Lnr;}) zsKXjcN_)CrhqaNOwxiHcR?S|%Tn?EY4yDndtciUU0d)zb8=-8Yee1t?+>Scxv@rGp zn|^E3hzNE^GS;A0k!;OxT{d!ltqs8bC~!Rgf#7&6Lb)d#lxeDpn|O)r63?S?g1hec zjsOyW5zlT}wm@YgHIkV}v07-Y=^kkMJJum7bbFYW(1r*tLujJRA}TknffUihId0tIWDfAJ40u6?R(Ew~ahX70t{;ABm4> zGm8qCTs3=Lo@#7U2VXd|nBK^y4k?w?R5N-?*PzNa3c(|;Kx;%l>%t^-3ZkO**m7Uj zP^im9^gM&BYUh<)c}=&faeo7py6o4#hA}uU#R~ z^q07&AY1E4$H80v!jPl%F@}Y@++A+-BvbH#F>(O0`fRKB)0H{Y8^%CAVIo61^HqJ#;;V%PXob53I?;4gv(;a8sfmZD_z6fN5X_T5I z|He9d3=YU)tZl;G5=E)MF`xQN{E-g&PWxxryAHRjJk$*H=`jhhXpi1GNk#YCI?7g) z@31J5VdtSvnCWWhPvsl4IUcRc{;wS8ndwGjR<`)?(iu4>@*LxzfoHM%5l26&kxvtL z+x;SR2aiTUT3Oy~8xy%UWk!jWq8d%vEe{7Up3Qr)6)TcaKr`keRrI5nX6&pKpit4~ ztbue>rryn24ap~r);4FI_}%^aTd>NKS7i!o!QS~VstDa`jwT)70W(5BPpd1qU?m`OoB{oGWy)@-Cs3Mo&s+OR?uhxz6ZCA=ma z7zanfeiy@|R2kI_`;G2=y_b5W4QsC})jSX_$>Y~P11{GSOKl#-5EY(6_o}eM^lu<@ zQpdH0C+t4YD@S(X-ttSeX=yujCdZ4?-gYd(aHIHtaWGNao_R}lMQM0@)>*2*gYLFx z1}Rr*`q-WglkQBR{vFtJX~zs|(~)hJT23a%PVBN&U?x52#I8zB7SR6AthBV)ogQ{( zm31Gfa2LiTZ+B|fh26lb4Z&TRzf{yfow`CXhR&x2U0H;*%8hOVc6ThCgCY2Om~F30 ze%+vK)!oR{jlGjD$5XBDtc>UN$q~6KrV5+#PJ4zz)LREn8r_{4D~&YdNJ^Anh3ymK zKK7p5Sw2z2`%63)i~C$$>2h~gCFB^Y=~)=Jf4I)lo+t8)DemHLw!ZbD4=|o^Hv={*>{+3AR%;BmG+{z^^?B?m5t|WIiwEuD zLAtBv;J0Qc(_5IwE3sV5Wut$3&1l_ebMPgEUqbl7SUTJj)jvI!uJ>fd*qNo!TI!>< zG?>U+3+%b(_@8t~c(v&Gf1h=E3%@g3*RhEB@k zqS2*v!RmI zdXc(wDC?$^-Yrmz4P!V{gu99d3}**R-I>qb`T|qUx=Yskm`2ufRjL8OXWtI(Nliwu zL#_^p%>H^~lrQ;>WPLoQN}MQ&6Y(G0RL)T!+AxxZcvKa!zb4twhpZ!+sqj_Z|H=?o zXN*EY+qkNy@vpO|-dxX46;(sVutz%mx9ZEo^&5g8k7KgK&b}P916XN1tE9j9O`&e%S*U*KSB2J( zXL8-weK?eI0r~mBi?$@}(d9~a!16p|L^xkS=^DsKt4}RkL;|_K zDQfjNRzv5$Mig!GzX~t(SOJXqSD}d$Sh{^1J3fQo&6#wQm>W%-$V$}lLXgu03D!q* z4cdNIvUk780TX*@GB9F;&t&u$np-gEUqyW}k>UKt)xy+pGV74PC`A42BrZNg@310) z`O-g=*%bG!T_WJTI3hSIVX19=64!-BPGKXY3hwl23i}dU?!zzH!VGtJ71=sXSRg!- z?7rpiZ}HakDAa({1V_O-A7xnH|2GX|MjC}MEs@hd_2g+>K^k$RjB3BBEKQg9`5Ocs z{6Soz>*Bg9Nb$eIo|U7Uaq=*UZj+mA#;@kPQqPU@^#$1rWk`sYuDR!n0t&J~ar z@28)3;uReHmVQiQ9`+{$din?Yg`&VZee-P7q`-avC* z4VU6XAGP&rwm@f?-B7Z@+OJ{#af9n+8nKr3m+D@l7i$@Fetv~p zf=`xJQM}?mT&0LVSrz@-E2!x|Sy}sF5u9+9HvY*z;m=*l|gqJMH< zp=aw@U8#6?s=A()bUXTYPTnCf9ra$%ibQ?91WOTv_n^R0lx*(X2JhgHCK<`&^eGEc<%r?Kd=gBOB)G z%EQMZUt0(9+Z!sjiQNuLM&UCy0`O=Y{NW*dBf$KwLWZqm$grV^iv6gN|7KRV#)b=; z$T~)Hk?p$3%^a?3w7_%;k#E90ZhHWpf<;!On$Et-8Ba_$!+0qJZ{r-R#|e``S9LGHWQ&)~g~;b6=wbw&I6 ze)5+BKp$#671hgUcD02~&7rS&oMLvf=7y$ih2&joXG;DR?9RU3%p7wDx)NXC-h2Ww zg;YllHt~o1@X8;SQTrpeZzqu3FUzRapJ=%q$gro{4WvQG)cSi^d7ZTG8jVp|%~)eF zug#&ym2Be_el;mh!bKr4yIRFR(vp<$awOV)#=(UBr$IY2<0T2~cE`Bd@tY;l-s*w0Gq?;la9&VCl8--3_uX8HYV9?=>| zZO~@zSL-@cyX7^(8|hjNp+Mpplt!n4hZ6j8O!n-$qf{uVPHlZ6J|36?N_ecR$}8n7 zP(j_Sv9`x<=V67WCDrlKZ^Osdq&mJX(++DH$^v~p*tnlmCqUm2@lI>%c;>Bwc)W~8 z6@CjSp>N!x95wkZfVmt=BN+T+6^t!$er=M(@rzbNZg0Fhv30;&zhtNlS% z!Ifv3y&liqp_K>O2zEb3OXn2dc?lT_OQdb~jXUa(%&mTRfReo~yQ0#y+xW!JE(mxU zbx6VRdL0mXG22(-BHaNdMDu%UX~z=>-k;b9$Pq#X!mLBICWTdZ@UgLAFH#9Lid5cY zI~kRpk|(!uCps_EEbu7Op>=cvM}T#yoXW z`@L$)QMOc)e0S0BCs2-f|(?b-IV7f^U`-uOQX^!SqXh4KB7;u=F;$8>Y9_x zLy`_{qeG`zeW~9zayo-i`HAh+{0vA6r>4=cGaz|)D22%34pqR924UPXBg*%f>xBh0~vFnOsIsg?6Zy1XLBVakw(VR9J`QQzP{b8uE zx;=rF;*TI)!+ahnfmtYqYKVh^;6l|T#1zVm;d51(cH1pHP0ZAyC*1iM<{4vv)q;Oa z-X<3?UPP*v#ke!`SpfZch1JYm>re2y;u2=k?XEI6$C98CBPwi*UPq;_vgT#_A?QCc znz|ObmxP}Way~JP>`b)wDhr8y#Z3vHDo)n!lZ$$Y6mJN*RRi*~E4-+P#}YK&XK|Xa zOPC(GtIyVIF&IWWO_+@D7~MOunAKe$D_m2an?W-CO2)p#>(n?(9LTYNZYL; z@9V6b-!;gbx6&CN!X8J*?=r3vOh2{UegI)Nwi?c^q5jvQ8*`IrCjTFtMBA=2kH8U) z;Fjol>S8-qulrhi*CQqDkU{2IHX1ha^>t=;d5qk$oCnPRlKBSvQ~Gg|-rm6C^Q?p9 zcN6oNGfq-F{KrmQ1?J+84&(0)!fM){MuvotfetEQ+hsb+Si|!Co|gLq{BvuQ&s|Jl zWQeZRFQN z-eHBMiOXo=9rl}H))JUN-JR(8!A3mSa|`{?@(D-h-(|_tuqBjom-#r?0ihVv2s<@4 znO@yRTOYER9PTl>*z&zd7P=1y;pozwIp?Mx_gGJlDCB@!P=ASxJ3#68Sgdqp0o8fP zs$f?esyS)_J-*MX>&K2eO5P7xNqyb*X{0=0U8PG4X!8R&ZH*VvnFnlu^kzOMGX>D1 z?9AN&Gh%>oOss6!{3@|w>^#x7hpd%UVm=jk#G<963#ip2Rt_^blOM6>`YmhI=>8*C zQQE(T+#X|yv0x2Vd(1{l!{=)JJ$sA^gSV8kgcYgw6E<0DIbS{Ygt~>>brk9#WUiKf^wWX$z?Ab5>G{T|o_=v+m}J7twH|F`0me z0_JGG5O!>1C({Ju0@9BGNru^0_WYqDsRSb~R};ys(J&sn(&Z)frPw_4WQ zaoxcVl>a34`T~+#kFf-eYG&0NbFe?DHr*^V4hg(;+zcP zXqM{Ra87%Tw9VCfDtg;{GpNo>=3zKK0~*CWLffudSo)nasMkx(N5{{gc`sRWmmr{M`S@^d z-=3o%FIhm557T)O@TQX`-&x+Qt(H|1H!dG$P{b?vHyc5g-JGOvrnpxuP|CfYcD!N% z(zQ8s_Z3^|uq07y?M*2DHTzt6q@MfE;$=Y?TE1PtA2S-eXu%s+$~V?ec+>54=#&!e z+H+K~pP)JlU^S#B!WFXw7jy8PrX_iUa7zA6zL*3Jw6kKX8yh4A#Z&!vjOjlwNF#d3 z>bcegLiV}D5$ov#!ix_<5i~Of)AQzpX1u2S@IqcUr&62u?4!%+_?&G3mvww#JxW+m zsK)$ki0PO3$-D!@L{&ZNu#c6eV*j!-QolsPZ3E|B^G?Z`mT4`O{>Ym8)If1ya28I^ z8GQp*oF}F5q$26=M^-TR36zAk2Rj!d$4{)Xep>IN6!{58`)v}9{=_;;SEkbaPt32_ z6PV3tbcuL+a8k}xqm!t_XVxmR##E&Kp9k{leho+Mh>^|uVxlfK;ZGacV3q@OnrOhl zz`$n4QaXh9G%9umNyaPbT?9>FMG;iIk${Tpd76n;D{JXhD@J9$u+lEh02^YmJ?Tq0 zFZ`OZ)Zq)O7`WqGCIde;f=xv;7~pqOWM^Or9s0sbRP-*#8Es><-={r;jB!X1>}a^t zp1f0JV;*F^8!4eHcwR6Pe|`z%`jwT4jTgyB8}shB6)7hEO}O=MQ8wc!E*yR^S?l}E z387ZM$uMr&UrQ;?J3;TUU*iEuw@MNrpdoVP3k|Qgf6M{dxu^cPb_TUV5RsD+AT}y1g zEir!Y2;MOH6)Q-?;)P9j&z4D=#%voKHBMQAy4kPPKixI7W;Qf*nLrDgMa#ak;zfpN zkvBhT1vV0#8f;Zz_4@|% zo#mU&bPN zkw0ATHB6zy3;iodlhbIn-c-jcxsxw6NbEMKdf_#`A+|7XvoVqJIi6nWO(nfX+HkNJ zw(4{njxhW_xFBXsp=Tq{GP`& z$o2KAoJrkXMSJp?ww2j4G&3pmz@O)eqy|?L4namT7{R?BL^JZ5e4LBN!Du5JxH#~6 zoZ{$EUQ-EutsxrYWqgP5V%@deuy|$GVz#$>VAgBEvUYkIt{Ux4Ke0>~N5*_6l3K>m z!F(nU@9n~38l1iHSe2J(CkKUXlhI`SrcR;{`Ak955(g@k-_)$iihGY4IDPB6>i@Fyy&6eJ6qVEMwb@f;KD3r3dxMx|O zzts>u2)>Mu=8|%rt%jNi$?l?-9YoWdOqF8CY{USokJZq(FPEhlM)_EJ@W2*0yMw@V z?fFLT3rd@`k#B?zH(``?lP1CUDANmenY{@PosrycjaGV*9>u|po_L^tTV z<=c8~+%yOAR>R{y)X3RXseG9$IE0!pmxMm$SlnlqKbpp;!t@VyFPBSBs6Fm|xKlD* zNyjbj<>=CraD$;#lZG1)rt(ruH@bw!|McsoC}bTSQ9;^*-Foh(6n)#lX;iVWshItY8(d!ZyHeA_ zrc}u^hzfg{T9qz3D2>l~;jp^%vtpP^MtyLvb=O>-Xe`m!InlB%>H-gwlTQD5KpJiE zG!=7jzsAG6cgE7om98th$T7ZWQYwy2}V z7B%+Xu3Zo;G4@_#NunTX>@6za*Y3GA-=9Bz&(Ft)dvAs9pB2g%5Gr-`qn9@cXwb!~z4KYWhE}LcIeJ z-$T#8%=3B#baB~#NY_N|vVT*2@XP-0VL-IQ2rhnSDvwk1NcO$NNgfjEV#upuP{)dKJj!&t{~g9NhUm5g*ktnC{iE=4lHcy1L&;vK+`7(* z+U@?ioi%#uMNe_NKd}n{X!%mN`x^mP)WN1QnzY1r8hUOWD~NhcOS}}eeX>ERFL%ZV z*AkZ-b%7W5G^b*^>~CvXjbHY!L@oOS{Ukr`m`FGJ1O-T|2T>1irG?b1BN@GwV7W=B zL`v%$RL|i<2W|9iKgTIa0KaV%R9!yMA(8wW2YIJ16!G8Q-k)en6@gTIM6WFD9nrMk zA-3@L{#33T1uK^q^t*NB-7y*1T93rGj&0Ra<)#AfAk4qLz5m3#8o#}tA@? z-&`|R&0myRQmq@qy{1k!9mi)DKB3&FDkydBD!sOFsFNh*SXZg^Bp`D8(m zI8Km$YDG({Dxp$xE4o+}Le#buIRq%x>HZz(ETxy=_Mca4SDclUJ;jpoRk=ZOwqAp3E+Asi9P~{?JTow(V5Cn$oy> zD~xyb;=fRh4UbpTuBd5maa|EloHpkL<%>%32OR_Rm_f$hswqu$a$0gCy=&$fBEM~t zNH1G>I!E)F6}<>kLM(oAqdI37sObak^Tt8wit`7oO#TxXxkHZecFk^DQWngim0dgy zmG7V^yc{ujM$kMdqgqFn;j#V`-=Seaq;maub?Y<(J?i2aC_inUNI%{v=T5<0J-xk> zkdhlm)j*1v(kXtNZZMGx12-TD;M z`g|S)Hw(_%dSfTD=iqYC!(MZ4nOt?~~ z%5X3-i+nXax{#}PA^uxt`S*KD4Oe32&ue`sJ6!pTgHarxk1z@k0&r2~R-U_U%_{r58n%J@H<~UEE*%*499Ya+( zPmNNfaxvr>rTieZYC$uil%W+3 zG2b*l7^qVha%`Yfla@E9mkHY~8PFIyH*A&OF2PIS+q z4vNxJ8Wcv`IN+pP4&6|c;c_#4G-XOh`IT!9ZDC53RI4^UWy(b9MJx?el}frD#_6ik zRp&I|ZV+FT|8NA0a;)jVsGHi+e~pv~xsOW@g*QgqEexUQjg_U+hniG5N$D-ks!4N_ zlwhfIO{^T0Xvw`MNy(_upTQK9toT*f7tB?}r)%=RIrxjsCcQjqSHT&EE3*;Dx6{pX z|G5i2Gfwpu)TCdNm0Hd!V)(LIx7yPzIRo}mki&cQImRc+N=sSp>X<_@%@t#f&eueF zCm4&$`wQj}h#G#{CX8>h{;^05d4~n%7>6A4Z-K7c7D$6yU?FJ%z>*dyX~RI{jTVZk z(;XzAR!U>(4Fz8pSRA726t9wg?Jh;UWyG{gfQ&T02KM=+6YHGTsy%3<2Vbt zfuIOa>f=Uxx+}+B9#-W& zJQgzxy6P(qNRo7aVTZns=6$EUb-7xlMC@WzUW;8qD|#qf9nM!OO&iY>^;FtRzg0Hw z@2N1I{L7bYGEY(BEw5HYMbdJq`@7Hz#_lO#as3V(al0=#=fB!Znd>qE zgAbbS7AbR*BHxaHi4Dw4aW}?=CiGUiNHI>vKYJ@Zb*XKRa;CHWZ@D-gn7@)A56t%? z-a*<4L8KD~@raUL7VQE^4{S-lPJ`-BKgtf}=TndA)gl9;ONQ+^dhL>sug-w@2#A|0 z(NhjGdEGI97ETMC=}uorU=K&q^#iA0cA&=nlpbXVI`CGwms@!X?dzxbR*2qvamB&8mKIRzi~U-Ge}u3tFN=E&0r-) znp%z)4OXT~1Irr?Llivi;B9LhFjRT1D^tw|vT6|x8LBuL#|&3q;IRj5>i>h%U)pU& zw|>BeeeMpj9;q~tTR+XFMkAGEX^Mq$@knK^P9FLso2rjix=FKTnmt7;&F6I9f!7N32i+-uyt2a92LLWH0kU%^nxMoiO6Pf(Ug%zb)MoHv*T z-BuS6k=%iY@#u>^+71z64?MZb)3y|QYlnCqJUI}1l(Z9xlb?+!%Nv^IjyRwl>ufRB{0Y^R{&J!0 zNs21Dl%?{MF%z}5r?!)oAlF-EP$(?htnv)}>H>7KBOF-TYg}odrIVFdNwKDTla+vS z=CJXH^&G_im~!MaMTu@zsnF^lHU-IMysG*mletxecGt$WoB-X{K=A0Jg?AN(KcUZ% zBwq^)PELK7Kr5#z&hkHZvgyE7B{9}s^*Ty^Lt#(ckknA1|{@+bc4DoNINem@J}zB zXnZp*%t9wg+4SG^Fa!S;8sG`95~zDxSTG0FN>i%H*{>6XJ(j!>y*#*APwuUf>+2KEZ>W-2(@kh%$wO%VFtGS$@p+Q*wEKGg|E>LAYlpHuwlq^ z<_s(m2jgSg48_|a38Z*;n+x6~A2D(aCXu z+)c+8(-t3&GnFX&voCn77218xHrahGe~}>UzMO~iXcW6z&k`CdvlKc%KFG)2ZW$y} z;WcJpfn7}KL%x#j1nk<&fKI>!>^cAXH-VBS1l5pt;$uLD5@0{|U#%7mdhklmc|mLO z5VGg{X9-~89LxeEnwj=50^o{*eI?vVwMu@&dNek!{ZTjm>^VAnYz`M zGG{42I6nd@oZ37u9c!TE*~;u5SGZa}LtkkuAC=fK-Y6DyV)cxh02`H>G#0`o2PZKD5j^b=JwHvSAjc3$lj*=t~eU?C50G8I< z(1|(93VE7CBK4lDw30qp(2==H?MiV^(BOOyT#rmJ2j+*0hW*Q)+ogC?z&ypxeLY5N z64qxpJ3gCVx_Y+MZJv@EAH}Im%IVFI@H%q?md;!oD9Y=Dr~r$dbX30OPr4`nLr0Lkg3gpg+6$ct0miN!dVEM6#DOVgG$tMzA{Mu_%^|KZN4&2 zSNHVegl}!wEx82a?bcq{H)`f0e1{H;-MT(Ym00kIUVSf%LJx1pH*WkeMRSPs;vvmiq%@0u{xZRg zYbxLo2Ijy&;8dyF|3Rd;iOBETiV2HzVI==oj}JIV_`T=qQ{Gs2|7zk zS)vS(7VGHh66IsuR}2ssSvOF*qeZYmH{64Hh@uv+BSig1a@mem%Bf=O^=`s?ec(N^ zU#c`~e&{}0B~G+T)aswb`gp@6cybCr3m51sTVkofhs_awj$NQ{z=x#>V+n?EjiNAT zN4Pe4o~ooliOTT5BndBoVkrKr;rtj zm-U&uTCpZm`xQ#hie3Lii+!W(`sFiSUZK>e==Oiawf#)aD;1x{KVvZ3P39&)P)4a~ zupj@-(k^4$1@hn0vxE>t&+_VJg3Ds|SO~SsJ(aIOKHZ|pE0x_=Ik$k69&b-CpIQe} z-(MAn=)4KsA=ac1n()Gk`f+ZJeDG!gmJkKG@lCmWT5yMpT`=JLTt~$5h(HlB?hYOJ zRe9%i=0*Zc2uJ4TWr*(Sd>e;ny!(&>cQHQF`Bm8G&bvWRS1EzgqZ{P1T8ZmYcRbH| z4WG5szN#6iH%i?Mf0gT)dkng>)Z9Ebbc;!xU};egW3kA-&OX;2W~F?OHWO9jg>cF~n4L8D-l?H~d7@$e% zYy2jHF5JdnFP*O2TPnX!sqCD4l}p@**Gk9wP~&wP~r?@(W)$csi~~X&Xg|A-6u46hvM&+@(0i2PJzDjHwep~7%%SN zjd1pavHLD%Io?GYLApKAoV^)tT)0PRt&>AeB+&D{$^^&lCqP{6Gq{=!t|I=%g#_h4 z>GDY$yH81Q?0md*=F9#vUfhSnIce5)ayg*<<7)}_LMLPMmv;+v-~cNdDTKJ-e+u+X z9+2T66bdUcsp~Ql&J<+Rl25961^ z&_75iUgUCAIU^Md`~ZrYBGW53 z`r!mrTGhho;R#f2*lw~pskEwmcn{=sm?j4wwBrNx=SzU>z}Ub>@w|Zic!{Q*RH`=f zLbKAUIYQ_A4fyfzTGt5H3kG9?IxfS729bKnLIX!bpT zEdU%@0yCNeqX0a{fjczJK0J9<0KYGRnZhw)=&0|_f!s6^8La{k?!_UiQ}foK84>Lo z2ldV}Eqd5@NW;tVEEYgh;cPjOmm`8>nt(NW0LaTxj{`Sp$@_93FULEc{AUe#kSFKm z_?-hy8MXT^l^5hFCV)H-ltUXwytBbM=ywz~2*t*`Md!f;VvuQ(;}I!*O_T)o=D=%U z!jiz29GLYDumJ~d29UXDM4fKUa~Ovl@|JK=M$|q5>3g1*o>sh5#|MZWY!<`!th@at ziqgHp9Fo_Jqo=vUmdD~i1O6#A9#NyU1a)`LDbFFw4o1H@= zmXIqiz}$IVI0oNlit+sR2vEe~khU^83lviyA!4k@_LR>PbFD@icj&l&iNOLZ}tt$G1J7=djiCgv`m? z4K$IJ*mN}J+5gHxsk~0YkSKz~W+7Be^m{?HDTo%syalXYDOhU(s{&Ylvtmp;0d?Te zx+>;ckme;l)sna8r_G$Gx!?CD=r02L51?T4uaE+w=-=n?3BF{b{tS5wB4-5pUV$Dp zTLd;}fxaTJR0|}E!0gR5=qxn91Gmxkvsiuf*-mHADwX8D3$iKitYWab?8BEf{@cmn z9F*;O+bH>*66UgV8<*8sZr{u8dJv|`V=&0jN84!0Ii;~{Q&TL|1T}MK@c94Gi3R#P z+o<525@h=d_0GF0Km}W=&Uu{sZUu<1NK$CzdC1uY-n8;OwhhC!Qa1l>wv`^8S87CA znb0x~yi|@`wNi+DlA)!^yMZo40|JYePQXVT!pg@JwlQ0%`30qdbYTmnTtFfFp&3Fl z^Tz|xw1L7_6sv%io9T}WimTLg3q8J|G_ZcZNh{els&P?C@ZE@D{sfKm*L@&8RQs-l zYUZb45wSUeYF<<-(bkJ7ck(8>coFL2#Le{bqT*|H%9EGdb+gg^610)lOE;j$(1tK< zz6{eCymgs#S;?2a??gMUV1N6$DV@HeY?hwyGLE{cSeKFFRho2L$(44jG!D3QEQuVpi{0TT?`#c)>MCmF;JJR_l z=%T4}=))7GUCf!e=uE~HUKvp@YIFWwg>Eqk8g2+`5eNgZLK&89Eri~6FOv)lKSXo# zF`dC8n{)TtLb$~t$^tw!-%7wo6vD4y?U2VHNswDJk(-3v^1=a0N~^|mo8eKM+ZHW# zox;=$dFrxSYDbYehqK}baBs;1D@7zKXGHbYXuR<>mAECRaZ;p+Iv>jG`{Ndx@)UJ= zYbnaQxDeC+AnoPw91Y&P5Pt6fJqA4WXANGb5We*QDEn~AT1|d%FRsaA4cE62_a~0~ z4nqh_p#7*2R^(i=ip|WE+-l2@;Q$$DPkDiOLEwK-a2Swlz1+DF=Oo9$>{N?artfB2 z`b=?k)N2{8z~wG6*?iqkr=BV9LF<NGN?j)dReBf(FlZH)YTj%eK7B7{8!!JA@Y{14At@3lQxAC=0z?aeI`} zQg19w?afm!(9zjvigW58OAE{BfrrsdGYeLVuYUnjSDr=D#B`s7l0>e0zdBsB;y62f z6@c75Dmc;(K(RmjfdhR2EFrJsL`~1k(B!pATz5_tP-AJRm0*=srGY&JW(I&_W-3AX zrHC2EW43{Ai5R13oA#v%T@t_q4Vda@D#`bPNU(r%n`hp%)&k}RSbm2RRDA^4oP*;_ zfL{x`&(Z*{RsuXTScSgi#it6Tk><^$ znJ<)DQkR*O`9cYjLT1vl7Z5R4GpXWB94y~Tr-YXX??|TsFO@}ggJ*KFnGKg0HOmdh z={KKdRxg44^@r2El5t^L+%z5+04wWsihiXq>jkJL7rV(c=M^MdS~_icr36|hn!sP^ zJ_kpp)0bC};T6-#@EX6bXHY!Uq-;?erUeeSFwCF326RTFEuU~OK^FD~07AW!c>!yR>{2+mbhcU5F8I2^xDvEJ3xCMilTkT_Bd^`_djO5w^RLl#X zA+ZYQ1+O;-KH4^vHAw@jbqkI#FURMv1$a~;`1;xf{{nni11p8#wQI?s1H6L<|7vZb zGGZ+?2Rt=EgP$#gC#*$1!^Hp>>tRtG%M!);@Ji&nv@k&hp1?vRh>EWumY@?~#BjaW zV*KzWC?BP!CFqfHK-TWh;+ziBnf}!=J9$>xF}(Q845|In3MKEa|8Wtjf8xt!31-O z{US5}vYke2{OR^EmulXl?o6YR`q0hpm*Gv0?E z5uOzfqQEvo^I!2C3@&8V9#@XrYd*(~nj;dn7>;%?C=Z=-Zk2p8TCfmloIgFWVDF?w ze#VDoSSP9ZpTo2q7e4kdnF``M#>8l8gdVE1@$Nn)?pf@@5H7r;(F-wPp4>`&#(8jn zk8xxJW!hw#ZOwvGZ+UZNTH!?;?Ei3}9d?|}2o<$V2i#Op#6pC>z+H)lBYSCN&CSruW_UEPL9xmOiY$Y>ST;YZ~}EBNdK-JC-`A4DFC$vM)G=Xa@GszWBkpJ!N{Z2_juQd^|asH)+GDIn&`s_`k^x0C)MalHLI|}700zsFf(bQ zvFZ~gyxHRz?XAK*tMBV^tRT-CTY_&8R&sd7LN&E;BYE~a(p6<{)@C5b8*HD^zbY%I zlY+jZ&;X`Lt6I^x02U#w?@oIISg5?Z`!RYNz^eF;?haZXkcZg3LC6*_f%zj7x$S`% z9>|jAN|D(#HIQ|YB3jU+KxU{^qb1)R=BVitO4;}oG$*eh7H5C3xz?$}j`Q|D-hze% zv0C!SI@y$R#iy}-5Te0;R)zQa=)sd9ZyNeC)`Oy}v1;;;W(h@a&$-g_YM7O}Hscfv za-DNH#XcZFsXeR%>0>n(DL-i{@GB6j&g$DQYO0|&%jT%7n$i5~tg`$tEZg+_A3n6_ z|7tKi|JMY^JL362d}z=A)nIu3uL=5%p*@Ok1k?wJ^U#|tPPQSk)SmxKPB16nXnN|e7MmzpHX`Rxu*Eu+MuxIv zr*^7F)BZ3|_8o|%)F8Gyv8DWyC5YGbq;rg#)n>KIFJU;xF+Jp0UVF$d^#{N(!Id=j zhi0prfZoe>IfPYR=~!rd;}BbT6`=4Tzvl;e_Aw~T|9utU+7P7q<{`gVp9AUb?!;M= zlE5GG>nk-(pqG0RoplWnx2S5BGfCGQw4P!ZPpNF^@pfg5Ga#kILEpsp>q>GIi>`Dl8q32b zc%~x;>A5g5mB5iYMzNN0yx$7#I(RJZ^rN|pF2Wy$pV)i{IO|4gu82bqZw81L^36`s z9WnGGip4tSBb$=N3+f`WcU{sz%YGh@FOmJF&a|`vYY}$>9aTF2-GCJ3p9*)bJby1F zH|5_SAfEs39X$Us(Z-t5>{p$<>17~YiDCY74}5%%VS#cRd<4WoGP~mA`&fwd{1<^V zKbD2KKSpEWxvr!9xh_?nV0B<6sxB17%OdGPEc3LwyOmQ9t`AdW7A|*1!k9P~QF~22 zRD+8!Q!DGp*maMwep*bbB z#3pM&DD`Q{BBj&KjcZymy-td+K?hr5JJPxtRcg%^Npfx4)f&6-d!cl_HJdDXhf?P@ zY^L;cEy{1hZb=1I>110pVVFPVwZ%5w)}I{OvD$&Oh-(ujqW7_Cg};I-=;=~nEKjsB z&0g!^T=ASQ4(0u8R_1>nq8l_3M*Z6{w?>%33jCV|@xLvwZYY*+nyKJ`TY+W=_{y=s zzaOMoaWqT`U$JycmOheSjV|!lCy}l_8soQE@@~&6xS8>UGEdmdWpd_-a5ZCsB=}`y zEH!P9t?9X&)UQ1YsrdrA#_Z7nr#VZ!xw!)q%B@+SH_G9hqm@sXiK?H&{q3J7AI58BKANZ$(V_%VOzb z2R2@s5G?RRw|Z;%DGLfWif~+qYt~g&ZfbVxHCw3|hW$;@pWXvR>w;1zXef z&a7qim(_UiKlS2M+_V|qSXfEDEzk!dQLJ{f#EC*~7U)wN7_GXn_B!i>9vX>Z)VC{h zw_b=K{MKPn8~rNUMH=Bj8@jUP(%T55+KsQ|AJ?I_-B~wjN*&tYoeh#|2T5V>{G%8f1GBM?$k6v$8*6r?DsqJ}Jf;ssOurx^?6YyetBQ!LBEq)Iam_$j z^W>Y!B~OO_;xS=3D~#b5)DWPxB|tv`#HV(nV-FUAd+oI;sV5sNEvQVt_hezpoO2?t z$C=10K;$(aQxQy$J&-#$REBBIPh8++=x+l@YzA=@+m^Q@w|)``A0sL8dv-*M3?+*c zHc5*>S9%$Oqi`za<-Kwn3)N@Up&;zD180{XEe z>11^p*^kw8-iTgLO6y^t@5d(9GaF%zYzy=ws?+cN*e`u=@Fu~a%b$lXoOFO^KE;-2 z9@RwzMu~v!29!j!tFSV((K;+=KW`|s(q9;bXzDW}{8)f4XrQ!0luSQ5*Pj_BV-QUn zz{ZxJw~tp%bD=iDm+S{ZOBLinpU1N3@MB2CGdm}Y?)*fj>7z;{_{c|fhTah%(NQo8 zd*owWKae%m={)GeAeJG`_MmBl*#xPrD_IU<{kpGmhfJ)>)rh>ZoRPD#1P`gZi}!@V*PrUVCZgV^xt?S)d6v0%x7$qccN)Z&xwspch>p!WPK2J&sYoq3pWk>Ou*_ zSZ$?ZrpRT$&LU2AttoO@4T2_4wW=cMbq7u%r{3kG7o2J*I8`yHqr+IsfCCl(4=#Lb z4KC#4Uq}|ApsYpphO;_SSxXu|9DAtgW$4{-?9%U9QuGKgVU>!2} z>z0mSl9X9N@L<^7?YtmZ?%WscY|&?HDK^|$iKl{*?fYE9Z45DwkMbbHrx0&)7zM^N z>Z$7}HcE=p)6G#V&N1R`(SrxX@E>O$FZ7K`jtquYp+N&N=q~! zAtYLapTb06BvQw-Nj_#6ueiS{F8d~!akDI_!wU}waJV+nlb(-faq`oz@sw|$;7Z98 zn6<~$q1ZF(ZWo#;i>Y?!S8=44+n0t-U~_zaw<#uUi#~`g$#WpAdT_%2)F_pC%DcbB zQ@>Q^>)m7s2xt9O=rR$DK1|}Hzi5<05J^p^vMO{fm9>!u+f$8+jO5hM@y1gVvAUD} zKEzYUN$j-jg-g*@C$mm+CLq%$vzGFzck#x%lc5Nc%e{}M3yEG%oYHvdf;F9+%1C-| zZtOLU4UyzEFXQQU8api+b+m2<>nyd?QJHki*q$%qjos5(Tb;b7z(nEvOy(p>#E zzCS}1`tH9hfV#SspNpo^Z}&9bjP`}=z5QAuU15RlE)xYZ0EDq|5Fr%k@G`AH z(&5L(v_%XqJ>I;`(k`puSG%n8AYNQn33-{t1viu>_W67H_By?x{RZ&AG{Tnun2#t| z;J^4Kju8AqKk&bIAJN=ZKJGMr33HS-|3Y(@u;zv*e{d(AP{j+;@eXpN(J#CpN!-`b z-COY_{lX%>9%O%u^>RrO*5q5%;TN`4YPf_x{K9%lZ}4>RQWoj-{quM;{?Zb^$(F#6 zlEz`6Y+A~O*;dWf`WRX4xj6py{>j&A?s66&Kg>C1(q-a9(`ByUy3D)rCS4{zG+pKjuFJd&<3hgwFV_9l2Z+xQ zZ<_HPUc*0egEld!gM9H$yh)dd58>Tp!5z+=D2e7@8W+e5y_@uSL916{)G?r5U&%uC zjzHx!PdDW>-H`+5YT@2w)onzXRGbxY4gnRX-LYeuit|R+F`8Br`==%z^l!tU%ChC{ zcumLo@|H$$17gCJpsI;S0Kj}!O2@eou;NB5nU4!i4K2NS) z#6d1-c_ z@OdJiz8X)h*FmG1f)DQWL{7%XrFD?LLk}LK>gy3T>q@-l^<*eMgx8ZcSK_}_cV-_r zM*TD5YDxHAnGxs3f4Q==(o`4$p(5WZJO7(WgElZ{)&|jB*Ex_I!Jsw`wQxz@=CI3J zLk5|mxSB<+e3|NQWTC#>E|nYt;8NJx z0mL(HC6cYZOmj9eSGmKZczRY@36UFIiq|}!xZ*>2J~2lE&foCIun7vBM;DRaq@Ub~ z3UJRSPk@_IMe%j-pUBH{1S!68eKPD~ymmA-7$4$jDhYHrS#y!34JZiLC);KO3fCv{ zu?wisX3T0C_?WkuMcF4`;5ipwlw0xE|~Fn;N8@97pqibW}E@CR3G(&X_|sB zu{Wq;M%1-QT%zmiBjSh`_yYcru{c<}i`}!PQye4AWfINW&3anJjo|a0`EmNR8`?EX zMDi9tN}hX|d$orM9*uyO!nF7Id5pAEV4(2~G(&zz4*PxzL_ZF)Xjhd|_OK_?xGVHB zi+QGQKB2XA&$^oZ2v80*c)O@*P+Miy$7Y{vcd z`JbTOMpi>|9ZP>3*)u6<3^m)&Tqu7Z>mh9&%>zl&`iHc9KMp^KJ*3O~S#!sIOE7EM zVPSfN|G_}JPT>dG7-{|k+IaxG<)jDn=>V&5b8aAdgzr|9uTktlR@W+ETp-Z1@6)7% ztco<_K5ae-Rnmm}bm|}r(M`mK2sXuW!F^EYXV^!ygX}L?Xzn4D;^i2+cZm6Vog9N@ zGe4`&@5c6ZA2zH{uYuq9R3j~iC!#~odXx8IDEZcOq?E(VV70J6@7TeIXx3roEESBV zb%$A7t8fARaFEIzfm~Ps_jhQC@b4+{2n#Yl^k zWQsdA&t!+Ct?p!>#qLRW;nEv?Vr_d+>zd=_att+X7fdO~*nxVR!6bMiVR($1_6B;I z1ANt9v>gx)8+cQ&4Syw8yOsAyT&VNiM;)_S3v1_Id`Rd&q|n}Mc2pX9g+}HufBO%3 zWl8Li1@fn>v^j?v+6^8mmTSNF6pPTIz~SpXE7Kx$=l*!Sp$1|@5Fxa0`CbT(MJV== z(BSbu`S6AFCrkUpJ)XlG0`?Vs&ye47R>wNa$j4Mc?r7?HoW)q#l;PfyJkHVP&hf$< z=$Essl6`NaEUv-EbH<#r4DY%b%ai4K)FAnwR)g_dv<$+$X~TKuS^oQ6wC&+;(89vC z$7#BH9w$ncxkk$i;6`bOEk#`fdp6ia_b)=`7i>ZvoH+!a#h#|>m!M~@y@48Cg5l7f zjnwB7i<3fn(w0lCp|$t-LNd1KMV~LRK&wr!MO~7p#${H`eCJaPfXv&hA`QRH21q?l zk^LR!MCFbGGWZU&g6~|5?)=K#1*&knxCY%S6Eg1g7qbvCH5p$zsfF2&$8%WKH+5B=^sIdBQM^cLrrO0{2M@I+-|K9sv@M6|It!CV<^Hco!RGkHtZmdrA#A^$ynRBO38cc9jr zf0TCKfpJs!qx8=mrrJGUSyUb@HfkE9?y^8#^*t*}&oyr$KEzxz1u{g;HRxW_eX$$ZNWz0X^A2>rfU=bRl%TI0RMf?+6v%i{MiuJjAXe z=f7BcY4c(l`WI^?cUz3T#wc%R>xPRpw*E@@{$g&`eUQ=7`%tBuX1?$CnQF+7*F+6h z+Ex>@>7tJYs`59xA)WnA6O&&^qqK*r{)1^~^&(KRLMjubzxGo4KUjslzfNTzv8h(u zZVIk`NQ)k^TIQ>+lo0EV^!^c>FYVt=8IM_AY5Z>b{V_xi+f82|v-)-q7nJN9Kckw< zD(DtN`2O!L)-4Vhbwb%LT^yOYSY;NZp z)qJqKuDNmCGxkQ3`p+;9eZdb6@69lte98XNNj0Y#x4vfGaWNu|9NsdYz~09>4ZS1g z^dl9JCtmZ|f!4^dbpZsTN@$5s<3ZUq)cGyMdWdo9TXqGcf1;ru*h8uJJY$=W?3Pa2 zoJzI+!vZBEjl4fIpX%+72*T3MBEsi43c^-^2*P+3>=;aXm&L;}C3BG>$tiQ9n1V2v zVMZ}@NgvxMJpi}~@iv7^4Lym~@BmFuJrmWSZOt6P!_RUKqq&b<*XI3MB1NX0wt zIsoyB5e{a)vX&l`4snX+KQi5^g8SS>YEr#Wlg|IeMCww2Gr9{CX#)SkhseHZGoWbZUfje*=BhseToQV!*3u&+}V^!0PY`(>+0X z7jB>kNi`(B!DOJODOT!9OTKI=G&M8%pFlA~k=Uf4#-)4@NSY$Ehw0c_gTZoRp7U(y zjioh`8Z29bmmZI{@~!yGW!_HD^J5Z2o;A*Js)k%M*MrZ-SXHg1N46k75GW?%m=4=P zsudB>h6Lus$3El4&yF$nk=5!t$CO2A3>;ZpxI&>tO2tL)&<@HdRMr&xYr5Ydwui;djB8Dafd`imr zPRAl|4ef)%^So>o`l;p zCWibC>?**Xxtepd*->sA%MS=Hu!79w0IYQ%GyeD=oNhhAyqUKZhKg`yhD3FcsO@SR zX{8#ZJu7IjmFgw=nX){GETz3?>AscfDgC)hD~FS{YVSMvtf^l_(TC0jNq%dwnB=ro zq*xOS6!;$-_^lo-mZp?NyGHy}J zj{wf+S+vnc^_J?n(ODZcOFl9ui{_S7+e-5X(vxy(e{(*Lse`RrKm6P&-qPAVLn%YF z^y(6ze`gBNs1l$%0MU?g7!&twRV(@X*;(|&R&{l75jYSs*BGBmMo%Jrd3BjIFN1cM zS7&?P98j#9K%5CVsEHzNg1u*cqvI}8;o6+wD%@v1 z>7~8eTh8uhnnC8`qi6>4=-cmqW{|c3@fpPK9-VhkYuCLw3Kit0>aXnV6{}Zv4!GES z!rV{BaDi9p!bolrAoK)?f@ue$ejPoJ zMQtzjJF-r$q)O80!(`!xRvL4Ns=28hBwV_j?xuEkY1|E^$2+R<6IH{sxP}9=vja)z z4pH)@8+p5{-VV3AWAVrhGE9p{S8C}Fu^e}h=DVxSCCh`x$L^|)PI5e8eB+_U=sLFA zDi%NwaELApIt%n&CPRP>)mFrsX-B6XfOJbv`+{oc^}uwgM!re1YxoGGYzG`da#ayF z>G^KSy2nY8ACl~p4-nwnj5OOzbq;(q1c@Edwm7p$=ELb@f!^mkO&D<)+~npz2BoP+ zy5Ob8rrLDYnlJl;J8x#qaIGP%QhA$h1rVo8UARD*j9Owsz+x-I_Rq~FmOFwa)7#o} z+a;V|`|n2ykU=-pl;6-tc{TX28r+S;`Jz(5Q;{G|i@3c75&Y6cVS*tVJdVR%OTgP} za3hEF9c3YYtOg(XEj&Pj|HU)rtGGgZ2f$NN(ru_ssfdq*MGdBYoACn;P8{E@1fkzG z_)QKksu?P_gTp7C#3J-4#4vK2$tgOONI5%IOPb3g{7OWO)ZpK7l5oUQROYT4{LZ&^ zR5bW5j$g7(f;D(3hnH+yXAN$j$_dpjkwL*AQPTKt2|d=}WjVZL9nWj<4xCO&xKV@u z&GD<1pua+cM{#`I;q@=7*)&8H+tW>o){U@lCK^xQc_i)dQ5)#a(hDEe-No)|(V^Y( z7QBITBC-qoS9K*nU$v3`#BM%968JMI!~G&Kb>lU;t~AM4P0+oiQ@*OZZIdfn`iKJk zv*z^DS9J|>XdiEub_0~)&#Vj9sZlpK@nLG)og15ESk&b-p;faq`N90I1J(CaL#(&8 zL-tt8_M#vBRDajx+lIm|8|QdlF!A1Kp8>STPp#W^#4=7aD)529jM=Fl*ra1JoAUcU zZm-Ae5{^MB(uQN$CESA{bGm{7g>z;*drN-aY!Cwz2mT|p8f)e31b7S3+Op0-671h72WRXRSLsTEBQW6~w!J72zXyc6#)lrgm9-*h9 zs=pyyjmLRw0Y5u^#nNu3OvxO54TFHQIT|%%q%ownT33=n4$**cwW<`Y5QSr9QhtPS zU$}Ztk}UR{GDy%eC^OV}BT{WDNh9`BKz-z#7OO?145Hrk)tl1oM2e46=X2Z8n^Ec| zX$Y=)NkJFd3s26_15S zEj8|7YI#Zbg)$qdu1aDFO24pu${Q=|f9IS!&q#3(NFASY(@90_ATF^bbEC zG8OJ3KyzTae;H43fse{IqBGsqyV6(>sklhj3g>au^PE0y#z7n4uhmSx{3e zM1~6JPXe(B>dT?Jvub3nYNzC)`tyq7p}!9hMcXDo=Y@gRZ4ES8fKH0+P633eMDGBY zz}W|Y8uo~+)sngvCWX1zEDh94q(sRlXyHL6Q2J@0{)JRJB3!JJnqykS zmMydje&$cjd#nECE7s-K7IWPA-+56q88E+eo{h;6CB2u>8@1y(WrU4H2rbrOnSsvs zR$b-J&hd15u)BAvH&`6w)khSlgeq%;A6HeGM7FpN(u$vyx!|M>Rc14ID){{0&vku5 z4qp&`5_Wbe?FrK3;XBoSX@l9{Q4gANuZgB&R{rjrcl2oU^ab$n> zkxsIkM_mW1$E65&sys;TFWs6=^9QL0xAkZvyeANv)Z>eq(N<0hcx76g^{Pte2dR}S z^a27{OF+d&sYX?!)nIk3BvsPWl%Z-f>7)}~AF8&N20IyR3{#uwB+m*oak%PZ_sNkb z%}Pppk=Gx_cGP@l89kjCuFjSeM`}Al9W9-4pi3jvSno6kj(IHqCbyx%^Lm*5T;Pk( zV$uiQh%yEKFo~=EgX&#=NmEV2!)Rq(K^*n?LG3Kvv8Q7{KyD@2Q-_g|p}zJscBFbr zYHvr0qtqsnwH+-OrLLE{mZ#{^SnWoXrW{B+e?C!hDuqLnkTMcIIl6EoG{DqxtDeTu-C3%y>lycNad>nJKYHonkW^8A3hJS= z)zC^Q|0kju=%mh84f1&4t(vXI%8~eZG+Xttc6!D|#QWScpa!fhC%O}oK`t@`amnPYy6$F^dyej%~Pv6vVJ_t zb)l?9FR5s`h4WP33ZZ^nNB2n7Q4GwTxpZcpTGhH;wXA~tY#d5b^gOkqr!|z*yhvG{ z-OUPxOrebV$AOzHIT;gwR&yljwiVedRHIl@B0snN^DP%B7Hts_*=yVIs{e6lRC|GP zP(!(rAW$&Rh@H9xP=r+yT^MLHHQ>*FVy5c~q$6RX^Ey37Rj0fg9R$S|_9sI0&UuYC z9b^;X{M#cOyos$^@rl|4bG%M5$fkw76nD&g2isJU7xt}A$%Ad`%b|eG9Bkt)FP`NA z6HuGpat(aA4YBEpm(e|F{1BU-^7r`odx%X3*$N-ALv2=sr1chMIvi6p`#Rc+%@r>E zR4w6s60Z5vF6B-rpG6MCY-(70JmeLfaa$L`hDkv~B8&Ml=84m|=`8{9T z4YIV`tJtU54LpSdC5vQ%H{5naB;8z&z3SPv^l`abSsF3G=)FR1q>~(O8waf9yHfL8 zgbyED`m5U7=5|M3L})1=-lNaIs?(zKrd8+f_UZBy(mrJ6%4r|7@~oL`>xfSJvA`@` zMsMod+CoCx#a*=9TQ=)~#iaYuP=G_WKc7vRtJFTyq=Cku)#??UZpGf;R2*FOKb%FO zYt`nqi>}3)6)BBr$T@F!a^pC|JIaNXY9X4_14KpKV{OKt!jJgQ0O;#pXJl5t!h*6^xNno@o0=o@|1$%RdAXH z1hMZ2E8>zDeYZ_rCA*xBBg^e-eyL9pV&}a&*V)8-DwExFzIyfu5`3gdaa_#CtKoVb^=nJZ23%%TLU4hIq z)hOxqA&Spb8?*|=t)Kj0?bws&06jE^cwlu^Gm29ir@hiLn4iI<<-wFPi*rA^kl(^# z@bw^Rhm%0Wt9a0isfXxlrWz=D93ov7G`^n>l3$kUqgzNymO5Fwc%05=;rwCw9{P}l z<0-sJ;&Du^QRdj53N3q zldH2?bn&?AF1>F{{~kx83hy(Gr1uTrGAPE?Th78CrWV}~Yv3LS|@ zXDyyIHjX=`TA52O2WjdVs2hJn{&~%5&>2-$-)I%L7wktkxX(qV4QkZ?W~iUH#Gip4vJ^#(*~M+ zQO$NNo5Uv%yw0(o+eqb}c%5l%e@QK`lfrgWpUdc;YwKwJWi;O9UG&FgwUWm^=pXWK zLjx$p%mLn05Fp9iAM>(Zq`QJ9NZmzV{5N(TMO{&Y9lO2HG+}d97kmcqq6t^jTJp@i zOxkrt9qrugS*B>$7}FXDZ!*9m@>+_&ij4QIqm-+vf9l+I+W1x7+%e~vbbrNnzS)mY z*JDW~t~?y_yN`_0(KhK#y0NsGel-zeTBXeNGa$fDPWFqyFaL+nau5LgA9uAm3Sj(L z0mRaeaeVS!GG$f?=^ovxjuO!&*?GGIhA24jv zhp%>1>Og}PJMl|_-IR*9_)1r5@EhevW=yA z@se@01YK^T?x~vfw;{>;K1-7m(uP?jOdne=c8{F57AhD!FHr4@%LArTo`3THqm&_z zJkO;(LvOzE!3(B3yU9*y!zio_%zG0@KIqSZKT|LmoHOO4y=m87HK@%5BZ3 zrTqhe3O(*8n_f4`NTbcX5vB2v zxY1NPUh`LC&VvBi;hoXsc}#ELNuddT>Y*=V&k;5VgKkKl5GuG#zK=7+g0|;+Rm2Bx zxGJFY#>x0+#Bjz3L1)NEvOlz}#>sC4mnj>ORjs{Wd`7>>CYpvHj2WZ5aYgpz?BmAC zH(wk1xX#!O^(b$ZxL+OcoO=OD4v!7#H#M>;#%OAXV;7@VHW?}7GS1BPk%pZD*dce$ z%{L5ry5cR|#BXTv7IH^}F`NS1S}Cw~0R^@SYM$tDp7-GIjSm7DqBE&M?G5Q41(&Jk zIA#h+CIF(avHkz0a2b`g<2Ne0xo9o1$C$>2(|iD66|V@z9OOwSr~awBIn zE*R6amKfBI%AoQZTwELT@#N?X&7Z)@xg8qV0Ylk~4<+}XrOYT^A;zm6YjORm6U_A?lGE^DL!hyQ)YeggXSP5gFe_{jGIQs!ZPes$f z45og>U?g%ISM-Fl&?kKAb_Q*zyy7oRVsQO_{frqq6fU4SY7nsqD69TaMc<)RkDU+R zIkm}k{|#$t;YfZuTK{F<7YrmB>?^sEMi=Rypf%|EoIDFQ?xHoM@bFDF+>M8ysNtbJ zY{N(0+IpocdEa0wpoD1twZuwM9_W#5suM4+weqqDm{9LIFN2~W(KHeknaSEDQ?h51 z8G{2j=0fT2wZuT51kTk8SQ=;eMMyeIK{vqncG{|w3X8RU-ZtKj0={%Dz&acp4gZbj z@lhC=V+fy*cKUA#JB6aWbD(?SKx29{Gy{$|&|xX7-`(w-PIq-U*qk0UkJ)2-<4y&0V8UF5_mA`1o=;Rhuf5cH z?WT|eXp&EYfV}VUsm|yW&i65+|9L_yC!(ByF`+-lYLMIgLeYedwu*+uIQ+kw(P{s3 zMk`I1O?g;efuUK7SfNtS90*LRSh}6_tBx3ND=-(TAl7t)So5CqU^$}>wtmMGC*OZ9 z)3$4gc4=dQbcGcZh&6Zl+N!bwyrkUaZ>%7|U`h@&nvU}o!7w8sL=6V?F_;p9`M4lM zLTHm2e`6KuLfc3AD>RptbtY9l!X^^^3NYo@J>@brgfi|VR!IASi;Jeb#GG5UI0to^ z`kLF!XY5yGe~4#tl|t!kvZu!F!%o}08@LsxA_eRZJIrIN`Cjo*UTVR(?EMF1 zea(SNA}I}6Y7M6S34&#iKF5I-%l!*7eu5iUn8AILGs$#P8Ex5g#iENszC_a%1+7V@ zTSa4=i_R(;`xS3zK~8uJfG^}#Tvvl&$=rF@3hUGtk;BO()2~X6Imm??lT3ee^d5W{}(=!voWDO4O-c-bQ)v^MSe&D>I&qCZfaPOABL)7 zMSe&}ILU-<>O*xyH-)_o~861W3C0CAE;GgxW0qOcp5D5zBvlIhEdQp96@81 z4YlbRvSUx}YCq0p>Q>%2liIg<@K>d;CbdeV=3Ius79U)tVlY8=wH!?}^%xbJSNEY6 z$+8J-^Z0V0IE*|0KzECckVW@RCQ`~F<^MvDT$25_>mDD0o4_aELfY_=aGkcw`l$%L zuG8jPKX0N>NKLnXo>e{*hVqPac{p@d&?>y)SS=^A9BH2>dZe9_9d6+q#+dU*r*t)7(GUK1u z6Kld*j7MHad=v=J>aM1cH#PXTyeY5ce=e-!zb-5mV#WV}4Jby6c4JNdrR;_m%~a%H z%j*brE!sGgJ3bGfOMfQDmDQpm+{-Wf^i9z1oIP!p9g{DEkDc0|^8ggBVJ>k|e zj;^%sK%}25_N#egz#&T%TCY($pDHMT7oIm1|NbxYwQ!6Y|7)~z@xz$eDyW+=LmJJ` z&0#n4C^5Evw_@$$vqs%qKog86LgMFWl1=ZLfK0tWrvFevsQ!}@)O|xi3F@+C!g}-X z5b56IW@j8#d|$|)O*f4lG&P8p7M{xOu&v$gJNk;*675NAyo6utF(mGwKt`n}Ei zt&BbG-x%Ox`ZONo$1iMT*`p zd0CJVm2-8AZp9RwmAN|lnE!mnPm-f+Q{Cr@xp6Ey+$O!g2CC;@(SiH~Q%+8vXgZqn z2nWW|gV)E|G*Hir?RM;i?TSm>QI}mdIXCSl-@$tCXhCN8_&3|K`C$d{x!Cui!e)u(eW5^HON>vO}P;On*cPk7!Q zD*rOEy3n^iHGP@*gRni8-n~p*u3cD9fl8{WK(*U#UjK?8N>4eKO;=tg#tC!klK-1T zL)61d*$Rnhxz5rvaq|bvpmy4h$FlNG|zA8ZDPgv#7f~ddsM^V z8(4tsF$0Pgpgj3twxvnNgh+l4Wy~lOlv&F@Uh~?jmEv-xHn;u7Z1c;viEl+=);N0h zF|o3)uD^QNfWm`)N*yMl7ATuymflhI^tH`mF50aru1WVNYPESf2;& z&bHvW1Svqw+nQ~@Xe)IP1fNuMRB5TJ=<76|Z#i<3OgHlyeM-Lq(A7SA*a&(e{gu&w|(n$&ynvSEkPErlQeH_hnlBx>1V`&$D({{jUCT|m; za;96KO0T9HieL759OO13?ZiKp) zk;)1o187JYH0oV{TFQUV^rvHGq!boAIL74&UO2lqg|ve8sV${bWbZq z3BCK1m#Y*K_(vJ!h1LOB=3~Q!$zo%3U}LtqyQ}n*MrhH`9Ofp$lcmOy>bXlk;?ecl z)ZSeR5+85MHmAEw_8Q@EZ7-O_;fwzf@5p<|C96AYBxNkv%CiNYV>oEzSLdq(vHG zd~36&vb0tqMI8z%j!OM1dRE{qErt;`gDdQf*xwmBq1Dy5-^)ukBmx9^nCDw=NgkHT#Bcq-$) zKZxr``J^hzNH)wWI2=|~B%n9XCY%Oa1;OgLOk2*d5NrZH1{6J>dFLR?SkaZ05Jyi| zmJT$ZijT6CbEM%0mH z;Yu?)R0l=l$`UVH=yIPTVkAE?4R5xl#7K3Wd#)-h>$isH#WB)djc1EPU|FadCwj0( z`JD>^MmP{{YMx(D$`wLdWaI5b6DB54gf0#bIKujD#68($87R9bg7}_A6a>Vy5slE(8G>Egr^s) z89}CQ%?)*Y+o?!_T|xU9+22-fBx%H#OS7qp9#d(~B6LVERTO7u`I|@RrLUZHn)3zO z3OMikFS4nUEUm0Ca|$ZMl7p23G7>BxwR=~giMS|Movz7JYUIYADnL8c0DvZ75$2>G-aE~=d0SbPuyeHT$ zx5HQ@a+^vn52mA5R9|4bQ8t{0CQF?}?bj7(Wl#4iLUpUv&SZD1)y~mytJThW1Qe^C znFuIWI|C6=tacaziq(!60*cj6K#HUl?zW<+6e&fB3Z^M3QajWr&$=S4^z4W_iNy;`4?I5LTbl0Sk zNP^e6G5%!RNy-#50%$=esd|Ld-$B;t`3 z=6)3387tHUe`1}ber100jkC$QXYaD>ibFm{jHu%+H1?+}oh5H!x-Y%zEF}pszLd~K zVq%6LjN)SAJspRlUT!HBI^Vpji}a<&;k5Gh+l}05)V7;cQ<(2Vv${#)+K$}bGGlZA zZpMeg?OZ(l)JnG|)U1c(7k6oCVOQ5yDB0D!b&FbPR9m<~fi8}Z(gC!!hg7*-Q=XY~4`l&> z?Oa`Y&_fEa9giIJeY(M2SmaNEJ*B!r|60_kr}UlRQIqU@LAW~a3CGM*OU-y%)k~6u zC{KFWONtiOL{X*Q(jZ|mvs)hMNiQ=yd8Pcpi?DLHX%L^ba{>`C6ANsHay zKTtVSfI+#~WYN|1Gavd)k~EHu78-20wfHd)DnCGKq`kg?SIrCHv7DGO$l29g&e%Z&W3r_HZe$~~-;iQu~9jBPv50k2w7VZU`O{1l< zcBPZtIB~{P@(mYHxyK}bew(56B#wq+_bNWR#HlVo8UUck1xOuC&4T-JPzQ3S7o+Vl zk+qg!_Ye{$NI{N|Fj}f(I?9fBdn5GCrhQG^0)#%<>K(5~@8Bms!7W1D2OFH?4m&Ae zn~#p}rAe;hH+Qn>O`0@TT>lwvT*czzkY^iej1&`620}#nnXHVR5nEWNFX_Wzv0s@hTTy%@#^Qm5c*<8AkgEdi)L+xkGXpn@UF~crdN2?Y~9_7k6<1^)1{;#%~tf9uijKumFt?rrnlHlixD5`6hBj@G{au@UhY{UWsDSco^GZCeGcD3~`Q1*?4S zjvCMkEc*)Lc8@y-y4JC5C2jpXD&ahDQvr-hZ$s%7ng)tLVH+P5<&YxozHOk8OrN^q z`{UWvGt;N7SP?NFmPiJ%3t|jQrFCNMTL#KoDkTVi-Xx94{9JCT6KYzk3JPPRvM*d~ z2$?s@ATZx@gRQBRt*K~{wtd;k;;y6FRJVOuqf7n`tki~12E{?lAvk;Y9fS@J0~5L!QU zq^gm3WIl2hySAb{8+iIDuCz$G;@W!Il(A8&C3Ze%po1HwI5*F8oSe8`Z}20vlA~09 zljP^{;H=uhmDF&PvRRrSc8STRbDO2k;)<&V z{`iWY!>QBiKuo1}Tcji6)++`I*edylUqifMf6=PPcbZZ8HcE+nSD`ST0RHr?E99L$1ekt|7m|{GIU9YLDHh&eaFI{M6Pz5i= z6Bb_DctD4Kkh-{UOy(U$DOhH2L#2eJ<}^ibmlg`KtLemcDaIj@k58uW+f`I%hh%mz zol!pPtumk7A)U}T?>vPj&S;tfkRB_{Yj;T#1=kSXY{cQ5Hxlt@ms0ILQZtWZKO1aJ zIY?**3-6{W^({ynu#}eWk(LNozBJd_E8Q0zB91EM-&{v3QcVD%09Sr+b-NOIZyI3#nQZg82^>>$m_l9~&>7MO2m zNzNLf_cim=Y^kv(vfn(;!^+IW0W&w2)x9kT#shxjTun)+HP5Iyjpz-CM&y!m)a#%W z*nB&OO4_Xm-I>nw_*bLdie53(xi|l+QIK|ne^uHIvwNO@GXLU^$Fg~#%x)N*^XO^i z=;A@ClQ3kg~hYY@kb=1Mp&2$51!CJ)y<^uk4p1|xJ+~WF{!ym z82%;Eaj9Vt#c;_a@Oy)e>Ue?Mq~v)k8q)?n>+&?a@9E8P$uG@WfxNMeW354?@^Ky? zz~q%W9AhsP6%mMF*={BFg&JGqJbWEsH-|_=dLx_Ecz%GNw}SIki~)+_>L;8y!*#p@ z*hS49ft38x04d8sFdX#@AsPSz9KNBF5YG=3?7rhha!}vlT%p1AdVHg#;{|ZNPXOMV zyGG}KlJu4LX7i%nVYioBS8+!BW;+`72|JCZn4hKjY2_+oiG`H&#F*iN(jJ?N4Y%Tl z!)U^LM48bKK~reu8g!QLT9lFHE8xL{26fkR9RPdz+Qrwv;CG-<xOBr)&y436ahp zf%KEo1i^3H;!{#p`yVQCINR?j>XcMX%-U+8^uc9y!na$g`CjHB%-KqFPDw!ypKawk zcxM5PfQHdEjksl0G^Gu1mI`9&Sx49sKC3;fvY_i|2Y z(QQ)WQwrkSS>qROrkQ7?O5%{s5MyUJ1PW|3T|k6Tc{9B}BTW~UZ=#83r8vj-oA}V& z$aTem>{+RT!*hRSDMvPba!1bRq#)hBIh-@>4_XENu<}T;MYrk~N;rojfVCTG%sFUo z&+MjS=OjZpcNCWuil$iwidH|9_jxEGxnP@uR9QH%j=EifM&RppH13jg zQE+rNxB5+bCc0i+&7}k&+}bG*YI{`*bg*5?XL9t2$~5t+l-aoKDztSBb26FIAC9AOq+?{(Dt8a5i6SJJR+QnKLdMmg6cw=g5G z2Lxn;C)?DG#kA7?Fl{Lfs`>QWeDj)w#~2&`WS}=Y<0{d&H>7*Q&}B6HrnIlz_GRct zKh$OI$6tG?<1L)AbPJ@Tx1@owzX$M&9w<#u*o}s2)#mXm*h8UzE>8LI*Z`k5`<27% z+}Hfx6sRYK2Hlnd+=eVsi7BKk7wXfI4Nit{O9@`dUx8N!Z-cH1o(V8;h1O5}^*c}J z^*lZW6U!3(b~(l0k*W!}<~Zz*blLU&D*k%FEkzoEzl+K9sN-F!pKFb!C88bY(JyzU z`c8M3DACxO&!r0Yq(N?L1FYIgtXYas)_5*0y(cvm8WP>SCq=u3e+sZ`4u#&AB89_? zsl$C7{ViWi6Yonk+`jSuq$IC7bmTq`I;$?G=l7+i4!0Jmo&SpBA4mrKwhnwEZCDKN zvrr*UT7Dwk7yihgT2HYx{5pfiJ;i~<-3(q@GQEC^v!4$$ zDe#$8U1$Jb4$q|iLid^U<1;B#s56ruJi`I%J~F%gCBXrA(soLG4gp6$n3g@420Bfj z&&dntJSWo0?FBR02e2a{VOzKrZr)qi#6xS3DoZu^!_o{gp7iOpb511 zl@zGCMOR;82K-Rgnql8~s`OfF8Z>jdwauSFnZ^mBMsY%bay`0Zf13Ro{6;^8uD=Gw zdrcv`H#iQtGMV&mAsy|WOnu%+BL)3rYx-oedkf|3^+{^_zDd;ct<+s;H;ImN_~=QL z_g0GX&`eTPcB!3`Oz#sjyW;k8gTei>sm42Lk$4f>#-H9{fSf0i>w77p^3`hbPNQufq-c*iX&?@;D$aUy`-7A@_1&g-AEXH3*#rvv2HOO%s=cli7r#$hd*i4^b(Wj@X%n;(nExO8TiL-j(nr+vz&Bfhg3)CIm zvuTZnwG=9krPmtPRJb;VT=zh?F-BbjMn3W`rrQ`lM$v6(yI4~_N~HQ)Q$Ipu$=8ov ze_HdN-Wl7m)B?nokYh$50vjJyj_IJJVzw#Pp1_+$*VI~!g|3UNyRdno*t1a9dMI#;AN}=@URNjfz z4%paM*>&h&>$#xV9zT575ouLh))-EMoLEI6a~REYVj)7WVYJtY1=tJsyn*jlpPO$x zvGN*m_;+D+awUuaHUN)`>cw_!f$C+@3%yl2&jnBa$$S8HLwRJO>cw%gs(Nu7(}K&f zMXHzAN5Uv`wd9rj`;o8$-OFDH7U^D2@UKGM%N_nzsC&ubUxm7t8~9RmFEfu&kPAx} zRy8+&P_S$IOI;jU>c|0>)yz2IMkyQZ)B7vD8qLnZ9XNHrfR$j9=^p2A-ztmQ2O%{cwZk4ulRDk9h#OKV$BLkED3;;NH{YbbX^}sX zQ+Mk~q`FM)gW0S`!)HuhFMM%}Z6jt6_rZ{2S(MJpG10pUteQv6S&Id4;_4KU2^#sg zSORiIs_(<1@xQ~< z{D4mc*%VK-7uI7PK`(K|9|$R=Dy5d zSUa6Q_hm-cS$#S8FBG5L+Nt7GPWKYgOWG>pQ$VU3ZPB%C3#6i%+CrNWQ>>8U^S)jsNMU9%NcT;w?N z3t-;%K^WQmec17I3t+o+r8*VgF#*$flddCG3uNs>zXV{>W=ct#im1e6rNDk+@i-Vt zm>aoKXJ}I(n`3|ZKAx-4T^T}+E3#?A_FHtKBAQfr68u22P600(DcEzkrSO>+_~CrU z#pa?K;m07-4YEuY|?#)W2+aauqFsvmxhcY}y zJ(}u=vR0Z-G%u7@^BY|cqt4YU{=GS6-!Z~05Y+h+CW}X)`};kV9TaZLdQGOJwQ=>CaEzI)}bJ3Ku zO)~`-;&yNWCo{T134jw_sAWwSlxANdmWpNTs%FcJC|>T!VgL;RpnrKpNKF7I2t*D0 zOMtRspl)heD@p)V1%Oo0$Y*Fe3bQRNY-kBs3m8TL=rMqzww5ITu6HJVEmomMP>I;> z#j=f6v)PvbnBSRZ)nbMkKU6Q?OC0qS*5;sQqY?lCo#|sO=3ApziP*ouu?iZwkB?qv zv{VA%d?%8lnQxj$iP&|8*+vXQ7EOu78AXY$@N&f$IM<236kfcseTpEWka(y>Vq#Gu zR`lp^l|*=DP$?^{6SO(sw(udB+RUl8V1yO`y;a#4woH9MvRM44oLArv9km&aH#D1; z1oEYn+RRU~^GF^Q~dt&&Sp<@8@5e{U|I3{jA%fpr5=_i>?a33;LM0W} z28ZIEEx}IhC@q%xP((deEqrf(E|;a2Hs$ifL-3#Cz;8)sMz22oX+CPJ=37&b)eevS zPx&hTEuW_~U&UA!8U8sA7yhxn4W~Y>Z)F=^U*qtqVCw&vrS9LdM6{vDvFx3F%n4t} zP2nBQ&+22NBHTJctsAipLiiE-p8wuDOwM{1rh9UX3z3k;_~jeb(*DaNO4egZUR|HY z>sc*fKz-V-XXzEQ8(Kl-%pD3Sj`{gT!l5z_?l4{E$<*4wTIg0bE>>i^GSpRN+HYW0 zh1#){YhY1=5KBIBtedd=AWev4Lp<8!Nyfrfs9Ekx7{nhO%L_t1@@dRsS{$#7!OvW) z)NIV06=cL*P^3;AExpZ^uJ9u|?xP~p?!W?0=G@y2UU3_!3Y?)v?5p*hzj4=F^`pSO zj?kR??mnI!Q10Is@%Xy*dt+8X45@1%-=!|Twxt0^bMq2(iJ1*L1}LJ#q8vqZNIs}X z<68xlw~7wQ^+4CbIA*A&f(=%|LOuZtsYj#Y*#sdZhF--(PAVNk<(sfz;bt9b*o5`* z9gXt>_2d*1Ms@3CQ1y46>rhS;)=DT{hsq~lKY6A$HA`SdacXU~vD)6X`9!hg`hLf^ zh(qeou>_VXEnV77MCg4AVgq(w9Vm=Olbu&`u878BIbMo$qf1odr$Lf2E?T4TceejH7TH0c_vB+ zwPG}MmLC6rs1u<|JyTZM01&uS&OdMk7QQhQyjXO35gdhg3*}JE1c>pJrQC zu)JM*4=@P3uRnqNI)dIOGk*t7bv`E^=kB4PW~`FKui@%vCZ#lEKEk$e8r+OURU3zB zKI@KvBuBs<64005PbEL=ODi3CMjlTeo3RQ)?QqI%#%ctJh%O|&F2xBpFO2tRaS85$CQM+HroBu|xfZ6IbUN zSoA*wU&!kbjuZ@QDQMTL@`|UZK$%7X6Ej*%Gg`9Inunxm#iF&}tmfQxmg4@vqWdzC z5?ZkUhh3;ddGd%RPR2_FRFvs&7oKNQdPz@J{V#x`n|v)Q)|R1r`H z8rrgOZQi!xP~H5@6WXE>ZEA4gW)F7YyYMJe-%!y3MOpxO>7uJ9u__CG(2*Y_Ue$2G5ulto!n=U-~ znm6KeVV`I5Qlq@-VGkDM7>1taW60!2bZ;CE+w$xE5FX7mIK&Wp;QE&9e^%|y3)%5td9^=i@FSC z#;OUgiz-Hoxyz8VoUg&-`^(~ODC=N8JCKdkXh&F(p^#Q)l%_U=vEtcB(d@w}&f{gt z;xIJU;+8xwUflIki-)l5+G`(*7xu)C9t~k;r#6jvH}UFAbvw8EVOo%P5P?qq6Jq=7{Zv~H1)NcgaC}W6!R8aB5-vS3l}RPP-Zj> z7T*oZqFSR_xXXz_S<0YsVK49P2YND!d5LR~I%_oRCx+U>M9HVB7iqs>_JXY~d4GXM zZWn357f?aZ5oyjBtfMx{);hhFO?zrZdiw>YcP|Zjk6|Y71DPer`JJB24^yJCBT=Tf zd!kS@wUzV87y*WL_1%31Pk~B}g-Yj+hGNIEDq;+trtUkIWeYECsLD9>BKbiUH6O>4 zgb6mZY8t3%m!cG<+P@Yn}*a5N@^pE-X@P9knrj8niDw0&)<}!flkQSTg~Wyk7smlkBnHk z%1zhft5r}Js~}m97ZS@M(=r>pQ0j1wP(gF?Qyet+BS2l%LQklWb}Hn7PaqF)$OpVd znGIH}kXO(vRKM^O$b}s8M+Gvi!6+50L zR>8WdU|yeqd2ukjbKSslhhwOob6pkg8GefQ``HJ;6{zX3NR)E~hgH>d3S|k7bTXaw z6#smur>Kc6PF#<`sEMqV*c^e=6G0Dw2z;Cf&EQXO_0(n(tL-)SEeho}izO&&43gJP zLJ`pj{4t45cG&s`c!QLU$809EKym6Dy+XxeZv^&EW-UZ50xnaK^zgNwYEEI5#G?qb zpTc^G-4WmPG zzrC`Z>P~N`HEV9a0#lm4$&~HjHlkO`lX=+PbtCoQa)7ULWH~ED| z%wdCtGZ*OI9CpQh#3)|uG9{a0b?Np&PruA%ac+L8O1)K{^YsVDYrF31smeSS?Yi7g zm2K3Th9Y{%Jcw5{&(rjItcGo`e7z0%Z_xUP@%ehXGLO|1%Ob!Z5Eh^Rp{Hu|*$eS~ zo}MZ#0Q7O35vkDY5jAQ7Ybf?bVD|znNA(a;vIQVuS-`@?r@4BnzK{ib{xnn%WUaXe z8gj-Q5{B{5h4=I{U?FQHeh#qB3t1Nr*IeEQ@Fj5O++TvZ2=OX+KLsvgl^uS4z-jwc zF2v(Stgg5Kh@Z8HRTFz7uy+w_V_OCQYSSJ9+cg(sUEGa8-^DCIT!g^P#Vn-sWB|cz zm9p;`J;Y?hoY#HN@ z{yebg>S+KrHJdxMh;*4Uo$q-SqgtFcV8+93_f2!Fr7T$^j=72Xz6^*?Lg4%|R$nZO zfa`MBMQ}9Gu;py17<&VBwwzTf?R*3LughEdShtKX)m*_`#TgIv=B6ufpee4orl)Z$ zv389`VC_mQJShmAS;;DkAqc!)iH-Z!t9oj@iUpV5aTN&5?7~m_s~pOrk*nBr(f_J` z@oMHHrXR#H@@keU=3u!*OoUh(BY%7~tLstWiaL^9Cz87Y!XOZr3j^q{Sw->A?^yW0 zW?{m~E7YGy%tgeUubFqP4CJz!GGYIl``pvUChsrc3C{!Y@>CNirO3;Q|KuCFnq<26 zHM-)BBJ9>+q+eauQ|KC;;%6h!bq#ACGEg~4!KOM_z-=&2UP=k~a0Q1q$~lP-de`6S z(i)~0$G`U@@3pLo|AuTnkKh~xQ$99}vwYk#<~E(&f^6J5bC0#GvPP(RfM%{^KZ?s= z>&?f_VNHJteJYO*9>7!Fm=eeASiCu4e-U{dsD*fi=<$p$QvUJ@;*0I2%w{L@R#z z+2<~3e^?hk53hO`^u?4H1qVGr7xeUQ0}IjoOqIT4L2e<|{93+rA%Er_J$3kwHFWD~ zr|$P*ajxz^YM!8t-!Z?+Ezg6X&cX4Dq6SuUR!=}t6l*_Rq2t{pw!1d(y`8S>i9D%xM}s2mR7rN*0CYvJxGTCy24 zy6qVd(PjvbWn;1^a0_cMdO!1{@mp||H)bbo*}}a1b;o$s+$RUNobC#O4D%D*|Or!Tg#heG+Esqgpf8zH16x%`0b zjP@rQ`~wJk{?B^KL?CVW&(^h&SJ6*dz`G$T5&pp^9~Sdc`3;}U2K&EPgr=7*z@hLI zVN0|PXf1eYZtDqPZi_04-0GxAZ3_WumD=_^N9tTPHB~KVi&{?KmU?Z8g8H7&Q_OZW z@3*KdYP+3P3~zY}^Mz|19MqhR)tv9~Q@jQ5(PS4VirtR2dl2#-+|H(m7mtJa;p|V` zk3i2IEXsEJaS)N_o|A{;AcVLa5nP}4>|n8Bq4+RzpEl#b!;3ww@s=fsRQ+?2qg zbFhNv#CpT}H_9PE?6f6|+=eu0Aso~53q$pU%~`Z&2`+UX)i1h+6{bGE6%(6&NA+ah z#hQvMHf5>zhejUJ(*`p{-6#b9G_y)BWsazv|8NXHI>dVW!ZkAD)Zzcyq%BD1J?Mix z+@?>tbrdn^zh_FeW-3)8lkHaB+wn|BYzaG_-iQG56gwV&NlC?^7uoUD zu@>{Vwzm!C#5S#Dj|k;JpB{Q@eT0L0#ROSAg}}KxWpv_B1m>QCG4UK8*b5=7cL7Wi z0%gOs9%3y7V$VpvHQf*L`SN!|<@Y&CymDyE4VVkgZ13jiDdP;>6=Wg6EtSQk2>gN= zhmpv_4X^cdV<-R^;+3md6%bHv&c4r9hieB_*^iUi#%Jlme%7#&6OY!wPdtC4u^qqC z#pwyOYxgamJ-U5HV5~ZDV$t=(nv02)gBUzM#BUbQK1?Y;Vuk)Ti$~qa?P{LzBV;_y zGdhvY{HxvmiAO6l6jn;9A#m3M|2$AC7r(H{DYg9-vo43>TJ?{|&0YtYi$-X*o2njS zDT1(@#vKB~Jz}OkhoBi9YNoP>Sxo8LP~PPkKG4&{%#FGoW_Un1FpCBr#$q=Qffa|D zr#KRUJ%?F{Ru1GlS)7?0;e#VIZ#1nw%yeQ1lI@Q`9C{s)MRks_v8BcYWLfez(XbRJ zA4enC4x&po)#&;W)i?v1U~_B7NKwHDE|)9f>$No{(1 znpF*b^Q$TiuRs~lV|!Urzkn!wr!ME6+^zsZGIGPxs__|Y&A-`9qtCFEfI|zFwta|J z+P15<+O`5!@2)MBdxrVf{53$eTZmNF3)~dh3^=SLdd||Wb2e|ZrsQu@2WE}A-dXlg zV_&uw7g}xhQuXtwwe39Wd7j-ChR>y@7g&9v_FS5Jft?9Ei*=oA?_pbHcNJnRmmva- z8F&nII5!2JafL73il2rqt10~#7zK@APpf}nq2*hySL<`~0V65&Lx~#*wRK)W!)x$f zsRl^n=Tt*DT8}88r6azuWA8ktnqq)t@4OMwaQ{G!##AX^b|6x%78d*V>&?$^xZvGT zmn?dB(R!oUfsMa^(}Yn+Y<46m+R@*yHc#vLN(r^8@`+d{DvLS`neS6HZzfJqL~XtrVEhaWJ~E|cEC!)!cEpmn3S(P=;k2M*Bd zE0C)qw~@zHW)h}WrPWti`O-n__(GRYLvG_rTccGfK3~!Ot1LWt4+c8X^gcBLegyI8 ziADDs>H9ROz$%KPeB7hC#-bd$PUk4KYcrjOUt`CGJsYUOb#_(AIB2eL0}3vodbWA= zEw)7y(B?}n3n<>yu<%zf+m#J%ld^{DPFhJTwgYU6K$KIqO#Mg;(?y*aP zyn~k9XT8gfUx;{2EIpv{v~=#DHOch>uzPGhMLb|S-x$sfFh%g}5l(dQluu0JfL~&i zqCc#m?hn{?A$SoPa#`DQQx<_aY0(VpQQhI~^j$6s44U}C7t8RKoIWo3c2kw`@N)t9 zaGs%jbaI~i!IvKAvS}I^qUK?G4PQVT^Vl-McY!(a4^~m5{eC`{2R>R5Oks<*jmGA~ zX6M!%TAt6UYFn2`rEPSHr%s(iZ}Qn?q5p3B6%H~joN34^$^X&l|^N588j+xC7KP4y~wPT}(TQLKk#3(B_#A}PL<7_(g zh*j%xeJRGFi}Kd1MR$&WGzZ5#IzpMAx5@*DnRZn@IjUO))v?~+8#4!BRbe8jUg_YN zv*;G_ygER;b7+isJ7v=`dp@N+W|6|OOq%qVjS?zl(!0mZKU&Bv(Yz6g`Q2t4u(=Y= zyfO=5SZ5X>1(!vK>?rfIxbG|M#CDcxj|)lA-|Q^YPkjERp2S^cy4I+wgXGH@Ex$jB zEl&$@ry{PTns3ed9L&X;w>fy@O7rk%usIAKc(59rVb|9fCtKWICVQfUc+*YpORzLN z{^dLSwz*1h%Hz&McH3YZd$DBGI+cm&*5Pe+w*x}ne^A+o_5o4YlpjWS1 z?WUHp7&>gK8=li?lCqANX}F1=+doEjlYiRdzo(%<2bu0laX#c6t*5okMJv<0?X ztyTJ1^D{d4jz#%Kx>=Y)V+r=aKs3?(7 z7SM_htW~+H>0k>t5o=`&{a29fN2o2l$5XG5%q+V8ae(|RV37?oXs(5M3o|FsCJU=x zE^a*V0HS^S#0w)YPte;?nTl0B7um}F%WXj#R*J$xA1z&!E1pwk-Llwm_>u_>+@)v!&!Lr{!n( zh<0*bcgB~Nl$M!uQ$)D9U+Ci!8Z4io?yY1uva^?a3Sk-4-(IdN*ksURdpV`TG)p5s z49da-6WBTP_%aKMYGY|c8V5Pp>Edv-1miUOfft>dTg{J#ILLlNTcSA*@`ZoB{3}49<`XzZ2+yk^BALKMpD$(}YmN(S%4|zq&IA18k{gL?4ul z;|801xXH{msL|6B1qSplC=hE4RtppvJetmxmjhhyJW(JEO1S!&S@4uQYJ^wAsjHV9 z-FXJm!ACKjv9$G6J~}HOx`s+lm3y5ZrHq`L5`{C6gZKu$9LvFkv=}w;t8gs$<3%qU z1@G4HM;E;)@QA-BnclEE6|eR{3IByd@9&{<=(#;q4*l8x2Yg}=g+n`q0*=qvo4ij8r>+#Th7EqhAZB3>wt&dP%qxM+r}vZe_Sl;X3X57xOUEMXiq5>&y8`FA=RK{x#5wl4RPs!+N@$D!23Uv3dJ z;=}<<{s?HDpSftA*PHqD4qc{tAa=iuux_o{m-hL~-;`Z5hGUd#%;0u8hE}Z!bEnAx z^6l`xpP_{V1@v|56~1w?Pi+nbxkb0KlisGtH;7W{)iE?bP`)fA4x>R8c`+X?468NsA9r*{L@GI@qO3dw)N}RuHhmL^NG(YJDXI|-VndCh4UZaAA_Ejf! zp?!6?5pao>H@TC#&|WBrD2IL(Ewu3*Et$HYM(RTA(DuJ9v=6^f7FyR@N>POiZR?I? zsw_7SvFXJdrZ_%{Rc`7xLSe;@4luM9-PjTIS7o`8Sg{XI&R{J-EJS9G(^{*2YWNea z3XyjR$460{P`O2makqIdz^Cxq)otZer(%1~-CJ`%LfEg?Gip96uKSkG=37F$U%uf# z&Y9Ts^>GHjGWDMmk&(zi6k?PuwrKn>s zbIouWu1|xy)A{OH)^2p8@)2^AYUx+_=)!v$)>y@X#*h}E+f2KZf_VPMAa^|1wuO0m zgj~r^kUG+)T5{J2%|+w{J>atPQAH{P6PJ~|XMQbAIc-f@D^tB_S<-!Jw9-9yt_90Z zAo15e=C7jVz8c|qG6{9$X3p9^p6D7Ctp~5ETi}J+4H%C%fMNjDU}ZeDHONWA?nN989_IZ9xw=M-st2v9SJgnFY(3I8mIH;1 z3N)v&>`h6HWsk5I$9-(_8J6>ypD>S9Q@wD|Zr*HgL1QMrCj}*j9y{U)C0k(7WaI$B z{CS+O2BkqgdHhI)gN`l$&7<&mpywefsMdy8U_=F9Ap@@fo6^~Bkn1t0z~Z^)@?8FD z4~{A2H&@CZ&=ZiG0I4`Q(oI2RK5XrEj0dVD@x=}fnbdf1PL(*+&|N=@#4-h6x%g5j z=K;tqzbD`4VzuP?gOW@~5Vjmc7*F@$2o7Iqfjr4E(ZZ90k;FTNyI~4P3&m6Nyc9o` zWud;II0kNFUF#JVb`W1KPP}9spYsD?6myg}2rg_+S=(ecl}CI7yS`#TCLT9GpQI*o zn8P=9P>YB|FVcEd4R9QfPtNzEH`0bCa?Nu37*xqOj8^d{y$wBTBJUR_9idGLvahi5 zi1|cKf>51& zn#mz$E{6jxKyx}UL65g4>V)0l)Tf!8;1&=Iy19pi4I{4%i%TqJHIsGTuj?V*_X$*bk!Z1fuP*pA#+T!JM{~2kYS1R6-fjKa|mlmr>ba z(>{fQKklQsDY8c?KR%uEDY?1qDZY00p|AE-4W!%@*;8`g2lC3?p*U`UOqGaya010A z$2f&>od4Pjn($TyNj}{0R>2j2{4CVDG%_nqv*5cT@zO-44CfmYDGlGT zer79sxCfE}^Qeq(Wt#k|epkmg96P8Vb=5@00(56AU2ZKm@@&=s8+WE(j^cvyp4w2|8ihbGa+Hu7C<=FY-(78~csb!lZ=NTSsy)1PhSzQW;|)TW($ zI!(Kyk zc-qYEg*DysE?$#UW8ObB?g?s{_%xlz`Kok`8-B*ph_uduk3VIm5oLFMw#57GbF|nKCX*7xz zV{fr5c0trwVlVtZv+rTa_y6PJxt*Qeot@pCo!y-+FZVb5`|-DY5Wo9>6CAoI>uNtb z=eDz4rs#Q?b;+{N;FSN3|INGjpYmb4_u_w4$8oT$THfGQuObSTIrTmmfdO|?9R1r( zom2U1wB$R+nK0jx49$g+4GUrz*(OKRvhHexA_D1HcPtzt1L;9`)mQNjq_m#6*PUMh zWU)xJW(Rf6d!t}8_k77{O6{T6aUER&+lIZ}%TmH1PcxnEp$10>A~tur(+haH5sJ*^ z%aF-zfBH2RdfYw@uQcy=vlbMjxMPi35E4*}zU!ryi6~Y}=94=QDc>#Z6e|p7f*85Y1UTW3) z&FA~zVRK$e@Z4{rKT_Ch^X&I3HZ0won>Bra=EC?Qav@7J_nWZthN^U{7pR&2$-TE4 z?EVj=7ld+GP@Udt@1ir|{a~MVAP|N7wEn6LMcu#Kr>!s37vw&mJH6FmQ7P3x4x4Dz zHYXz*CON!M8x-oT`*T*zF8mV;&xZ4PdocyAZX%?z17_neCSCZ)!Y5=Om2p% zevN7Uuhsk<;eFN0MGbQutp=+(#gr-@RCBaiJ9J_i>H?>OcqEbs0(oqM2WCuc!{Jnk zyIu`#XwGOgMVaF6P{yds3{_7T<5KP6uXXZOg5*%xa&F0G#S46=0D+j?Z9B!LW{?#!%LJc3Ih1T7YNdhcHU;~$I?9CLcQ>E-7B{k zt`Fr`z$X9^?=A9S9+2*MhuVqMMaWZ%j})v8M!o6=Q_6UCu<|sVa>lC*m6b&(WrEs6 z`7(e`PEZr{1ISRQYnjj8OHy|NPN+4oOv1N zLcLC!%DQfQ8ArXRL(ZlG_g!kZMI4kzYI)Wd^^)yVgPi)}-g7W!RRdfP188GaTf07b z5=YCeYNX;C-d2CM8tuLAU!=}%TAn*8=5JYxXxePGUqpis(JdEo z9!-OoiV0b;9z8u!jvnA_T9Yb1RMn=|QP6NbZEAqmQY3%ZjjxuXIX2bD_4I={$4@pD zUu{Z26`cP>{n~Zrqd1ymSKInz|AQQGWYX<^Qo%c;S`=FKrd_SzS{_M@%~5+9Tz~&3 zj)r8aLtK*poCZ+;qC*P9>IKNNf)s`**dO2Nn4ypv{Z(YP>E#|gjw)&ZW9mONX|7tM zh#fi8^c{7~#k<_&Qs(5A>O%+Ts!d$yp4>*>^HhgxySs68V4m8<+jN)bi??#KdvV!) z?#0pC?wG}1yn}?+Hw7v6=;$v0vVniHA5CZsk=<-JLkx>rpFvO)iX- zhB5n|!8vB%n2#~rfH?2Q?BoR);n(F$gHZ0ko}BaMJ2Z6x z{Tw3^M&3QtAXoSNI4b+8TEq1SS}nJi8tD4_?KmplTdi!GcU#VcbDQ_@VdEnWD;Ct* z2;2ulWN{$g4w3)laVYBh-4J;JC?1PHqvVCE)%7S^@5n;+y0YQAfTb+l2OpjT>@F-~8DW}BGV2NM%of=tt(^=Goy-2W>BzKtlVdcKbC?24B{M=g% z!_bqI5>Acabv}+v->a68qvvEL=FZmMcCtfwZkI(O6)1fC;5=$Fs)fHFUS(Fj3`^;o z@73?zFL8c|vxu56Qzt16&(p4D>giH5fZl!?3_F+UJD)mMFIO8I6tA;%?+5jk>**xe ze|}UGUDsWUqo5UP3(qmhoXhT0tR=Rcq6sV1>5A)VdbvVPEwvhkviHf|dE}V!lZrDP zsehC0XZ0)B*@@ez>PmH@YsBR^TDDTH{gku{+OJ#!jbEi!4Rw7NRC_;4X5F2%izk*06ogEG2~;jRMqa1vRcdJ2GT;{9 zMt3u$ddtZP5A;blsOV}n#@!p{S*{s8!M7SqU+b z2i1fY{dzH)x^I!`Nrrs|mmx^_z!V;Byud`7x@}?=mA4E(QCWFk!zqj~2{s!%SyJdN zaf}|VR)f87K(D@sK6R9;tx-c=`$FxvTcg(PHIOsQG&cZK+kOT)Ou;Z@Kud4>LI#{2 zgMhv=z*7%6st26mfWb20@pG9XUk^C_F2x1plD|f+=<0O@!nIbd?|SlZoTKGh)oO4( z{OdM4woX-D(|+CN2wJbMGq{NZ;59oD*N6PumZAI&YPkx&K!KUQBe>4)!W6~(Zk9&U zdkLore^LF@N`B7u_4Zlcm|!j7y>CuDWCfEMv!%oPNU5qaW4`|YxlfO|rq%l~f7Ov& zakxr<5Rn&s-vU`U%XXnHzo;hV_E|azW|AiJDJF1IC?(j-uRmN zt)$GzCV07U0FCTfAxMF2wVB)`xOj6Yb>|)mUbHOd(*rV65RwIXE(jR|N^djd2QSNc zR@uP;>3Z;0ivCTFsQ4QOk+;$0Y7czlyB*^(%STOoe%~?uH?^3-HKoxun(P`9q`;Mj zs^;#&^u;x#M#YBMkAD9{9Z9xhT=>kG14B5wZ*cB)qgq*MeToKbRL8q+Uya?zjp|Tk z`fd^qHN0%Q-MUWhR-gOvT9^psf!1w9+-ti2f>U1)hPYRCjYe*ML-9oamEF*`yZpsEUg;m-qfu^UCwsYZrZz9btuJdQ`#Tu6(uE$`fO3RDHl#rrLAfww@Uq)RK`V` zu~j{gHue+lJ06$&aswHk!_ki;SC`!-3e}u<4Iug|)*!67^Moxo_vbp>3X<(b@tO3z zO8QLcG*aSzF?37buZWM6$FS{UmSx*nU~ae*R54W;#2?cf#ozH9&8HND@Ci;j_@_D{ ztubfI=OOeHA+}x4@jto1Gv6CirQ`y9J2)qIbS+)YXKs~uXmcw%KbX)NcsxO}oh1l# zcq!h=hE4#RA6jh{wAMSZz^9b#f$3>(E~%w3d#Vc~Xp)_NedX=zR^uR-z}rDLwy7bm zw`<{|OLuo)-y7Vm`9n*)H!_fe<8GJsHZNkxKf`B#D=C}*r2gC0X^O{ey0KlYp}1|M zvRP`7p$^r}!WKc&8Tv5`>$@Sn$$y9Hr-YoK`a7^SaDOw+-2uDX$St&PhZE*u2b=YlQyH?EO+92Y=fdGk3SXVb`hza*!lccm`Zg-qg?aRe$ZZs{ z8xxTA09x$^-+lJtB&|ADv23C%yVXkWKASkRg4|oVZt*F!1R4+kq!WUW}rHNWBb&~u5JLH?o->jZm71+q5Y+XDy}oD;3U`qHQH4J zu;Bpq@XG?oJAmDr-60@-5QQTEg8;OTox--jGTu;J2`(<}<2%N8h;JX?j=%fy3^jkdZ{r2zCj6Nm4XqBn#7v1$)17IVXkfUU3HQtB0$SDc|+2i2;|#`WZN zNUbNPtmibQne(_=c|Id2**G7=1GV(e#uTZ*{2OS?qc9w~B?eA>g#<~r2O*tvI&$_f zeSSzC+I96FJ!)&-mF(&M1ar_@Fvj1G;ITI)s=$12UEKRu_0VLvrgRvY zqa+9T-7x0^`!Ft4UPE0Eqv~r`+eT@JRdbQqYcYF@|2(!bZ8@y^g_YglWKU>}D=80fK#xmD0WSi;k#06|tLoA5kOB1J>i!Wk~jAL}JlE^CxVh?~h}mASyVDK2h%n zJfjWsbVd-3Ii@yJng!ANV`}Zv(@}X)SWdI~BOC@^PEU@hRob|WlO16}E7=kD!(pD> zoQM4SAe;QE()N)J0neMPE4NqjWT5#s#NIYtmbEt~cP1>RZOdr*aqQCdSVQxVt1SY$ ztYW#4U-xj!-Gj%SdEs?8fb~<{DtdZcC8gOYDX)K1pcgN(ea*1MPd7mAv+aKUaLWQHj68 zgV>dW>)~Z_H2Sab8kKziXK?*+T2bZ@DQDDZ#dkE-JEO+=99iv$eNAwkWRFdvet6xP{A{><{olF4$~%v}Gjwb{$sX?6WSTSU(WrA! z{rIZ#IW@pz4#=`09#i(pU(uFxYPAaWzeg8w-m2(Y~eVx=f`gcH<{dpUcoz8&mO~Jp4fuX!~VMDCdl&>+E_?A@?hA zb5p50TvOh7uvms!M0qQWr(Rdoa%p`h1L^NX)?`l=r)M>H%_yhmITtU$>FF>Lo_tfrF8t7UYtqle;|Py+rHY_at#f zVNP;x>?thx9Z1k0e$J3@L7Vxy#%V*H$O85h)@=jMy|9#-oVGHerE#MB>?>s1tTU$b zRhmNzt9=uD3i*#_PkCoX?(AXP7gLMnj0ziZGlzsbLprdhkOj$Hd6Zu!GBT^xoSv;1 z)&B5tdVb)L!bAs|c8D{i6^9g7KsRo;BxlIS7~%eib$aG<8x&Hzz-bDbciG7Bw09L$ zi)j~gQc=q7B==A4k_!5Hks2@a(YT}NMZ5Tzb_!3@8|j*Q)}u>bBe1(vd!|} zmwmOJLG`&1h4F4N1wP=*?Yfvl3Y&iq*JC4R$UY`eSaUyf-4|BY$IM(eXSAmr67Td} z<~H_ofn+o7TO5z&kv+xXVfUdPMMtKOqs2FH8oE7htlUt8%ALUUjE6DIBjgSpZ$)Jq zaYo~YTFt{$U02kYw{Mt7kvG*)uagrQWu6J^66OYp!)d@xwVK!0I#%ddkKy#~O|`E3 zX>4Y}*iqh{F5Oi9ONC+aE)TcgPQd5GGmW>4QpsCt6@LYIIj7Pm+)1_@e6YV59#uS( zP1-HBLXmy2o6yqFS_RU)TWXnxvnEI$>h+a8JR9oovW3;tcK+GCraQJcLahbv<03IH z=%z>Y(4!t2N|$b_q3%CmKMRcAEJj|rVC)%|#~PBP!Qw`AUD2z{ET{#iN_)AxAW-UQRS)YWpLIc2w_&uw*wqU_i~ zA$Qg4J~k}6?c$_ihfSQsK1dSRQON@Hm>o3iF2?RwJ80fr)vVOpK|kJAA1jNq=$m_( zH&o1W{CN+Hp}LDOUvx>SC|~g`FhAzmBPRRaJT2wv-@icc^WCnAQBQD?6jumTyQLK@ zzex`|`441_h2sKNmQrgndaikLQ>!B4CCd@bX!x3!ey>Lt& zvrOK`-CJnVBQ?NnDli4+fm>+FBh{S7$86q}?;}p6mk-~GL;2bO;VfT4w$TsV7{I30 zQeN_$kpo}0(aniQS-f;SxN@-?Um~#~D(hE6V#f7U6IM%TssghouxRy+^LUBKJqJx(&7EiHmm94ziLA9 zSv7qbTk=I5g+Er;^vK83#wE2hMD){USYu)iji${#Jj8&;{FTE@8H|+gyQ1(gE97l} zKi#rO4>Y3_$laO(^8;YgEz>0avmHq`LN9I27KK}iQkHS5*feGpA_*N_~sE3e-Qg1L!YawP4|D7 zd=sQ=WXfPGR)IMU*u0-)1)*?J9B8$8bKWWD=@t(LAU9LotroZ6<1l`vnb9Qw zAQAs%8}B+H5Pk#oi^J9U<)?ygKEv*48dQ)So))B2c+Udl}eRQpA>>ABNIb1atP7%KU3iV^6MEAA^go zRCw1Eb_RuiLM zz=-fp(R60>hpuS!3hxBOTSaO&ZoVH!mtLzeUgi2oy}wVDdVh9%s{BR`b`=P2@difX z!24*YEM#w*S+=kNR;?%d8#SicEm&(@Qi9`cyPd7vl=7?xQ;p(s20;y&ZAIw7g@h6>f2Ewxf>C|D86X|u3Z?95>GYJm2DsledlsHWLpfK`Z8D*Vay_rhu> zOzKU9sHOzedzLV1xX2fxAy~D?#N~6U>!u}HqW-_8Z+8UkFm2pnGd5=O=m_jUDTNo9 zJ0KSfy}U&YE1pkE8!6OL5*zd4Hft;x>!MbZ(N$FHcIb{wXvzqK&=i>8z|fQz$ns;4 z`2}%B=*_e}d7eOUo${pvd%%}6$@vsNEUU47juePI6OrHg@d-qG%-Gvxbrmh!R?%}{ zec2LmQQ%;mz=V(_TR?e+?H=yz)IVVQW@pmOzi`ViV}EKfgg!)}QMzmF@N@-6`vS9h z6;&)E0_*)IvFB5;wF^E@t(1K5Zge7|dLo~NfT2rxH=4y*kGE|lO(-HJrEU0``$RiX zf5xoq3OxWs5?F1YM5t|-T0f>O5>G9r+G1KT6}$0-jGQ%XqS_a8i~C9Sjhk~3!zbIg z2O<2I-vbn2g24211gK;iS0jMc5VFdYsd?;3Gw@VKZUL|vud>t77>^Wgvj83OHk#6^<)*Ma@^|6cUD2NJssOsHQ-v8z9H=eAAsGiizGH ztq@?_mB771U4d(nA}lQj=3fVRC>%XmE>5B8>>L}Z7IoCZ;cvc_fy!l8uK`j&Da z3ysU7Xej|W^vtvX)gC#UGqrozkPHdb&@0LH51BI0KuUOQP2e||6G4MTr3pl(7vi~0 zgKg2$N@gTyF&Q$Gg4{BgJRl_IR@_Kgndq=x5QJPMoi{&7Iw{d;9t;~;0svv#q1x_; zo=xieSCXs8D?Sro^LWfoLyuUn#&Y8kBYCu30@vI|Wo0M!Vy+-6uws7m14X%uvaXpw z#8Lc^@Y2-XU3e;sZ_;pgv9iITsR1sJOw&TTPmykq;c~6~6ZHkKtip8G}6c;UO9nsT;^h5;Ei{!Wd;Kq!H3py!n!)5xcNzU|X zQyk4ph`vgcFJnNt#RM7gpVr|r-8Fcb&QRSAS*5!HB_`2%4>4M4Jb^S%Q9sHuQBOV` z@Ae^|QqJT>oyq3{&BZ&cy8+cFa`MV_-cv*=ZsW<*B!YvwAaH=G`aW!aZh|W2zqBN4 zmLWeASmW+-^pQ#QNQ(ezSS%eloQ#Zm%*SJgHv#Mbkaro?12`M7^?6%U#!>jK&&QZ% zHpapq0l#siEqbIRhS$9ZFVo@jj3*r)z_4A1Ycib17BmoQ#GbKwKs&(sLx8N!KyLsg z8R!Hce;$BV0Kyp43_vzxEC5^>6AfSsW5NMkV4xC!vkdqE*u;PrfHVg1!Cmu=d1b@# zeT$7+_Mjj{3V?`s;jTOBpc!c7Ac-pPcb%ioKb$ z#y0rP$s2!1VhhfAgus8%*lHBbD<}Mw!6O`N%89WC_dUPCww~#6vK58;h z+3io2d_|QK(N09k-TscYzM`LDfboF8%h|uq=UqHICS2#G-}%U;2X$U@_ZJ3^y_sJU z{1iVQ8SD&O4OdU-3^SaMtagagT{NZ-1<#V`*qdjKKUNQ*0Dm!EDeg{7{Y671yf|I- z7qvs z*mh38%6)^QagrV=2h*y9HVCaCv|6cy0=TBpYtEQH&c|W|jTKU_A713y1d%;jSAG5w zFp-{vWuJ|KOH0Eo3u^wHHv5#0?*(A|LLeW^*XNH6in zZk_c{H~Z6}V3Am2h!fESh=35W*wC6BAtI>654-e;Q#bk3)ezCBMC>NLzB=qep{t1Y zB`!I1#FL%=xTh-OTw`5)9S5q228Igr@CqO*V;5J$(4Laf$d|yuAM;fYN9AgwjG_3& zZW0kPvopnqik3>v9yB*pSWA}Z&iKy8T?nV?<%Na1go)xY>$~F0&a4~Q#m%dMMeG1m z$G@0-awna9s7&P*kc?E^4xH7RZjQxaBHZAXgtQ=efDTs>fs`LEepW_xrbQ7V%Jqkp z{*L_-Vmm$pK8EboMJ*+63~jG2YPi=g!wVzh?Rt)<)x`({u8|C=DQYWyM>)Q#DT*3A zwm$YjM{B~c&pyxhPsc{`KSQ*-iR@Ff&wH58JBUJCb<9RI0VX2F^HTBA!K~?LDp@tM% zSXWFoJR?&*@m=$19IE1a*e&#WNYUMx3vgMrh3Ml?2%~xYx{fnkdFjUr3hb@Duo_V@ z-N5&ZTF}#a5Ux*Z(8>BDLGkY8@NFR642J8D$_+&i#W0g*SVXMiKbm%0#BWNiTQnh7 z)HL*1@pG&&7`oHSI8jA8IEIeLiF#>^M_@LP>VspX0P=fdm5-TNPdBS2odZ&CVd|5; zjt6Z_eQ+s6PRnj02r~kCN90q$a_aNl8UR+~6*&6Je-MY*EjF22PBOMJM!r2#MM^I^A$|g-L%m- z3NiAv+`Lr<1#E{=pwaQ2A>Z%(*(7%Y(e(A~V}`tqS9RwA98fa;12@}v7Qg(r*I0?O zi^?)>+!tT}u<7Gr*(=%eXJ#oGbfz z%^R{97_|GjuVuS0gpZf(84j%R?8Zj@^q$kxxDJthfb-etZ%-f z*Q)qi88d)R3NfJEr@i?+O7mV(1@TXIH%>MszJ_vIxR2UsBC=@EV?USNY|79?vY8vV-_d4y-L<@Y3*{hWZSK?2SrInZ!){8gn_2qc`o)B+) z8a9-EKZ<7yu_)}T+c|Hcs&bFgy;fqX;VT-}8uPcMedy!XqL)&A39i+OgtYZwF+1@j zQla!Iamn_c^>U~7@o}-^>C*EUE|y_u1;(T^Nwy+<{N>pVKOUavzVLI|UL2dte;mh! z^JLpuSg@Nt;>YK1L!T|jJrlW<8_o;16L`HopZyt(RQ9Q~s*R|q zbm~oq+lUyY|3fluLSEuA(qiS>jc$J=YR2qDm9YjYgP~WJ`BPj`C@k|P@j;nAuFEnn zdq%C=iZ23A;YvaJ)RQhL)vWbAQY)hn`>>T`OI%uDe%OPGv=jZxx9i2!Rr}QWsh+TM z*e;=73d~h{QF=Sk*}F{%siuE;F3IkE<43pKiHhZ~w}gV0_C8tD^{>j{pYKrR_M%e9 z9-UbzlTo4{zC^+FhTjjQtcHmUa5c>S0)W-H4~||9tvJMP8RbmmU10tJn0(#HQ>!z5 z-5w)!*Uq%1z3{6Q%du=t8{>vc5_dC}`yxrUPD!vaRYF*@t*2Mi@w_SA>)5QYw-dQ_ z5EYaPJ*Zj-;h$I@CmU@$qOxF{nt2WB?NjgM{{7HL)-<2N%k9+tT`f=J1Bve>>%?VQ zUd@ec9YkPtvy8c^ICf^O&y|!kT^ezj;Qcfo>uEaOrBfY5NR=ySa&R1#ySW=@g9kpY zCZkSmC0TMC>T!?#OBFkcM&$`{qyB;je71wXzW)0l?g8cvT`9FAR>Of^>7S0GrT2Xd zrO+=4Pb6=>U;0tYPQt(WE*)J?M@PT(bJ^mF;%(=j%@xb>}MFd(xA)qs}CKk`_tdmcm9t#@d&x*Mm@>BLcv`{V`WeT4d^NY1M77HMHj9v zSsDL{+?$qn6+@LZ9mu;IG-$5&AKBC*ZeW{>mec(|Go5S50UKtx6}}p7EK)2dkEZY3CO1Wo){ql zwov(AqDi}CO={092qi>h&b+r$wE+Rry4?$aR2w5R0a%Ue;9%m0j!=g~?3RvtB2z{N zh!mJ-YqYi(^hIZl4)p>j4K;e%OVm)l7ZlN3#4GK+Xk>4ZqnP$k>psHY@FflJBMvBc zS~?o^6`lqqqq3t#KXJ#PtY7P>H$b#=RVLk~xr0S-FWb{F7-i&D7Kq&OGgL5GEL6<@ z(q}`kl=^oyU19gDqf~vU@F{&UmQ6S%KgMA+aEl5%n?+rQiiYk3UUU5!bN%Vtp`t>X z_kBO!TXjEyw|wKR(~7b<2jWniDPuay!y7q6=3|5ffjXd=+jA4EDx_cTB2{s2vb|aP zCU~!)-cS5QK#_95W`T-|Of(#8n?S}`jjB*j3y;AcNPll1lk1~RFOOc3zxT`sk`)fp)!rYrRI7;(T5PlLva zFkW7b1+@VU>F8Kd)8D@Vst(GfwbV7PN%jf2b0?P`#qnjw6fsWuz5#ufBH{u!dpoUZ zp2^m|hWr^2<-AEyl59&0@eZJeDOdvDuTOPS#dXEEKKYLmy=&KnOy+k0GpP>&h$Nf% zLbWBX$Ne$rj`m>?XH(*(WE`C?%ynEnT0c&NxUYg@Ls2^Kq)X#OS+6=!GoW(oge=mE z3yvb=A$Q8;P^vspELCu#bI(MPk458#Ntonem*(^&5%2CFjXB{Hj860&z84#aE%^(z z{;%ThuT9-1qqu!))5*yw?!@B%cX4mH2T-jk0+*R>ZFg~0^S(+nV4C>Oec~xu?5cl| zR~qQKUXk>EJ0R($oTR#GqMFwM3|$~KMaMQeNh8xlU9Sus8>3?%pP-FtqN;NICgr4w zChCmpJYVRGKK$3nw;lf;ka8ye4FK8kR}YsR{|+2`LS3sPVa#?2>lQxcnqpPRa5kO%5C6i(o z3m5hfmp{;UO@<`ya|*U8L&f1zTo6YvUMJ@dkEEmPyO7rm(X7Fjz(!?Zr9EG6+r7Pk zz+*+Gan?;#`^@7h;R&XVEk?&rf6nJ}Q_5oc`|vo;pCQWB@X}lFlZ{X_=Vi}?Z4Ld> zWvA?y124*UE5A>+<{7;2K0|cj3-dLsqMULmoRX~K$Fwuq2(~?yR}+vn2bowcOT(n@ z{sbPB^V(3ZBH2sb!U6K!%YhiOa`p}eU^N!SLSCPhWpaq!veudCBUB17`7IzqUA|;a z-^*>Z3k}Z@HI*7+v?N26^+`ia%v+Bp^KycBWBlc`c~F?+aE7>Oa5rv|;ycOL@!Q8R zA}PD)Q1EP||7aa`m<=@(_5&@PE#@f3Dpb)Xsw;noP#c@*;ANFHWscqUv|`wKt8n?Tb1d|MWgK0OITKN&N`g zWz|mEAWxP^F8nvryl=!|CC-a_ek-nemx=x=3cm){(1mWULC($kZ&m!c>&Jn89D z@ng559xS;UYFKEfXuVm+0EucXFjoX1o28~In`IOn$SgvhmXys>-kGRufmtAKz9$bu z-ZIADB^}Sd6W5W=y}7yGxW#^=<*USaLlg2}Emj!5bnIR&+8N5HoM9s`wuo?PAEp+)Qy>VQF9Z2~F54Iw}o5p<`P`kXQX>vX*aW%35Bzhzx&XsWack zp=D^^pTf(_{=JOSNRRUBb6WSOxLN7wrx?rmlkOK8hyswpbCPO}O&f97Cg!h+ew35c zrmWIx8XegtzE&o;r{UYh6y=d;PXC2jdi3tX;jISh@F(1o= zxLafy47VKP_lorfrA|)@-6y_MdflYG`-DaDxk1LiM6JZvH(*q;-Iog^9GW_HJCD>+X0cOw7f?72SlR(oh$!W%yU;sJt$Txu~+HYK~c^B)nz?q z8}P~ZLEp{gZeO8Bhs4Lqm@BmVkcjY)aK`-K68T=Cl7~Ta+GW~w82fxRFVmC5!s3;8 zDa^&>Xk5m6IS$j@5-0Ep2~6~N=1|HJF;Gd)p^HaEQg{uHwGoeEWqLFMn3U-sFE{NW z6}RWfM*3=!Y^1JMYJF5JQr=vo+@n}bUAgFJe@wJCc+G^Uq3hR~Afr7=cl>@_l*N-G z=jqT1@rSbJ9NA8aW=i{WbnK+~p+snN2p&&{A2p-te~Yqp7k(nsC#1^sMW(?nC66cf z8EC-$B@CYe7iltMhUw7)^k^5S(&4{F!>I7Hu~?cddd8z{X3P}Ck%=)1FCHOBa2Gw< zh7?JC^i*naN;Fq~Jww)0VvG`ZhMt}h)0FL}Y5ZwyC#0Pw$7zwKY~F`ER2a-QouX-H z#CoN|UsUs~Fe^i6JDQ&rw+vpd`1Vq!dCf>kcK8H}JugNmvro|4^O$J$JwYeV3sUwU zr(qXBcG7WLeL*zxYQh(cGR=>M%T&LPqh}Y;#2b%M`bErv=O3dVFGBo3qK=nf--~Td zn=gqkloLm&Z4T6Bqa*YpM|`7X9;U^Y#rIzSpr$}<>0n8$*BEMfMf6c-9inwtuot#| z9I6>FU7GKGOs1YXPZm#qUEHHLs4ehvi;nF( zXy9GZ$o++^xtKRqY4u%kLK#@aG5sD-l@@PseEyF}Fovz@CEKKZ!aF%W+Df)aU1Wwv z%WW?+_^AiEJ{Ii)9+Oy*f0cl*d9Shg;LG$D+IPaXeLeBDyM$ zO_cUTEDf65NfN9U1jm$c6es9R89UI8@SZlv%!{VDr(&yOXh5%@VxYqZG6p^q$==b= zVqGwyZk}Y_0hJLm{3qJ+43ov271ZiE>;!N6lm83R$or4}sG6T(nN2br1=p+k)8H4P zmm!DtzrZ>=v_IW>A*vYurP41&yrCVne<`XNY&7kqsBU;gYhGd%R4bfLy+l{??ME+P zibP)hHhCpFdrs^tODxF_?@K?w5&)pTSHhJApn3IIELs3}RH zyzVt^bTe&)Ex;VGoV;ISfq$`&!}41EZBR~{9c2o{NkxfX`6SuTFyKWo@D#9AXL_<5IW?hwN!p-Nxtq{1toa7 zqk+4&%HSWjoQE|`?w;f}y3N*XIdf+#dCYP$l+Y$BE0@uf653^-GvA~0@>FS0Th7-! zE>?;|o#u|zlG-l@<@Hhu_0(!9_$*_0Pc6)ASsNLxT_YK-Od{c-(L|-wQo7`+Sv(gf zqm^bz)oDK2nLf#bgHfgQveYFHRB_fMUiA*A9PAE*I?^`aE7%7*7Tgx1LQ4d&F zooh{Hm%)K-p;vS` zSkQAntyTF8pK=_$bBo1ob|Q?-+}r-iO{QM{nvXKJK27%5sw-c)&?#pjlcc86KjGtYd}IZz4F%c=~QS{ zj!{XEZDpA6kW6O4Lr0DTY}#1dw6QD#DyMN*iLRB|no3vF%DAVd^ZYF4%`~c6NgI{s zI)@9)=XzTldRyR;Kw=QglHB{kBMTjRKo)v19KF!@?Xu9`dO}l1U+{F1ft5A?hQkoYC8dpX|Cm#-At=7^wEcb{_%klG!S`+`A;?f_ zRoU@kYSOsMT4m3nU;$%KN+9e=0}s)P%381ze~1oM)?%s_N0{CC>L7^P{C8kIhZ6+w z@ZtZvZ;$1B*eNtvtJ1X7C(KERlM@Gg=pHGPdCwOdndRzs1F%t9`6aPn=fK|U4N>Rq z9+No(yLrk%`ZQPzQN%&o9IRDRI)6eJgSEIcyNwAKUI)7k9~0zfRiwEq4+u5@D6q=d zV|E?T^X+Q2w36v~zi-igP9u#!TY!`jGKA^y@_r)LfDp*daP;2Lm_zKA-p)i7A(4xK za<1-cMGrh|%%unXG zppGQeL?@(#(a)h;D9mrTI4h@(P zi$k)Wg-y=_Mkg;0v0I{?sUQslfN@I0qcl2ST}w|JoyL(;H$hi7c9Y|TsWguhMWGA8 zK>>3Rj5s<6L$eTDFXMb5Wf|j`0yCg(17J1Ig`=0zENNTnI1_<^p};s5G`M3WC_-&Nt=U#d$(qvjXKBb*e+SoLjf~BB!w?WjumC{wSM_=Qb zzh|{{0Ny3#0;qq(x$5QK38Z9cGyus``W66IV-_5prRE%Bw+wP7T8{JEz~q0OWZm`5 zYHKYt?MR9t0lS{ zxOjyb2E@yVaXM(aZ zVe}hF)`nWVa%O;IZ$qtzL7C8hMHP!?P{#G6YO&gn%Hh6rG*)}19PQ&c9H+HWl&QTO z6`N?D2KT8W!_Y96D&whMQ_ZSOui*Hzsa8r+UXP?>iJB#N+Q)1O%81{OM#>N7-E8DH zSR#u|;5mrVpQu(djCp5xa|pi>3?P}r$fa0s-lGZB=@Yhaw(?34bKkgUa5 zxm*l#A8Re>pE7ECK@qGXQ{B-B=@1%eu|#c||GwudEtVc#A6Y=K%nmSj?k*ip)+#FX zKcWZ8T5!j?{R(;f8~HnVd;z5Fhd(pGJ#-WAhFOiz;OIRxjYI5~rOrg)@o!+92X6ZG zqhT$yd5YsBdfh^67CG7jTvY-`>mWq=KBkS$hha6)v=dL=y15ldarY@MyJ=4(m($D(8uT2-$%5Mbs}$BryYjf7UiYrBperDJOr zrO86;r1b7he+#XlQnH()l&1AEcy02Mi3h)wBtwhPj82=Hdg78B4K9iMKv?2f2TD zaP)wCJE%ijElP>HNw&6HOpTW}5O3yUERGUQ8wa@Js3IceGBP8-D3G>m@#)uc;?rL? z2aO{)=x$q0D>Vb*@=#+%qys+s35j-E?TRsXWga!)m@-;{UF7YG@I?(JettvxuASCY zQ5(|DcIZ;o8dCB0+RqA(E&bje{x$#NLke04_u2U}`iy+4*Fmf0+ZtX|#^=o9TW;b( zwQfFTbkJ&)aznV}#vM5Vzx5IA>7a!x^B&Q|4q7`U_BthX)PfZM>olYzR*`{M>C2AV zn9^e(; zPFh&ymk)sR2N9%ybQS5!Au#9)fc?pa6F^?NN-GdCY#m2Dg6mbuXpi^XGv;%IAW^HW zvee02>18J@<{oUJcAYWfKZu74*j=%OmUh-6m0#}Dkt6PVydkC zbz(!=TU^iN_GaqYRV$a4$b=I?SWmrkv+sLipLY`W0zGXYr~MQ0kj1wjM*2-g;=E^m z7_jJVz;FcQ&4L%>EuIgJiN%8^#<@%acE4@$-z zYR?F`;4&}vSgYh%T(;vSbheDNV_SgTc(+Zg3r^2lF1DeG-L;T3clh6(ARYuCQ>sw7 zJQ@fD)aaX&d3Gh+mMcw&_jz!YYK8n zwlQ89^l)BWdOV78l$n2FMQ3k=cR|g85*_cAUkd9iPk8tbcRq7qrQoIK6VuESs9IT? zp~wucdhCM#-Tvr+`Buw!3y{u~F$|p*S43f3O*m@3`7CPYaZ|!WTg(E4+RP65Ys0Vg zMb{|-*~V!A`N(;a%`zEaw(;`?6xve@_H79tkV))h8{5O-zicDE%ssHDc2OIJBD}Lp zAZ9Bduw_cxVdC#;pvGHC6oi;>o^;rcNFcLm2iJ&-ikST9rMWjQy(8r@b_r zazUej-dgkUtqK%z-;$e>3_A+049GUt5}Z4F6gK1EaA!7m%ac;&|B&YO*1oGaTSbAM z2bpF`fpJZ|Z{6W#6PNSaKz@+Eqe^Z1XtPV+ZV5W_5sd|B=|&%Ipwgu!X??Y-%0QLI z_tgRwO{FjTYK=f98OPOzl9$P=HC|EeRMX@g5WrY(b5ZGd{FBT+;~6-4 zVgI3T$H~fY1SHwL-et;2zhqk0U#nIwR3Y8S;@2n>6!*hmOkXD zyu_hOGUHJ-iMe)|P~3pa+a|3%A4Xo)oL+*E`USE|vL*FGR#Wv{TLbK)gN*?;*Fokm zLI;^cZyjU~pETzpL(#n;!vL*x(R%Rbg;T)*&0KvWU{Gf|U&&~)r0F`ybo%Qc(`nJ1 zwhq*SA_p%Mhm%+Jxnx7}YeowPX*Clb zu*Qdl&0NC;b2blL1u8Fh#D2KL@*LClsBht6AgH~A=il&}r1n!O>(?>pL4z*8O~nRl z9V>MJrMy+>4DpkeQoS09&V}0;Ir_&09r69p zs=nx)%We z`VaA9XC?6w|4QP4Ig)tJEjl<C9qzU`8T~ugNAFtX^k6V zGrzbjoH3Z;HLxf5z}R9X`qMVI7{$@J=0`!|+BOp1|-*9sZ2r zZvo@pO_KFlsL7f_B}Qn?Th2g0e)BEhLVvay`IKW+G=L5aKe7^$^0418{Zx{J3RP8kw!`y1cP#PK)`qS0(y(HBph zV_9G^Lb@@)3zKqF0pJY)IQnRF2{bSQ7qG1amlJr$=qBzZB-kFL)RMNykXZ5_t@$ZS zEmVE9_H|7~lFv#}@YX9%H9z_k^$)Y9-S|K!!2vjypAsW8zTU|3VzlPtn(!S@4Y_~h zJOaS|qn_*^?IDh6SJ&O|_m3$@WTRfYEgSW-zh$GgyF?vRHBrKgDe)3pdqnF}wem^b z>vIt%msf3L>v6$9(vQ1$V05PWD~KdANZ#f9cGqV2VM#t*Cx79jB>(kAGL6#`5_LCOk#Fp-OpDQ1o zSi0;o<|v)mgY)#qcrCfv22{BGlDP2kjf$xb zo^gi+!i&tRZF*N@%P3(!`z5S+B98j z65AkBD)Btti$w5vh#!cAIQ~5(5wviI zHo`*;VcSQ^;vl3AAy2C|sCq0aC%-Zf8-JG#nu?`$;kInpjyDB-BF%gQ@`ES~f5*3p zv}#^GF36~+Y*~^cN9cl8Yf*hJN-ilC)hX?{{*Kb5EKtI7M@k|nUHgsNWoT8r)=SAX z_ueZhRXjqUWN3EJB`9kif6EF9&RwI*Gqozp`A}*(Q(K(2y&5FTl);aeKv0w+x}dVHRwK%QGoqmUh_ld(2q! zKGUgRyi7r}(HtkM&_}bi&S8I}^B_0t`gaxeW{b?HKcoWLl!bCbHmCMG{+_J`8lW zN+k-;gwf(kMPB3GHO=J5G+-xcx{~^5YOTT!{y)CH1H8s0>Ob!!SG33txzY(jL~l`Z zNe~i}w-BQDy4oVqNw`t2i&b;at0-I*>AlG>9zL$1qK6{e{(JCD6XBiTqTknN3lu_QiRLlEW*r~EeT|BY z&FCO9me2M-15mV_i+O}M*&Es+f(0T3Mw2~+W3WKvByyhaItr+zw?$16_NT)!|l*Dn}8s3~Z$=@J^m;gJnTBj0T7khV-mlK|V+`{lo_ z%+9UCx&fjrSkCMg)}8h{6`zX@7bpB_&|Ep9Kt3+DAy_oMM7!t8!7;KwU-ib-TzJF$ zZ|n==hiS6gAi*q@1;}Wy>VNr(UEF5V!vj$ykm?UqQ53A{2dLOQh{^Dyf%D`#eLT>* zi;ruL8ugGXSV{r9ZFbs+&9-g2_@4w@(;1%E%R=^bTnNq87}*xzHxKR zI$(V5&;J#=XOj?G9F7)A&hHRHXYQu}k_S|%fIJYwet=K=KZOnPp`8R_oqU|aR-okL z3zZfB6_&A42&)Xo3t{)*IAGW1zfytu5Eg;V5W+^m3;dtLCKSbER1h|>s8iTBF&h`& zdHH{ZUE3gpH4t^w@^YIHwsjwUnlBG6-2@qsJsL`+!HH!L$t{@{-ks(Bhkx;Wk%i_g zkd?wMERdoi={(WChc{YA|1OYAm@V+bfF6fK!dFb%OQjdeN`XiiS$xH~vy`$Bm3V?T zty(CDR^EjPHW;M)(*IS~uCEhnMjjK~{@Eh9ZQDa17s};}GZx#y8)G1)G+>&)lpdS}wv8`?bEnX}~7q|$UWVb{*uQT*?v0Ta=fLei{XEq74 zX}hWT64@tTGqms!^nMXFz)$lf6k;TrXe5f>O}&=L5p04Nty&^C%y$a40#3GpE4^4E z&u63N>Gq{Ep5N-{NvD^|6UtsK#C_3dUq_<^Zi*^=`_x6ia5)t}UVC!BVD7Ptl9$VE zE4BtEbu{)~mBDbvD=#L2oWt5q@QOK>rkoilk-?_d?(}K7TrtqY;Gm%f=C^>0naHN>+W3SwJi^=RX((pdWg^8sbj$s8e@y=c4lnv`$JLMmSW)P^NZ++W zt|FCFfd|c8RDfIoM^^}|mlxH>a%+t+-{P(0xl(Rf?&3U=-seB*n;Pjii;{2YY@{!~ zm8PwfJ2o8y!*=8sLECd9!6;rGk3hY1fF{p{dLIIZ7rqBRR$&*@V>cSHN-kOHF-pI4j&&xu=6+3q<>G6G z7(L=rtHswm+)Np(CwlU>zbLKVA7nZW10f-WS(dK1!~XYE8{6x;LvZ$?v>|_9h^>=76s>d8SPRTnr~K z=Ot~m93A`^Q-G*MlleWaxb#7H{^T1l@N*BN-K*u2mVXT%kx?g_CIN$tG7a{mXREOk z^omi1HF8495Acgn?N}8L-bWYPcB`Jm4M&;Xja{_o4${wSu(aI4oo=j=DWzJT+*Wra zpUyFQ_)7s4OZV}Jh*q})W>wJYE=FK1-IwARhqbXm9Lc!!Z(h8xOjfK#%hEQO(aE!xxz6k=p znRYzbzZ+&&C+ZylXpCzlMl>n8RV zK^4qL6z7x9cqG|Kc>_KhDHpCIul4YjL#9*B^>T@XI8fkImU8<$tUp0Hr`2`>fm3-H z-XS7BU`QiGY^Lo9U=d*IaE!y+&LHwkD}$vAJaFqq8`jJ1*a=s1-yjb#NANmmmySo7 zU6I2%KT+BSEaX(3O}975;dxgxY}xWy#mIl7yv+6D=P=s8QSNH?K>LW#I{=3XrN^zN ziksvIg??F&t|MRO1nS;C3fhd7o+~b-ZkA)JFLr^iZ34tyR04eV^r_!#NnWgU05k|q z6Svu7!)B}K^k%uc`7>%3pqmy6=zvvJe~aAOb^bqLL|f$cgIk z%x5so*eVZq4Sg9#dAG^2h37O73FR2uaoj^-I`blox^Kg>Xp;u|@@;YpsnB86v)oNc zUv{PMkjuG#{A8tyJLDGSB=i-a;g*Ttl`c0+C_QVVP_H2g_1JP+w?n?*y7!}%#_g2r zRQv)Tj)5n7e%y1k_|{FC$*q(-Mi(8VMj&~#Qx2^i_R(q#LHnafK&XG(|0-fLe-e`4 z!P;QF7CsT*(`P9)+9mg>=#eY1IkM>g6xi~El`idqz&anC0>>;90?XO{m%!ySgg|#g z;2!ul2pqA5s_&NDxGs2ar5U^Bk~mtvZMPia+8MvE?Uoz5=0_w*$0*Ew07dKAL|keh zP1fZo?dDr6FVi-oOc$&DtxPw1XEi**E2I!*TB<3^bQA)kmS=E`!RK6r<5~aMm2Kph0^u+a$rhVz#LPiWn}$-QBsT z=nd>b0fgPVr~bD$aLXZV&D?ZASfQpcT-HjX;7`~>qxZ@cUB|q! z(uTeA59Ruy08P>Y{|jTMJu4`H#p!mftWpG4Vp2J^6f6T&(RdD1%<%lJg^SzkF6cZ81H3 z+--g95!oWK-nZz)QTb@f#D(CrN3302l6YLq4-2k6`OgfP97VAW;&F~v=d)(=q|#x4 zqB(k;L0ON-m-E~aloj|-ehvxAit~88Z&YUfGsd9Gv(P`qC3}ED^Kk)6NGA}yL?o-+ zH-h{8r>{Y9m8Zd}1^&&adAt(RAL0o=6?oLzzK|m;^xzc6LPgOT?T_^UEbE& z4)PP*4%|v&DvgN^oa>0;-VS}pu)%cxgxtK%Yq%-sB~0{$$BRSwJw&pi&>BJCiyY`- zBshV%p%hqHu3QVluYz86GLn<_@CFR2+aLu{zh0xE8FFRjL9{AEu241fM=uX!}E_7ky(F^?l`l|K&yfMv6+G3ik=7RpAcs?f*=seW$S*ZuT_ffpaP7 zkHu8w4E8-;d_XPF$aV6CB=e+}1(#^v8F_w{mQw_OssI0;ZW=aR2)I342$(Te2#_aH z$3NtWED!0A{*X^fZ1P=w%Q+lD@l)r7eP0Mh8F^m}J{1>T1?o{3;I#{fW3~Xl4+qwF z*_VcI&YVFbFX0S`?@j&wCAqf57G0x&D{_GMfi3(jRizA1+Z%Wcz9`*eD>b_!Ph*{~ z)3qydL$50{xG1*?IHi+w&tW>^OXaW1e(rg%TT#6W(6cLYAoT`Bxpd9ylIat`w+DDm zOu{vnsaEf+qO~{$f8xCVYY-r!W4jx$+@0w^0C>W54I^RxPk8>`SLxhUxop1682Dr9 z@QQExHQ9%izD9o6urUki%hde3>|eOnWh?HV;l1bC z`LJAO3cXCjucO!0@-kh#F30Da*;`oGbb`We$OqW*im?~k!$^T`|0M20=?v2r!lr^6Jz)Ef({LD&n{z&xJ>83ne^1dC5 zj(*ig`(ARlVSk6aa!NKM>uq^CJAa<;-j)8fAi8FHqnktmM8sfaPAKWE1Ug&BhSeiQWaG}p)K zLff($1>E-IC;UZbuvM8ivm3aG!DEpaMzW#<;rLpHT zcSrdmr;nhG*#k>uuE9@~k2w2)t^8cheDZpPobFCd9?7xZegb(cX1Xfz;}QI@Ru5YF zNUrVuMS5FUG0==bhmzk<`<9XE?t^1CMx8#A6&*E75@LkL6-zR$4KMD&CyV zd|Thwr8I^H4dSQQaOXG6L{EH_6wD2Hi~e{lkCM7l%oDkSZ;kKiBJ#vekHGoM6H;ce z`ZVo{yrXnqFce(y-BodHF1!Yg{l`vE$%8eg@q5C!2RpR~uL_|ByRwv)XX2<&^inF3 zCC?3d0(8_{H*?o9HWT$d)Tg=jnR3T(`gqxX7#nn4^L&e(C2>e*}Mw^F15zc@uzate0vi_l1e+% zpUD#|e1ez2x2$XaU!Els1yB1B!Lx2hLGZCBZF?q1d9L_1%q4Y15tn2>39;z}Nzdh` zh5G*hLAMM+YerG)=W>mFWm!N!hlm@e zI8FT57Ha;F>|df$jocy@BUQZs!ya#ifb!brnOsW(m(P7CJm$#ujC;1HGv{u z!QvJ!rfghjOb92ha)g5 z^4EO;IIP7v5$Y``Y{W1BmpKIv9~+{dw-%x|cXsCK-r=~O9J|jzL7xB0g-dS$8{DRr z`!*z>f9jaEqOmVp_uE4`J68Kq*|Ky+EcOkZ5F0 z-(6Ndjcj)QY`2Tfe2{CktqO3QW2l)QFmaEvHK~%5geBX%nTLv_ZezXhI|Fy5XRsH+E8ok8)_r0-$YHfBbbx>I8mWzw?^}Ac`ao26GnNV2%0y zcbh2ltsy*hj-?}@S+x+uM#icFbuf8K_Ef>^`VQc5{t7oMMPgEt z*e5w&(nl zEi>=nmm{yed2pPgd2Qp$^Dmf1`OkftE$WE!y(NzFf0dhsUvK6MK1oe8y9mlCyBr+v zke#IC{wL|h+JZE;3)#QQ6`QOUDkLX3#y-mI2Q~=tayd){ehuOs<7#LAwUcXuPCmv) z1q@#Aowq-BF?hX=qt9REg#1roJ&+YfZ=hWc_B(XhLdSFDc`R!4ypBpVJGFh@Dy0nj zdHcLGN?pJ7P3WbR=5H5?N49$56tIMTUCJgZE-62-hug>@DIuP}ZR0#W(+Z*|m?>?i z1CkQU=53^WOliTqwoyl>gcQi$Dg;|It8b%eOli!{Y@iF+ZO`g$rRuIqMOJVtb#qnf zvJzWqsjCvnT5q6xu1YJ*i}hAa=k9J27{NRzsOTj+ObP3$nVZs$9o$TcNhwuw(PqKB zXy(JsJl~`2O@gC-&ySvM)(DY)!#aBHrnFa>N>kRjGWE!dFP*=h zR_9gzWSiE}$b3o-7P^|YJajmebFLl(3Mt z-$9+0b4QfIJ%5~Iz!cB4-%v8(HkZ?jLNH(Ga`N<4D!RR2W_6(zzgG381W)BO+qH^H zn3Y&KxPE43L*X(%TU~z5&j%R?lS}ZP=XfgXrIcyeY6!mdHu4Uov=2uKH zbcwYO$Hb7OYZa4sM)i2!Xdto_UDx%af7e_}9Dh;hG$qD){9myp8( zliKOBMd`_^Eun~_N)zcO*@`OF*|_<%r>N2@#0SGto|(9DzTkXe1CtZN-|EZB zK~5r!hFT!4^MvNzbb8^V6!osm$sJbTs|bv8hUphSU!_=}2hS!nM;$SKMv6O}y^-Q! zI<@pwcCx<~Q9eH<#w&XPw;mchM@uZ?g}?WvZhlJDz^#7hiJivHfo7Ma^H?SXZr3%; z4?+0_w8c*;=Gl&?nB^p^6hOE9loIU4Ldx+|{Hq>;J;1dAn$+63Y144LZ&3t(nGW5C65Q^0q=38G z6!jZyh8wu~-g>XbqB!=fCcb$>Elds-0-bR|amp(0_cBw;e$B}oO`^_VmKF2PP$DS1C`juPv+q;2Y>4q?3+a*h5Z4xj)KqTP~P*mdXzz3dh2L)DaW!3 zNznjJnSj*r0*&ooMO~RbkLm;|f#qL7;p};$)0o*I-Dt%RXUEw1o!Zzju`5!12D&K+ z=22>pQkL~kr}aTfRM3Ta-*fl<1gPNII5&6k+(_PHN&=fRfjSjae8LiE@<}#aaIqRX z#M}Rk#}n>Rj))L`<%cLQn0xD2JF~<*T8iZTm-_nSjpIdPnz3?4oRzX?;miIh{T1tb z7k=U;Wj-?9@6T@~r9Suz2lotEQBppi6(uF&T&{yV!ZCQ)>bxNGOzXh2$6}dDC5kI@ z<=TR)pYx?h;%QNeaVx z!<);o)5GKascQ)(ko`W3Qc5UgnlcoLs_6aQ5v#6(3Jb3}79S+XDJ6`Zok_tVN~x4CQ@P2wmt4bVfecS|20&5oM0VMk;K~{rD)8K*1@=iTY%YU@zN{RX|r594Ai1ZfDjQ77o3^6^viOB{b#1wQ= z!M#Lr-p1{WXMsfjWn#{Vb~%ud@;o?8UAa*V_J=LrP(EaXid5E*&nYMC>M>XlV$hCH6_ z0DMDAW&t~|7tAy5KAK12%TXP=hYMG)m-2VndkfEzA%oAArCa5cpptL-RKBy1nVY;x zL3lc6x?2BIKzXI9C8LI55y)9QFj&+mOT)`6p)B1_%gZbN+K)z(qa~5#iIL zOkXr3k0$d{7w3q_O<Ky=z9#l6(M3EVM5D}Yc8v@?9s+(h}%~1|(TPGFD%nU%BovQ$jJYo6~NdAxS zuQ4k6598@fMWsZ_?o=)|5e&W@LQ+F)Nl*zd@+YuD?7iO+!8v(DEE>)=9K&xTF^G!e zoID`eK29?v4-@xx2_|DiQo}kHoK^qN&pe$m1CQS}Yi0a3K7ZJEJr?n#+*35;5$-A4 z8hR{1U{vm4j&WF{oJ9EgVSqT#!n>x@=}Jl+U!Ewo`_-7~Zbf42xrL0Y0ll#7fQwrz zE56bN3a_jLdvAN^gFG1%68vpp{f`vtTUoKPaVfO6vhtHp8Kl6L&v<)Jmsr=1xaBW3VMgc zI+Ad~`E={z1lmJe{VE3sjh^xy`yPtb)~E&Wi+?X=wMr;vN>Eh-kBdW zn)X#!Dx~b*Z*>_`KBivKE<}dnNoDI!M3BfcjlVybV+j(p)}??NXUKR~92~`ai^q|A zl94)TFgy@|fFuDD+xY}o(+mu#I z`NEz&CtGc04m*F7eCsIV^TapscgftSFRPGK)-v%BT0Ny% z+1Ep$s^`S*I72IHb-|&qY&L#j!6sE0o?k1I`6pegr%W?9&hmlYN8vpvnVM- zsZ!Vt(AepL-`6W%)9whRSL?q4&*ImI+pJ>-b1`X4F#7<@{k~m%Q`CM8tX=q4u{%Hu z@zW6zMqj)Y5u5220`63>2a97I*0k+3#HtMGbmT`cjI)nj>zS5?alLp@9;;tG5#NB0 z7fzxxoR9ii1hC_tRO<{*glE<{0sf;rWdXudr!B+pSo;al6Mgg}HLR~3mfDi3fzln5 z)qNYFVH!=-5U@7`X=?)*zRFfQ-au(7JtO}}rGj*jnno&NU8)Zh68eA}lpVH(a~Ef9 z76I{XHLwZ)!HZTa%<3%!g~ zYOzueDJ)7EP`a7oUOT-yV0JAl?kDOI|50NiFh)*m`0#Y3Nhn}t4DMO(|Q zUJ+>F4sRia&?kF?(5L)HVRiV_?HuEbBzt3TUjf3vpkC zHFr<1Cc*Zx%MV-=ne+%8cthBgXOP3=_7Ak6v9iVd0Nr!UKGe*I_I|plWg%)4tppaV z+?R`M#jU@dQlk}1qn`i~BI@%9H+%eAL~N$%2n@4(a|~X$U5@|?`sY}F00gJm#izXO zL)W5};wc;Z80+}t+_JJ>(Q$J zfxoQzMH$Pkb*8h5 z63@nWq99c{#GWssd#X~p(}|9JSyk1*bQ0L1`y z4I;vLyB8y3Gx2FR7!Nx-$8wCry3|Ru9&6(r_3oNdU1If@()<>R+G&3WUafYu=K}bc z>NDqI2q*!R_~`S%3ITT)B4RTYUzA(cR&$KQdcsMB?!jk3oZSO^dn?Ua)54#+v_va^ zwLL9ssqEsny(VDtr)oPIk)QTKjpX-~-=P7ml*)By^6%+x8qb62HIC&4=gf`UzMv(CLMORG-935$IbDOveNJL$dl3>AZ3%D6XzD@>EJmgUm5n=XdK6Pg^O+YR6;>MUA1t>2%(R`E04odh@`Loj+>o-5}!cd?;Ach~<0p?Bxt^p{V z=ww8M0_kIf0#^_iPP8M(IIQEGMDPPUtYjOklvp$Z=7Qo88j84#!d#%QqY^5Mhn!@-UJ{DZZ@BUJ4kc zT$6s#{~e{^L6g!OsO}i0n|a1VL4N(9AkVp?Zyclabz^!(@)@s`iR)F7D}M%hY~uYL zqflG`P$)kh5uyA{-ubYZ&Lc3CPvjVfb%c{>ZjNPBMcqDLAxRpim#`_TBXoZN7(1W*&Hq$BuhJ^AQm-6{ItJ18S&BpYKs{$Gm8Fw3Z?-a?6${dR=kTn`@TX35vC6vDU!OEr*&?yB zv-Dc&N|!^O%VcCoO&V=*7%m|D=urAiQcuIZUe6`RDar_q4rN}G=Eg`D0l-u^~- zyFWa*^|-h5R=!!U4pIq+o(Zh5-n%$qy;||YdT1ODaE!xxS2l>??Ys+7$rZ|K_Q+j7 zwL-y8%&7%+x0Om+SJrB>UTL-BaFtfkzIDpn#8z&(MQw4kL5`Yu2tXn8M?{3o6z)*5 z6o>#h2ANGb21|iXA{4d7fbgOQByNoRWj&m2W9qpE1ao% zsW-jbqAX{dlJo^zv1liC)^oNihncjP4)0P{cCY_F0*dgny%XzvQ)E4W!mYRE5#Fry zsU?(Hi@<1BDszm(syd0BXM4v3DhAP@Hio(G?<4e#-O4^!DNVQSLrHS|`BjAe*M6m} z3Kw1=Q=Jsg1n9@zOF+Nsc=-O;$1cy ze9%)p6yvBJ)4gR`xasHg<5BpdSlx0=Il;{Gd{F@wB#R30yMvydP)3`Nkbo94pfw!& z@C>DnWFD0+z{iG)#MyQ_cv7iiu4_Oi8qf{)^i#?ObDMc0>B=D@>B%YjgHy`0d?|+l zFnjRREZ!T#=_>qi78UCa{=q@V1>|N@2XV8yNykrzX)peYnZT`x@F|Eigq}+=1+fy5 zxH{o&{H!ix-ErfXf(ZKL8NLXn<4~X7^c#=X^T-3l821=^is=gnVBQ*(PSF>ya~s`E zPKH%8J5?4P#-v_+u+BTL9f8HWnII|kon$6}kb57zgxqV0#CAUs<7#Tn+1pJD59*ob zBNzvq8+yz=Nd~Oj8D{+Pbvx{RK)+;`S7Nv451wfP?AoQ3Iq|Mrw^G^OnB%gFekGZDU zn7Q*rQ5e?aA!Bqt&yUb=-oT(v+M}<&trU_HU!d!6w23vnzI|W&^ZfhZaq=S~)baBa z>a;~*IF5UK$l$O_P9iv}nn>$(R7cKHfxF7G!Ywe*-OF=OP&s!ImhP6H_TN=b6dsx1 zfNOaSxILdf@17Dau_1Zr%zboOzqsjM50nCu^fv`PRK65`U?dFM#|(m3CaUmA2`qh} zA*P-j$;+d$8v;FJ$g-?(CLQTay3M2yc%&3y-OFd-OB>^f;y6dowb)t0j@KvX=3}Lq*)&pETx~5ZKCHhU z{zRF}SodReI!kHE@*Jb!zm*tv=BWO|->XFOAS2|EpAWbKUhWLihim?3T>!@QM`L-OVk9 zld-S!EtkvI1_%Cv+Dn_&shwA+R!IQ9bHWR&Z_fe__s0V2WzYo8%v zGi4$$%Jm?Qaad;>M4o9YzyvMZ=6T8L0(M{%nI(0%MEVIyWs)?PPBC?>^iUt|s>U*Q za2*{tsSnxBwfcrU>ITN#i&2|=YJIjjfad2@E3*%)=wv=MfrSR>#q+C^+}Nvyw5E{y zDlz}E+)}FD`=;$M0EOX8BORd}I>@bbYO@ay8I!BVn8V@_)!^b;J z9hXp)m%7OuAq(qIE-$RVDN6t9g>N%IZ!W-f!UVW;13k`L{UF74n$JI}KWzDnaDt*U zdI~^MeMj;LZ!D*9&wy8~5g2|z;TSYePNEuUp61i6qG|=^IiG$nsvfDeHbw}1U0MkA zi}1tkDAIwrFRHY-$<AOT_LgCHab*J-BPZC&6%rp4kb6V9b$GR(tehonGauH zLomqp5)8b`(!%m;y7^;u0gX1G{X!|Sf*N8zZa}>ZXsu96s-S){|5{C?&0`iEDwLs= zimKH-(17ms6wte+^)nUKvJ#t_N^dKvSInKN2%>c!0)4I&-Kwk(XLVBaR#numQjs11 z7X4qxP2TIhs;M8C`E)r!STwI7j3`Ef!quymM<`IJ?3LZ&B@X$)OH4PZLDascnrVJg zR-mfo;en}5AhoQewqgGaqfNEc7UuE>c9==9TO2^1wbkq9hM|IHx|@LR@YmnhR%b}& zRVW^i@`4Gl#gEeKs&&lm4d`kEdd8RT)>VCLS2v*J4CpFfKbI5Hm%f+04JA?XFx?bh zz)08HNH@lpD%VpxviC!2Mm@EUC4FcF-=Y^2~s=4fE6|B@D@>{*w$PpVdof! zb-h94nU;XTFCOmxk=oZ+*RvCY$i0DDDQ@5(&iyD7?gFKg`walaDA10G;69)GN1N#y z0=OWI4?A;=!pV^e?D<7Bm(#FF#rsfs$06`GM))+OzxXWCJ| ztdvegTBzaqnga$uo!N%87OF2xFG&4bsLhzelk^s9By)dAA6lqYn2#rwZ>bJr%L>uT zmTEY=)Rk_uR8O!1U1?>4IvWG)nypay{m;|PR%&FMi)~;RwEmcve9l)H9M-}M4Lf3h zR;#VE{daGMpbd#?DEru2zm%vhk`ijPf_TrgQpQry@N~h2FS{%TLNvY25fP%>%|*m!T8O|9 zU4mmA)`kX=XWA1XdT1*;(pKHBR!R_@r@+x8(cpRCIHv<4I7cHQI48^zoaZ7iI0tZy z!y4fvQgbYW6X;kwH74cvmLSGP8{Q+vW}eCTY4jG)&iy9fHBZA=<91tq zAz%c@IIIhtL@2Un0pUd!WbF}+dfPZ8*exkaxL-f zyqcPr(jr&-@j2h5X9FlI+CoHx^mVg@^ehCBjw<*g$2hFBoJ5d*91t!Y$d)Z=bO*I< zzjGRA-WttZQIYOHhs44;z4MUReIyf7Ud|L!s&FZy$<{f>VZAmR0j#w;x5Bt{Y8)Nm z=S=1x4eq8|R@1Xy}>7FG#~a3We%)Rs&Opb1W|R>KDuJmujEaW{m*Em|e9Q zB)ovH1yIOI1D`Sjlhsokz*WI*-3<%Pa7cFKjTylKL_hvjCKbMM++i(ksKcD zpi7-qOQ(G@7s;m{r0*ijAf@qXz#5-69TA%;4d7Y(YI) zrlwugG6^-g49~RpVqU~L(n*3(TMC3o8PB=#PwQaFS&YE=v{Htg#s-mR+I}(5k}T7q zE^1OIis#~QCe8Y0!VZ8KCR{ngPz`c202FeD^YmQKq-luQOve!za^gA0VIAlsf(ci~ z)10nqSjv+)&XG@kR{GAdrQnD*h2?VeGB|1mM_{gj4n!ZOBY=fac`6~Tv$gpZNbD%X zfLZMIv*D*9tkFgn(>2A~sX`ZvwV+4Av_h2%tT*$_pZ!bui~CCKC31N8JWc7<3w^;tDSUzeZqq z6QN=2ZU#e}lL`|FdjN4xBt#=>vlhi)Oe6?S)e+|#xuXtt@}`|V)aEIxV!7mQP%3-s zH>J!-Cer^7tdM+m3L-XBp>Klcag4*d+euU|$C3?5c1djbs~pRprcKVw{O5d8$ZrU^ zOR_Q5)){eJ@O`2YUsUAl)ikcBTCMBr7_J0&oOQn|!P5#~T!=^b=LZ=|bVOiy=|`Xg zk%<$*DRYzhaL-)BAkB5uzr~POFLh9fP$4GwlKe@W7}G@9d}nM5nHWPWda1>k+f+K- zORZA;f}QtW$F*_hQ~=UDx3ZzAl?Kn=YR$wBO}Xm4Ca#>q`H4);0MMx0hzQjuP2wt= zjw5i^ZH__Rb`qf`t^~wc)T%V4rM=bii8q?$GB5F+c@x1L_3RC>g1MW)ys^O?nA05N zus)rPz^Ei8oXmmT+=PPrsKugsB9XC;Vxu!1isPT0lFwbG@(4HjPZJTbna&|FjNaN{ zK72Bz^ih3Wmo|wow#gQ6qHpP=9%Y#3N$RJT>6xz)=g&9!scD8D;GYbjC|Mzh2>#V4 z2>wG67)APHLjWAs04GuL9E*yy**@aB4E}~eVU)2#g2}mtl+#cBfi>7m{raonQZHK3 zU$y-3EQ(7;>u;@L`zE<9*or*K%lY#!^BFHBCn7NN@Y9MWP(byA{ z*7EX-f6ffr$y1(aWTtJ4BZ7460QGzxcCIdYCaJ}++*2t@9iqC{g%X}=zk>)on~XMi z!UX+*5Ze1AA`}T5iHObA7lC1dmvsQZL4GIEWGp4rrI$%+3hP*h1}CHMsgcJpbuXi> zRDPOTO3xmqVvV}uIr1B!)|GbX2_rDFX9NDA>m%{|weCGi-6yfLW%To-)nk%tVqSlm zKUTG|*;OcLoEqdh3IOXkH9G&PHh!4o>tMRjhNg{E1M{yketT?eL)*uxp|07k{&Z`c z8d4?BKv)t)SoTyePs=Ghlz=eNLj^2*47Lp}_MAKjms#AbSidh-Nl{zpKb|PqTh1oZ z%oMd;=~tLbjM)Pdov#)5p#?6z2+9NmVd=7%`9oL{zv<8f)k2>psV%5UsyeK|O9K^@ zn#Z3`q^ge!NKQaolK>2&_{pl7nvGZEX#4~e>!y6iIo=OTlWGmN{TO{tQ){wO$Ee0c)n59A1o?cI+}4|;gCe;6O@Q#Pv>IW> z+b+(pd^b@&6_U-j7;vjBU$EeVm8Z`Fgr|=(!ipbq=>9~ts1BpFmDsf|)OrdACTqS> z$`sYN$Zuc#klL$I>Zgjy1$l-XmG$*gR6MDC&zBC^)mx1D(&DLVTh{Td{%$HCywzBw zqafA5^H~9U*BNRp#v1$S3umf!iR}%f(z7uJ`#7GS&sOKMW)~@Kj`|~eg_)2!7{g7w zpf{YW4wG2OCE7PnEyn&muiu!bR%LAbbMlz4hWfqFTh=8ib-z2`>xGRgcd$F{eBAVk z{V~LvGQwa#bp$Z@=Z6DnW;}aqK+_0UG%RR zYG+s0;DO%xjGDw)+Y9>Tv#=1mK2LvhUX5Ywr#aN%qMFX$&DO78R98wY?F@~%tfsLu zGl^YMp&6K=>g9O}k8oe|nC+o| zzQGNa-d-Y`iKx(YEywoJ#19^Z ztCOkQ19cQD*O=}+P}|^=hguKSRqUS$bo(I&>02h~#UH7P#O|2r$G;%?4yKoKB|l)a z@h_B)UYg$Uv3g2ksY|I)CTdRWrBpi;GeX_>5@ljqxc*)}BNOumta4p_bhi3X!s+PF z&(tvHeTb$$Q){!++x4T*)ITNj*S(=AHYvEqfj`EE8urTzwWM@k&v>CmGuB|M?)ggn zSz-Z0^&PL(DH5ACn5=Kq=h9fc!dul%Vv7e+wRh@Mws{~0zejaEQi9sNSMjFPy7Uif zXEtDf-t~i;A(^F~BKP7NirgF0hU$D$8#1jQjs2unEhOLN7sp({YJ|s}7rwOrlNuhF z{mj?pbtBJ|g6H|)9_P)tiOUU^FqORPOCF!q#jMm1wBfV*p3T}$zkE?6akJ9nFKPyx zw2iiYRfAYgZ#wr?9m5Kj)#G#2n-VLyg~~}>kHfAG4;;S3L&(R%+dmkkYiFLSz@E#Ky-zPcb>8ce> zoC3Vj{W~{C*Np z=bpdT>uH3W*0JE(b^L<{Agv{v?z?H;?JFY@t_r|juUPvTjL(oj$as<|WQ1}VLfy?A zVmvEc z%UXsQQk!~?plu#nsp27XQKNBdY_G=lLEgQh?W%WbL|&L(GzT}^&}$EEEDIb#Lkej( zSmbb$J+)#ic(^{$Q)@0s@wCsZm11E<>8@G3<9Y5fzoO$(2CsKVuKLm$FKF8@fw~vg zYO|^dw4|_Bm3?hV=L>5GSwu^Hb`h|cIpmvFG=;BZcu4a} zv1mzd`GBHBRWu*EW6?^oGb*v7n$9XuHu!7HSmD_?I;-7b zKR44W1Zu7l`>PLC4bqaNuKMaAZHdI}O)0v#wuJrNgdP>wYWaWY$&I{ZnPKFES#uEM zM!w&Z!h^NHr6+oD2`!niT8(rnrS(R)FDkvXc8@)cqz9$7nasZnC6&?2uu+|9aT!f! z4;tuC%V-uU?Bx!Zexm#e%Z=QCvsFtA;b5R@J9+Wwm?xs-^L^*is{n ziiE-PUp;7jn6@E!cm#?`cf23S??sPsM5?wCkucC290WiUljKFo<&ZzMj? zjQ%L6Z47K#FVrPFl9!r;S^ekcwY(^cygVXr~K;aC= z@CbJXjy;IjOs5eT&Y%UyIIKgRMDT2D0dYEl@R9n0aP79lT36Ob)Y5z6e>9W71jesw71d!~uo?C_SyE^rkSipzO(kc52?wUW4?_$j%*<1Pq< zO3$|=Vl!3Rk?T8laSV1F>_ot){Ena!=UCj6uvDY9HvbYLn!lHlX#R@&(!6?FJo|eH z_a7GeP*1B+CA1gsE4cAK!p6bK_3jCiK8vT>O+lkL>$r^f@ecQRM_kA&Bw6~LT19Bl z=9#4g9O^NJ2RI1GuSN*(p5sGq4Yb|nAA<$SuU`bo z{UUU#f!2kc^rZ@sT0Pd_9rcaWwll|D3X0NHR_84ZiqdMZsc-0)C~X*9_b&xCMEUyd zi;v#0p%x{vKkw)h8^Lv&7X=8$m){G<1wCn5W34MIahq7Q*4lgWTWnvLZZR>T(|KrA z?gRCY)|#4!`wP0m{3O?OQ>F(UiPoY6C)~o>8HVR0k+P_fqN|Z&vd5Z`CYqFx(pwY( z@eGN>dU&nKKHl~{0RqJUS&w#UHvhqk;)>OX*i82k7)7yv&>%f zxG6qTg3SuVXuZsPeT0f`Z-k2F3efl%___hXv^7Ta@mZSJ*Co3OK5he#gnI7AO_Y`u zFX#@C9-;qow;|q|UHzO`tTvF9c}zoMH3!Q&rRT(IGJ3#uNQu{qvMsf#cf1x~$aewv z-3jpBl{^w^>xbht?7w3H`e#}D4Oh2@)2}MBq;xp_qiQA1C92hPNe(hD`pvQY+EFj1 zX{#l3R}Z1{#=nKm7hLq~Ei{aV?pX9130f_QMZdv*cC9H}o2c(bD5b1U=`V-5i;?07z1*wIX){s2Q9uYAu8ERtAL$9N-r%&VjW{>L2s=QJJ*En z^wwsus3tV9j}~tEP>grcD`gC~?m;g>v=fE=^*w#GL}m^^W~#<%A@wDEoiId|;}N`=-e)AcUzMhzLy< zjuD#NK!Cmy@~bPyIII&4BG0tVVB*}|jb>qG1eG5Gv6r3+Ws08`%0%C%UPH7B=J^Kn z;VA*lcb|S9qIEWh8PFsHy6~RvHB{S$QC4j|BT2i$%+oRjudBxd)t8(4=HXhfWbW`p zfY%%q;6HBAoe|n`^Q6ZD`sWb=eRo~oIa2$RN#kkK7%jAD+w~UQE7H7OtXA z)jpfO?+R$={Q^4i0+kuBrAy89-^Ob{NM_gD0xRzm*d&bmY}!ldE|pEwN@7Py^E7Ra zISvICrG;?i6`a^_XX$ww+Ry+4+RK1WJ4?kU!nF5ph_vMmsCJgxO+=5R)(ZM_qW0F@ z{kp(z-Yqy?Izv|{X;aLnQPRP`r~!>QLxU!3KIT;h^aU=nf>g^HN}sItG*>pDqYUWs z(|Um^+J0Aa_e%!jZ3g2L`c(&7GM3p@FEc|cEJ>ug#V)%yTS6&!pBAYP?A6 z#&&zr+C|zE^CA=p*x>PUA+exN%NA=5&9Me_t^wV*oBmm>)iz58G|qqy*-dqpXv55- z4+{>*mkAC}cj><`(MmA3zmWc5nbv}_u!(xjpS7A&)U8cOXrIV0jIw=|Fuoo&kCRv@ z@xZh3`#Ae=wShyxuPp(-HkvCC#2CM%d-|r8n!hV6K9EkX*6ggz2ufI^rLyJjbY+b; zoE_ds%33WmZ1_qae%P;Z2!9u|UG5M5qNO$W2Y=HtcqMgNr=4Rp*HG2G>Az zw>%xclyS*yP20Aj#~gZ+8g0|cvv~{kAGc`{lC(|Vv|XDbxzfkFRC}jZHonovx?p?~ zPG@SpZ=PxkfMUkE6C%P>4X7hL)g}b6Y!1ew9OJOIauUtTu}lTaO*^%Y>{e$gxJ#?T zOvm*4yR?Q98}v6#*sWn!D2oDgZ7OS)MQe0)WZVW(`#oA2X(CP7qup>#eN$KOyjN?P z&%az3VfrQkux}A(oZI?cXF79O3ocY{jDhE{LO98N7%lhY+w{W`tpRH=l{OsF*0JJ? z^wvi;v&8gv)aRIXoHfbPs~p$-Bz9xD-u#4yQw2#nea_H=Sfx`Ga1uSZ`MW9VBnsV& zBh-Wcp880idQ$7mSfk67eOin5+8I)h->NMxK1bi@*(HiPqy5YZ_tF14gD!E(>U-Rn z{D#+OCAcyFvv^Xc3G=pOt)-8hiKR3MI;c&5deMy(!(ds+`l*`B;!AKs-WoJV6TJ)rp) zw5{yvEoydAYm!*xCRd!V1+@$ORti!86x02c5E1%X{e`|`5ug-cMaGR|FeBq6!jPdI zAbc_e$R{`G$wlp{IT1I1z&HHdN7U?$arE$#HrKpks(^m&EubOe^vRdC5^gD`i5pT4>6#>brXCZj-ZSi+8uLR znn-)DtAM^4PP=bv{ml6cXo3M9HC(THOH0XX9yMAdKGsGg&K^hyA7K)rPD3j5mp0Tq zYm~s=N)%W&P+#|#hW&>Fp)u^Yx3vJ@?XRDCtc6SFun_{B)JlLC_9L%M?HJp9oUUbR z>6ZP&1lB#l$7Sy?mBhBZvOkb5OIv6Dnk-PUEd}AAz7+Ad*37)hfWBxUpq9Rr{Jg zvjFS@Ruq7v9}uyb{vTcM9T3&-1PXJq6j#BeEg*^@(h;P%C<+P+3IdkXc0sX#y%(^1 zQL!On*Rl6@RlN4t#fH6?Ywr!Q_s%yt2k*VV@4dHwZ01aQGMOZk$;2GlkV*jg%qKs3 zLwO`5#rudUC&fOyp~DBTI^>UuDA=RZ-t`FIx&0 z818mf0tTo7TctvqZ>_5|+@WGQ73=%I(H~&+(%$jh)!4|hgP6|ZL^bp!R5-^~#aBGr z`qnzswwa2Zp<;KUIO<#L9=2yXDfzjm*oG$P{T+ofM*84;>tGF+LhZ_07ZRrgZ(NWS zpE}yaUPqHq_~3~8 zk=bjX9idG${2oFo)R<$*$L3V4f$|qc?y~(2YM7u}2C6gS(jqrHVJ-HqZfq6R)8cq2M$4;z>d-rGBbUFq)a~nu_d4JtPVvYdE+Lc>?M~L zS@LKzx+T?B;Lj7{TMh14Lj zuYO+j!8n!%B}de`-{!>k;mEK#)XRl$rqLfj>coUAdj6YtU_CmjDxYVruMwm8^>RbM zvuXkb;k3F3zXg@{8!CRQsw$LqDirS3;GJyisMs78yDF4gdLmt*ZRn_){5w)n10h~~ z826oKdhvdoeo1{Ljpxpo{+zl>0V@U5rQUo~&NePm@tf+Tq`$oueDdK#Z8a*kg^JCp z1$BM-0k)&-DPd3Rm9XyuVV5so!?u-*U7%tQ2Eub+zOn7Ex=PqY727uuqWpN#cD{;z z?5LzrDG-+W@!e6KyrMtf!uDc>62(pRY!U$7{CR_Ifr`E6phWrQ4~72xX4}&SC9JcG zjrNB=0lb^-5*7R0UI}aK4+{eLVB3x=c7uw&=m*yW_}g47pn?2g?i$pt#n-ky9j>Nl zr^Jl*g+8_Ty0+s~?0H)S`^g8k*W%L~C4F-=D;;TT2z&TE!0bhC0E# zpY7RDCEY42wvjjV3g$y>r>NNLm6a6AykJ8xQic&81oIKZ;|sXf<}VW0&+w`?UxWJ$ zwjull;{Ay}4B;zsToSwrnY6j*QIyV|K9Ll2j7~4`DgXkS$rg*n?f|k+zM^dGPHf_kqX$apNCdcwk zNasFqA(rC=TV$Mk8+GV%fjRCwsUl?b36s#VBC6`wWC2klU;MrT}Ee*7lg*haYHKzkXT zAtsR@%Qc3hiTnvX^_$w9Z%*E4!p-LV#MGF~-;?qLmZCZ-Z$PB77ifckl3kDG2;^F< zLkC$lrW(q8(EMq6B%G8dAf{Xu#SuZ6-n6eX{I*d!^J;YxfmC9^p_uZv5E(cjcm3LTcqYc0iB`DQy|Rgp}?}5XiM?kB-`wPcR1+CU z7DZY3??4ZQ5;`NGv{Kes1ad95p`%hl1Ll(--ByjH8~a$HuY!2^vpXN(a(f(W`yNVm z(O<{?lW=tLb&^`uw;+^XadmS;cCKYg(scMiilQeg+sPUmi zIGeSNfQvo&=cJ!u&HkP|XWKB=$E-!B`S?Wc4f<9+3TJ!q5jHtE(In{N45dsByD$QZS|aZ<*z*Yt&+!}%(1E7%;(47+{X)dt(_ z+1gpkIR)A#j_z*-%ZBq$NmfsoG=jfMqP%IwNZ!HH7##R}jA!@yr`zWvQlY0r1e9(! z^-#KfE;=ebxig>q=$P_IIL5OPQ_fjPAU&PSXK-ZQ3tR z4Vr{<{;roWR!$_4@8)mUjbR#%1P@ zzFjf$Ftz>y!}Y07W~F)reloxsfjd)CRgU*{^Vp~K>gEIJ=;F@D<;Ne1%RA_Q z9fKrJqw^=?d_wfja5E3rTeS*c<0L+swEF@tC-Gk#GACnVIyBQ+LfJ^eGt9~GB_G#o zDUJ{_nfE6x&Ow{W{6dm*9==ZIYm;u9AfN!ZPP6RcTmiqA*w{nC6n;DLJq&SEk&(ts zgacFgDMVODTTbIqlPPTmy*{1qLrC^mNSwvDC%bZC-7LP4Sk9oSv-!go$Yk6W@C~?) zG<5-AS%a2Fx-I5|$)V|}7sp2u!$i2Wm_JUEvSHB@{uQYTP_UHu6hGJ~3}PF$7WPeD z)*$?7>THnbYq8LB52K~GH90ZN2~ajVMFGA zu^nK*XBBs`wGek44`@d^!u@5ulYVoqBNDThCNcL2z7v&x94F3ai@qz4L8=zASMl`-KOO#B#doZ9 zwU080494$ekkeUkrH0kKmXNi*AZ9IpgB-G;)&AroI8Fv(9Ush%qhr?bmK^!ApH5nj zjHse%NF*EF{oi}T{z85N`8*PuZRA@KyTLGTBj1384WU;zvhn@26L6dO0)h_!P20p@ zv{^Si(kv<8`ZE%N{d4HM8AtI;+zSxnkkx*$nKg?8J$%9EIJC}z--$^VU>-0zlu%r{}DM?R6M@?F% zCLK~92}f~f#FY2KyDyP&dpk0-%+V0PgP%=uu*y4l4^r_o{Mx}+XGBW*AZ{-tQQnt0 z{{>l;ADXe_6RR(~%=ojeT4F4V=V$Bzw${CXzDil{8I-bkMEvF`YnTr%y8n&z6EX34 zStW#7P<46MGz00RAUZV%TJGdsG@U<1(jGhcBrfOG2bLqNy#Z4zC;7CGEJvJivit2`EFb^Un_Q zN668&^!`D<8|U0U1LsD3%PaQNQS&TkHdV7ny%M3rVZIKTdmH8)=9}A@2Pw-7e9`hC zE-wtxx8dz!{sn1y3+^7_JJ~Hm9<45In77-Fn;;(LXQAoKlB0Ye_m1LSyXqYIdL0bM z_;fqoUkQy%86~m&>#*V&KcBqDHvy01bQJ6dJCE}nY)<n|rI7To z)`3R%_+sK)1XlNXkxV}e>GyeO=L*H%$UFmD3~e?vmy9zhCX19H9U@=2lm1_oe)Ou;dE*%1d|0FXt=H5WKK@q zf+R;_1WCC?PdExEi8k*HMl5g}19YLYpwVdD&N$KKt^%r(M<&6GYJ!K$y?R&$$3NMf z#4L~4{n%S9!+Rd!}TqpAxz_Er@f&6*UO2eZ*= ziPL#OF4(yXSIDHJ_)dcm=5o>SZ|pj;2gyDj`v|RwTmf$T2vM$=Fk)$^Q9~LL z?2WuPC{kyyYZMr)zCyUKkW6x&V6?9=pLE;@Zhpdb@^vtL@e@Kx!=E(JU+B&e&K%|h z2p*bUKlOB1fN+_RU=Rl6Ah{@9OB65 zhwwH;m{Qdh`veEq^$?{g&SOcaJHyyGKY{t7LXu@n4JWf_9iTK+sAjk9y@OdwfjK@R zS;RIVpL&2xn2<)&KSNfS5Xp7`e}xH|PM3K`#!r!qTP-K3ZHeS-NXP)caKS+Cb)oIU zh3_2My$9MFguUd)Zg7pjp<2=twnt#IR_p;@b%o7#FQ4koN>(C?B<9;={3;*eNnN2{ z&JskkmkC)z&15i-Kt2QTWkQC`VKBZ-Xc)`@e3_6TdomaYrXiC7_%b0w8X1fV{Dzhc zz?TUbvI)Y<%Y;Z(hGpH9p%oH&P&aNN(ok#xEUG6|A|JZLx_Uw;aqmWTk%BvCALE96 z;5?pJAdOVUHf+}oV(JT3ZL`<2F=6D4$l&nd!v+lnUze579B@L8KVBS%_OYAj zcEtH&Z`gQsf?*AXZ8`Ojs!J|o!=-T7k<sLn@^=C_V{NroQe3z=eT)FSu ze|%r#I8hDP7q~q_7~__XRFl1epQSypZr=B>pYEb^H>Q6OkvS|-sv2ku3)B=n)|E$& zRq+cL9-q&4n^}(cSMg&R-U;y|%JFt8zPI`ruiJqd@(<9diW8c1M{l!ccu%BKeB?>p zxa*(G%3^bi-Y23iC6c1n8M?*@7c_sNj$DISp#kyf2t#58nbdm>=VOI_WZDhXp%eyk zWJw&*CcDzb#buPc9(mKrqyvTCI z1)3)a?!@T~^i04MLtWr@g0PSrbB5uGLa}Q+HYbj(IOgc$E{rW=|JYzdAJaC?1wBW8 zn$ZDCLTAqPFO_9%WX^v>K70UglZ9e(G5;hg_roCiua zV0R&LZD#Na){|TjelBO9iV?N~Lrn)5)JpKo8Hl*juBv}m2IDjk-JSuwiWC^!1d&M$ zh*pt}7#xhqx(x7Bk%0`Zg2a`x>7(wx_2<}F%AT9ZeZnW^cP5Nu4_P_!P{Aa0Bnywxo+iNxr2(xVD^(au z{wjvoslq9;_%rNH!|^RxD1E&60Wxna&KG!^CNv?WDXrB;Sim{@#VOuHtd)*EkCnAJ z12@|WNt)6hdhkydR+Cr)7t#fuG;{>-c0vo%)BxG-gyuw=K@YaW(vzl*!PG%GL>yj2 zwT{^L&PVW~z0i|u4`m&N6qnsc9L!?#n^lM{j#JrBJQ9OV{TS$!A4e9p`p^T?8*qfA)X_cIcPilPhvFed`7^ zrK^B<@%;C}=x*4Dj{9IvH=zqzwikYN6IzowVYGF3VH(<0ihzeb1TW$p0otBIG%5ZF z2|b0iq*FEeuBTASk(S}~Zg1=x?fDZ*U)?$ZA$^4>#3>Y9`UypD^ z)77wA2Dd;F-MCh0D=INxGAntY3TEECM4$3m;}L_|tZ|D0<+H|l2D4e?I0MROjlBr} zGi#g<{&&`3yi~VifK_M_3>qL*CToLX`~W1R!5e@ZC^YnW?2Lu}fhEPIx!(pnh0l9~ zgRwyGhHz<0B6l2NyA9BJpb%R5?s~*kR&kfs)0G2-ATFn^6C%6fNNdi3jtDenK$`^a zEy58DPDCJ>0l1`NNKXdW!Gyb`LTAY=COj{QuFR0$Y5>AutYY*xJpwqgS*%wGC*+;Q z<(6)T4y*qlkkt}eWeYCeDZ4Nn8_U{kXepi3{v6p*PzrzfbcUFm^Wp|P=*k(ugW025@Sp7*43=(#7Iz$L0 z@1Mi!AwmwBv>1Gc3ZKaze$-=_(3d0QvSIpgA%$%W?+q6QkvZ3?X@s!ehLmlk7L$Zu zT-8sP+3l34%KA36C@$~Rvo-HIBxwD~%TRr?@S0p-Ky?K|RYHh8)S4<3dOP9nr3`P= zjoi7*nbC8Bfh*SITS{D(v-0v6A}lx3UsHu(j)Yu>;OT;iOleOiPG>7QauJee;xcroGupsAW(N(jBMiwMQrcdSz*Kogf zoE9w-Zg5U%f7Zh`wLZwY)28QfY_S4;&=c6SM3_o09D>-T!eU}*1^1T<>&SqEv|yPq zUqjBUp#G}_3k&ByFWitQ%x$^jr4D)L{g0zSp};2f#hUnaf(1DjOPl?L8#TP<#M)EgxpXw*$zpE!NOuMZ$Is89$W% zaYPu#k!peP?WpjWLsZF>@%R@-d0l24ONT6kJO z8Ha@jwYwnP)@aSzC;>gD(9f5JUYs^3U->SW485)jja{1#_)lvZr|a>>nqzF`+;9*a zzbXWfMw8*gRiSH@-P4e4D<|fTtYDUf)T_UOK{41WnP&ZsRB3-BMOi8O{NC<#I6)g; z7g}L`9O<-Mf)hvjIMB7Xg|`~6H_X2;G$nhB=!5$LibNJ30P;{s<<>*nhl0p`fz1zv zW!yF@JQDhIc8y~l%o-P)hhwkdKG5dGUV7!RkjL4tZS=oO)8IYO@2N1;PJ{UWgrBq< z9G?l}?0Pr+AM}S^blo$|#c@v*&aQda%41Z*nl;)9?Vk(tZ0mgYLY`++xSctApB=P9 zso+Mme_@v+dd*e{eknMU%7bXLm#phkw!r;YLJa8`3pHO0w@IzdK;8)b>;{-cGO-W} zdWvy-8@+xL&3z->upm?0)ApZ)QD~bR?tR7~7amV9e8FL@arsn_mVFc6avCo@!FvA# zXD@vkH2sN7yed(2{7(TjZXZU$#b3fM(nb#jWx^`%^7e3X3`Yhp0m_N>$@F0Qo)a_C zu4GLxnTtWF`Z3;I+)myupwSlMNDJF;603w;t@g|@1h%IqD~g*?6ygITti|437JY6l z4m2lgCeikGB1&>r#?t1FBF?Yl{pcTh(Z|x-bVhI1B@X37jZW!dQ&ln8y83U7#VI{~ zUR9i>CGSFEl(#sB*q@^2K4LARnfuF;hWm+s5HfraYz+__lIa%oeSp{tZ*%)Y#~|^( z`#N7Fw5rMssT(^U1?4GuKmOSsuh2oqU~#T(_yE=^7M`K3{LwG!gSNJ~mDH^d`)iA7 zSj4_fXw>1kL)u?-;;Hqogu;z87AF9f~`7Hx7Nozz4;Kpghnvu8W3 zI&9qF4dDWt#%*cars4(;SB2UHF^U+|XtM-Sm}Ucnh&FIeGg83t#kMh#HNZTNaj1RP7|M! zjzRD-O?*LiSi!?K;wiFm3T$dCwjh09&q)_|+b5q;nn$_%YK6n@VjP@K7c)o`3kYc^ zRv|4cAgP_$fonrIv=jB5HmSHSQiOM>SbDNOe&bmp-0vXHBwltfsH3=uTz?Px46!L$ za}&}t#Bc4Alj@;(o0-d+IT;zDdgQwkk&1A?D+8FsdC(*TaxHeCqYC$>nBvc%?km-8r&+R4_op+&KLtc=|A?qO(@Ek-)mU+!p@JqBkH59}qIG0&BBT@TTz z*&<#_YZt~wbx^;C*fIrf=hi{|U~vQ)evTdZ2p z(|mY0R2+mC=Ag$gv9Isdg^p&qUrCl%?pF@q&O;@H<4A{2pJGC7@goX-aSSxSI%l|e zmxMlo`@_YmIgXDo$OvT577Q4a%soK(>qCUu!u2f!I;zOW3}(x|+YCrnkrx<@5}oK1 z45+Ij_c0jP=F!_3;He`2WH4K}E@Oa=ik!_ogb^de=H$l%I6gwGNB(*MWTbeKBn*VJ zBgLxR0eC(ViNbW6TK*xP=Cn62C=K_1C)~^thmsLpKprJhcArEyTJ#~#ouTe%aXLA& zgPt5M;$G@2&~szNU`{*aoD%WcHnhwl7Lty===QPVDvs>94sFMa-Z^>K9aU*@GItM0 z@-X}?9ge^N24t(qE)4F1$aW0qpdxt&w?<@h1|+G-h768EWCQ~uRAc~yYa-GU0med0 z-Bb_z>yDN1$tU&0>N>47K@8Z8y3519MhuQg-Pm4? zowKk%5mV9*znG4M%|Lv|Y?9bcTkW;dj4>PWa9dnOmR$uYUu;0SUxgX@I9|@Bd12Yy z+U$ap`PeN^1vGcE*nrcP>|xOiK|AQN0&z7VJ#C@yG_kuTszW`vaLVLTaX%IjL+C$P z)1TAC);Qgm;O$Zo%?2$OE5LvmVkh!Mg4QcUH~2V13?Z-6sqaj&A91qV&L*qA-i581 z`}!rqhRq33I9q&7#-zclImp_xXM-1r&9s|S+3>Pio=USooQYRGwNyJ#e9e*MY4B~n z*h{-ZQUa`zpzi`4)I|0fi!( zK8l1ng<^nR_c_?(c?mJaF+*`LXY*g&_;zuDBOfeb&r{+MC3mWb!-+-Yl9(K_*OO?&<09V6zxW;zxtq7V#s8#%8zT zWYumwZL?MEq~V%D(GFa))wv0;c8CTt>JE5Q@e?`K7Tk7X!)$=PJH}n$4+e+d9!*paQNw&f(C7P- z@+bkSPbq%m{pA9~!`~ius>h}BZ`}@C<;cfy_g+o5*9?g~l#N>^gWFVSS0u(bK3L0o zUEg>WI;z0cuoAWuiGd_`CB0iD4&k&*2Pr`|41x}a#L2{K5q)(?>`Yw47b*BK=KNO< z+qDPneN@E5%)nbPuo#!XksaV%v3N;4>3U5z(^?Iq>yL?ebm`{@`;VjlXisv1Mwow*bwfridxd(R7Ml>e5uWwfhNflt$@TV& zF!ZcAm3UpG70-#lk<-Ir#d$Hy;kEStODXFD1Y8h166+bX;DT7d5zAbtb_tU{JA^K~ zB#uIv-xR2MMQm+9qqP$C-(!a}bo>=DiX$f)(IZ!Jcsp!PQ9}H?Y>qeq-L8wXNKzX7 zx-KS@UxOg|hUi6dN74Z|*s3^f4~)Nw^wwrBY`=+|B55$(x{2)U=p^{g+&A06^_Dn} zMD?f3ZsD9;DJK*AnTd?#OgMI1?5GXct2DlECWPL>fiq?>^tywrr*nH)g5TO<3U}Af z%Y*xO#3b$p1l<*@5#cZ--9?r$d;-n6E215%72DzCeKAd|!SRb!kku7T4@8r8ISyR> zY*nA`dLR-m;6u3D#Zm0wZ+z%Zd;VP;7UO4&)r;NIL;qxQ0SHPoOCBJGTlx&#bcPA#OoHJg> zfppUOYZ*SHhN?L`q2MtR%I0v``54z4`*%b56CCKxmO|SnVgPYWqZ6Nqb2a3HC3rp; ziz}7%DsQ%Fwdnijxae?xRLl2^vL6h`E29_H3Y*8>`y$=cp;{vzV-PRq93VT zAHKa1-HF3CsQOZT7qHy_KTR;MN*y9nq3$d3w&T0H_Bgij&^ z9p=K`H#lX?UIOplAPd~q7X060tioh4z7=nAm!Qr&aWEOZ5enbokb2k;wC}~Pc3D5` zvhmTZe8XQ@2&3O4wa*9L2V5frb)(52L^P)JsUJ=KC|>4B*`HARvlvHq{R!EhaqD%U z8u)(^uj|c&u}Aa1;pR}?{U9Ao_!FGJVtwpu!Q8JnTh$AK^IygDHIv(5o4e;>%?X~Q zq1;g2bXEQ51Ut3~9R4Pb;EvIl?_wv;B_lu?KK~^AY*kG&W=;sET@jt&;t$-QOSvH{n-d)ctNM~evKr8K^`*)vkhO2sGaE~- ziRUBwq_K1m4;HJypK;PVmn~_md^RaaemET+kI#>{#d@rAp>4EvB6KDV9W6Ov|mxUm^({@f-&emr9LE)Oa|(6bHmh*t<+>!To?r%O!UdJO(b8TB4@m zaQq^bb7A3fsRgMv7G5lux^SrPxdPMZ0BcrAZ@Hr|Z>6-5!#4z0NpUR7D(Nwo03BCL zkz6mjV6}ud`|%a*HB!T>Z_r?UsRq@~Q}UwF#2R~M^<`FLMyEp{bgfjETTHvJmG+q9 zTY27v622EW1m+h?F{IW|xK$`cR((6f4u#M^n+E_)X{{9kl=_Kt@HDrIW8 z&k?YZN_ChifJamk$%ki9f2VYjn6H7#yQHc_^9%xaNwFmNDfHPTdFBjTjmZs5<_@CV zu@8fLhTbf_iXR#51EmAZLuN3Zj#;!|07?fKvN40B5vb1qlnyXtFoXRN@L>Q-2N=?o z!43%6GXSLn3~7n5ZilNKGE8=5LjTm$tTD0)F7jkH9&cN08Vdfqr3~&COx`V}6W>*M z@F2~{Q=kzrVXySnZhUt;CJ$$er~(s1p{T$Fnk=&L-W`u&o(qPTHD@*o%P zL;3+}GpT+bY>OnxE+<5p%y`tNVbbfJ_voM^Mr&hh!;FJcB6kLE9hAC|=XYq_A*nXU z&4jGO_+38;ZXK4^lC`(!_#=`R$8Cl6N2Mz4Zr4%Cy_#o7JDl2v+qNu->Y9*0u!4>X z9k`3Q_;^;Km?a?`~p#b`0u&NgeQNFfR6=HEa(jk0H&vwTIGU zQWaN%!BS9ttYtx+KQL@PB`gbA0@aV>useUBwm2?H<|MT$U3FT*8@Wqrz~FPplG32% zdFg$nl}#MjO#)4=FXc+mT`x$@IQQ*gN+VT0i;gb&LPDOj37fEO5#t^~ViBCVDEU`u za>fB4ipJ|CM-uXiF_G2%KBKbM(`Pv0Uy>5EbI}_cdf*x8a0v&#HxJ`3Nq%;HJ4N7n zZRJ5`M4yE&h5eVL>gY(hQp^v4q3gpaW2z!NVigle~$15qHB=w|C zaF4-}GkwAM2AiYpP?-4!M>H~$6K|x2WM3cX`WBZ1ZHK_jw^A4h9zu`3m2eYuq(0nu zC*3523xU3uh7erfhkig#Qcw?4K1d6QA%s5vz?iao0{r+WHLda7L#ab8(ZTHVL8il) zfU_}Puwo7tx$->7{3P8b{hCAL&r&-NYqbh(WCt{Q{z(msLZxl$zVt;H6n&P`D{lmB ztGB8cD+3_#3(inoBLTihIrdlEqF;<6u*zm~`p-S6-&ZLW)fYk`_nVYKE-rzm-=tdP zIjWF+mr$PF7}CE>&g4KhI_SHUtRZP49QlPKu)|d7QzoTpOL{3)xE(_;lu4*0bh8^c za`Jm37l4+?S2UF$Inet=Udq`Fe}wuPI@$&wk?V64mi3c@VXwKoL^J)q18rv^Pv_~;~)^aqd-UilM%bROlV7;rHu9v+2Tvjq(edcPt6_Q6OR^!$%7-l0U z`gE$o3S;xj86BL_@BqKSCIdf|Bi(iO%+nC(4kb47P_8LtR+a;-yAF0V8*KefmugWk z1m;zi$2iOn!8Wo*F*CET8i$2`^iQ}2(y*_b9}FOnk7UYN2@3W zHa_KW8p79~sCXm7Rm$-q!mi~oiZg9o%Hb9W+n2*h2v<_!?D2Tm5a}DC@OSTtJ_w-N zf;TWA5P{VQcp`T%@ZQc)RS-DB0F*>~Ut$2BX?Q>IfgiSVXOh*2X4=W9%X4T7^s<*1 z60a#x(LtV~os`U4#G;@8mO03g#6gBz4srl_DM1BCIfeW^8H|pyNJdYFLPxncXAzFL zl6-~OqVo|BJy9}>eQ{rrgAYSQX@3L;C_bg#RdgqY#zu*5tD<=o-GZT+x>ZvZ-9Sax zV`wzq937&fy;L+C>ZN*?imQr8-4m9lHGbg|SGS`P&R^B?mH8{mD)OstoF2O`G4E;u zK6;RQjP140dpejsbFOS>h9@KSRoia`HaY5edN2u+o#aq$nDS|}b`p$tl4D4mCmeT@ z!^yD(_~|6al8Xs6##z?k)7#L>MNZ|?VV#TonOIrFiYl^+RI-7eRpi~IuN~}imEE0& ze`VXOPYHSK`WpCt#&x$LJPKa9%HHI)C)HP#kK#q?G4xh-8FifkqhX+jJjlC}A7jjp z2btnC>W8t}BfX@>Ef!mEqi{<@BXg)+Lrx%D{Hdvid<8dpBWdlLGG4x|nE~NGa+$VA zgfa{sHG~GfGTNW}L`V6`j|e$Fh;|8(YY?(E0P9wzEj2{6fEyw5`HHD!xCJh-8U4#1wuQ=Vh+AK37bYhWE*aA6$nBZYvO4lJ zOR`6VH4WrGn$O=2)Fld6owoLwo;ax)E(Wr>E&6C64QMFOCZu0uI2a?JBXgd^j9598 z1o*+xSh;|lccWrsc`_l7mcoNL8A$DWFg#w)B!2hdUc6kByr~HmP38AYsjaEp6ZaLJ zn_>HHgzwGdZrlp!lpqI^PBmb9f_w&F^KPFgH%D96>k?&ylkaU?wcN1zgmwm}ncaWOP5u}?tEod&sabDG`W3lt&(adT}QOV1XWo5_WS0ZU4 zuJ>^?QG_-6h9dJMY>``cflrpBNM|>gmMqud9>LLMc{o=Lu_qTBf3!sIR1obPjX4!qEQ^Nyt>xO}?aO5y!bzQdz&@Qt*ATE%Juq*wIf=mfa{ZKOGBt6K@V<$*#eLcJewdA4as7XAHoQ4cM!wm3vekzUP=syAUjibukMH; z6@$I8+>uzkY=^g!P@(gDmJ3@+eugcX@_1$juam4JWZ5HV-bEfq&U}IkUF1UCG$&{t5Rf~@$Db=3zpuJtRK^Iy5|^@H%ew|s$g2!peIkcIR= z2o8N^;I`8BedR40LPyf}1Ca+3^MQ1Cmh8rn=;8EwwtST%orluRgXQ|1Gj|&)wEUR9 z+V5j}aumo=tWS%-KpHCd(Y`#!=+2Pg3#W(5H?>;j)B6t}x@nmFgd>rg>D&==oH=>v zNbiioS(|jN4yj{gcM`G!hK<3Ii{{|Q$TBfE18uJC!R0_eu594e&djwv0JCP-@jSL=0uc<;OOpsS;h?x@v=gWUrZ*zl< zXm-4qp2+kqQ}BgcTzcg*J<5t&T640Tsv(=~;E$L|svoQCX_oDhnBUB$*o=$KmpYYJ!o@odUGGBlf&L$5KWM*EzD?#w z(1w4>5=UN5qq8^3K1`Cdce8wv^j}A(ZIP!C5^)JUwqqAJ-#{&P$XjeldH~&3EZ-&0 zp4AwqvvE=!eTd>1!mH80PRKQ_bmsd16pYi;`{(678Zx9BU3^(StRY)nXy!HfJ0Uyt zw8Kq#7DqfB;nOW#*#>gV`T3kn&Y_fzn^H8}!u}-)gqKFwjhMV=YcIuGBzh zSQua9rLjn5u3kA8CRH%jCXa0Bo(jf8l?hy@InKuV7NpH(x~G~Eud1!+4$rF_Pm#~v zfVvyqHQj&M(d+KUZ5)|!4yM*HK64p@{hyMD79NxHE8=WnIYMC<$E@g7Ph&XeA2d?Q zDvhCWy^W1rlpUwee#Qt5$xNdy1C9GF$%nR7 z7iPR*NmhH)2lb4v(2@J-flySmR6gAA&2c`pheE zZ_*Fveer*zXj(q~jr;;j*6>y7jmE|{91|DP2)RpL4^!qqH}Nl#-rT$JUV^goo4 zc?j#&F9L!)7)4jt|A#+`pbI+~@i6MrI0(!zo+X-b@IAv=NW`(QF4Kr}Ss2vqWIRL2 zA!^y#xDGWqYr~c<#yQ09DsA4?*wl(!OQ-iS_O&7bIv|6L^SGX{aFFqlcdr5V%9(st zCUak(K!IS;T!eLghJ#%cujssibk$(v2-I@e2sMWqlgYP*wEs|JQ{>JlbsT0~P4)#t z$Kl3FBs3Ua4mbJ{t6=Ip!r04#Oz%s_jW*(un9v6fk1;kTx<1q@*SNuobnF7#Cn2@W z>I`G@jX#|C+_Gb2fAXds?nYm--6*oOuxl`Tvau=Iwg_%dHa67vylnS-&tM2$qD1gJ z4j~0bKkbR-%ID`}(6PW6SO@MlbEDJ1lVne1gBG7@KK_E1wq4K0(j z2X`Swp6JE4lwNW4^fJtY)JUNpR~T1n$?zt0L7@?KYFro6r5lZnEy=S@@MxRy2U)b1 zKG<$_K(ns(;PX!7Cephu{JG0Go;;3#(A~z~WKaZMy4#q@k%6b6bdPa8`J)cZ-D_M* zyiP*>eZ~Tk5>8L-Gv4HgOFpbVU`!xBq44H_u@xCHi8d}WT9|VOXu=U=G%9*}(g{b6 zb1IOSgW!JFxQ{$;LGPS323y*G?8h3^CNi@QYi9e?wAvM;3#YZqQ_wXsAo{AYpVniz z^0~7k?6``}p4v(I9MX|m{%y?CkQ;?`#0_IjqWyW)gLTjNBe3?Ck!Q>ITgE_7%Of7x zTGn=>4ssKdHE3TT{e1kmN7!7qPkXRAC44eXxNUT-K+f->?H(KP`H5|??uoG`H;tZs zg7xMe(P1UVSC-@j&>nA$3Fh1<*!saZ0NZab1rsbXrTA;*To-_=aj$(o_`b2Ssvc>5u=QFYT^j#RROpYEm* z?m7+jFwG<6%0M{bX?joY4S@YMP1Be;bT5+!hc>JGd7FNcp|7B?kEuTSX-n7pn3i%z z)A--jTXO85>K#EOuCmMY0})WFH|D5Py+i0Q!6sUzVLtiMJ<20t^_C!pRS!LDjfdra zCIdH~Uh^|O=ENttn2By|ArpP&NBgt1)OKt!7D1*}`4dAZ8Cf4jAlD)p9aSgu67$KA z{%`~xwvp(MiQr>{W5F)a6iEDipkbiN!*Na;G6V(O=1e&IiLo%4x zGTp6jVTYqZ)$MI}{$rV<$yk!C2M$>JV9YC-tG5v53)-pD)I4c>;Yjsd5FSGwtYZQX4 zk~cAcQpxB05y-U&IPklYo0(63^u;1{#+Ak8y~%P!Be)o^+_1#-92#BQvzM!KvY!36uk6x-q zLSadkYnlEcA`vXrVPxe&?bIQV9buZH`MSxDTGlmLSZdy`v!j)xP0oZ3>_(=28j{$K zK5lGs;56nd?cjS8Q-LOHg&ob0Gv#Q=j26^7!Gz+kycEzUnlfCyQy8IJSK!{3EXFf! zq;+)-VUt3qCz|}t$@2DeZ;I&!u^pph{7Zl41#b25)RfSjtxUce;x-HJ3#Kt--b{+R zw3RAX9<;@liTSV0|A;I6@xXm)QH4T=>6V7BS&q2tDxPN5{dJLM;sW@t2DzPZz< zlT21b6Y$lYzMX8+5;W2IeTpfh;)PT@4tVu91?Yb&6GGg zaf9iAj-06jiF-{miNhm$XRitEnH~?LE%uvua}rULEyWKO~Z=)+=D8b|*2qm7Q6 z9B@tOL&cM(VR(}6P0yb)C08I{szLS3CcLXjyInRF5sMd9aA?B*YDQ;>``a{_q*kH( z{x-cLq`MOo-Z15pn+YLSwJ>@G zt@6+`%!3O=fa+uZ1P+aT(N zNhAxm(HSq05bWGEb(mHFnXk z9(2TOlP_xR8DP~LoDF;<;Qkv#AbGw;=PC_-Yr+jfzB^6-Xu_lR;B|98n_Ah;`WnvU zLdoTu<1>HKj-O3uIqNg8ZPi;N+g{=mAzw|kt!Mnk41H-!2YofoGuJ*D$wrXDX$5up zWjcok{L7%E%#>!g?2VG>Z(-AorQ5~S`Wmu$5lqvhzIQp*iAA$f9Ja~HOIeF8FmoYY zV4m6;rLQ%BSf(B(UFXwdmZ@_HIR`l9r?$4M^HfRpx4uqqfP>o9wUyrA!lLT>Mt{F& zOS3Ab;ytDLv+1l#sa6^?eFj}rIdwH>zw26gb(UUb11S3XG>~jlW30RW#<#d?OPATE z_SRV6xu^!Uc#P{-SI5-v#CbAx)~BMatgDm2J;2m zIM4?*Q&*c4kDoBfFV(;`qeXtHco<~ylWGD|(@}RX8!`e@3yE7c*wsp1=lt@E4Ynz> zi*p3+;;3t-BhUVZi?vcii0epd9+X-WFOT%1{=un-&B?@1@G2}d8*P<#3Qz4p>b-~a z;i+@*)VFJ$R6lMoOskW+if#4`skONOkE^SIiz@s6ybB}kV2njrC^n*EkBXvVw>a-T z>{>f8)&d=jal~5n+TGnx-BrN?b=O{Z?RG)zu9g4y-1mU}{rP;Dd+s^+#O-)D)@!}O zZNIwZxx3Ry!_(W*2DZSX@L?-)ZYHv}E!fp3VT+M!&TYsAtF5|gf%cpITq2J9p)C~0 zkE(72SJSzC-pVcowZ9tJO1t{JT5B+UT<^y{BW9%+lr65K6Z7fR=&0EHS?5KSEUm5c9j;~&h}KIeG?sQNzxtd_YSt5l7z3Y zoow@@<=nTk?O)bBO!JAh^_AG##ae6^TNShkiP~lx0llt=r)mAXD?O9(u zK$3!w)peWM&(NJ-m&m16#_+RzlxkBw!q55QqkUu@zNb*JWq-}(W4H~O$9c#OS2UUHwN#ktYv6<6H zyFZ@lO=s^#6KxOK2rupNL>s-`&y1J%*3gRbc+rn9; z75wXL+kEd~HX}%MH0N_{rM*L=4RO_NTIw7dwi~_a#!Jn!RrOwPP<)-8c&~Z3D&EnZ zjG$ek`RaMLuim)YS(H4Jt+TdazO9Ia{VYkp*m_IM!-tn%XnV?vdGp+h(6CIEMFmA$ zD=O$QFFtIMZA_kjZ35lJKv(zDJ}sB1(w~?&{DqD=VdsD-n+|BswRkqjO0Z~Rs*Csq;we6~RrN%~xaO`DY zV=L?ZHPS#xP5HlTZ2lqFvJkhLfC81+s+km#(zF3;85ZVaE@5Qy`bUsff33~#>Rm3v zU=M4=|JYz#;ytZ_fk&}f6rHzthFJ7BBf9UNxJDPL8)dcam%@_QF*V7?N`JesdLWXoqlt^ZJq z7uReXSWqpVs@WQ|ziM*xA=_4_)YN`GWGmCmt!9^tYK<>bo%=UX zD}2@Fa4mi@#f%nfRJ>>(BOPyG-={(EGP73o@8XKx>$WE>;jFgxFI%Lyy0R z9a|li(ua?~V+&(>`)WJy*!D`Et%nM>)5-j&d$zi4NI2hg&(@tKcjcb4mG`@E z>tFgtq_6(PC9#VXb{2j9dbH-v5+?-bLabjO4?>@6sg=Y(+_#ls(HnWu2ew#tc>`8$ zZM|IcHScc58hztyVOfXF@^$3iPi(ac4yj^x88xk$J&qP5d=HKjrnYX+l_$1Fo^O96 zso^ywzUhgrG;xw-^IE7RP2yCbmcS<}N%s;)fRR|fLSp_>EUYHZYFfYJ&szZ1j8j7nTXZp^?Ca6M~ zm!+|A-u#uVBuhEYhrY5Ea8*79X)|8gyjagXN1@T2aGjRpoYsy%NTay#_Gb{#6=hn= z01)%R8s7ofO=I9-Mi0u5-+*vhi|S1HVuyP@@F+|dB|?peiRc>9Ehe*6!=wED8(VGM z;vD+c7J|>f(Qj=%*=Za9^{wrEktV-Z!L3P{#hA4&Ws)HG(6WEnEAaZyr`r$<|jVcX0YV}yya(% ze3sVXlRn$}c}ZANq9(xYJugaaVFz|Bw$fK(;HK1qVj2L9Du4rjksGXv zPY-!YE5cPEa!uHHBWg7liO$)J>@nM z%U@P2<}LfWd5*7!HO53!w7XW_EaThLzOsCupInA@?8NW*$(z`mk=nF8h?M8}X+qMY z+1jpra%3*Hq?q=huw2B%OjmGEoV=9%bXnU`RE~CKmkMgRO3HaKNIzf2WfTv(-=zSr zQc9le`4jwTo8|NTw^H&T_WT?#S6aTy#y8fyLgZ$-Sn^5UsiNGP4L_-Es3`j~-^+Dr z0rXYa*|f^zKJ&1T9m`_liR{DQhswXP-bs9KWx095*TFeuwP;pZedELPg~?Z0bx$oV zOdg37_3yQqsxrRSlxxR(hsz-h<4D|h)0XXO%kPED<=K?B+^4!+rpWy^et2;=7T2h} zZE3FXI(+~jdIt&*%(kvBUkM)sx>$~zR*(u0I=u8BMvxn=iM0-~inndU4198H-l>NC zo}F){wWuj$JK}{FJgK%^#r5kJv-Y93+!OoRn{iuRxi3v{>dL*P;{9n%ghjv6dFU41 z^eWhNNgpH1!6joZH`N-~lefA`7(zwJ{aCIinysN+K`KA_kq>Ifc)w=O(SFT(bqo%` zjoPkh0|mBDGiUW2Tr}U^NbbyB9&wLI`4-y}!JkCRP1w3CykcY7pW)TO=8ffFSd9k! zUSs(gTN};KH<8z{6`4F~rJSEnX)4FC<(~XOQ@Jqv(1m|*Do@P)+yhyGqq5=OUgdVF z%X>WSp3im4`!K#DN}k7lMDxnc;-#02r56$I#IBndXd$p7+uo@kB&6aYVz=W2l3yyPW zNnhbAZ@QJ%q(pR$Ifh~~DP#3IwHe@_adnZ)q#RaZlq}EJyd;5VD zom(#eqPZNg9Dv=l7mnWT)glVsCo`DPx6A>?xMemVhVN}B-_8G>hMZK=N{1yml2ARD zA;Z?UN0o0|m7i%ZzxDin83rYqHvGerJIMX;q-oiX@()ao%XE_4u=#KJ&zUP{N$mUCRN!2y#8;k za`7934Sw(}v2uS;H{?e5l;Cze_&Dx_4BDMvBqRUSW5zUjKH)8qF(Q^4dOMD>GydWvk16Q*pqU_kMA8SxLj*4=ehlbiYW_5!+p*%MUrW6d+ z&5D7eoAns1U^3}Uq{W(PsZo<>j+RS@YyuJdkeg{)Mgbi+=zC|?l!)NGVz^fe4dn5Q zdb$q0W%J^L#-OX|UsiLBk+IqT#~=Sm^0Gh6iLTdu{P^LY<+82Yc~UGA24Vzdx9+b- z_Q%4GZ=;|^Pt#)IMrFDPsxbcUNC0+IP^>T>(|`>`aazxe;+4nA1@pduH!LaLfloEm zryle6--yUKVEKF{Ii$QQli|Pl+p*omatptec=#$ zPcJ`yV7xrqQoJ-ZQ$Bg@$(i;qu4Bew_%+&#H<*BaGX8I_OhCWc=s7tjXp7eO=SwHaLs+%`npd2hN2<~Na&B~beN&p| zms}i;g6sAkg^NGJs}Z(1fJTSV?*@!1aLCOiLW(l4OC3Q+YJW!r;^juJaV9^$I9^tx zlm0^ZK?q+H;bGsWe{#DVw;+qqqisL=mUa~9yC*28&gQ0WGU195;PB!T456sEOP?Y_ zT8@A@qi9R#u$sKJT`pKL=o)c`|E}ex-LP3=BXzOc?h3K0E;ir_pEFtR6PsT;_A8 z$bH%*{Rt66DSg01_B{EIJ;}N~I^(Gn-JVNX_MG~UJtCy#$P?bkE|;k2k2HkHvj_gi zjtE`c=G#J%O&3@Ak~ZH?14TC+1f|iI|1|lbw1)qfCa;tR@DHNk=845aDZQ!@{{1^%V!m9CrM}~>=VRv7 zFhTEXSGJYCwR`jBRoIr-kjF2;JS4b19>b7-WKF@h{;jj zX0OaHqpL@iZRD4MtFJ1VYQ56#ERxTDRK zVGuVS7-JCUky|^nT3#+m>v^xW@;wP}xvi7;vFi=_=XLU4R-&P{eZ73$m3iIe37h4T z?8zOzh1{e&{K96rd=>vY7z|JUNHZ2pc9PPc8#5MHfv=A2qM6L=^a@7CW!&cFx5(+P zg+2xHf?MScuERbCYR9+AFC^Fa_kr53?HGoz-EVlrE_rdWS!+;>sL5YK07kyyl@9|5 z5yy$cr_JD#Q{;loCsE70TVCkKo?PZz_G3ET`Z7=5FYjl&Gx($f@)l;w;AIcWTbb>d z_UA!L*cWM9#36Y;E*$)a|9M!RQ%vH9qTB?Kq76H96n&ehE1En-TX6(!DVuzj`yRy% zu*+Gk`cc_iDsmNzkml)?D7GRMf{obzn!=T1a&^z5cB(OA|Zfz`gX&N`{`>9Gty7;iYMd&Y-c~dR?Rcb~UO40-r3UkHml2q+^ znBI3k0#%fxx8+1hs#0E*Bn&nV6UAx0SwUxt%(R%Fhw(BeQJa3s(E6N|-@BIanTbe5 zAO#Bp5SJZQgJ(N3Pah>luE9Z|J zgtjMRbd-c0e3tG6<8~@b7XonKI3ZgSjN7R!kp$qraYEK47`IbdDieU4V+k2dFjhD$ z0R-UYSVH;{j2+Jw4+3y{hmhaVdW)N58J5K@Pp{HCDd*)d&-&=m(iDfVy&#)8ix?BP$0D}IKyRpc# zJnkaDrk4LKYqR%_pocNPe0!ay15@A@fbm;b5y{65d92PF#-9b$H?sORbjl zHo%KDcIhW{VmO>okW72c58sqWb{%t-iWN1G$MRXE_!|zwTnU~d0MPB7IZ^^%w+j>eQ>~wNqV3f0tc;L$+E(kfnKie|=~MXKG;}4oS7;vT@_mUtUe2FAlOHh0BJGb1IZ*Oix`gJ4T}=a) z@Q5FBX|I_}b^q7JeE1K!oSUC-{v}%b7pVK}=lR;5S8{2|tKB>b)5R3ANb`LoPj&S= zk*Fi~&exW|mz%RfcjpA+cs(tH7kg{2&}lJNvS622Z~o(>94+nPoj=LvJTrQVEW6he zcWB7vJvH6CttXH9EI%yf+l;8z(nC=I&mw9K;(tUz#(^G!X-f~@?F*K$%HcxwFLIF< zJ!X*wF^vsPpId>e6_&cq(Mf2=%S|1NW{IMC1D5K>)S@8e5CnpULGYa@p|8gfUf`=- z-*Z!Ukbl7%KY#U2ZpJr##e8jZH{SA_JfC@W)9!zheet+RSMAkzIZEF?jPOzcCq`c7CHNO$;S28CNn>Qh~Ph{}L{;4sv; z+frWNLurt&S2p2MVL|X&%IAA1%~{*U{H})*$SSnw|9U8uStjS9p2~Sk6&~o49*Fm~ zn@MNT^FX-ooWE%+N_94{7N6&<6lPO?;oE(c7X=^B zK_*R2igZrA279K?7lA`6^4+#%l-{}sMst`J_oJUWjAUwj(H3und53G|T^D~D)3OC<8W zeo965cL;yr2OF!*#VJ^&1IyUM+vP!&lIQTjd6ZIY=^VZwkJ3N)&$Cd>eMnHE<)o8) z-I%^a`hd%3b?=Uuym&q(zQT11)emTJ6eIW(Dz6^oSMoN?=3q1VANiEpB}*mZ zdg%l+tuEjM{Omxw>_&>Mi?ZpX7sM0wdAay zCxhb{QRjl{_9#6O{V-zJnL^OBzJOPQoH0fhUXSo+f!DoLiDW~V5W-K^!w&~C(%Ddy zspOd=+k2HlxNwxQJ>(bp{gIT8B1ws+W!c#3f|poJg_h!*3n+oO3-e3?r8XNhjI)AD z6np64Eek5MtE`CK{>@O6chI;?vge!q}X%yVo7K`vi{dlp86Q&ab56z9zfD;2$R57yhsyMy#L_=n*i zGAOGjn=y#rEv%%mO5b>D5oIPDH;8xiSJp7sM*Ne%Qi&C=sf7e6MR9ZT04*v|;gVNk zKay!PJ?YQWgOsXXHGAoWxJ-Xux~Q_G?BS2J=PJBLaa1RBwL7xbv0VyV8oB*1LZTtg(-SiZWJ`o#C6sL8z z!Gwv>Nnng!#BFEuNyX4qHLS|F7DIbeyehv}Olg$&N^i2&&+@R3a0BP};nj*OGuZuj zeyq4slYI~4pNlJB*sT=pT?yrdq)jV+V4Uqh7R*h^85#w7)``DGQ{ zWAp^?(UezOv7ov-_D*@_qty0NJd7|_dY#sMClGNxnQBfpM*06Bgh-~3$3-$#PeL+@ zwY#H4aa!*t>rCx3E%_$%l9iNZtb05kQb{?@3LoX+p~|3W7MGO~&A@GF7y_gi@C5=8 z8Y&zEU^fkdqYwDrO#;Gc&2KOvBbp(sF(bV`iIkMpidR;WCFzXzC`<{Hq_^BsRawiP z4B>~XD(z@rS~cYX`&^#Ctfp-4>ie^Z?io~Y^hOZf>PWUdBR~`JDh@!{Q~8jvXBZqk z#-GLtdkX1Hm{ZYaAalYTh|9Dz{F%QGSK3H;eX}~sN7u1@SaszlyW`DU)==8U4S3}Z z@f$D%$rLXm*{vT&3uEqp8>Xkx=g<1HkmKW(=Rg^%UR8w*1 z4QngiJl*e$YJB&RwzW2zGxp&Te{c|^?QeC_F;^|6o7BvzERQ}mEXyR*UR^r2<(C@{ zkUpi~09BZjwhMsWRAx6!5|mv;aau3!frEIg&$O7pl(Ae8=MUp`>nRQvRMw~o^^_pV zd&d)8vFnG{Bc(gf)j;{idgtY?5y~A`pRbkqqX=aXTOceOz~*)1M;j_1au->b8(pjZ zg{E^up412lTd6w9v}kV{DMy(!lkaMxyl1An+J>e|NhV$AXPYVQSkN6Upt<7iiuOFX zm9m-DzM)-crC^!gudp8XjMmCWR_ZV9Q5&TOlN{W?y>c4|fR-!sZ^9RvN0K3>(%cP@;%=0B@Ht6f~f^W1bb7@DutPEuv z=kd)l3fEO1J$=h4N=MhCspUk^ba)mY{ZLq z!(c8Yg7@pDbY!Rg;YYhE_1Kz*{NHZMfHHk5`MMmz$igKp;j}kQ?39EJ7gLjfh)nG1 zh6Cbmr7^fLH{^4=D^r47QfLfnT+(iy$_hQI2tt~&LjDxN!+R+23I?N_#Mg|(@T;)$ zIIhm4uDDwRt$t5s1dAGdH%n{nWRlYgT1ifiY>j8FVmOlvuVllzR?;VNU9J6kWg0Zp zt&5>JR*J0rOK;^fGv(v&`Y7wzKlio8ebIsBu5--WCH)50!(jTRyZlCfWpjyt@AzV6 z0hgspxX{Y27_Lbz7ICIwm=s^X;wXIf_U&lq>wZ#-vnpnO<|pOX!2DZ%UDAtT9t?eM z1Q!IX-xvL13tu@vX~IhU#-9#Qe)#vt%pe};b7HqOPpn&6Y!Jb6eq5kS+B}{)P{Cb2 zas2KeWlPZMNfa>5CAQjs0A~EqaXv%|kGNEo5+aXB zY$Gfjw9JLa@hnHPP?ZJB6{xLQsKAKC7)hYrcH?WSWnNY8J5;I9Tqg3?LzPvm+XVh< zC<^w!+-n$ytEDFJvcr@G?9X-l_%Kwc71#OWVM-U)c^$7eTxnV|_c~wkP5wSaF@kBq z=IdGj_$E)tmuvav;Y!)yQ-r(TH|y$wPEn309ikH3u&^t~q+pTxe*|bBhAVhuU_|Y# zCg|{Z)C#>-ybGl0yAlD2I<%ZR4ZGuQHEj%&Vn(@A~vF&R$;8t))jBvP8_dTaaGB$O)6-MC!+CUrORuFCMo&Rf|TPI;*|63UT$q!JStQrYhP-lhT_JeTbKKqWAT5> zvZW8>r(m%bsfRl8`U79>RN8pg)Q}%^$;~yUeRV3kB=#;p-dGADs?>vToU1(ZTY5p5(c-W#wH4kcumxJ_ zA#LJpzOt|7OMkMXH$;ZVWZNMJ74Yw2;x9^2R}Z0pmeAU$*$QHT6cH^+VyI=WA`1m0;OGjz6cGxF=uGB>CKw@P zT3R4{dL2N5`_Cf$%P&eF_OdhYzCd|PH!CbuwzG*YTw0{GVXgP@_Ha>H)-F=IvRrdH z!@K0&Ed>$fW{w}Y{TSU%mu#aZ0k3yA1c>gYGXQiqaE$JTC{9zbU`m1wv|5O52OWSn zM&0)dXfL@k3I_&u@IFhh2C>3}?^&W)Te_XtK9{bs>^At(B&3P_AJ|U6L3_X-zjNIE-(Zq`lj$crk{Z+nHOHdcFI)QrxM` z?1;>c`xPKX_Dm*#vS)5X0Cv+QIC}O(6UAvAYcLt3fi1xP?{}$CVECS8!9}&CZHl+# zI{8BYzpx$i)B#U;p&iNz*1nN;dxz2&=WkMYzT9yNMUG80?1IMmN0Y~9No|oL~&Xh8B7H-En|Vf6+<*wG<~hC zo!hUNCFX_ah|prh{rclgW_lx(db@Sg=PZpcNvN(d-xovU2~dT`%Qb|?JTv3LoLQD(;zF{LrBCb*(9>}Q!e1G(Iv;xJMX&B%Oqh!xs%(H;QiwsPfJqfNr`-1GG?N~R`O9Nls9qK z42paw#3P|XcI3AJDblSA0hHYR%8AHtf}UXgl@TAg~`Lxw)oKo^gaY}lYHET(so)sxT3Ts9aK-NqxBdj?MN4KUW zQJmJnI+Ho!^#8MFfiB{|))WTnf5!lA9iHD38+kecI!t2`#BObs4~~m@VN#Uc zx(K{3PT%Eeymg9FAa5B^QcTy8JYL{WG5M6`qf?aN+C_ke!LGXV9CY~p)SY9xvq^X6 z>dw!))3Xe}mV!0MIc4~_6s3Hn8r{5MfC8dRERNL_I%gom6}y0M*4_>8kkr2<2qP}aFdIcqwVnv~<-nHIDezn@jA zvEV11ol{cSz{mXjIi(+a`-oRNuf((XNBrP57_XE4oY+r}b^|Z0kb6WVg;Gp}4uxY5gmQ^qm4zbGI!^-Vqww z*iB#Ih>B9vGFcb6*5E3u?<%+otgxgf0K2s+eqBbbgDjog@;;w(0gHCA_xaunN{G7x zI;HQWk{B0#zo3-z%Lk0JQC;+KxYNzFqBsw`sFV$H?SgVLzOIWv4i+ktVi~=cNcnA4 z?+y2Gibv@medKOdUPzP*dfhL5j|{^o&5cqByO;Q&ho(ypX^| zc>&_=yS%|A<)7#Ycd~?^1fwB54M-7d4nPRsNP{!12Ex%ZdN5J28fY*<_(@=ljBa~} zPr9u1EV&Ord%}?QkBMmz|~y0X*p{`j#gF{LWS7Qo#_4 zA2JDfK(~Vy7dvK)+|*88Q{G_Zpdr`(QjRe#m5=#bxyY(E=TSE>{Jfrz54nNQOF?z` zxf@u`lnQdUn@X98)z>IGrzEj2H3)W!0}_3Jb&0KE9E>;xA88C}<~S|Jbti$q$I8e$ zrIr-ngKsK@y(bk2a~b&rDKfDDp9ch+d5v$siJ6gmM}G09QZP0BCFRgEB>CV)(G#vJ$S@zbkE&XKJqq}Gm5Hw-)*H-&%F_tJUBibf!55llwEH# zv98b^v!G8n{6%=Ar%T!{9Zz3le{}l~p6mF^z&|zc_>AF_7N_G20dF42Yu{0dMK`;g zRrKdl66p2n0Fa`o=uH6isU!VF`s{&^V2t5S;AOt&j#ALG&I*wr zfxYBhA-$50!Z+QsW{+?MCV=Yne z*+gfeW7x5lOv`&NPE5 z3HQvBUr3kVh2#s$J@7%ki@}6)9FDL?6MiCv`}~6@XHyCf`9~=kXCo!%gw51hV-r0k zjh=4FAc!7i7>T2Z#N`qZISvPk&~!A>74I~q>2=n1|_#a74kAzm<#STHkBuVg0z=NPU}@S z-HMW#77uV`_zOE0f@rt)CE24kh~Rd-9h9LA%3JdQYLp|&Bg2A#ICPhGpP$W8>c*`+ z@!z;TAoKN1%mpDr<^hBdww#hg+}vQ29ycdZoYw6*Ob>vuTbq+i5x03cq=OWg;?{=% z%DY&|qqyydtH-Slk(^f5;6mKy0b|512%z2iDmf=^J}3Bz=SmCDi@yb6X{llt7hd3n zvcgl{t>`?zruXGm>b{uO4(0068Cr6{$r&O$B1-J&fjA?{GSIY#_ zm4Vfy2>a8xj578IKd|B!6v8jP#`^z##$UcxH1806jqPjBFqz-^^4)JRHvEBCwcjX1 zT~~bev6NM@jdBv|v1M`v!6#8CE$IF(E zw`opo!dC_NT!uQi`*&qf;5_VIn0j_>fMZgbDCeYNjraA=gZv`dw)jH-FE6i`t)Wk=J@r`wD_6YFhw^qH*Xo09+phM=xq|M8Wk@IZS^7 zW4G4BuU^!q=8(2hU@B@h0;s6=lA^i@_A4-y4)6qra%XnTFYWVF3YK*T(0tT7ks0k5uuVr1y}gTgrCpkxAxqIf%X zCFkMj>DZnqPV2}VC7XdUl!TK2=f2fcJoNe4*PYN~PB2%VhhUv+2PoE=$dbseG%b>Dy#{qt4;Lfzr#LfscQNGkN) z@kDW2*BDI5kL$otQi0fu_?_GIq0|ww}U7 z!J(fVJwt&p^nBk=dIlojg_2Y7>PozGlvGA=VbncPg^riBpky~yA|1hkbbv@s>-86K z^upzxqh#@RUY)6X*|BQ;8B>d3=eo&NZ4uPDnwUzIs{aP*-)JmmCgudX?+#Oww7Mnu z5LfkMu`*NW3yFRR0mp8G(B9zuPC+=pn&waD32rJbn!!cRCUrK>DW;j!GEyroPc9Xg z3RNz~L*3PVZ0Ah=*j+us=8fa4J=87H@`kKfu6e54i?6X~fE2y=C;%ds36BBTO{d@> z$1<@xPZXzhkimo;TLTQGB#4zZ@YSBGJuZHImhhA(*}|U#DT?zl07CfIM?&~BIC{+) zP86qgzQF|HDZm&trz7z@M;0=wX8^oY~on03?(&(UnZgR^m$(r?pa! zl3u_VN?xzycfHiewv*REx;epJ9}83TjY6Fcf{4Xp07Aj0hXCxRf8gk`7(x`Mbxsb` zsdapWw_27t@K&3*TDaFFT}ayOVQ;YBdv7hpqzhO|=diROAyiktf-3ZQ>Ux@y9znT8 z6enIihJ*5!n`VdlVrS60$~;c)UeJ3vf(UDWS*Q0OSKR@?8RHkG7x+IL@dg2W2uL2Kps+|rREyyM^%_Bd$S z16Lxzfi9G^wnV{|2s$@bp%2CSXIj<@?5IN-W=Lp8O=lTk(2+#pr0RmC_;-@WN$sGY zihZ%8R`BFJ>Uj2fxmGW)`bLue(kkay^SjX;Ur2p6sNF(X8{zmpR&I=s zgeaTz8C`EqHIT5`<&cPcPk4l>!)bxRZrTG!PnMcQ!B;4qv9aR^Mo60@9S>*{lK`4H z@OvoJ;cp(-~E?F1;nXT>!@S>gh34l;FmR1bxrbBR`3N2=RqByPH zbtc>^6B~;2vJ3e7B5E*mTfom1QSU{6ologceP_hAY~g)>F$#(WfDj&hMF{T=M~_Da z_`u|2FrjirAgobuF3evPpf+aJ++3g+6II1 z-wWB=O3XGA<}#>4+x_2#wqk!|CCnzGIIXD$Q#kHEP2{zTs})(_M6G{u6_+h~xp?F4 zLiG(RkjeW5tLHk#I-tv(kgB&Y8qJ>dLUkJiQJ|avgubOTleC*|!qGFL2T`2XDLRum zVHSjBTJ||Kdnq+RVyCBb&k(g19f}G;G)M9UA!@m}C)0#Ny4D$&gQ?`nX5+YSbmFR`MinEu#*_D_%RwAbUSdK!l*IK3mH9+(HHh6x;4R9jh1sP9ywaljN`ZUq zB7QWk+xT31pVvfS^r`IS_QN$RF;uBzg-<*%A= z4b?4|#Ccw;+O%RWy%b>o06J+=u+485@Za52R@8LkXakxgL$-?50U@^j^S36m%V6iuet=D_B=% zS~>xru}CmK9Lk^9Q;Wwe8cHhKBk%9*1{38yo!qB}_d7@k3vZ@nGr^=w+X=vK`UFSU zHHj!r>ng#G9w*as4VVmcA-#w40rl1X*i?44zS=%+(hyPFj4^HUF0kpv`zZ*bcrOGX zl&s$&lstx`7w>^Yaaw2PFeL+Hx0b`NKBOID0md3_;R9Q# z6WO6i{;U;ZD{tnnTdVnb|JLd>c5D-QMzUtY&Ch>sqYh&yH|ilW+o(fWeZymKt9E1O zHt3Ykc4Vv9=)p>Kf~UISnbk?{!H%!iDXyK>QLLKbncW%E&acubpE|2; zJROg56fV)SKUHfJt^Vf9p48yJirSmiS;pTeNQvnRlI7@9@=Rg1gqxo`RJ9jNTB3(w zF=}h38lJu}pgdZvQ|`s6-Pz#9JfbUx^F+J2up5N{Z z3mVnuJA0^so~aqa8(3ev)V^;WTYYvw#HxQ|*#%HL`(mN<^N zd7KpeH|}`xtuvR8?5mFGJ2uG+OB+Km5Et{MT}`Q+(RkwHjO=peA4TOBLy~i#igI>? z`VG6OFggj8TToUL1y?;7OlZYF0z<7hi1wzuML+dQ+_WYn9N!g9e}M6S>fm#bvg+Vs zf~hobLPr2C|G?1;^AMt7FfO?1<_y{sfRoI}2ruB1S*S)p;Snma4P(5;ZJI1oz~H$ORuKOKoHfmIRIgZ4V?`PSpr9|rp1VYF_+F{ zPDrAz-+rphe{Z(vjWA*8Q%4<2X?zVzP@H|7+)p2j@ zW#w9}`0QwOt#3rb7l4RHz$6h36^ zD{pDYhBIki3p_Yu@K2=t)8Q2ATWlW&sNft{dGPGUR{ZMj*<#lN^XHFlsuat z;!+N}bRBz$f&=)I;OOHO_qZG-3qXaEE=EjLcy%S~jPU3$QYj3@WDJR-1e>NSIRi&m z(uycf>yR8J>wqyLQdtnkM@2c^Gu!&vt)DvA&C*gYM@ugXMV&)l0!T}7T}uzrA}DF# zgBH&mE%k&JK|EiVyN*z+=Rb(8&bSfs4?j5O$V+GdOS|$`Bh*9DE9+#beL|A;%Hu^c zN$p`!h1%3{0PH4@@mXr;5Czu-8cZk+Pk=GL{f(%jsUy`^t^wm~QYzuKx2I`iC(!_J zVj)At@S6PeXmw2fx9@#ugQVv>ICKy<(Yit7ZO5pMm}MtlI7Z!)|20T3*!?XWVY_wu zLEe6>+N`5Tb=YoBh^Bh$w6+)ni*);kfgm!cC;(x9rBTBEA#n7}`4BGbFQ7A-6Z|10 z)6%p$e>GOE$ByOZp+Bod+2DV8+n?1AuIWCx`9>gI+W<)YSq&_@$wyxTGEbX_R-sv9 zWU!RJ507>VwdbmFw{dC-*S-i{cAVPHb(VK-?i{DyjjLT%m^@i;uO`N3o4ghT!P^di zFuD5(Ve)D?y3`P&IIU4SlR4o4)ZujuVAHn(#3AeSwV(ARs7Nmy9-O`bckSyJz%2tr zbe=X|&C9M-(Y}vY*W;=1Bih=DYC-0DHCJvtxsKs=>Ci>-Y9SU7%8SISU8N`5&++O$ z9IY+ND@{?~vkIqquwA_vx4Rsb8v2s=iN;!bst1&(D8Quipo%Emq;ZSgR0KmDL;;jF zL~&YA=uCKX5L1=q-zXZdKuJ#oD8l_rzBu@3J#D{4k-3N{>XAA6m5)eJOR&}D_@V^# z)|CPdPF2rHfd!8FxTJ5yE+gz1&mHy=TO7;@ZS+%pEsF3*)6_p%g+KU_>1qhu_Jco~ zu2yIH3UmJ%>U>t{DBm?h9i?vl6(U+-gFnjQ&hvm*34W{hallq=wW37)K_`Z#a|kk? zf)}RX!aIRD#K}Y9y-xO+z(kQ)^qt2$)FyGB#IglQu=`T@f2s5)S_e?zWpqeW`U>yE zBk;yiFyVc44&F-?Qh2QtcpwGFW_c1AB}i1Smyx%+E^{RbA*&&9ElHRs#R*U5QFubL zm5rfr`LdObrJ%jDm0h90nfl?+`Xv1}6<%?{L1pru6TPCfDhatPY_irM38i!+77+VM zVkgt%tUeTOQ?_nj3i??{q7l4#?`hF*a68yk?6$1Hr9Qw?h07_z9Srr!|^ z*2WM%?E}^k z6V(sWMD4+BwVo7L@eq=-Gm>t_0V7qaq7Ea<%lBvb^+K`yl;OAQ^beZu&nTuR;UI)h z$7|6>ie5L!*-5zGkG}ThuU+f9d1`S4Yw5U%XYTpRxoW<+;4EE7^pcU7C0o;FyOC_J z)4uF8?3hh{QMaA~l`$SR-!*6%qjbUrBJ>2|Ocp`a2?vSL41}FTXiFudkxp4j6e=Z) zvRKOLglR;evM?!&kVhvBG5pyv-D&3W^VBA+ubHRLQ@6*pya&z!;H*bLZ}Y)7QPNv@ zL&=N#OROOT;QkV-P5uPq{t|0G0&sr`A>9ea{Uz4Sy8v*12_fGRjN>rY3<6La2zd{% zFr+@Rw0>?%a_!xGEEm;WcN=NANaSVsY=RR3n-89ik}l($>IA}!1*VyEIG#XkR;-mX z0tzjE-r=1Vs&xZ@DNH5sAKV3!Hn-dVF8jSsmUhi-N2sN(cBUtHWDl5s=eFs6zzgba>S)0&y?KB2XQ zx(zFRvM+5bw)HjT$@J4gbIsjC^Jg>*`Xy-_#HD0uU$?xb2beI~Dx~lWOR+pPzzzn- zUuh*AUf8u5+XOQ9PXjYvb};Lxx!;+?+9aRZC6zXmY z#iQW>TnX?4;6(t=5Sj7-K&p8}Ib6j=nl`ZVRm;@otl>?bu?)>;?%O>7a=2q|a_e%e zTTZ*lW0vC!#L1gHe!2P+oAQBYELUr^nVg53*ADto;nVGrSo%Qd8StWoT>?O~u$$Wg zu$$80=q>C}qByN{4JLdkIh}_OU!mq>QF-{Z73v_*ThFi&A<@NLrPgJo%v@THqTz4m{;O4ouiI2F+9rWvyI)w@l0q$}s9NMj^-4qY zVzZ_R^6b`ux^NtYrf@rR!d(SHtU^?oLNaI~G*6d)84i*Z3lv>+=|AgS=7fEal4;rM z%V(`Y?RD@!*Qnum)WdtNy2COhJFF<5BC?K-RB4@3&rUCB5ed)??YsIiCk^P>Z|1 z{N~RiHmE6n`uideUx!3#1SNtD}374s%G*Us)ikAq)B>x-OfsFmow%N?vc{pzq4C2Orn%>8olGg za?@-RJLCdLGh0Rgm7_e30oYCL;OGeDoHzX50ri5ee$= zU{B2)$uWX0X0?#+ktMyLENF6;R(^6JP&k*Q}I(jaaZ*=rrfi6JkL`(QZ zYJ0<@Klr<(@1Q0})bpairi+sxiQy;q`nbd)3gHyw9tAg{#erwPXXz3_n)Kn!^k!+R4vg#wF2XbUe*x~O(F1DsDZZ;8Zdv_z9H z@M+sI{k}bwZ-pC@Mm>!_Vhq0x*nH5-e6WBRonMYEZZsnkrt){&)yf67NP2@mHP`#h zOqxufx-XP?tsUwdX1c}C?NBfD_I44)V~<`uT1TK1=tXfP2%@4>{sfVSN=L=|0PLp0 zaP*@1K9f3lt3_utCyam~?7c2zwcE0;QEiAyVTEdBYuFvK5@58YR}S%W8#Gs zyAfVkahw+G?50dOx)sxi;Hr)k(DJ8srbCchzQaIsLLt#NSf5ke^~A{oSt28o5JH%AkroH- zCNr5ND2s^Vv}!sNHmjh?&a|X`59Mw4V$gCTjt||dc8GiMEi3KI?Gq1)GE@W}y$topQ87)hqnn~K6h z(m>Y|Pg>#x$z6oAmdS}o3@}-HQqsQ)J$hN)2am4jDG24ysjMw|)xc zr8IQ}%Yy^BntDZA#s?k3mcU8eaY(Je3f9xKL#mG?;l{wj>K>*&LHQ{m_p)rTlfYek(2ZwZK9WnF}=A8>TASV!q}#tam;Ao8Kn?^ex=uD0ZLWfup19QdasAKYvuM zjI$!&kE*4lC%nWl>;jwfP+N6OwMfi$5kGhwdxAo)@aM3OKu?u1%FVr64_+)1^B zpLpYY60FWMV%H` zWPoVcu|w`H8ull+Zc02Dx>*s-v?_7)ooYHX*uv*5*&XdHV_Y_XTMi_wTZ#)e(&Aa;$WX!w2J%-)gj z@1L8w_vXFNn?5^rlrRDogu;T{g0YT*acPa|aU(ljfMv5hctY0D=S5JyHStiyv*$Co z+>*k=l6G)Ju3jb%8UdJwgg4vY@#0$*f;rFN<-hj>(9A3;11H(FK|9Y~`7x zf_3vAQxqFOFv<`F;#UaFyORLYNoX|$+K{U3>Y1drem$8t9dt|_k7*Xnle9o79e9q_ zKbsWavdkf>7`Wyy4OhLMK_LFPn4?|AI7ch`#{9g2CS!;Xvp<4=oA=}{{@#u3m$OM# z^c<>f;%pXo4)odwS@OB0Qfj~>XWsN7+eG=E0U|d%3?6QHYnq+5 zne1?YhaR#paU7P~LI?wIo;M1pIeU5DAk4Fe?Af`bT5^d=ti<`GvDVTL32Pw4dRK8_ zO$U)Dd;@qmRx8X*1Iq?SWQiAX9F|&!P)MF%Z%}drBl&Up-13nb_Nx3#7UK|`W%mQ1 zq%P$;FTvkBtr?1|FN&+@g(MuyoW+`5KnoOqg(Y9WYFRzpA$%dJshoV6ow|_J%(^y< zjBF3Q9I`_sND=7a3Xg!xjXYn08|huqY2*UpI4pbeg>pba5lQvU9m!>r#s8E(NI;vV zDgN>OXr^ggB%NLz$;fNvw+;-1!0jq|ehKh%I90%{gd@_V8gWqC3^;5Rn+OW+eJY&A zPFzenXT8vq=F+HPcs%+IJ9mz2|uXY0sfq5L$6yloyw9#TG| zELVOCju^zm69-HALdb0If>tWeZxyIHvG7G^6r?!hSm5WH%H3rXXl! z0V2O=rH-Q9&Wn>Y^37DEVmk_C!U3WncieXb;RSD)V^<>RHkc%nsM%pG-t>~7z0B?p-|I!wu{993QJ$0nHXzQvK z*^+CRvi)>}WnN3FV_k8Bj5z^AA5#Q`F`kg*D)xiRjmhxm#uRcHGnY8%yA2@+`fgBi zZTW!M34^YgUsTbUK+N`~A{i&<1Fg1#!;un5A4OXgNGsupXjdZ++Mj%>iJ;*2i%e%S z*2-sfpujX<$^)0{DU+<#zf<7nw87)Z|n6Sa=Fg%aKOGLc~Y%~BO*|PI1Wo&LkOkW3W`yhy))S_ znHaZTUd)=`NQxO$_Bw%d!?TnS=}oqaESU~Ex26GkD4|;UaU2^Q5iu{~pz|_>%A@lF zCAZ{$-o`|rY?D?>oUCi?)s3VT(q;YIn@KscHSsbeahraR*t9z_j$R%%OIc9dQXWEG zzv3<%W`W3uLX9Xq*$_p`5jN8dIEX7oz&^ym%&8%?8E3yh$z4mk%P|phI@c%kcCKgW zZM+4DIjtS&Lw~R1)>BmDzl*t$DsUt;l^4P0NOyfXQaOPH&U)fFEWZjNyf4Lv1s_4p zspcFOO#Pj;znipPZrh)IxSO<9Ue=GTyN7cvdsyzhq-@_Iee1ZTdgqMsp{x1)di7b}RuYrC{CFN|V75;e6BpS&F6(?Q0GiVAFhX$a&K2g?74~-K#rMQx5gcR%5~v(;9F__>wMj-D+#EzhPbzQ)BJ;XdH0C`%o}!{aUAcuIL~-nk;2fnW zJRHAbL5|-Wj=+BgIS6?eLMZkYgf%+Wv&5tlhZ}}SfBTdk2z$4X3qU5ll*T(LB$c&v z69;f30M3~y05%#z=8QI%sH9`U>J;mpom9GTX9NhnjjTP`L$_xq`N=p*vGjRTf?O_~ z~5xpLy`RKe86xb|T zzd$3+HO4u%#!zqKpyymW3gsp8l{X>fy(pWqA;GnOlDPIpLOVD|iQ}-`#~d(H-E8-R z791{uWR4fS{EK`k57VwpazG>}NYZCT*98>w2G0Np^Sn*>3ji@1zQGD|Q1a*{n|5*I`3k+m@&Xcu@kL35w5B5j*ZSLAcrc|1 z_sfRlSk#OC?W0NL669XF<@}< z%H$!~&^PedOv!LWRQ}ung2Pf;2$}6U*p`59e_1w-<@{+0~dJ~Vg zL!Au`z#R`FucX!C5t$=k$HGBe&?SA@#$n4FLTHCNBP_K;U}kM&ukw;2s*gh;x0KQ_ z@91l89zPB>eK6J*bB54<5sKTyZ9jBYlCAf*BCOfYce*(&l|^_;glH(?Rp%o@aAQm~ zePc7VfdgZZIp>Mvu)KtINCUGyh}Y@z5X>nDZ){qTHfd2F^iu96{)ssT;el036pwu* z)){G6ERs(6;FZE_q%ZAeYdZY%TWs2IFp?;gsk1Q)1njeL1a?P(JsL^?z~P7S`SjIW z*&<0~zoPg(g*wocdtboA3x`Dvyj&!JEh&WB*|_T0Q2KD>mEW29feE9Lfm9u%F}c(Q5IJ7(()DlaQq4MO>8qAgoD_k97Pfblcque z2{KSZKZfR|-N?0}TV0I~c@5j?qjZ!Dl-3IrQ5qMN&gzyD$_q~^Rj*lIX@zIWQ}mG) zl=V{k`Ey`VY}!?`o!(rHr57pRL;Kl_+&t3$8X)}pDYCaf*yR2*tIZr%{8=Anc%j&nG!gt0>ZfQ0rWs29Fx{{KY`EWD!XFoFcSrd z{!qsdDUheZp-l-muwICVvjjPz=W*yBpyt>Kn#3QCX1UFjA=ZS^JjS=tFh$}uWo+cx z%?&1-sIu+=pBD)Y9*#GH1~4|$HaH@tVZ?D*x(PwEeHC=USWt5o@{W$?zkPY2_)92a z`}quiSFmiB*h|SAzZnl`DK2zSWE(?(0(0<32*ttQVTb|ndM@w-#K9wiyI5I^(o`<= zoOQP--Q@DgY==eh>veAwr6p~BrePZu6R2(r8JFrm5Q3{NAXK-8BjRK1{}H2D?+B%SqA$XTp-!Tq3kf@k z1gQn7MKF|+O=-+yGfjXa43LO}AxXXgiJ%w;JRZrOM<^BhFBwV7spZJpAq<6b6T-PF z^A3dI0LL&c0YDxc0WgC&*dibVMRoJyqt2_~I9G0ZkJLvrS2{^u9u4PtRZ-+s;C2^W zyAc!@bW(pK1c&n6%AsQ601BK-#KD~yWCB%($NVFs@(5NUQn@4F8K%FBRPgAz{FoJN zr9{wW53Q71(n~$Lm9kWpI_cg~%1x>Ch6p;ejT=|+ek-0lrd>L8s-jc``!^c7-Z+AF zi&5&x+Xu3lG3bu2nAxKkC8Fi63g`(+;BFQTQwcGS?u~O>VkIQjn^sD~K4G6>=xyh_ zz@z?oO*>aJOA^m-mPCssrWh~QFIK55PYc(7h*fUNUBdeEl(19ZfYrlwJW^C$R)E3d z8wU?hiC#36u$h*@5h+obIJkdW2;p*dKGTuXm;Dl_wD3IJ2i4{;3{lyRL5jaztS=t_ zP-^(zEynGc`vuR#%rUhus4MYGVM#vUn+Gt;JXHR)mGIb1tKf(Mav*VVTZ18l3NQ&2V=2;;GuxbQ zomLU6B!l^BNj)<`Ns~IXQh4fY5venS)QS#m3m6=;CwT~S$Z~jWrcH1JW?kYqEFBG@ zF?oJ76t<(I@|C>LTq&;V$}YbKxAhnsmysF% zz4QuQlt6d+bs~#SRQkx@CbG?m%D3{r-So!Yl>w48L|@rMDO-S^Oi(bId8LKhxzC4kVSG>m$HCYKTyFf+Iq=v_fpeY4ZzYbya&aCU(PFqm$VjmHV8%L>Aetw^ z{*&(bD;kAWMsQesk`$y_@8|iAAs?mCHv;4rI3k5&iQ}*g%?DWpiUCp^Ub<5``@JtU zuOH$GgeKLS<%9G_7(S3TlP1C_FECWX04E5i_#gY8e zPakEZyg!~z>7z`L!{hb8`zS5l5Y8;>nA~@H^r- zEV~RL9IAN+3SF8BW(vGCe}M`2y3PrAkT8$ zDEp_t|Tx4pRGKSqM5$~m27VneVRiVD9II;>N(#kjbyn*MOJwpy3NBC*o1jXZ@Fg$ z{mwk)sU(jsr=R>z$t@@)vK@<+N^;m-c4e`WEf@7;N0ulJth-7$llHH!u1J>mqex;v za{*i)$vd5SB+GW;kpyQQaU7PDh7gkWFHnpD%?xQ5@0*vAVmSzh&XtyMM#Wff*DB7|EmN9WPZe`U z#it9xi&9drxS{<4xLo_2j__ditCLf^P8`gB5h{cb6(0y0QCSLRoTFbhk4j%26%`~z zh3=$;*cwBuAfiGRaD!7xC6#i!06PgsM5P^ZaJQR~GTVK5R8|yYOO`8Ltow>OqwuAZ zum(LzWvABP!R1=BI>2KyRdi|HKpb3;WC$S&UqCUUFdJSYH>kJjgukM`+Oa)w^@TZ3@KfYUaNe~9~fDuJdt{{4eOOq>p4$noQt({8PdqhFytw?+>k$F zxFKP&PD2h82df`#g%HxU7=$RwD1WQK#9rPY-_oUaiuxlHXHr4>JF)w$?X0aA@M*kE6i|oz?k@p@;;o(+o!jJ$~ z<-kGg(XWgmj>GbuA%s*u1q#I;%+8#-;tjeY^fYGsp&r7&q0WY(WxbC5y;B-#aXkvP}^j-eb$0iFWOe zpAgo1x3bKqE5f0P$JylF72mL()%y4|_dQB^_u>dpb&t}tc$>e;i0MtJyi!qA{|?cU z_bAEka@CRgV_m5&;jy2h2b6Pi&Ef3c0cEg9`Y=zoHqtl`w&TxU71)twRg3FW4=ODr z>A;5LhZM>B@U1iXYSec{sr@^$1NGwzxI9YUp*%`)a73ZILL7(XLp>pc)T;p@qgw3) zGtRM>_8=RVqD*wb{9;I;U3vC41jARtS>(j|u`b7X0!QEs6F777aee{Cz)5%;!loWU zd0x?noj#(p)Rw%V*wA-O78xuu$OHk7vIkrqn-g_7in|MCHgT}?$Pg-p6(Ue_efb^= zej9k6-@rE^Y{*fit6cmNOFxQn(E^SAbyNxW?6r$mo1VK^#4)9`Jm;$3tzQ-432s|Sc*3Un>swDKO+Ath zKcpDZ#FTqgSB!!lQTh(Y&J~P9uOI}E)x%0WR+TIBv;$`oaj7Q|$YrIbT%ilgxvVsnX0pmxln`kj z>v#ooZ{<3%Iaib#QU|6J^#pr*MG2PP>m{x#XC>(@yMGPLf0^fXWu{yzfqj2nxh?CN zY-A=9_vcJDJ5x!LtzWb3Or@S&dJ8LiLn&P<>TA5WS{H5D-TClTH}5+RfBoKt+5MP~ zCFKMC*?=2L6}8NDXYuIk?@G*>Ao9x8kUUg>+hBx>`a2ztsAk^8!8UgxgmY<6sa;n= z&8Y(~m(IS%p5DMP{8kJz-&EGgmJGJ%rqUyE(N$*!yGv?Ck(93)MRF&&95WrGLSVXK zFe(cEOyW2!8x0{;u)Clb1%K#O*8i3gTy6nKXfWtT#Q6bl|L3OdSy$P%TNu>z9Ls!e zD@oD^edulFlO+A9_r0rZlLAH_L#cJliJ>~0_6D0c9sOSeIWODFYTj4&$X(a5tourd z(Crstf2!DEdLI6q5PlvlY-BPLIuxl~Fb$p{9HRRbe|mK{lKepuvt%iO9a@lh%6Ev# zOd64i=>H5NuaUjr;n5%J!=t|$j);CJad2CO5Hj0m@xg@cBHNvXV&OB0g+EYg%VUyy8xK%w0xmG` zhbT?v3#|G>CAh?fF^GaM9|6#@1xPIDLv^<}Q|I(7!qOfpO-rpEfiP>pN|*u9V#Jr$ zj@EZ&w;w8H$U3(w3e?e2QQ^`WkWLrTTtY!-#v;ALws`+&Wi+-jI!*S+4&y)sO=w;8Jp;ECedPX*?o;-Ln z`xx!^CX z?n`BtJmDwy_$3;U{Xa3USIQ#4!#@VNM!S%+w`Np_mvVMt2>TRd7u{h z6ZO#|Vr7r=VvMVRz~vFUMSUr*0zw~+@_;XgHN?SHKtjlD-^RC>Nw#b3(OacunQnMo3`eN3Rz4@}`B?aC8JX8SinVyB1jxU%Wl8Up#$C*Z zdBPVHU3@2C8}^L@gChjO!|e;B9^Gb2g(I*(AL90v6GCQtL$sQCe(evl>~~1G+|I1x zd#uLJJH(p5SAxnOL1@%9Y^={dC{evdF%?O%`w+9eSL(|p53&94m3qZSwMI3?GH}`{ zEc0V+I6HxTevbmTzA-EPL1~p3eUQ?VwrR~JlME#w@~?=#@Nj#R|Kj%UgacGq)|fcB z)YuRT#IX!ea?5jOnS=W74@yUm#8o?;+Ml9#H?$Yt1uu<04uZ?IpP}y4X7WTwD;mT3 z#KF#GL#RZa-&0U>u>l=oV|MDjz9^U6t>d?nb{wZL9eaiF|Gl5{9#{y>uQQ!ZerlJO zynx4Mx(P?X^d^qOGL1{;rTl~mW_~x|4x$_WaQO3gr;1CABXK)m=MMU2Nxfi_p6cTZ zsgERi?OpC)UZ${G9ZzjmW39c_`iZsIIumXT`f4NL7K6yU>}Ys+jJiD_Lrsg|AY0I7 zmm>~#?-@d9^~Zohtv;A;YuQC_bz@@i8mID!7*=D2{(_IXAhFeIr}Ehk5L^_f?I7|7TZM-!AAX-J-wFqoV+hxPIM^L# z2toO5P>lY{e>J;pRx4uL-`Q18^+og5HzZjq8c)FG>Ob7$>Kl-H&N)OJhviNd9M}Z) zMY(#;T)B$XHLG0`E4Y*oG{Rw&=>U-@UsF;+2@@-nr@;}a?kkjs<|`ivijnH?SCZ|y zQVW*hs~+i9VFg8i24n+CjYuB{h`fPl3Xj0J1CPy=1_u~uz*efzQB{+lextW2rdE^Wxy|%J#nnYp$<49o-O}UJF0{dwCu>8fn|_wPNH6ZE zhD&ny7}l|*S`xF`gG;JU<)@Qb2Y+?HTzfL}Dy6O~asPKTcu}EPcYKTs)Uk_U#Kpqw zcqz47v8R{7@o~{JE)`~fmQuUR)l}B0v|3m0r7}CY9h2C;(&{rQovjQ|XD3cv;LN~Z zXoMo-{|rQ)6N|`0Y5e15cxS~m{`fE0~x>{cDE3<9Y)iSbA1$M5w8Y-7k*}v7*(()IDl@3yy$wR$Zw;*+q z?B7v;6{Ied<*MGgy_Pysk{urGkJ@Tfm%-DJ3ugOCu|s&vFRsX60)yv-l{}OabJO9m znJ&T+Ink9kxcyWJneFR&+ZyBwyruvUr(PD*k- z9w!`Tv)IN{!KA}^XAcM)z2j)F+zZfS({5r16aV`I{&Hj2>)hDKa74O%MI4;x71Fdj zjjk~}0xGoxzhOTB50Hwj(jQZTGop%AEym|RA~3EP?pv5m8`HCdzPq71Op<@7$SO5f z=g9GO*^b6)7tiiR`K(r@!OW+L8Z965XWg2pRYD7ur7y>@y?I`E()T2kxh_VkQJ60; z&ky9EmXShS3SgU?sLN%)f_h9-)k`Yn9>V1d6@p`qe-rEIhC5g+%HM2gGqthjb0W_% z*#@vJ&D1*Bf5z@KQ$u8nH}ejMd()4FliS~e4GULi%1dI|AK|F~DIR))MU9o@NzrU+ zgnEls;l?&stIMyW*sA7g3;DAzd(d3%5^%o_Zf}^Gg508u7T@Fyg^x;mMjMvULiLlY z`|1N*sQo1QL~C{~Qq7k8v|@W(s#oL!t@PQg)bVoZ*)5DO&H6F6Q&P@RY$+fG|1@W1 zqSPSy_vWlklv*dT&R}PgGj@~6Sv2d*K;)&QEj&C!du-qtx&)3WKNX3C8+#2QG&y5I zq0B^k>^@jO7o`r7p2Vm@o>MN+oj-o=A*^I9{+|58|6UGe z(Xna+&l&&nzdr;sJ8>&D=YRVL>w9BWi{$0|nf{yVcMD*jS0shFud{fu;FU?i?v@gT znX)pehWo_NUMvk1`Px~wznwZ%o^+p8jZ?$gd*elg+*Y=K2Bb8a_D=z9!%6i-tNk-d zN^USbrWZIMfZLfW9W3A$DaDxA{W7{_S~?hxH^GRkH!f#E4 zJhMJgcW5&;TFEmDL_YrL zPV&^=4HB9+z!8moh|rvnuh|Yt?gE-X>}j(+RmfmE!x@AbafK@$9J&56_;KG!C$t zzJVjM$CEe?OHD&4AkR+&CAXNN(HKEK>I_A=-^7?t+=9R2v*%shLKm7p1UcF%usPbb z?>U;E3vD@Zuxe`vp-p%PiUDHn%o2L3!;6h-6M%bZ8bm}kd6U`@B|rPNezBK2RFbQm zWla^eeust~0dKbNLyADz*90P}(@HRS^AZmaw;*Xgw_rIO(JECT4xVf@gr?w5-j3{u zqSmCBpA@ySJnl5}R@Lg(B^^Sr0pX+BUK{PA&C&;d#qz+$?_78nK;$u=1P{k^%;R|H z;E43?L>!zV5<+JCJ?uNfIVwwIJyf0_TY166^O}pscJh}Tg>uQwUKm* zHPX~WWt&Fkr%XxP#1=>dz6dbK()AQ8(p#-k`mY>Z0%!ZHIL_7}b)wDtk}BoJ%?W$l zg0=09ud2f>*y!Hq0^gX}q2B0BLR+vez16DnlV_|-AGNU@{VP-YU@%qanLectMugbB zk#bQ%ZZbK9Wt zpN79NQs%*t(3c}4*-%@CH)6+!sMX}CMl5@X`qDSCA%!^}nwM)viyzyNJsGNoTi4z7 z!rb6%k}&0kp|(jGIn(Jt!5q^Y{Dr`=VNja;BT(*hLgv8%gwk6CI%fn*r9e|5bE@U787(ExN^jzo_bM*>~w79Pkdj!^f?$KJ5a5o)!X%ii#wPND7) zXP`pQ#5vmdWEls0&XfyDE^E z#i&3Bz~vR_^h92PJSI6S&^+Sc?s7vY9G|J-tmA04fgByqW{$>ZN~3W0dbCdEt_Pw@CVJzrCVHAbagj5A@!@Mq<=Mw`I0&HmcMl zywDJ~y#%Y6q6Tyr(v(d7no!9a(Y9Oz5zDX}dFL~g{!n)}?`wE$rr+R*ZmAm&W*KJ) zM&$YZ+?35u!O-5jDf=ZwjqSX+32CQA-un|=+6#~v^?19%<(hw?;jYajyELZ}$6?uQ z2)UkOxY;CxB|q&|T<+C`jY&nd^4h^xr>ctVxs&Cks>kG&Q(5|0HB8;sh=6JG?+?-~ zq7~5C$bmE9a^M?dIdG|Q&Ky`p94w0pA+z0%Nm|@@0&33J@bWfmVk6dhochYzw;^HB zgxDs?8yFWs1H&$aad9eA#@CzKruRoh9qz7Lh53({2K-g*E154 z`8Wd`g2RFQoIq6tP=6OtE`k8aJ6|fYA^T&zT2a=+nE%%*j{hTsaT;PQ!ir?N1tQm+ zM)H*Ni&NmSnQp)lQP70usfG}ph5#jZAs1^2uWe!}H}kw9)Q~yG;GpM6+OpvWhDfkz zA!wxys>49#a%JG*a5V+oP#4_W2H-d>#SA#4STw>KDRvc1fbgT>09lT|0_0IDq(qcF zoESQ3q9~mj1CP~aDwyJo(;Q;qa!*4FAdf&XKt{ofXC&xxWg{|Spa`5v`8aDR7|l_2 zfQRE~go7=ta75At5(isYNr-MT1kNOo3>;5V;Mlz*ALn9yCyrk}PE!g-I62^QoIgi% zoW?GkW5mJgWC|PsHO7d{Q2ZB>*=dAF#$2aB1TvjeQKBsrAXnjt$n+u( zu7Jn~*$Ijfmk5&2e-fi0{tAqECx$i8Z!`rWkV^1ykh%h7lndljJq}VTA0!@O4Uk)4 z0%R~n1odSt{-VC{I|kAEj)9~o0bWj=1Eh^2b2>T1N?GGE+8LX9#6m-uFZBczo25N| z-BLz?^+P`EAO)sYYzTQs;rL{D@Wvh-(LOdO4i4EHLjN;tcMva_W{&wd{sd10Gl)3i z8w$A8@1gv^jD+qb3jEwax<@8K!td+M+x#8g5aG?4zz^ zCLxRB^fQPYycc;W_lFK8G}C4{!mLo@U|mrNne7jGs@Ooy$>gbmvg1>W9k;2;C63^u zJ8n+7El)72Ft@&mq1wj{EXuA14?puWNNEq_(*8oH{`tr&+q7%n)X--&RTK()3x!}kgCL|l3@JQwWEgi44e zCMi-erV|EKXTQ%>OL{yBB$R!D?Db3xH-8Oep0m`=>YHkWN_C;pAm|b0E(t+-Gz!DR zQ3D2W)ShreEFM4(Lb4%*T`!FgHrJQmKW4P#N2;-+X{z1Y2w{!l_evDM&s7PP?h0u& z(1kh}!8ocv>87{__UEVr;Rw{1kb_VWLkPvM6~a>SvpS1kuN74MqIvPlXjKte#7fM@ z_uF_kwqdp!AtzL2&t|JbN=jv} z6q!m_P4w#FGIc75{8POSJlxcXL~iO7I4~7+geKzPX~rJx(jv7?=~y9w%!&E$qhDwJ zSplYYl*jqAM5dOr*6}B_YY-gU-G#OcM4m8h;o)dKx^c86a74mXBo3x44WSj7t^_6b z2b<+?NzzCU3|Gz97)!yaSp5ku*I0;a3^GNNO3u1SEHuK2a8NA%^TKdh{}BGdu)MS+ zYp_@i=@5yas1H+-*pRRr5~vTGz~Gj5CuNkSgSx_FGi`t)EDsSd35HNd%=?sNyB4br ziX8ES*3_w7{+6HqVX^weUG7zk-CmAmkDy|#$O^T$9Q2lrTA{X=2Nq@NE3gdGt0=p@ zLjAdOSzn4aeWC?WuJW`W4#)`&c0PE-OnX9gS*Yaim^HX1|7)%7U8;yb0leRGH# z+qtZ2WmbHZ8eeSjr_ye1`+4ujS9#t8d{^ID&ql9O=UU^|2fMX(g#Q`qNcD(yboI^s z3d~0Tj~Hp2+!~#@=@&Pea`m}-5hc;w`gE=aT45usGt5dJCM+`k3|4C%$TC2PneBynf4m6P95?EZd7uj3`u4S0f*kDQML3;l?gD4ONSR27 zv7lqO^kDk3q2wr!{~RzlmIDrlBRC&CLBMU#hAi5=wg{W+%T;m(w5gi?yI!3nJ<=!s zs3ytM4E@Fibz%WICxUhTS*J= zYyjs9ajxPBY~B9s3f@#_v>{S_8n&DS)`}(?9fFV`toAlb52|EZ08+ znGZY5apC_AJHtt-G3?AnFb@5x8Ha8}(7aVRN*qjVU|NS%y6S!rk`}|x!otm~w%~KH z9==QcQ}XofP5p=I?F(kvgNgUi3)rMRSQ_iGg8fWxg%#`$xsR4J@4axZEN9_+)e-XA zNcQtywXYn#Q1{)Z21-R1)u9aOavJ&IbNM1`t*g_D6#5UX)kSti$9!YtMHatbWr5k4 zS9gZ5dZ93u(;lGE({fsR&T~{f%1`eL%UA4iN`iO(;Q7 zgn&~tA7|y;U>15r?b5M4smB8nVzXEuBQWa73@|uCm=mG3K(NCRF)1t%YUc~}f!gaw z)C%sIZ-UvMN7RZ|TF-M!`5lH_ri2jf+$RvZ?w@G2k6P5db-49^z!AD9@pVAU3PWfK z-rWQx7dtQyQ4VI`!Pg2BYO0YR7}Qq+A2B%%3efrsK|TqT1`da7OiBqZTENY8!4)A6 zE|$o*u|FsuTXpa?+y8<_jJSIv-$n7k(DgONiU!DEC{aYLDm!6*2baTSg;;sCD!Ons z5DSNp3@OZNegP#Ht24;U+3?yXR+P=t=b+f-e1{)S9}PM;W%^jOHo3z&Q3268H-VX{ zYWOQkM#JmOA+@ypMZqYnALU~EzV90Gqp_mK<#VC${y+VE-Q$xyE1Pv99 zmR`$+{qNGN-|JvjIUQZQ_bZ+Ot3?JRP!fxTNdbe~Tm>F(MZI8d#b`LfioafRD+2O` z5)c-5af0ra(iaBik>NsLRZ>Gk#w#R6`T4MRh+A9KvdW~8vo;BZXALPYdhaN&+CQL@ zPt1r+$3L5;8-CqVS3*jJ8Tm4p^**k4>i6~qVbk|W<9uv|^b)o=c?i1ZA3&jfx#25JEOvqt;SM5<&Eg=4 z|0LXc{HK$=ti@@yB#Szs_KK4!2p{ps8v4+(i~y0xsvJB#R<&#LSPg?CV)gzxH==|g zR1p{H=CYS3@UfWjoE18$wkvlTA&dg~TiyRHkO(sJq*_vre$Hl|RJ)OT|D;;VYT+Vz z+41_TJj@0J(SbC@X89q9Y;1=(-yt(Z%1a_FE(8IW+juO9+n5IjHezr#gE+WN!VoH+ z=XVE`+`)?dbELuV|5W&3k4Qh zhBCy#)A2$G8>Y}S;22^KJM*hr)_OFXVn98SDPL<%r`DU`a;-EYfUFL3Y28j7hviZY z5p7g`6NHSa|2>%8c6SWsaB!^$2N^{Bj~$j3h6r-=m+YF*LWL^X2}E-e07{1=%2c!f z8eD_TJ*E1~^Rn57Q`n+fAe)^%rAAx7f9ACRCPiH2z`r2!RM`p-xBoEB0N6~Q;fPXV zBaXwe-VlQQH$gG%A4u}~YaJo@n_tJ0^KlkEvpSQX)KMktB7hdUfXWaD$)7Kk42sQi z>nRydty86ZRul!M^!Ni@uCIX5*P8Tk&N<@XsufD7>3Jc}N>&b%`IT$~0^uY%LHwtZ zefN}EPGdni`YD@vT8)qU?FpGc_o0*&+Gy3I8H9M+JqMSY@E6Ti*i20@MIq8Ioj4B5 z12PQ>UM|nCY*(~=TJ0lV3f@6C9JCV)866B?m(Wj++S$ zG~q)U5XCtl@){gY%BeiG!~6^q&jCkN{bIy%Sn3->tMdE?gObbgH)x9+8IM2eK@p=} zwjLrZjco~}1_wdhZagwTo>k@`)ddJR+lb?^{6=wxWtH>%3J?hHw*vFKN6d0gt<|L& z0-;#@@b6p86eBDE)`7u8c5(vr5ddr8h>Qvn0F;^lFx!9USK&-}#15ZBYx&zlGMyTg zlKEN*j%$4mt|(ZQxY%bI^ip?^D1N2AOy+&A=eG5cB zps$ifdMNc53xpeRU^l)yHR51hpo(ZsHaukW&SUFE>4$9Zd2FZ5d?0c>rk{JPEu$i3 zde3-ABPwdyHp_f)-BPYYK9kZzWcg_m3iKck#bdBgxsg&8fQJ)B<`q!gAMo{tx)nAef#g~zp~ur6x)6kd#}PeG1E2lxqBm-at)uB zZ6eu)YuH|Q|0IjNt~Ql7rL)P`@jv$jdwX5&ny6$tOWAtNN*dX521I<~SYOe$o}7h;2d;I#$Ak^cW1@ zkSu_QBdsmQksiW9H1e>#M;u&Wn=f<>6q_Xoziug5ZnKQcXDy__RHRywhb->|MeShv z_oR@sN)yXrX=+H-#iSJ|xj}enfc6Pij7h6ttSr2~OhD=QbxU2$W$#|r-`>V{$;i*K zSfffCgcnd#(_qa9aQyV!C=MwnXoA5noE+?XiE~8xVoB+SZSp0Wclcp0&0G8Z z{4HC17kT^hC1>)#FX~E^QokFCk^wG{#C0=Il#;&AL|H)`tQs0ZNdEVrP~IZNre0$0 z?_o30cN>}Go?5;0+usO;CRZEhgD9kdQp(#2QbvIEf+G^@A><(BZV0(1Sn6F07Mno! z{Kf+AtGL_o0L#3uhIU$Zk$|YLe@MDSVJbvg2xuR;JT|}j@YtA&IDx(;4&Lc8gj_&( zF0!6k*e2KEBAb$>R*93j{$U6jo3DQa36uVEPW`ooewyYK5qW=s>n~vlHNa5$B72#I z`Mup2SeXZy<@AbTT_31lOSjl>_$%cxkB4e&zE=({5<{E?a?dsc?cQ3$(FNvmB-jTCl`xk@lVyYa@{rT z?Nc>XUeuorex{D}jGIEmHSD{Ude$?lvNgilv>Z$jhFjSA9Ce-Cq!~+ou2xSfe3GoA zw@f;jT-J>Pk@q)&@NnzG+_`nBa9|yJqR%I|b>;GfIw34(oKs8Z7U3aoozOo#SEozT zV|~I4wT^_;-~3XIl*=?>FJG$9l{$^FMC6DHl3E4XEXR)VEIB7WpkOOy|(+f%G_AbyC%ZcN+ zIJ5fL3m42|5Jg)E4~JR%oWneX0~ploA;dvjXb2&zkAY%T`?xLa8+WaWTzw1M;I7%b zjNiS0awX($Wxu_Hm#XiBn($J6lLw)+6F4o+xw> z24SZ?698h@#X9i*v+E)be>qYw8m(h`5)P0svaC!TOivop{m={l#EyDsz2x0L>Sa8& zesW^9b5P4%2Ode{i!l?(enW$G5 zjg>obu;eI&VjNvBrF@^~*L5L#5TFGlZd~Au%j>HMCo-gjQ|k$Exz@|*C!w{NOY0Ki zI4lPZA;jf1D8?9J(gN10jMk`207z(K=?Qm)Rq_G8A!%yYL&-yCw-5kR;Rv$}5eGNV z3L&$7DF5W|v4GtzqsXC#5T^+11o8G zvhOOZUs;OX~WQEcm~Sj)n!4bDuV z_+$@fOKWNoa>!?PtEN_8TBaANrHz)%cmFO#$r_V(FD~uxY@FV%t$mRfPh)d~wNmoj zY5K-s?I%fAhw2^bXfd)hLSGW9?U3XWAM|$hwMr8977uTL22^^d7YfrlNp!h%LoHtJ zAHy~^)SgKf^zRyJcnWXaSaz(jHm%BsR9Nr2^yW?=ADPW5>gBrhCMJOOYobk*yQS(` zO|%v=URd z+j3!o+4wr3)NeGJv2ZO)TCU#<*J?>}nPgTtLMto#B(oY3S_{12Jv2h=CPnGTBeXlX zh>ERjq1Enwb2tn#+vkf@fMvG>M&xSb2triSxnVC5iW~ON&+ynx&9*uX`-M0T%cE^@ z=tDN1zZFtqgkCvPTOi5)!sB}-H5&-6 zU4@X@zMP*aobeUg(Mr1@SGuQLTWi0{)n*MM@FO0m2ib=c_)2m*?hytR7wC+_9Ts_h z0|v1n(WvwN+27Gxb$UuNMyt|A9ms8~BF;Om*&vLEZKuHCwxyDXY_tCekIi%vjl!%ziAP9qQeMer!lPty*8deq<3%XC$o`79lC7fym9NOCCy!=4;`x znXGVxIR%M>`yve?n3DvGVNOopV2s`hMP|?*1Xi?FTyf?er&W@h+-8AsS|_*BPMvb0`5*j^haOT+bg9km9Mv|1n3NsE%D9{RQ}TCoCDFS={N1?5tw*&9`B zC0)@QXj-Uj9TXpoRmaD)Y3^yfqiR*`&;mv5S{j3P*(l44LF5f|G(0>y-M;0~Sp-M4 z>*a`p9a@GEb}fwoC3h{Zk#+7`a{OAzEu$6Rv=o`qrXpgdKklO~mgK_$Y)U_EnB2A< zd(lrzEq$deBx_=Po&FE5;hp1`{L~Ylf2PpaY(jr+gdEpaf8Jl~Ep_P;!%O06QN5Q_ zGc2n2aWMF}XB0f#Uh7P5?=d*A7iBJrI1bAoA%s_CkkWXbB!;aVsAb55qS>H9nih9A zic(_%0#=>n(jFUaX#W#juD#F>Rc z#cfbxGtg-v@7!|E;a(BgG!^>_L3aXhK3hFZ3$|X5BqI`GL^(qTz*|yS8oIs(mjk;A z;1&eTIT^%pShBx?gVd{luT%)-`tyw23}%iU_&n%x{KIm3T-rq6)@fgP$2yvu^+E%W zr+b)jMlWu-_K#fhfF3(SOP1(v;*r`n(iuHsl=j|TuDzSpPtpF8_xrJsRBf(2zKMP` zRg0A*#yrMptE9_p<2da%S>D3F9I?RhE>CRzIsn9#>%pPu&z(jOj7S>wO|omx54lY%4-hi%Hz{s&~E5o@!3$D?8@f5 zq4D<}LUZdR>K<&SNI0+#gQU~M!I$tbt^mi~`FJp(HY;n>^5k;sSUI~EVcoK}5VjY8 z!Q;}a`Qy@!YdX`zN(x22TMr_=T*5Od0e?86Is_h@X(b#{JE{=}H|ZLJxOZzJDE!_n z6!1dCO}meMI2aS{Z=IHtJ;0R5ZrQcjy_?q{P})W{V<-Xv6miAt0UJS&OUrUTCeRUo zIdbp8@YqbN;fS25P8^3N#*jwknG6b5o(w!eQA0mDUBfNuw{&e52808qR3$hnzrPL; zaHyxxLByG)bcn>Hoyhb1p8iw)S%c;qU=QGk`ZJh^w4@1Hy!i2+I=h3Q=1hW@W45l! zX3y4Q<-e-1YqPb)4&$m2{}%RhEk{X4U6uJ*7MBn|WHA^pEa6UpppC-|OeU*DgxZ zFn!rV?JFs9MJcj|`nJbdZ!j|1vou+Q(fWRHxiuMy+?qn&xi#R-B@UjbGK7%Kk3lg8 z`N^f2vPdh{GXx~jm8a$_UkV~u9!n}HS-T76i{T)uP+oyJ4oeFmgh~Gt%t?#}(a@jg z&whjc#KZnhcwQH17tP5Y z5P1{T4;~)R(VgJ2nRdZJdZO`aLL7&sry+zr!Sg_&J;5J~kwz6vi(sP1phS9||DtW=N;qFxwm8yExA;gk;igi?|L21xGn@;q6;HCo@&BYj{U?Z{3#%7->SoA}V?!cKN? zjW$?*<;`N&YLSJucn4#sG>V00Y310~wfL&Lox?t@)yB)e7G}xo&|pj`tRG&dtux6< zCt06O+E96jiCx&F)sve{X3}QuzI2p5*sLv;9Qvd!T8v4(|D`td7P1&CRc?+bi01Pu z_L%-#8|nNIBM4lemFM>oL3wI_4o4`PDcr(S8-3LY;-E2z;L>J$8Q#kH{aahFyb~W| z^a#&x?XL7(FSkeQEK7@6@;%nk+pC!;As!b~Oqv%NBh>}>12eU*8L9^WiMVP?)SevEq*FL-Z{!yD9JD^qR zc>Du!_^v9ChAxcYKRCuzUfgdW1jl$*pJP;S;KbNQ9ITcSwkVbbptk-&t)Ba!54G9g zgWCEe_MY_9;NTwQj9~5%_HU;yvg+ z5Dsb6dL4X62qO_#%0~!9V2*GNT#oQKlp|CHkU-c%9EasR#R}F}z}C*7|L)Cy|E@M` zeOL?e>4|`@Rc1D_TW>#o;bE<)B=uoG9nn_fi#qzKwn5I>&fXl=!reXI)Mj;#X)*3c zUf0$q9Mk$sGQQEW(zWUCaS_kSRyv$JorV@7(Kds~Yg`X_xUGY!FSePe?-p%i9pYfw z(h!Qo5x(ba=Si)J{4j^TIH?8W8IyotwPgk3UOgrRniC2ObRqP7LI^0Op73ylQq%$4 zOx@uC0i64ggOHmcguUKjPndL8D(*12)U_>YbAvoOi3LLIvMQ4Oo5C=WE5Hj0~^6K(BOOL*&O_QV_^fSL{3GR`O z+Y}o*eQ~+0OZ_Jhd3yZ>4_Ci8fUExlj!3Ub#KD%CvaH7ytxV~3A%Prj{NEgoyp7vL zwInG;e||-qCrfkm3D@x{C}9We9j&4q(UzUPqg}VU|4v3wSKpS}J&|c6LFCn=Jb9=J z2K#a&hQkq6@WUl;gr6aV-m)#i8lSP5V4{yhO`dNq>W;DFX}msSeREzfIB`-T$d})?Kb<(I4e#GbPL;eU+=ZOVVO}e;=)llkZtLL2x4`t#HgV{o!k^y(||v!v2r7caMv*_~OUe2e~TjqAV8$ zMMMR>fmcvbP|!se#Vg|Vc^)sBrlNwEN*7&7)@=<_8m&~+EUhfFaJ9lP&0BfROtbQm z+7ec3<=yuCJ~O+ZpU>y}eZ79K*N;D#>p64g%$YN1&df90@%Dt_QdEY$5&TAAQPHiE5f=9&=h|zhDO7N(d3kZ#Wi5%DH4E}_f9!v<80teXF zJeaUh6J%^7{EXIu9b|etuGym>P58@SV_+dZ@=G~B@3@H={U`uyz%`@=wauSRpt{;5vJ zZto3Nuc3THy7ZF$c0+leMq2W^y&_o7k{U0c<07=*TduCtL$By$PFZ^i9bt^lotc%{z&<@6k2MxHk76aN9^%}V~jjUBh9n0s+MwdX_$rm(o!BEE$hfSw35@M zaG4dfk_Qa@C(#cN@$jE3)F<>oTxYZ{OsdHgIh)&)t<_~nLp=4J=yd-=)se{GaWSP! zLNYhRF1O%@*v7WZzqQ;+`hGm?-CEZ7d?yXa0*uzYq?*3)L`4crUk4GUJ8n#U1*UhC zIHoZIQ@e%wE4D5Zs$dB`}9wKoH{t z$Z?7BkeBR>V&!#`)N%leXd@>`b|V|qMjkEoFJSMskvmI^g4v}u@`sw^thBBCi{>|$ z-%ehm8DbA;FVE0O%fjq0c92z#v}J((QUVyn5MjNP&N_2TAgJ%}lm7SvH#X=5tYT-I3Sq0`2H&fK%V9fV#my zzj5!FzeZi&=N~>s1drH4C5b+7q!K)0TY*3@>MrG&`FBT{K}(>F@f#Y*Lqabc&}CGa zo+N+Ipq}9!nc4_3GaASwC(MtHyPErUUQ%dZ~E>|Lg3Ynn&u^YsRUo(e1brb z9h}did&n>6{xb(?`8j&ex|aHduCHJ9|FST<60V80tYAW3`#lSzwAK7?A78UH{tJ6i8bYGYMxF* z=5ll9(QMW|MP4=fY#w2y9dV?nKm*bTAROy0lya;d0&6M)!Hz>z0%sqZA$DLd9|@qE z2KeBeL%x&8TKAHNnTDdCIi&^uLr#a{Mq%|aa-!gMm_w;O=@n&jiZ>PVd-Yi^Y#?C{ zGC88=h;R}TK19Uqm4`r(aCa6bAxr>ii^6Y{mdZr^>Oe#}wDj#P_OO@y^6&^$#@Ig; zM>7+42(r;o5n-W$>O>Kex;`Huf~UL?h=KAG4aEx}ZP6F}XlBeT`-R@J-Bap5ovrRG z8%&3$Q9sD<6!?BE`cBgkwXPfGmhB6u#9R4o8*e54U3V)VQ3;I6_YlxHg*&J_o`A0o zL==ZUgam3B^{OBJikw3krI=ZyBj=_2df3OL%Plq8EuJMRo(HCBKXg%0?S+gX^Zag)oRMK>|}=AD*Tzt-gslF`ZzK@KCN|k7~HGUhdDDXUZAU@yYh7nKCXx zOq)m~lM(yTTeT#gMJ6AYVv5kXtlor(+3O4fF)m}M1P_9^fS_wXLXJz<_L^uf9VoYx zG?{E~77UazEPastm1elzf3WcOAI*yXF zfJ2*i*9#!D*%ozOcHy3JcKr~!+*7la?HVpWGx4QdYJ@gC$6gopA#k^m3DcUKEI31> z1alVWt{l&^;q4VOVf!`EtQ=!SaSTEC7`-u^$Uc>7&e^Nzxg@;Q~@XMD;8&^G*x z4}fYmno|~XjP%)dm8%fFx<=KzQK+&_FF-s?^oW?wl*o<&|C%{X~Tv-(H92`!$33^Q1Iq+4F_rK1UY6_U>Q;S?Ds zPa1gffiHhLP#;+z7DgkZb!w9N1)WF1K>9FRZ@Y_c7HwU6T)=(F#NXX^#>dxhkj2ti z;*;R(8~Co!=4JhdMU9syM{LNZk>H=NzWEv++^G9}wJe)$951KHXYZrQ&cGh*k0#r= zf8cQjBoDBZG+L%7kpvrXj~rPwsi^$i9ZpK)AAD;cUoE@F>3HP9>DXSw>6r1iz1IYJ zna1QZs4c%3pOyS;{+L15&cDzF=|9XV2eW7hegroIUO{tWqI5)ME5z4rH}%P(tmvv)t4 zJ^ypoe}+6p=lxV}gU{K{8FG)1&+H`L=M9!G>kxhR8S|VeKO;T7!k(KcXEvP>>W^=A zkNxBRR`(0U43>=Dw1Mgy%D$Z`HxFNS1>#6QkkOQ;g8K(D<{t4Cze#fc7;8C8Ze<$P zn{#@T;Pe}Xf@$D%6$KX)C;pm`- zN^nr)0!no13y@P2myvk2`ft}$#(f)-pj=1lwo%*mQuU9Bao{W6$un{f>|%egp055ym}KptdHGNGe02*E{B#dJgxF-CYnp8QiY=Ni zXVl*rNZ54x##(=NY`#28o5VA6Pu65+Z21DYtMvYO_T>V3Sr<=U zY1Eh6oFn+CKK=;EI0~BfIgX9>hW8^DAL{3vzODW8s5idC-$(eH2l(3;FO-LS1)P1` z8^8B3Plo(le3xBZEEnj8BMb85${qbKn`DxE2YA(041AaEFv-IM-r7|Yt9Tu}x9;EB~RK=d}*q1Jq(=-id z?(xMh-+*KFw$M73k{ijsGs~l-dV5%hBDrnTwfZp1ZX6ayi)sH*3If7HDA50;w=XP` z4|+;jvsqdRtfO0OMu}{Qxm8XpUP_*TS@s5QT313o2(riPRHVPZ!G69(4$|nZucvfC zR2f!d2qE>JTN;R5v{nWbj0&k+cL>Psc~1loqOVOzr2t?Q5Gd>4#BWnxhh7Bux_}+8R>Z);R#9t^1P@2=&w+b>X}v zr2KP*pX-V|2@PqkodiYr_Nj>A7iI|`FDViDwgD)S5W2qq{Q=o^pC(R13cs_B|4)9J zIH(0Hw931rw_`2jc2z$i75or$-2O2>b;t~Sz1gj3yR-GChul0BAl$L zGAC;*0>RP_RDweX7Z6xlj2stB8#ZU3+vE+>k|y?XE95TH;NL8GATlU5PaErXLPTXm zKM~PvMAwRFDx!-;Gy&0xA{vFLK}2 z9?`MD_q0_&kHm%R`^bOd!u2L3dDlJ~!@G71f#})-DzT=lbwR^+J&v3jUE+~yCn-<5 z^DsPuJ^!lQ!sLw-@vcY~ul~flBF_;T*SjKN6sO^BEgE}<8so$JM-$$d)=jGW4Ut|1 z@$_?c`%(2jpR`nQOmt&9@;N!*8D|Vj4y?iCA=8sZt&#gm`PbOwHS&R+=k;!e z?xV&8CD)P3=TR9VoRYU_2Zq;B5C~zLP9=B^#RY_Uv=2G1c{E7R3fIcUVc!Q6a@xi= ztwY|IAW3)JMkzJ~3TwYnj+Z)5VWT&q_(l%fv{61IU7y1iY=V=uaREECNp2yP6|no8WJB|T z^TIp|dv^3#5T&&@7-BiT?+;J&WgpG0}c}(3bPm{Wv?Z-D`5%#RO6KW59 zTOOd3c3-!T-X*8|NIzV(msiRy>PwbN_SIo{xHI;$pd)g`xOvywkZ|Cg$;wvL`zI#m zk0=6r_-{a!P@F`BktQ~KeUCu!ZakG(Q%oEhPn_Yl0&;3#K&M}0%a6z{#@)HyLPZlfwN3mC1Zcw9QVb88=eROeP>nX-y66na z8mfHo3yb<%{zJNoUz_}j*tqu9(09T#4`98rM$%kv>ced zj0l8We@i8Jiz-?GL9XKf+h<=#s}FDB>CO4IP3?6-c{}^+e<4I9 z=C4wOhH7IJZ|gDwF;r8j1edt#+S-d8mu)}rvfX@4{#nwmIEm##n{mr9aae-E{3YJK zI^7_)r_lo>X{gE@V6&Y(Vr+AgKgkR)#F}{=$ z12a2xgs(^8!-)A|yIq^Fb8`YL=P(ZIa@mq!Kj zH8?(ApU#d^7&M+WzAm>7>w>ql_}=H6rDE?>g(jkhioK8L){$*kuS@u8Roiu0o8xoX z&9^~*wS1e4OwPAxL^$6PeK_A{BM^MMcZl;X+yw-_4MJTP-!2?te_hAQ@!kjQgI{qh zT)vn+{#8!J$2P3DLr#=FImqTXu%XKTioNTAg@|8yxa^P(;b-a5FB^Z@(LtB^4HHLv z4$?pA$of>v*NiV7AX-fruKixMv^tQSkx&_?Xv8*->Lg0axwahRyQ&a!^TeD-FnB4)3(2*kXOq7ppp zDS)&^(GT%7TI-3y5sH9)!dP)X_ocZZpqUV z9)HeTbyVg-Bj1~p-x`BvHSPWzXD}+e9d$WU?sPhie&d zt~VgD-np86a9e)p`SCSh_Wob;T`6Uoeaqi+fd)V2+vJWMBOQCio_0s>t&uJ*W=rqN z=@H}YL;?55j(JR1Not=H9ByYIpMzmZ`V^&{T}S@m?DD(A z*@co)D!~y)9nke%Ec>1uB2C%Frrv`m`XzSmo@^LzuAm0U>3tNyuJQO2nOsWNQ-q{! z``?Jb$AmygSw5BE+3-4`qsU1!_aEgfn26tjrBy7uf-Sx;caE5Y5}aqyH#@T)Qa5XV zsdH+D{rG+PJZ`eHtq*aaRLYJ&lqiDUqU^C)-G!Y z{ZEj0mfBA`A;rOcBe3fUZQrG3l|6MWWqud{jqJfOhsLvWdvW7=gykwKtC zP3(ZTk|;GZvAf>NaR26TQyOjU2K?g7M))Y-dG@*N%i8%WQJ&2Z$@W$DNiE)Hjr^2Y z>HZd$;-?Ih+74#R{gi`^-y08|HOsIUi0RKn%~5w^US!k!m7I2844~=RWk!2)@?Uj3 z`L88&=eiMk6m$1+Z!W8q2KI-)a#qTIo*k^O#7LW-XE*9A6QygL*pLRw%Tl+6>|z5Y zCd#r}N#GUf&@QvNx7Wu!^~W!m)}WrzR+EFSIDMG6(OrfibNzO6szK z-O?)IQv3$iK&NyKT-%43=JiWyD6WE)GI{;jR-Rwh_Z2@Rwj-Tw)hT&WnkQ=Kn$ z*M(Zj--q{1Qrfg)i-MG_z@HnzuY~vG@dIEw$0>F$NSP`{>RF$L=xOdZthk}ls@0fa z@;&9p=&~l#0zy`U0a>u8Qh7FIbPHyu8YflUb^GOMh7cBOhGO9%6qBZ4e?_7ziWw$bcs3R0;m;Soc21RGzY^e zLR^eJkBHf8Dgwd9KLH0IT^&#c>e6pn7PrI{sc8yiZg^#7jGBa9R(-js&gV-?@2dqZ0hWh6@O^{(>BrB<7W| z@!`r6DMV$L!$I??7g=OuWmV2!tK5v~4n(fTCagx38VjTd4P4~6h?u>`ArJ$16>wPr7M3NF~;kU&)LH5NL`3kc*~mD5OD6x7<&&oWQSKwt$6y*fI1G zY0#9(;UpJyl`}DhWi?fvjhspK#aolp&yqHKF(%AK%EVIkZBu2jY3M7UQ(Lqdx(3ab zfeu%NPf@@P$!8Jav@SS~2!1#N0qFr8{ZR>iIHL}z3_18UoE6mf$tcRGE1E`?sqf)# ze6c_@1PG@RLSFPI00E^FQ234+=SOvig~>4pM4?JD9K05V~Ci&CL=)qoOs+AZ~zK$ z0ady$lnohud^c#-bzZUmOfqSP53gi4M za$E`lzhOJNxiZ9b?`0yKu=lRRJ_+?Wd5s7K5gsj&s|W<))qn$#e;xAPb;!R#A$pKx zu)W~>X%v(re%>L|%Nsa5;j6+V?rMUU42CC*k5=~NR9W3b_#6XT!M(QB0*&SclyV|| zJBodfIWv&w}j1ZU!(YUrl*Ay3sYl>?U2&zlJV0J4;3D&g~NY1w8 zNM38t^evTB(jbf7(NcLyl6Jks@?x=yzV{Nd#wxv}<6YU;vC3Z48^s`%e~9N;^EK{D zVwhdsT}dT0)yjurs#&wDijL&Zzz>g%h4J*6o|wmeB` zAx-#;eVC+Jq}2tid$Q7UaC!l?cr?vIpKz4`p{vpD$mf%HEF!$o>7O8C_IeM2P$wO! z#F{e91%#(XN($^BBrBgv(l>ML(|RiSx#K1Vb|FO>EFJA{Z`(@=k));LS$e9n3cr+W zKc5OyPr5S3Uazk*5LZ1%vmxn9h4gQ(-8Vz&s*%o)vZwY_dPu`#r+`Mh1A@~@v^V{o zS|UqPz_)I_5#dA*+QNxkfdE9p+oq=yy!#`7@Z=3Y;(TTbJ7G|^XsYc6nTnrAbCH<_ zzzD~u^LGa*v+MnH!OMgFJWvT`=7Gv^+N}&!#{0BJE*5c7Fx;q&)PMW~2H>4%~DBYM{9Hq>aq<;3{xysyn%8%JJYQxa67PKZp zPH>aeFa+GNu8V>xZ?s&@viyuMrBILaFzI$>&?#3QnIAL-Yhmx zF-f*wY+D}Ey?WX2&A97H)D`j{ zw|kywdBT5eOh$&we|!(scu#y5AY%6Fk3cL!$EgILvM)q{p1`>q132;@f0D!+&r?Qg zs#(E2B|Yc2ZqyQK=R{Xs)EP<8)Mqb>kaiBnbVCEf5r_sZ01iNp(4vb~iKy#RjC;G; zb@P>Ajo-^h@I|35MH9!`El?gybCOxxg-U+MPQlP=7Ho1H(O=a#=_!L?D83^%=tBk! z(-jo>p&;$fLEro!l*~KBPA^pYPFm5Kk8m13;)PD8nI@FMUF7q5`8Fb)-io=1n7#f% zAm-(ZRD#cLTtH3m%nWjB8tvvA6ddE=xg1ynBRaE%&nsW%Ozq@m%ZK^3&746d*M(+8 zcr≶mw>zfM&4T=2D3@<#`tn){763<604WcCx?vf?}7X&+4&(iCml6--;AB@0J40RZ)=^{#bhyPF! zF?+S2$UBUZ3M#RtoSh_q@bOT60Aa_-;aH0{tas0mp=_{K(GJg`Hnc_EFzW$Q;sQZC z+fl#=YAj(R$(}CSc@KeLY)8?~Fad-+!?53QPq+o!X;s?hd=>2$j0SaB6FF9h&n1*{ ztbZzqU@;O{QL>9luoz85AXG~O0FhvzaBVc}^0FdJ-J%(LS&26N)|?ulr)d&h^=VQ9 z1F!AnZX{SVG8};r@(X|i&?B@;jo@jTMAWS*#oM|Gt`qlHIBJ^jXI-^``XMqo>URkN zxApeOh?%|aBM`U?1@4V5C`k5MTKy z2Z5mI2$fhSCUQ;@So(}~Z8W^m27<+-v@y4tg+QuwB z$^4lzQ?tUpkST3FrGZ}dcV1U!Nz&Q%_ONwI9DeF|tG(}fOthQ!^WU-2@Y=!od7CD-qH=2G)8rEWecombF>wEKN5sGldxjwr4Yzx(EZi zy%|1~hy7WLw~+r`f0p}}k}a+3&)$1WNswmuXFt7#EqO+N7QRIpE;;+zXKhh_Yn|4l z+SlV#dcwp2k2iiPu;tDJ@sQ9PxUKtQ3~8`FE9p!K&zu;ny$sfQ$kI>zmNb$v#uxIm zmA!sUX_lrR6zcIQek!;Y&Cvfqa|Q2C{Rf(vb!e`>!yX)i1$i`!MI2Y6OgsL&(dX-G zFMWqdJP?b!UtZ17aP@1{Fpk=?K!moI2&&?2S+*aQZC>9{8NM)Z5h%(QqRiUA9(osM zu+cxnsL$gQU;8g=L+jf2e4_n}9l<{?0i1sVZ?tH0Ahj9zA8mT?k7pZBD4{Kk;7B!1 z0W{c?Y6kpA&9A>?!%ise!gX=A>>l!VUC%cd+1e9IWQzAI|3z!y=~@WiS_o@@T&~N3 z!&VQrAMP5w64DAn%WXAkY6l$UCx+GqxK%wx440dWx~z zMcGk;Yw#asD^XTTRM@(#$Ge66EdAi*EZ+q?PDqrGBZ@kqIxQ|7hQih~X+!GH4fpg&p2J<})YSc)HRla>us|PM- zrA=_Qv{ZpkZL=aEr9R&@SiA9Gz{UllmNTl@N2e5hz_|fv){i$ku!?#Ia_}A1)F$UNz(-pFqu!>RGjt&`{cBe5b({3+9CRV z6T5X<=_I{+g0=cqY1U(UUD3;%#ORUNL!1unH|)DsFVp-LeBfW*#Np-he%Q_1CdK&T z&R+Y5tPlvK$V|5JTcwS3^*B5Jtr8PlVSrI%(>E%J_t;C~>(rN@VBTkxj;5GmA#31= zTNymuL0OlvA=}8?jvBMyVEM~nflv4#rWDG!L+-aCH>9o0+xa9p`h5gxgL9dKX9B#a zD&Kd92C@kxh44pt6of-cf97#H*-UFMX;04R2C@tg2v7-U&nixduWW7iddL11%8+yB@5y` zYTDrn_*T?2*w)*0vD+^8?R%n;PhKX-`*Rn0Y@;bMBqa}bl(XOt*-bU=X-HB7=V;71`R{JgF2=w zrXfTsL8M|uD(VqCeopDCOBJ~bM6UdZ1)o>ab=e~KoGbS+D>|<oPq^d+}lt}4_GrHob zH=W+pi<|$lz#o*x+5}N)ji^-0e!Q+UG07tHu*l34sdFL~B~lKNN&r>}DIYcL+vD-! z9-l?iZ-!-BellAA%CwxF<~te>9Am=3bCOB#gVx|#)ZJ4I+L>QY$<)p~F-7|v?r@uI zI`9KbFgTO&;bbP(r_AIktvS0bBDT&!h9w72f}0Q#nsIv|5|Akf2u!*SW==j|)*`F7bCeqCZtig{;xalxDVbQpy)1r0i%hr*GzNabZS(_W`$XiTARtU!4 z$%mQ889Pk_l%JHtKB^={_z8_q;ytQ!>De0d7GjKt;ZQMYJHGUrw~+Qg=PNzX7x=%m zMSfZ+T5#vy1GfD~r30V$nDlT%!23yrU@-ZtxQEIfoXHvB37;64<0N`Wd61x5GRiR; ze2(ZmXky%>vQ*N9sfZeTR1iT0u;D5xg2y92=lHoYhE~j+elQ^w*Ey=-odri5NCvBo zWd*df1I+5^0$#Ts97pC>xP{%a+ zWA?{S@Gt6E%b%6z8ei7`XQhvc+Rj|^uL%s&E?WfU$`wB~cM&5&a|;6O)PM74yhopd zMO}zZkj#-Zmef8yeG9q9SFB$`)U8`c49&bmQ(!XKygJW9?=8hi)B&@NnB}a&m#gcD zXxgqa--}8E&yu2TEcBw%%*0XB?|AZ`H<^Aj0n>=DbH^}B)qFw4Pg2Z7JUo_*3h%tF zk`OzN9D+KuuHz#D{gxjA`R5J07>I3AmRM!5HQ*}P zXz6OO*s9pRi%KY`2qOPkyvK4r2kYJMc~`sa6s&BQ++t5gW~yaGDb0x@JC#_9={KJ+ zQe0D5*6Da^Q8{K+=>S^<&Mx-r7DnTt0-9NtpFuRQVdeGliwEzbgI~~=G17#KMCdwS zH_79F2b?B>g4#Jy<2g-(C1_nXu%pRQ6qvWr7i5@Oi%LE6TN;w_zRR*6@#x(JgXSDm z2ga00g8-H~^+#TZr68dQ_rN?M@D^B0#RLzJoR#A%G`$$+?0}h$YDdHqgDpul6ee{* z4QjX`!rdnDBxjV*GgJ?VqoLuYIDUvRB;$cDJ!pU+a~dr_9IO}K!QlMTVEGp7E;VR~ zJ4e9UWq}0CpE8W*B$#;DZqfv_^~6-5pQjj6j&JUX^gtJtpqkh499Itg3y$zUXi-Yp zpjEqiKlPJdU9{S0g)qb&rd=`=j4c7p7bbT|)2;?XN%P@X?{QpN$uU}6i6OL>5fr}4 zsEqfZQSL|jM2H=4g*~|JmR@TaVa&9IU`->SOv^~$w32?hDQT8LNf{+mx?_UTGK#a< zPAQ%wMWoa4>PDLLOrg+k+9J9FjuNujFy_33;?PcSPzf{>tqPXnC|X8!#e_ktGuh7Z zu*^Y0Tp1Z++Bh4jEn53X!C8y9ax<`R0-y-vU)ou=5`>spf6nTg;4b=d@<4F?2sR0 zOunftx(R%J4%SQTAoL}P1AXzw0SHgrO)NcL+L!jW!e=VB#oka{B$-qC9!l?Aui%-! zhw621KWVZG+lxT^E;n}e5<=PPKZHNh-77p_Vla{Tr&O;1Qde7r_dDc=8I$j6i`s#r zz6a~|1V!*dbF{{|dqU>N5dfF@CRv(CaCO#+xt?*YO2ipG7nQpq`B6DeVs~-&7IkO= z1h!%mpIuhNJ%c3+_7Sa4g?v%xTA9@J(~5?^c+_HT6-o$~3czRduN=H_=Ho*d4sEd6 zNGWTB(tPpSl6&Z%HY^kRa>G)X(_~o|7t;$6uK8iJ<&m&H1wzC)miN|h6NNe_Rt|0m z5hQQZVD=*T>5Hu2LZqe$>#4^2(+pxMVH0!jw z?MXbSFz}#q6oPaz5k+WQAi}Z|5w2S!!Yx}xxP7+>D-I*P4Zuon6Y7dV9yc*}JYs&= zm8Nv3ETbe{8*(pQ8&^phf$E+E5ZZE35SLMf=sg}SCMysr-ET2 zz2ILlnZ2-1i=&{xsy9L9(B8^cNn_y1SU~c<2pb#B@PwW59$A)uXg}fFmEqLSmJKj% zY}pRdL$T`;c(5gk`WU-Cfd>_-Jg6LyjW8XuMq3GlHd&<0@O9CzS&}`h4(gu}fBdHBqC-Y!S9uKw` z@}Q!W2bJXr(t&4(2yIm&EITQ}b(ciA<)#R?d%zw|H)(?^bQIABRYoDy#$uR*iW7KL z40BL%swlwIYsJ~3;3P3Ds8|*S<%s5qf=i;HP^4%*L0PFt=^#}|m5UUuSK6SK#XCgO zLljntRDu9JDN?1(=}?+zWY(k_`yLu&pot>&53^IiC2;0#Pep)DbfiR%>3j7Gni>vy zH9&&gVAz{iI^#VYVb7C1c-_~ajxYBf}?lIXhjgqy^6gwye18Mr+Gv4@fKNqaMVw*1sv^0z83bn&&8( z(%PanY{hR%6Cb{|B_3fre#0VM4bpaMYP#~Pf^ZScxes{S^V=XJhG!2Yka_RvHH@-h zH_)nR|sE;E8@9!*2;j;hMZZ0n{#umJ!&Q-*#jrLIc^#sK-W2 zM}uXwFU~T(E>Vvxec-+D`srRgn^2>KnaDk3w9H90TBgw&Qqm_Ib^x)63~k~1aI}A2 z<5Bn-d4}+Vz%xN7HfX4UuDa$HE_9ghL)=rE5X+0Fa&T^IBDH|0GIkqm^}YfM>Rld& z)^gbF@SJ9na$YO+Bf!4MimiLj1Q2?4q6Ac zHCRlXsl`W-aR}Zw5LZRr!VzgP@x+|;Z+YF+J3~a6RAb=QybmST~m^nfBu(?bxxeW~FH@H|@~ z)j&mPb%O>QgfdzmfbCfULaP8$6GbXdq>hPHxk%YXiq;3z+$U1IO)!Lzd`~24(Lky~ zq^d+2Cj>h;QFcU>9TsIDiWq{7m|dfZYPw7Nue;_px~c~N;RgS*}j@d+r364;VkN~5TowRK6Oh@#TkJo#Vt z@-3yYHx2;|H{)5=EgYm&v|)eTQo^$dx22fcs_B<*Da%8|!sl^KHza8qTlh4uiAA!C zk{P>eA`mClhZ%+cH33LcyR?^OIk%P8nrvpet%R7kT*vLxnom%N&0A=*M-H!(2*Df5 zqJ1Bj#bf`4+}g86oaFPBQF*`H);Jl>jo^XMJB%&0~gHY}~n2EfD^_szD00)I2GwvL! z!(D(9awBOCnv3{d0#5SaWl&1BR6ulyQRqBvcj1u7I`D&|&xIZE^Ybj6gyh2g1Mbm@ z3pz#HVS_a?@g`(ow<+SPt^p(<^>4U`9{h;|jG|u~6474QkTC4~ zr5-+B2)k*p4iCGBX2~{iywRLoQ{2p;-IccJJK-kUL8M_*jyj}S1);)z>N20O+4LjR z;ZqIDb*#bS1bs+CX29$zt^(X86Q#82SdzH(kv~YAei#cB$*rq#2y8onsx6_?<~jxm zR3x>vsCYtPqG%na@~1HU@&_mCR$qb1RF9(?vD=LY<_AZTvAYff$$uR!n*@eSgrQ-s zV}EPjEt(W=9DHWFdPVtlm}p|vVH)*EyoorJcGt&anl9I|P@tmOREvryM7JnfhbrnR zRDpu1*+f)euA_s%qo3{Np@zo)K-D~4JAq)3m;3F$2oG)Ub=ck!`$2;|Z)1f*osj)Z= zhpoDYipZ}kwh-8A;qa8y2=-Y+4LFd3eN1YFA4ff7hA!_3jY!%RYiNFe!MX_hnBQ;; zf+B!sgC{o)PT#c8Nh2F=+esJYcdbc-ayMaB#ktV`0M>C}ZUwkNfTKQX%}PS()K4g( zgJXVRM&^eHtP?UgJTM;QnIvNc4nIDPihU`7IwNIl+2TNuw)~|q*cr`w;+^7up*QJ7 zxyj8;XM-5t1XaO90n1pg2TF>GkilHP#{)lud7lnOEZ*&f1(1Np@Ffro+v7%Q{7d|H6DAaz$X-uWkq9PLJP2zDji5m zm>wrdkgTx4;4pz(>~FYJdSaNt>R1{BvGyayjX+ohh=z#*u8?t>&Qr7nsq{tEh+`0L*ia%xeM8%8L@EKpk5sux(ac3^E2S)zJ47aR z8=1R#3cSSbOB4`mkUIRQiMqJuByus|&xv|i)Wp2SCEWDyoWlVmcUFyXX z;=MW?FYr1)XqWy0xxyx$h)jkFeUXxjAWLP3iHmUn{03R+RxTCB zM2%&{?##H;+NC{siHwTr){1g6WQZF6+%S4>p_ziCUZdwanmSo@*yWl_=&+}aW|+}r zL0qs9Su_RRowH``MotvZK~x+VA&2HK>Lh@;>SY*1XeF3+m}fX=CLIQGx``l7t4ohb zg@3J(I&a=mihvY`a6DB|uH*j48W=L-3_5*zC7*q^iu+I{)Pm8StU9^;0slQgf z@NyUZJp597Z)mkkXhy5+r-f=A)J&@z*#UdHlgJ|#TSaBK%z?ms4_>9EATZ4iqrJK- zEgWJ?fFnp#-KJnhSK1U*o<#p#n}X_Zz*zzHCKR!?nCP+Eh@R@Jq+@E)KrOnq7OgoJ zVXO;t-i5PTrn3ReIp9w(;mrUcjlaMMReTCXmI0m?$!+b!(nSa0QtRz5t0?(O@Z3@)VhEoEl>uj8;(&=cEBP8byk zXQ9RR7siFJKCZP+TWk@mq~XTN(~()97e*wS!YiS#h>P9?;2w zGkt*8Ue+DjWsgzd5_^D(p2)&&4>*Z#5@oh>3WTU`Mbv8JZhH8-RhI{5}`m!WMiqcl$lDx>)7po{5gg?q^+__v^4MhTCJ!L{1}s@nb;?(FAu{6R;Z*R~ShIENnN(=SdnGi?`7-&(~;~ zi903ZVZRl}f>XE*7iF|ei8Wdl)5#~l31Ll*!knV(g{=MY}z;ChjGU z0F$@@U?typXFev}9R7Ab*}P&g1tW}pe~CeY%NTADxJWB>#0NSN6{OP0L2zn*Bsk$R zZle0w&*bD$q2Yz`PIBE>y#+6 z645NOvdU}lg_}zT7eEG``Sl=-7aR&1S`%_Qc_T6^A z!nnnao5OIo`_dG4OB=t%2~#KxnOMo9c$ttjA+1ind8;PHfDXDl@?HUY7R|Sij*VWf19&91hJ1b;5L_YK5(n6gu9=d{Gm@c`K=YS=HpXy+yE-NP|yv7 zp*>l}Y{#aAvOTw1l z5QkDkgNvSmr8SI1ui9`Oov;NA8w57j8;#R`e4l_%_8|$jfSt zQFD4hp*({P%QW8!dugABUCQ&w8jdVSj}JkwwIUnc!3~L`Yh2{rmv{aR(7trhlYQ!= z#+qarIh%fA7wp4HL2?qhTCOt|%(PH{VjTy1FE1p5)bwnN%wi&2AQ5f@u3 zsTy~=(d~4n{@5IHxjhsR&iwwsPBjYMmxq#SrQoUIZ{>^O|LQ7dY)2y)>kGy@_3x|% zlLFBiT{$u>DyNG~&{_YFJ4b!0z3ch}x1s1#pZ`mjs7K=F#Ys{u;^qasl|*u;tta$_ z`C+MgcwcQS2y6dD)2$u$hnXVb0z5! zxCt{akXwX~tP+TSLcibxE>x;+M`g8gvTFgd0*E@3P5*;|L>!D+2+t zeda8Uhu$lLWY}k!iJBB3-lGA9xkeCLKwye+h^FTW!i^T5V>jC0+Hi7+uP*0w_2Q@M z(v_bPrG)PbRCP&>n~*or0fWut##>i+^%Hf;TTa*oCC$%0NeOk(a+I*E0)jg3#$Ls- zyO9&oqG~rzGxwzPIbtI0Tr{dTK7}2OaDjMn5Ke(>T0y^2r>lSdzK)(i;$9+W-kJDX zzPQP`k9`2N*`N+&lLXYHW5jJ*XSYUAN6o4P`YtV zBwM+5Jf0ZlaZglgNQ(QZN}~VCybf0m9A5`?<9AM+n^$#g4g8O~sJe>nXrP9egekli zA8l0s1u1sw$IK=bxxe}{M6iZpb+ZLd9g<6-RQOfjkUYRuQzq*1n~dRN+Hm^_HjPvN z*DSgd#__nk=X6NUZ~nrrVi^HykZ1bHZ7erHjjFp<`Y|oLu3M$pQakm_fTnsK)Nh16N4&$L==i5I?vMWFnpl)^6&d(tGjst}XqV?O zZR$r^osos#D5U~)OUz%#vJn6$Ja2UeVdiK;L>aPq5ngt`G7JoPu%3j4*_F%@4;GS`RGo(<>(3(D(llo-7kec%Y1^>UIYGV&i5%EUGW9f3b4hg&w2&0 zwv=hnyujH71-M>|g3h8~Zwp?qD;Wj2CXIq7D9|=zJF+M1*;~PCh2%MpjnS*^@h<6d zy*g5Qv4CCCt1n2-eEWnDH9{k4Cb31K>Tc=RNvvC#n$+)W`nKGrKbu%fL<`6#h)8Y9 z3%;WQL4?03Slx^lyiC*yB5o~b6=7;CUD{-1L86FhPQBU8YQog;Q43TIpm*9ARDfSn zMx8Ih5Z#Ln8PWsD#SHVVKYVC-soQ8-0LyNa&Z)mV>FJ8iT@@3ImMCb_X?@w)a5Z{D zSFpQA_XQ!_OK9;}0&=wVd2`Tc_^B0WC^Dw-GcJ8;z5Uhkd^b#{W={R{Xo8zr7xIHX zCGs#uIY{P2s(9CdcUgZ5SLaE7v)PEoYES9wJo~D~YA1SN?@$x9xh9=eH&ORVb7cGb z5$ZeiB*gHh>P+d=vFt!oHA5Qq9IM|<9U!$H%W|5j&84f;?awz;@v9+!jka5(R6otL zj?Nm7Y8&RyUYt~V7~()>WgcYKVM;%AobMPTHG~hv=(ePQ z4!}QAgHPE~W{HuT)Cr^U0%ysQV)nSX8j4>F)<>%|q#vfTqG)xrw0NrhQncDyqtP?p z7<8vEi;q!HO3|b2k7Cpn8qEjH+Dc8<^kHAMQjOBUNvu(8b%AthqTSY74c15(hT1pB zsv3=E0oxL%Hp|}H4K%NRg63DEIL$X2JW2DwKu&Yt7@~PQ6fUQEl;}xTn_8N8E@Hv) zYAe4hLxBX7@O9QVULB=dG7L4a^3Yh}3jJ*MUc4IPnG&|memY(~C`mcdrY7aXv(J%idl4d501xr8*PqQW2J`BqF7bRV zXq(;ANsZO`oeIIDEVeFQCi|zI)o_hxrgoeCY!?iSW-ilpQ#VU#X{@4~y43SpgKeyP zlKKUHLHI$E+Oy4dSQ~skLYAHSSO_)WQQQXm_-w-wB+Lwa1QOYYDaq;tDeM~iGFinl z+*jEj$!dZ$=NgOZuBy#H#X12Uei$q|i47e54j-ULkX14h@dUvHwz<37p_MckcSeiv zEOt+(U-^31xewa4E-E!xeK2odzsl})#}o=;ZF{Kk_)XlL9%}d2)-=dLM}uwA8W?O) zcNniH5s8I!N*deULoJg`gV^AnY8z=&Dtob~+Qh#&Rg7-RC-#j!@e_;E@?7>-FLg55 z#c@p*0J*5YLw=4k(z8j*IWHwqe)>q($vFJa4LJgkJ`&G<^xjo^LoTH z#owg9W+?Yc^F@}J@FYO zoci~naPZ2N?C=De_L9Nw`iL1XhKMpHdL^%{r*FC+oxgh`OYc z4!=(nHtvoOgiA*lEZ5Rl%XBrtvwyQ~Y*f1XoHQzl9ZXl-O7Tfd8KyR2+6*;Z`sRHW zpP?rEW_F`lXH8(!Gt_{M0Y2-Gh?WazW-s#Hb_k&8v?aIFDr?!wfcV)lzgPv@V%GCbqA>WU;5{+J6 zS)xJhAT{XCRD;^mX9RMop<6#^HDVhLYN#}+GyBwlsT0_l=`z&;L0>{;K~zpU7QRCW zzV6JXXJQ6e*y>Ew>fDKH>6tTA?GbXNqpRxLx2b9kAn^+athArnNm|>HljWCBb?BE6 z*uH&(!`aaRC~MY{IR~h%8r;RYM(q4>i>C+Mn3NOA1`Sk)NiQUdHg*qGdo+nd9Zt@r zx2TPdKw#0bNZ{k{-RNFK>$b>{$55C`jW9nr`kZZ$5aPCVC+n4Rju#c$*=Jco~8UW>)b z+mre_S4YMfZZf_Q#urr~>QQakzu9WjMjjxKFzwzz!cYV?;=!D0lY2;Q;#to=lcRmH z0zg4J`m@)DV2*|LV|#}{nme{(H-@O;KA|XvTaHD|@(Yf56c7DIi@tkSIrY`ZBJho@ z=TJ4)Gbv~rduFIwAf?8$pNE3)FYC6kM#CVwm2t$gxHjD}kt`XH@NJMcL`63a2E6_X zUN$h=0zP~ghuK^3DAAdC1RqhPvx;GAPwi(|h}}});Ze%+hGS)a7%No7g;=2?_QVPm zu`-rD9e%a08N58`(0S=pamg9FMi7EfEQ6toLJ?4YIVE#Sy%nk4f3>+~e zu3X0^em8iG%_1b!TjE7~X-Z`i#XlSr`ZFz$Y8L1}Mk49DgGYCpERyI=Y zESbBouScrSb?g!`jHlq8djj1acbo#`cU;-wL|esBU}) zVSG-ond2dI6|7{u+Bf*teQdO_(#5?>RK3uQ#FxFYH%8yS=@b1*y>m!YpxIB|gJJ9O zE9^F#zW1XzkGB)HCtztl9?7yPy(yBtI6;l`yXy(c0%_6tV5&YP zJTVfiu+ELbC28(`kIS-6!NS&VF{>x5{lZR1K&WZGsC(D~YfAM6rpjvPR=c3d%`f;$ zbFNxnXn_4FPwJ#O7pyM?b0|p8s|oCLS?%V(Fu@CyMsAX}vB$DHR65p#4N=r!t@)fnU|B@zRfRbNfTNJfpOmQ62J?84>*E3q?>PUS;DFw{B18qbT0Pg7b zlGsBi8TtKsbBiI~veG-Y2-R8vACA!i2bXTIdKw0?|J5kMv2Gz(~!OM47S`q@3tR)MxEgA|I-D^KH-C%`|N+!7Hs$&wY9YG1N)-?L)n|a zMOl6S<1^2I;(~+7C?X&#%OI$@gNlNH&M3H}RIYO$_sk7k!$B5xoW?Y>>yl;Vf~JMJ zjOj;3bInq+a;Z!z(;2BmOGQ)W|33E_5c~G|{=Yw8FPZ0V=bn4-x#ymH?z!jAq7BST zAK6#WHoZ|xs<)FzEWqYDbG!Y=1*XYrDRQgb_^N58TK%>CyKK{KjrxH7N{{^G|zt_abRu6A!nT(oyWeQ4lj+QAX&E*qT znN~(!s(1!S1>vGb)9RtpXH84Po5F7Z&?mPe`Ls9CHuG%-P}kA+ZEu)@)ImjFf+bJ~ zJ>$ox`g%z&o$V(7X|+iwE%}>QtTqMfCZHc2>V1VSZ9SK+AppVC+--Z*o2J4#(&AG4 zw;N4uJ;MisDzp%_PaC1>>2LGHI<+r>RH3A{7^R>=cLcS;d{55&TDqukK_g0OcSMq`}~h>*b%n*+CI1t>HsO=9{2mu^qzF<2Dg8R zqOqm!qTS9bMc4o6E*kTrQgq`N_MbkYqIJLLn|GVukzV|fCx3#XpP_d>_L#nwww&fy z_n5xa1f!R>?=_9n9CSu2ImiG2ZW>=~roADQ|xWa;B-2=O2RpYQs_cFNd)I)P!CW#e;b15zMfzU-5}Y zO}nIo!@O~^>8!NzApfb@^p*7fA-?w*R@2x+Joq>o{q%r+@o`ffjr6(Q{@O{?4{FJ8 zFQ0PSj&P9^gqnyfM_gRR1A1c(*t%ZH_1=BZb$zz97U&n)d;YHNft(IG$LbhPbsrcd_ z6B^n5Dol+$rJw>H`4lToI}0E4)D$n3X0P#Kt+mk$3B-w1+q6wsN_Xx= z9Si~SxDRS4zs=(-p#bV1Tgc@~lc9O(Qs4spO%_DLEHp570YN#PY8$@^3;h9puF^DB z>hmgU2;mtnlXrBJfzLy1hf%DdumMjHXGq5#!CedaDi<`phnMiLT)^{{g@mWi!Fe_D z?ACuxKQ`F+r?lsxK3cbC7Yp_NzH=c~!*a+UKF%QiN~I}`&v>(&2e-RS{?fDp1#a*H z(f%oOYr?(F*QwbM-&Lv4!`;4?;QClQ)qo3K1AlMZkft23fcZopM>jrG%6hCv_dwm+ z(`X4cic@slTE`LDcP`zdd69h7(%@jeSzjhmo9f5|4m|D z{b%+k-um?%q=LDtj!+htZuL^$UBi;SbRu=`9KKY;TKUex7#vk$)Z8#WSt>Bn;7TT(o3#lV{`z8rxbP^Vc^;HjcG6OO&$)qD9ctUlJx zzkE-l^B~g~z9FyUTkN0VqXj*LTjwQOpR`hKL+>wT zCQiFZl-qfex}r797@~XoH2y_ggGr zDb`wvWSN%FfdRf;Sz}>SR*as+>(PW%jr?1XpH5_{GfDTJe-pGvg$oJ=`Y@ov+K;{~ zcIgJ&B2_ur#h7NtkSWzVF#8PozIT$@7_85rd@9b=8?6H{7gCB5hgM*wS<*@fo6Nuz z>i`cJym_Tqhu4R*AU8&omUV-!7sBUiebyMSSdOUALd~%5&k<%;dQsPSvJ(3_y0Pj= z002Y3kBjAw&!C4$wNCYc7*jj(REkPnb}%s~8J!M z_qOqq4cK_iMRg%>qh(2aRf1n5o~LEaH9MT!_!fMmhV-KK7fS#0&`d8DBpHS%gXKah z**vX7)N^~Yp@T@XXN4W?maO(Inf5nIO8FAnN zslYMJMQ}U+Ap%BgijQ_bu3Kl$gSBI3cR>=H&yE8_X+suNcUdy+cU_K<>-?jJtV{ihcE3uChrvsP048W8b-F;UEk$<0i@btdxh zek?*7dyTK~W5MPGKzHd{gIJvz)HZk+6*2Q5E|q;Pgb~4m99WI8URgdnLS0aR)s2?yJsJHkziD`W=g1T!; zMZQZFsYo41V3_>8RMaXb*pfin@qgYgsi6WT8b#k&3>t z)R2m_U4>K>a0(}{dD&;wSukeKE-~iy#IlAybu3EaB_1r)H>N99{D!GoTAIiwG-1IF z=0Wd_`3$Z1gq6+>{Ea3oO?{1*Hevpf-+2DG2@9+9JK93@yoKxiS%`0^SD&YpSC{b@ z{aJI#I*BjzXZjZHu_1vj9X^>-Dip}=iRXtYy~Jt>!h$gv z#Mca>VK5KRUh$wK;`t<4y@=c{U1m15{}OPA3glHjAplaqXXCjwfOU~x8pS`O@EI&XDh)TT-R-o2}Sn&Eb$jJ|vXA6&3nI^=w9qCtxaJnrrj7f$bPA zC(%)2+Y6)8mxl93VeCbz>l{8kjLq_i%BWdz+8lm2j0H)f)45MLi`Ig{&zU* zA%)%HaS<$9+Lq2IMz9F;qDUH_j3Jo*4csgQu>3Ix-IgSPft?D%f?q3Q9|ZLTuXuRFH$P(ACT z{)A7}v)SrJ{JNgqQ~%8`Ml#mu;qh8vu?apCw6DO+Xr?#9+|Zism3)Tq%GPW|&)Qgq zX;th78(EibJ6a?*UAUis_3S!aiy>W%%aYn88lA@C1AQZg2UF<#fqZooYtwlrw#4uW z*=8Q>4OK?hN%Ug{NhLVRL5#RoN=lVH5v$H>hDq17CBGlV!X)2?JTRJdQNPHCMzh|5 zS}=U=e&N)P{435~L_;EO(|no1cSkdSpQOQ67!q~`)A`kCP~@xu{7E$HFP$97jcr)A z=8{(-Kih^4kkZq5)3&Ucq@TvSv}H!gHh?c~%Nhq}4M4MCI|DHd7F{G>q zZ{+jaGgHG=Z3tp^4rth=dvzlAeKWsn-AMB8&($4RC+TuO-n9dp9e%GittN-*4jtG6 z-6b8QDs(9xO>ObX!9-=jeR)X-7Nq%DQ^?CZu$HZMjHo6%6msd-BTVW^vIG#mxY|$P zojame_8WOdM>bX(X5?O<@=$uonbA2+~xO8s=Y$BNyyGe%zrhFz}(h2?afbpVEY+!@1 zKmkFZH*+NS?#!Char9N7ZsKh^v-f*n+YC~Lx52y<{@8BPI_)X;FOo(F&$B_9HDAu3?WD0kEe1%LO2Q8r>Rc>_h{{Z=%h*B##Q z5$@8Bp#skiEpBvS6I-4o<^Z0Y^`iU4)1_OID0W6}-P2S)tt*RC`|)*M*$9}S-0jM` zHrfpGKl%y!8qelkBp-LXq`r;)Th5VUbh6lLdR}S58+5}SsgQ?tW9`%#JiQy+ zEWKpse|KX6jqI3-&$VbyBG-3kfzsuvyia!)*)YC2uEo)FF(<$TPkDa3pXYRE1DZUb z87%HJ{Q`P+zKLbSrGt6SuiaT^^&7ld57snlEwpONCD6pUliGs!>!?%$QaLzh;$B=e z*+u+5d0E)QeSAz0);Y*Og~XA}5WEIvJa!|*nD>R0coY{W}ot?0L+FYgl%5oqoUd}2K4wIgN|7QTY_ z&|ec04@<(u=Bq> zpf@7H7(~);C5a4V!IYJ9fAv8hSWXmhub!-BN(Ts+wEE6R&i~OecUv$55H%4{3~if( zXHd6j^CN*3z9c@qCuFoH!#VHCVxq$KR7?J!VsUXx{#wLhn~n&yur2~Foji`;>&e0b zzx@QEXY#f-rxlDG#dy^l!aMe2sm<>vxvAWQBPTB!7u^s@&{&U1;49?s^a6cd=jVGd zrry9iB(N^c2Vrt&e*>KuJ}@|vHg_F$VKT-F^D>q%O~CTrB9@;_V9g`EcK-u|Nx)NJ z&=bY?s0E~sgLu6})-eK$9!#YHa3b)qX8#996(Xt_QJYYQHT#51cXc2ipUBe9B86(F zjg>WfuS++hx;$OU66Hr#m+vW}P`)3c^5K6jxq|ZKyR8N~Ar@xR?ohZ0y@n8cgdrdH zBZzS}q#`M=S(0#%g~#<~oz0jrgz>CjMQz0*NZ200ok(B{U7Ric#gA!7l7|97AF=Qz zc8OFaTSJ`7u|`=vyCd1Xoy&bTs9}iJ7R*!zH3R{eXw%GcWu;)PZ*l;PHpyV?7+;x8 z??vcb{^00z&S13joL->a1C(Q*sC@G^ZCBACQFg zWh>+Jl32LkzKB4nI>%H;AipC`^*5G$ry=eSzb zVDF#D*9yc6gtV|rWDz7hPV zK?>{Z-}#|VbudA6o?j25{S#>RN=E{pmco+Ef!BmnEBe&w68p!E# z>Qg=OhvZtSvi`vGjR!hH@>o5J&RPjXiGw8`ptYCqoCo#0xVHfoK<#t9?nk|&8w5Cq z1|pNemKa|f=n1K0;W*^pi)n@`wtYxFx@sU|ve8?h2^(SzpP~%?%PBY1@e*_Ed_O*; z4|~(>g_?z2jrR+&D7twLVddEM{j(NMM(t1Qf|%j~1@*K-3(D|9hJqezB+%Q53}r*A zA_s`b&iVI&rY(D$&;U7%_e8&lUIK5gZ+2JcXo1}DB#6mhRbHzA-9)u2ZH`|GSXgn# zpc?pej`v0qtp72HWl;zQ@1Wo$Y(AYnf+*taCx}_#?N>buaxuq*Kv6b<_6xQs4Cppr zh!rIAC)3N9k&v;_ zW%x@#gYFIhv58bfw;L?A42#sN&d5e}i(-ztRC`0EK9}w_5|!Y89k^f}US#0K2IkkF zB4Iy3Wr(63XBwi6r?}K(&3>Dh6VB9=C>J$DjBn?v5c>8CVE}a^$lH z&>Ran*2kqg7!}E{HyYoJ8`7Au*+LK~HMaro%2<*-PGhlwk;pVWza7PQrm^M?b3hoX zOgy}CI+>J-v)b_=(m);?xbFZqQ<@ph3kI;Zf#a@X;mFd1uVdW1?YA zTJsA7u!p?Ykp~WB&DAq`kAW;f9mBH+vP3`49by=|F&>z-bSKHByD`FkaUh$emK>Mt zeFn4pYU!ueJboyPGVcT?U?Za}DOd6;8fy#aruoreQD|~2kfp6tZG-E);o%iS3!Il_ zZ^F$Ir3Cl|h3a>M*qU;MghFvNh$->a(@NJNkQ^9v=P;LERhEIHh${#aQs3l*eL-go z{tJ zfN(`IUtLLj0r#qP^)8CkE01fY&O$1Rd?q4wj<0IDt<+%mIs!?`ZWL{CoUN>crxsV1 zXSJ#N?^8F+#^mYEdz}Nwc=;f+3?<9wii70yk|8}P0|zJi{Gmx?0c5b8VQ2g z3NpEYzNY?pnU5RJc1Vexxc3P3Q;SZ#%?Or+$6IHPU@xhqoDTea1`Ck#Tk_i(Oz(H> zM?nt#(E;Le4dl6gJp2W=y-&{@B4zX=q;wQxz6ZP;m}DpkqbMOlB&=5wLKRqne^tX; zY_#iMWJ}djNpsj{qT0R9`N5Z13w0X*;Uy+Z(am}9k&qaEY0IaMWIOzhd?)H$`JfuY zv_3p?6x-i^S*b{Q>poICh)O>I+W` z=B;RcU^I)7@&ozp(HNSMf!uctyA#wu3XrTRC7|cic%LQ&y8ujWq3#*fj0cZp(SDZ8 zqWVUER@V}n#7B>1yZcoBAX0+wAw}t?*~9^C@&4CDLJ%b=-Snc8aO09l_~Ex|Sc$#u z>El?6TDs`Z-!?J-kOL4MahysR{b0yD1*Vl!oNSAx`3KNJ>HHfL+NJmBeHg~r5y2-g zw#%>hYXSI;JJo=v_u>&U+tOwOF$)~T{(%&w_xAS`NbXe<3MoP9y^THjBbg0}I7ZSd zO;ivS*4wVgj)~a5`iw#-&YtM_tnn<$qj?ww(upg!1rTZtcUTR#) zOPItqt7GlIPGY`lsh}BuG?~33#kc2^r?5iZ-@XvLs3uVzologv+w<_LtfiFMocEi` z+6Mfk#WYp_f-Wh<99j!q4^Eoz!Dg)me`6}^5pw?XZKS2z1Z`ho*(!*DzuwY|nG9p2 z9atH8P7hO)`n4NUuU>Z?&4(S%?hT0iTRZr8<1gbnNk8hvG zy0(4|BJ`>JAac3{YX-<3*;jCUBYO2LM6SH-D{ArhnMWzF`wB~z!W;8Judu*|DwM;~ z0=?-`HVF?h)#cf*ux>&3%PR3Saam`y{qZ^CqK6A1$^e|>(8+E5n^)L)X=*g@J{^kw zpvHXMbT+W{f9m~L3?88qsxj#Fqk_S3KLvvrl&gWkIxhu-u-nxbwEp}#49Y&+#y8IZ z1{+%QdNbJ(>HA22ZYEnKy%fp&&tjjd4|C7iEJMAFkDkpssJrl0vmxgM@{ZF9g@73_6f#ndKock>L5u+n8~`tI2J@u3Xk-Ka z_FVRXdN)s)$Na-Xpvgg9qyoU(A*9gM=Dm$Lp_QDihc&c6o=fM8=CPjYRDO6K>!%Lm zUh`RNsmsT_(|l|keGc%^^I5pspBKz$VQO#w;d~a0n?pC|vrGww4=*l&viAnJEPzUV z27kJMEmpt9%?qJQZv7E|dm$3v=jRr(SyH=wyw|HNTV2Etzsi>3sq{YCEEwH5J{vN` z5MGeY-cj%2p^MnPMt|(ksroa&#i{DgT|bkXxBP27(u}jCO(LPw>QwcQuA8ZG$F%us zKGDnu`=mk2v68FCJXdY)l3p5Ke9bG2|88bpZG33AZAUL)&MZDpe*K&Su|+rLJ<@2G zq$4pI1(Og&!9KiY4(o(FbR%-01N@8U=Ag+7d0`G48`BB*7SWZYmk`5L4oInR>y9w= zV`K3{r|9AWUs~KBvybt(#jI-`i@~IszeY{thw@p=4s)MEH2K=4bCv5= zmg711)Mek1rq|lbtup9}eG>_SZo`2qf9kom0LMeK1Nqbf7UKOAmNZQ1#;}(}(%J&{ zP;>1sJ%3{fo2+*6Crd!&5^u2-qMe4Dma@V4*}Igj$IrlJ>I!Be$?V z#^Q>dwPW&02v88P8;a5lrJkw8S6V9aN;P0+J>J2}Refc3zuCu9fk!v*gg6h`?^)Rq z59w7k|70Zzt-Np*%WC{Cz<>(KDV=uzM&W@Geat()!LpEL`x`7@a{=hbtY(*dYuyH- z3Fr^^fb{X={%^8RHQgP0{>_`<8d=dH8x3D>dNuvFn-SHiVh5fn^Gu_>UXdeN9}co?m*KwbC@g$CI~V_;U9;#%Ci7 z)11AoxA)n|+Dl6Ih3_&Zt@xgozRONY1D4whH?akhlzZJC`T@lMKx!tsP6gViy3To+ z0@#H8%s<)8+G?V&xT~s#4^h>P%X%KQ1(hGUthW!|!se+p-=llK*vf{egL(69=)~om zZDY$cExy&m-ocvSPRWx zU+Q_YkJ$^FwfLC(F>9@vijU17vxI2zY0E zFx%!OZNI_4JjtSc2j=QjR*%JHttfW3UrGi;83`9;j~{ldkYIQeXb{(;lq z|L`x)V3k^6|MLvgje9-4?xOC|tFZ>*a3M-0u z>F2C*%k$q7YJ?1$7UY$uS7qUnuJAl$&4SkYqkY`}955-`#k-ut%KXU~{_;82REqe6 zFFl8q^63h``y5NuJf6Ib-#^ECCB=TMr{muE5R$?0j1{_qpo;@c9M{hjT_NhDGo+yH zi^0#ExNA(bb^%}B%jbTu$bL_dxq{`3pz_AQ6?;u1?ZOoNK_szanw7CcD^!}a)(FWKe5g&?<@9oxzc z#HEX~^OW-}N#i$Z8@Hav-m~9F{KR>dnm7`8(Hn>E29lEnF_vx%qcvaE?9dB~(4p}) zJ2Y!IAAA8O(Tle8Nf%g5;O3WV@Hg%LB%iWoAbR4)jz@g&1=h07k2^#&yAoUfe0inI zd0z;j*h@Wt7Pg22Ht!Y*X!&pBw{cAgiy3qkI69MsGS&N9FY0|P4wsO75{AODVLSwT zIDKhF8N?hVAdY0fE`#|qObsR-EyfL*O(2OT-$*RsG&xI7zgmM&3IhP8^5v}=kJLrx{;RH{Kn#8qG#2Qp8#?0Oji(a%|Zl|BxsRo%zqgZa~o zEI2G>PgN(^6E$fI=AhJb&3WISmwv-W1UvJ|QYWiMPk;O~saL3{vp4bdZ&~NCSE^%r zY*PAu8%XCla6e4pAAif5n(2VZIS;9@)v7Z6BU{aRVAgp(VQrLZor8Omc%oxHx-Zqb zD5lH{QTV^e$AGgMkhk!8QF2_5ZBwB-tM&m}O!2zAHLJ*A#bW}<qzx$I6_;SC^=LZ}x(o*S z)!x|Id>h|>g=I?* zKH&PRtV`P?m=3f6&cW?xp&C^RPZoulQuy&c@XZm}h;QbvU1dS$*(igQBFFlLmADN4 zV_7sx3Qo5j1BLp9Pe`o*QAfnT2M9Equg)Ne@uA}`?o2}?kEXbE@%a01Wu~EwOV?cF zX^Ov9z-^x1G6mK|)d*~RHM0){P zRuvQrM{~zvhWeGkvPP^I*0d4?u~WpaO}CRu)uU3dnQ=5x3KpUuG{RJtpS#ZLwk&j) zfIdUFm`XILD)CcqQPlyy=zG@KoJP-25NOblZuoPc3zdv3pMh3znjx41-o8=_;`~de zf3DzkrQq6s7A&Ezu%@kb7hH>iLmo5Wrme!QizvAU3|yGvDh*O<75gr{OG`vv^ zgt}D_UR5B_D)LVV*b+`cLv12W!Bu?PbQ1`KZxx7c3JAl$fDpa?Bxl?Ou~XAADp)-tOGLpnVj&@wA%F47>kV#+cAbE_$*dFv6r-+Q9cXtSt34du}bzUzDmjgq?9TtGx*(W%wIg% zjK~TleJws!N=1UCEH*DCBq$3LRfN*eRGYu@xe1>kN*;fuF1->GbODFvN+?4~i&a8m zz%BG{l$qM3D9e;r;oQTDS2~oeV(=`s3=zT@T5MyL1Ti=kn{b_GO~WL$&7cI!Mslo{ zR`7e8Hu-7QI9O~sh{WLFB@<_=W!Exln+f6#1Ug{CQ$(;X!5IEJrh0g-?y|CL?6OdzV+ zRe}OSFrt5e_^<4$$8*i7?EYWb)!I=tu|3&c4Eq0tT}`Hf)g$tnDENP3S0kukHM<&& zg4OKG_#DhGNKzOSZPM}X$7paZ@v5$6pK#Gw&9YQTce5-n<&#*JkMc<@%U^s7mSsjT zL`fl*rB_mBC`=?qNmz-5SS3NMo!}=*0x>azlHg`ygOre)iDf9+FehzemCsH1oTTLO z7j>9zGnAm4i_KF)AO>Q-IZDXQ#g-`{HxpZ_gajQ}Y-^Q}n~7}_A;H87l>|2vD^e1O zi5;L%i+N+Ql3|`w;~9k9JPmy953sNj#DIlep==5VGisAzyIW97F_twB`oUX35U%Mh zAo_9Xo~+ZW;2K{%00fJ|0}$TGT<>ZK5!7dJ$^~@*joqg&=&=)mmIKcBtp$Y@q7F9$ z%xqMxpP~fVtG`3?;YJD=tv!v9%pKn@5T_BIM$3gFHNBH*D^L+WwtWZq(i^Z3C~;I2 zi8`w)RO-7<&i*&9Bmoew=3w=u_h!f{g!bf!S|*~b{bT4X3b)^4A)3)>&l8K~t{kP{ zGW=ji|2uk#MqHbZs(|6B131LSbKQ7$$c&w<`z=DH=+BPGb98dh6j;K8=_WV|NR3k9 zSvL~u(ATDFwh&~fvwaYOC0c~K?;|{M2Novg_&|58CXv`}G6jJg68!*6bZkG%$XGAs z{qSX#wFH${40$auS{VjQA{+&c^T9G>8_Vzh z%9@$S0uOCNwTMl%ZSkVMwJsx_I}tLGjCEOs5+VX#zAOa8xl9q5tcDQu+9qsK^-wuO zX0GZ90_uqQk_rn)tMIW*6Bz9kV<%R-{^%H^H9j>IBVZeZ0@%%ZgyK(Xs1N=Q3k|_z zAqj+Cq%B7Lh^L5z?!RSYpl97UgP*y{TAO`nC_%vf_{BhlP6iRrMCW+@NZ8}`_CaI)$yg8W{)Al}-bC}i()d7{tj0!B`7!9K8=cy3PsXwgZQYH4w8OWBZ zE=Cq*;MS}aFEN0LJj{;7hC}(`odKMnoYQ=Rg`Rj=xr69l!^na-e3nRJPN@6}1 z699{?Nc^lhgP#d`kJGjJ1BmkM$CI`D#WkF~3SR(=1?HJqc@>Sc`QHQWyo!cd^z6Kw z^Ailm@D!?~%CR&aH6~KctEivV5wW5VLo`5{VL7Ue~sk{8S zx`EmzX8_J=|50B~jr#H`!m;(S3b_v(5WHUq#1ZL6S6e`XiWx{lV8#?IK@HB|5cdz5 zZ3Ij)VCY=R4G&q3wfWnrZy)(nBY=+5Qw4{qyyCas5GUq_Hn*=o`bhinE}9WYqiK>F z>D1=NQoLptk+lEf!!$)ySn#z4!aJlmdJgvIJAa2w+;u=r+zr*4Ko&GyN-z|1EDHn- znl2!dwfLbYHI=DU6Eo*>>PF>iGJvU^3lN5oB8c1Ftg3N5gCU?KngOE9c>L1y=}LuF zqvm{187k2u+K&rpmZ5nosg?B5ZVswJaR*=Z2XrsE*o}prj@RLQSe0l^0SgoJnx^V= zXgk09-{8)E1u*|f42Nm5JFkig#l*G^cO}GJN{GFrU9t?ZIfu=_fFG9^xERj_?|-nTuycY69Ea{ojD$ zqCD1s{pM_ybF9)X_bkFRLJtr#a`!7nAp;@jUZvmMZTuhEcB`>vKx4@IB$H$ddu^2T-NY`SW<9RX&QtO+-ydT`@|z@ zLVRf@A{3D|uTq`mh4-%0EEQ#K43@jfXR75pBOY}4>UNs?>r^^*y5*4?=MkH#?fYG7 zPqSv{-U1Sw(4a z-L(ZHku}vaD#vIYiZ!?(fo5uCi8i+r1XTlRQ`JxuyO#^GhScC`t@Og} zKd%C!e;pLm?iYJeyrCAr*WANJUG0C)U=s@1IgdQFtB-korErw2B5lD&un!Csttoht zkXV4aSi2uuePyk-6dUqND?u_e?NY6ya+I}_4zcE77HIbui_2$C@eLt4YgihI83O@R zyinMOKJp%$y=HYxv1Spo@G#~buQ2AF^nwXiYMr~8SNqxO$E3!IMQ_e$hVHfu)HC;> z(*%InjE};*oMk%&(7zFb$Mgi;JNO1%A8jsK@oVjg9(d?iyMJ)4V?X=UIi757uic+m z+i>h>JzUj!8S9uY8MQl4d07*^Pc=J=ob%nzg(`_FUP9-+{G;NOto|ZJu6b2#XQUl<2};L$B-v``NLol`}}s5l_4t)!#zFhjTycnq?#kSof_JKd=L}C?Xat_3zvuXc4oD1NX##{?~+zEWg)#}|n-kqaowEc&`N(8B6^ z01=pX#rP4sOATQg=bR1sJ1qyQ3hEvZ-9P%f_MYngPE|S@2ZT8Wiyuh#<>xwC*{Tzi z#QkdLB>Y;EynlA)^Y!6bOu`Y@*MS^7Vt#^CIxt=*ZT^y5 z+O8lr@xd{5CXV|HVvz%_p(O-Mv6@;C{!eIlltDnF_s{SY48S?VDaySetKK{gyoFlanYU4WA0mMWHp-y3lRG+)R?2l_-V=4EU#jgW?YNDuFB~n?ge76wO=>3*5EK)Q43o<#o!^2fRE)-qpE0{xS1!Tuj^u zXI{kKpbr&4hp+Idk6C~xPEQMp`0~dPvO8KAlp6 z?uVqOtm2kCbV9fI01dgaF%+sVZyIPtC5E@h1y>V_NLvCLrS++&^%?4;^?3;ju#GVk z6S*dYe$0_JA_nS|y0pT-=loNdyF^RyRN#hGKu}=qOe!zR50#fFu3sa7H5>|+SAVFy zG$g-NP>W&oaNCi(67yI@K--lBvQo5N7J6r#vi-T)>{hps9un(M5mkdXh_n3Md=^jM z6wufRh6-j_Vwn7x_C4azTYW*inF>=%QmMdg87voxJTZ4TvKu zm3H+hg+%S>u8`JZ7K)>@Wb62qS=v3MwOfg-E}d!#)m-2}z>wz!_mOpEUGz>9>=LX! zVXN$dMN4viX=WXvQ+`v#_cB`NdO4Qi8;>qt;VYi7Cc)(>?<7k-5UR40ZX_OL$opH; z=Kah+d%`-_CCW>SyQ5w5;ag9cPX86mt;A|F4(2(SrMOt(TqWk9=ppiFlp0qtXAX~l z3Y`%6;a&Jupfb2uMtZz5f4Eg~MaRz?%DQjRZNt^u8J*5OX zcd|tjvcjtHnDZL&*n*9-0iwHhiq}ixIzy4b8n+wD(ky={lUhtMx?BO(Wq}hD!KsE2 z06a$?DU{Z>o zHKii*u!DZ^DmPk0$EV74qCJQ5&aAATQH@imq1n<1Mv$zjwe|`zT$`$ zH-)sx7ig77x#Q238!pPF6oWKiqAru?mCqf=d%Qui?ZpPL=)1gEiT+eXbA%@ z2dPM7AZZp@%1V7>DUn+KPh$(EFl@(dx;D>M7Nr!NR9#Sf2^$1)DB!L&R;kq$i&`61 zbHYc|PE%$dJ=s;2l$I{8qId{7BZv5nhs~bQnie< ze99q}rJ$W4@PfNQlz=_uJJfQU@jeBym@A0b1np(j$wXv93D_)(QW;RebtObtB6NWO z=>?uBM^owF*}HxtVMaWgx0U3^W-5o`)0G0E9CWG_X%&%pzF7-M6r+u-HD$yLV5xP0 zl~BghG*j-{3azpudnDFp>l%NI2X+T*F%c8QXWAwxu!+jRY=}}I>8ynKhH-0yE8~fK zsWO_m7z4qfEx8rou~_?PszbX^b6o4Q<_a&Dwk~Twht1UWZTP!d{ zLOwF)RqC{PbNC$(dE5&s4lFIBEeGTt0w|4PRSM7hIg}&?U|t_~X7ajx*>}Wliyi z-j~D}sWR(h1pM*k=-0CnFGV9nkR2l-#yTH>6e+KANZ@evLL8B&OZV*@J()ltg}9F6 zo#aZh%NdP&aM`{(iCW>k>+1xp(TY$Uk8d~ubnLsSMisS1MQZ%5GD@zj zBadpJCSe}}SXMh&CBMQa*Of!1I|KRab>%iv@j!m0t{fQFJ4al7YiY1~&h4k%jR-K1 zL~McK${S6#VT8!3(CL{y922*k$juM1kjmXEw2b{u1gS0N)$Fq3f zdh#IY+B&|jp8SP0cLtwWUv95CF?<`}P+xvYx;dR!)R+56?@YHRc*_sf(OX`@y=agS zYJk^^TM%y{6=?I9!{SbH?FtbBNZp1D!Ks{S<(Merb0w@*C3;quP_|7jUCvZ~L@Phm zq+0bn=vX% zoZayV(*y2+iBpk#?Py`&kIto5;_;n{`i`nH_@y{i)`1NtLE~Jp>hw&I*)%JDzDVsjo-#!36md6 zmxu9*;c}96oAI6DvQg?~<`2W=7EkcabzH;NoWcBpPn8jkOTwXn3eQ%g!lgH`xaqpLPWkye!%fp3Y13 zatGzNrwU%Gf5~OEWKPT|!pO4jf8En~j zI(hwkzO%L5EYf$Or|O8W!1M8|f^)%enVwoxoS=&74hNNg4Cdvn<(ATrS==v5?ozLH zgu5Rejpyl6ayuzwFkccSzaZ@!&3}rL16%f;hYZ&7P+DglfLh4~ha9%})AOq<`*?;R9B=B9c9T zAB&d%jJ`6m#=L}!)iVfJxneAG?C4PGN<0O3RuL{;<3YT%jT|F=F@XEFm7}HeCZ5n% z?kTM>@tn5u6u3(*Yby^_n|Swja;j7?j$7KHp*KSL!FF<-t|_R3v=F&d#Ng94lv?As zSByMcS~Zqu$6$8M9?Lhx$Q@et8cUt!a-0VDus~zh4&lVSC(^777{VfO+_C%)^82b0 zpO^iOG;0^)0q^b@9@Jj$qq+LXk56bXZ_#Xg;>R0wkTW#7fBW&39c4eB-9bL7Ir7*Y zdZDA-N%M;m>Zj?6k82&}U`+waS9X-UX;$Jx6dTw{?yT`cXlW-oNizW-Uv`o^XjWCF zH|{JKYVJMslx$B7dw|hNPsf@-$7%LqGeTu5w+qra#h( zW98MFE%*KG!{g+mYE8$#{OtbS<&kR5iF#ddo%Iss4Wgq2&tv$1k{HllaB$eOmC%>fr*gl}Y+*p!I zNAd}2a-_7Y4PTWe2WXu`sQ>W32QC$QhN}3%G`U~Iph1G$WCj>*i$MFr>i*zLLGVFs z#A4VKOLuD?H9(G%mbB#=1LRhkpMKrS7Y)FBfGKVHx&iW|hC49tiDzYyBGsj96v;mr zD0hIb*GmKCks))+{Zt3L0cIHl(9Z8uC?27sIQPWl{40hJ86+Ro{Mk>>?{@L?)GpJMgiH;+~hH%3)Hza#zs9n*#sPi`D5 z{(9+R_&4UKCO`(dqvLlbfcKo$aot2YraRXGT;AWV z%*Hre=eeu!&8WlqYclQ2zfP5$G1$B!=;8g z{^dluB{xr$ef<(Z2V$B5n(}DyS*&D}knKG`9zRKL6(EDZR6Q^Z!f-z@43D#Ze8D8S zr6$2o&s~G?@@{r4EY^(Sc`hV$rgY72V#{FXq4iC{ITFS7vZ zo8Pu_>l}cpU5j6y15n4yw%Rw&1*qWnGy+s_Q8rWM26gBw0ySR6Uzjfs>8#c(l0XDx z(j3e6_7B`lpeeNo(-!M{W~cTSjgdbZwMm!gV(sP12wpm04)o3`eM(NCy22L%qUtP= z!_)(Ky9Kf--8vT{_A?oqfN1j{lcu}s8L-L7A)WzCwoJp877rO)p5lr%_D1ADU-unU zLon^~!Dxb7p-A+lf^<<{-So%T>2!GfVu{CV$u8UfaKN>tPn(@$(k zg^1j~7*1)uc+o%z3KO@;}guTo#}7s@!k-W2K5r z*brPP!zvQr{CRLwo>a9k>l%C}V-mD~aLV&K^c`y9z7lMjcob@+M+j(#0ZVV{37ipO zygz&dinn+^L4JeP^L{38ku48zH(qcU;Xg{1l?soT`AEmDd;e3O^95u8P&tkcBYm*v z*i61PTaHN5QX$+vz~CIEZ7n&@{M(P}t^p{Z8j$uGpmYV$gW23?ksQ%=3ks!L!d5`r zlxm58UHm+B4b>ai6{}~(%X;`?%o>8-_+vu-7~Y4Y7dWoW=C3T0yY{(!cB{)73;a%{ zT!v0FpProFJPI_auEgwj7w^0Gy8^8r7T@>=(r zSWV*JKJKSF5-vJzeM15izuGq&Qz}~S(OtpoLp2aiF z@*qv8rxE-^v)rspW6%LuEu1+ZTe;CyKh)?-Q{f3KFO?&}z7e+g#=n(fa&3V81oJw&~!=d9BUi<4J9j)GQRa_ZCWllY?4$HY5VuRWvKfCTM-FY^>p}r^_$$ynJ&fQbIdY8pRjytv2e(Mu zx0Q&h7^lQ%NIzrwG12uc@@8&B$fau_@r1>4gue~rL=O7^WQ1EKgvzybxOuT0rRnf@ z1mCq-ZmMaFkF$&A-qK<(?wtz;`{CmV-YHkk(0YNW^RkQ8SD8m7d|B^8{HNlEYScm)J^Nge8M_c)mJgB~w`SI|4IivCD3NdPq#?a|P zp@^Zr2B%QQAu}28U{D^K#zLwdOLG@+ zXH@A4@cI+H7QXOT1i!gN?j*g~kOwZ6J4*2yK6t6zGOaymU?|5PpjANC=wwSPQXp0^Dq1Qumoi3d|U>%w2c(r_(E&_Y24_ z&P#8K{GU^PXecX@zp=p>rn2P0+a5Kzxq5Q?2%dV(1{kLS4zfUoHo9X>tX0 z7FgDVL_nygl>G;t>YTreavC8SX0LUGoOtRwLIgox)>lv9QC4g==0e8@r^Q&j>BX$~ z?%C6=GMwketL$fAleKE8^gaHa4b#M@0e@hVw`;~@DSm&29NvG}X==H6a&8qh(&pV7 zG2&smOK?W5M7d76qoA8T4sjf@Rh}!v>v2WuC7ynaH+o%e+1M+DM5pXqjsZJpA}{a=T~x~(~%`MTW5f0CUTX*X<9KqHBkONlu*)h=C26@UA6IiOjq9mGeoZz`rT=po)Fjv0mHxYJcB@9;4zp|R79v6pAhUIVs7LB&MGv+LN6jcDwec$TQxH8I8#d+hBHdtx*( z@!rqua}>?@_won#?C#9&?C$LB?Ck9Bl-a7$>l8XzN*sSsavP;M?5CTvRsYcMC-aEK zgK<;dzfY~liPll4P{1z7oeCzR1pT&{LA|N$9JQBYpS*CII7js*lB(lq3JryE$s0L6fhT7z*wl7e7b7>A?HKv7U=`kSa(z#XHbtHT94AFbJc)a z(_ke}hWAgi>1iH`X6%-j4F)@yb;bmk?DV^E>Bk#u?;~<(;&y5``erWF{y{Eudaml{ z>c5N|+UVHshL{ILfeou{^U ze2PU)nfYpi;&pmda=@-kxA|(A#|4&(=GbD0f@INEqB--`ddh_|bb7w}Y5OH*B!^tK z_FOBwHXZj@nPY-C$zH!as9RTSGc5t;;S$!x+nHCIDrgLrC$SOTQBJW#nwY8DjhAf` ztu%IlTDGduDu<;UYcq;VDUsqXS5;7!?7z3xgF@ndQ3{2<4!xf(W?b& zd^sKPcC#GJD-Rd(01y`b@4q~KK)5^zr?v}W>Dui?6BlA37pe{1vi`9{^aaGz zg=%eOPFD&gwMq^3JK100&Bu70(q?FQ6JcCWfim&z9^6HxJ%`VV@$B;cFr||k5`5(_ zYIma9Nl@Xy*Eq?4^j9ZIBx)7fG69o44UqeTmwz`WYp&%LJj%a?{hQ}i}4+;<8KsKDGi=vGW!CvAqGAJC!YYLLf2AknT?U<5UKPcN3M z{_eHFrAe9AJlt{m^)>QYp&C6W0`MT)2koZZ1OL*96>73$uZ7u^vqCLjW8OWUEDIIJ zr4XZ1BJ7W2>)i~93GVdyZMoAQ-VCQct}b5Q?b|?M#~QhCVNj|))m^CuRqejIK!5Lw z`L%Ev$}?O0Hkt3NYcPITA#d+MQ&z%AoVJ9%U8&YJECH#k9O1b?o|cYdrwttOW~CZj z+5jV$d>gtnd}ho}6ThWOU#r#926!-|B%@s#vRcYdK5J9XY%Z+Gv(`Uj$0judm{sv% z%yKZEyUl|Dq6pg2cN}MS+JGPVg4fshz)k#jFt&e&((-G#cA+aEr)!XD64F$SE`E&- zjL7V4@?52E^n1P@52u>l7g<1xIrTT~M*7FE!|CEGwYR?WOKwPb?%~n19*RxO{V})){&L8wS=(E5>U4m%x~=o>^yXLK>`J+KQ2geqb4+c|SV|?VX0^!3z9WYL|`4cIAB*iKG)8>;_`k0WEsojjG=e5?Aph9YwYFBeZEzr`}*W*lw* zR&DOT|K|_U`1ul`b)8Bc8-XU`G_;;^6tz*U>v;XIa2m2v-B4yA$?4c*GdhzuyHz! z4iY-*6duk1Q|FDsAghT&(u_Dkj0EKN5b!8^-70jr*=ZQiV2R zx2m57UB@tn2AGbsp|+I9ury4A4Q_Ux3bB@s%XDePHnmpSs~bR}KdLzm3e!m_Ows@V zkdxK}ba0#6+~e|Tl$Y2))s?M^E`c943 zIUY~XrVZb#0gmhOapHTmyJI3g!gi@a#lzEKyPHjsyHr0%7etzNVME@UhPSDRB9OBI=*(AhFGwy2|GlyEvmok zh1LJswkgw`*4C$;7BwcM3L4fSB?wIHS(%p{yk0=HJ-Wqrmz9Qj=QO$PhO^6rpTeo- zZm3udv*`2Ps=srWncRxXEn2o4qw97}UVzwA2O6_te#7^PX1ue;&Hn-~SAcd1!d_WD zUQE57S_L3`J_flB8v{TZ0))3S;<2{DCT3&U-lc&vHXR7?+2m=M)-TLlg)nG?c4O8rQ*7hL z3`HA`9z!$ssQZ)!Cn<3+);>c|(#XB=PP6W(GkewgxF_3XpITWt_anvZQ{#2TY0N&g zoa^u(nHKC$rcnGo)r)rQgVXYQy0}jbHQYrT@Vse6$-8-j5qkV7(*K|agzm5>`61~C z=`nT^Y=%QmIAN%7b~mpyM)eQW`UiDl;3a@M@Y7Cb@B!0)p2xwxs1}>^GY-<@AJk+e zHJa+}SF3axn**fgJ4=hQ}6 z?!K6Tdr2fRj6ul9KIYL_qi)g<`&Ivr{r7{(96=+dtFw66das=Bis*LlYq6!P47TiX zlx!PJz?%_~m{lx9Q}XmvOCt$aG3j?1E+YkPe!3P_KA=`=@-2cX!A5i7tSrT0c2Yg7 zK4TUHjO_wgAxXbIayrsetqGYE)+YCRZ_Enp%IG}zT|{M#Sx)V0(fkAIrcNnfnS`tf zYxNmsPT(7QGVmcMU96(H0%L53(Ff2>yvc6Y#CNg8Vxd+HJ_5!QKwyS(YK_c{C!CUM z(!zsktgpX4?;=ap6HXpA>G44|Sa}>pZaHc{W%G|TG6%}gv>$0|KNg z-H2>$hA|IGe@Lz5x$cJ#iFk005)Y|n#k7a69Ks%G>>kn`RtG8DPtwrC&{Z-{T9zDE zTj)IB?EzwXA@vQe(CbG!-f=$s#6fQ8B%wBnEm&^(zp|9yh{ZsESxG>~FK`C1e^dyuWlx5;MCJ8sEggXg)<8T-aW4hhesM zIKU&{X1I-Tp0D(f$=}_^1H9X5<4r4(1zU ztS%UPe+L}!KS|&8MN(GmwGe9fvl>y)wiPN-#Jgl2bZ(xCpd$RlO;88D4F#l#coEoZ z(oem{%xVtx3!xQ1t9~v`5D7t2Cxj0EtcJGqLolU!k~#L@&v7ZU{Hvx9Vi^FedN1@D zNkEUcUDo7EOfL4ou=|=7lYULI*9M+*N zs*EFgs6PDCrHh8my>)rE8C=$-wl-$P)uyx|if= ze&)u>g}+!0M-LV>KUO@bKk3e^zFxoJ>jR;4EtrDOVI{RUm>Ql_%QpRelw{2w)6jvM zkSTKsoXfQa3r!5>fwmr9nClHw54PXdVjX=X{dZk%Ai@}C-GeL9?c)8so9~BSnm5yC z_;-yq(mn(Dai^NdK$qJe-^i`qWbT~%-`M%{oGOcMI1WXZM|rru{~WzOr&jbkioC!x zSAz<8rVVniE~gsjRiBbyj?y&IR^@bT#_)AzEBgSbH_L)()Ooe4el(^atliV)%5dCe z+I(Iu8*uLsC~tNaqI&8YZp|qZPCBH*&DN=A5Zyly*%z{nTrWWW^x8||7u4u#E+FF1 zX47y)b~1=NSW8lvfO*u;kOv)tB}3x4S)jQbWnNGN{fA*F^1?4AQCpZ|^}w${%sNa5 zFF?{y-9UFPpz-&trAEK10b%#CLu$lpduW@q+qj6{Y;gTjl8z~|4YcXJ#(4Qu!iJ@} zX#m@&<^$*f0L>l88F8B|))Q&=6wP80afIOv*;?8sTG~-9&0R~IuBG{YfcFx@SQ?IC ztY0@3iZ&}zP&7PmTM0o{DYpjj3P87kUjGVXZsr<_yr`C`|2_5#^%<|xN^-1R2CM!) z+dfnl%QU`VNT%&8nl_;5_f97E`P|mR0O#<#& z0o3i1Izb5zplg@Z&c!zF1GA5`C3;6Wxk|~b-MnNMC_#87I(NL}KlcmVxNI zBtr1U6zfg35fMs=fHHcJ*?bKY1@Q1}IA`&n%`g`cFxVWR@mJuaIkYcXuc+mmM^9pu z*$1f9RkdcBA-sDI&8j79R?YZ^RJ`?U;i3bM-i918n}g5c>j4Xh z`!SkxRrN7Eg`#o*S7Q~pCt>So4gGLc4NZHun(Ked>V+8+uYpZDS|eX?Kf>#uV!-5s z((E|qo|x6yL(1{vcBJ>r4D}Gjhcph2MxZLT)G*lPZhR^>DJJCPmDB=k4+nisks+7o zIEL%Et^~9ejDtQmSZO!VOP2Ru3JG}8R8vdK=r$jEJ%-^7a|2%=V|Ayl!2UgJS->_eU zw^DAXRlG)nSqjc{LAQ45IE)f+sIf|?l{D=JJbt_Fq@6d^5Z85IpoTE88NQ}FH(+e= z-bsJoP(w@ZUjdr|Kk9vd2UWhQ4yf|&GL8OVEXeKq*z5C6Ob;P*A)>G4-BK{x3Cj?a2W;N#@^m9 z+o+KcF(}Ju*^~RobQ{L=)NQo*wi;ftEJ%`Ov!>hVr`zfmRbDUFXk?>- zoyJADu0W20C~)hP35&Eg9x{YR-BIg$@v#8(A*{5?+t>;7Zrg0ye+OqlRxc#)yJ~f> zdYHcRs9DrgX#v;=l_QB}=Qj7QgVn&J6iBYC&~1)?&i!9tLb(Ous4tm^D7@thvc~|6 z*~5lQ5~#f1#g}&9RjZZS^*M8k-*?O`%2N8{t~xrP!e|b4fOZ0jH|29@Jc$dJIuAVI znL|_WsonKEcXGcQc@1z&^RNu%qxK*%pi6bT)$9#)Lb?uMBov=}&rQ1U5e@)T% z)f$d(CuURs`|7OFhZHW~ckuv6>*WT|6`LUnQvybSHM1m3!0U@B-~kqS<<`;I2Wq{b zIlXv3b3Z*u+G+Nq6?o(IKy^9#XD*_%58$TQG>3i%u(E9-ReK1Zx}EcB@Ixs5&pxBs z4yccbkOk^bWddh!q+{}V<~pGT^f(q$!$egwFt3ur0-R_#hTkJLKKgZcE=Bh{$9 zn@dftYE{R7le1}*Rc%%Ei@B(P?oD#$1YFtk+h^rrYB-x@9*b)n$D;Y1**?5@B#Ser zLBr^Y6~@<3hEd5}wO*5xs5<&6(bmF)MW0VH+}jhUOw7mf2UcQc5HFz?un=*!TkA22 zwL+b3hPZ9?Wv*JaY)h~qPaawxV~|Ihmd6|GRyvcbu8$}_f|bCRd8@FyQW1*TnIXWC zbmGz9d^+D)Zw;rFk5&KD8J}^6ngJI^qvc!a)MK@->(1dE-P-wxiGIrN7(iip>daCn zA&T)HZQeXoaQ}DGDSc6eA-+WVFhb`p!L=6edUdPn5@Z+6^ zEDvLr5yu!C8FAvy7WzphwSNLBGrT!{{sgUmqq*hS6LqLg32RQ#&#)Wv$u{ct33ashCYMCHD_3D4Zy&75NjT17>C?m8Tm08NO4RGp+B#oKF`&v?pq-*c;$^J zp?>H}OP;IUmF(H{_Bs5KU6)hz3$?2EAD^P{E+pE1PJO_l9M?|F4yQ?1+{)0*7m$A^ z=FvAVV8R%@j4r%TBfUF(44yU14qol0T3Xq^jN)Iaq5fN@BiB$5Gygvfekj<2pjp|a zef_@#liEVNtmpruw*M!&6)M3B616SaS(c60MQ#tJ|(l&|}fY40m^F;!@aml&|c;9eTVzTaea>TRLEqT?zD{#EXXVS80LM(N# zd6)xJ5U%Xv9IDA2Qi{>YHf}zVX7`@`xQ}rSjdrSXR(2530)yHYdN`%bX-ti9U-d@(;C^V+VNO_4@-tYxh+8@((q}V<7qw`R6T` z&!Od|(@%e>m3_v1g$y#U;hc8fH&A=w!3X1MI+c74ady5pReh}n20FIqihKWvHZXR; zXTRuKMqagnN5?KB(`$8w2`U+?qeripsyEbaSVbmy%a;WA7M*6K@Mf2o~CR|pEMWu~=2hIX1Ixj(|5kOb`u@*O=T z9gnw!Eleguy9^2RtU)|`{fh6G?F zX}LivbxmesVt?t)**G;vh;1X4owS#~;&-Dwn)A2nW9-4J6=<%w$mb+zD4cu7B{GiT zMo(x)p-904cAJg= zJ@fK7UN>-;tOkptl`A2PR+JM3w)U1m7c`oCU?Gl{gYm)5Oy}RJ^&{5oV>u!p)3F(* zj02FJ1k4_G&owqoZ%C(@e_);4G>7{BqefNFZXs*n+M}Qbx1ewQA2leOPKW=&X@T)+ zr2ebc@h;6w16>nqu;r3?r{~GE^wwHP%sa8(Q~tZK`Qg!#&!A9kkO(WzoqoNi@)=#=2hW zs%6)FB5ku_F8w2t{$}Vb2z(71YYV5LpDc(H4O~6!!08jdA1y+4j_0DYX@yRND&xk{ zF`a1P*gHDgqF01Y*Qk4~YV4qexSRkD*^2MU=;onrH|q;*+vI^T@+Xz+D@S zVcb__c6Y5o(~5~6Rfl8zX>kKOGBtpZo zI{_op8?Fv9d6T6Sn9-3L5JK9SJC{diyG-itB))RYtdUI*oy1Q{R*Iy?-CVs0HRN{r z6F>J1&b(KmbP2(z z_QUX-)0lacuFx%tj~(&W4b5g8F-iASL?~n&u%YDp5mdvxn&Db~jc+U=p3JQmN7+&T}R-&!P|D zo|#U)OAAAwrKKb~-y=m+xp;LA9bp8`EiG!4F9UsywdwOW9nct>axtztlnXw7XbXLp zM)yjKR?6pJQY}x>AZTtSDchcd1W4aSk`k;Es>nRg4JNavsG$T6qJy5IntSWUW%n!^?JXKBzVpc9EoMY5#Iw>l${ArZuu6e4vUuwVH!}v~-g4Y* z%JwSwL%L9kLDWtgxJDYTmNo~dITeefQ&7kX-OY9;FrVGB{&bDoEN}oTB?%7FK^83I zfeV+R{!Vxcn256Be7&VkgPYG2Cnj5v-kFDA}Lu^|>>) z2e7Wo?TQ(IO?$ScARRBmH{=D(8*40VThiip3r?PceZ{~)<29~n2I#;zV2j|uBy(>` zPiW*ddg&|bD8CM+%6=j&v}z4WO51QL{^ua5rB{)>joo4w%be@c(UH+nK+LCIL zj)S3+j)aHJcugZwI z%7EV+b7z9T;fLKAlbJEI7w}qpBupy-+J=;ov^@mS0k*&4oy0G>^6qO9BR4#>=Xsrg znUvXCV&-KdeaMID55AyDfg(=HGtrSi$erpvNMBC$@IBidEu#A^Ia5^1Ya}&pOd3B8 zrzzz`gfhE3Z7C|V8K2EK@ckDZr$D}OreCt3!X&isE^HSvtbs`^Sy zN+GKbZ>zCtrP>;+{#{YBYEXcz!xz<22W_}+_%xqY(|Q!LYJ#VPs~QeCja3h6BmVzn z)y&Qy0B6VwSan)8ja9$?39Op-0jmy+v9qeM!%P4S9Lc+|1V~3;L;u5?{~N5c^25AJ zZvk_{7)TK;filv#k@Cd_Pa+?t&%m%NnojAVBA{-&3Q~dZTbJ7kssXPt`FY=DNYt2G zT4HE^qPDt#-mtGfT?oYpYTd`uKTL$_6yGtHS>fWM&hf1~Y+n%~#c>ioZbpc9j#csT zEJBp04wZ$k<4d<}8dg~>cI=1`*D7L=qqStVWlR;}t(06`gWKy2Ru3I$VWfym+Yrdi zKLzqCS$8w3-k-Si9hWp4<{}yb6JCid$;^g9G7Pdl)xu2>HfG|YMr?&PM^xe)Og4t9 z8kCH!p#er|fN<|dI9~%X8u?TTo1T@sDkW4C>25E|ayhIFoM}#Hs|oK`rJW@+{DlNN zGt3+-8kikZce z>;0pjn}gZ!Bg548inOkl2yw3E$9eo5Pv>fhJ<8-Hnh`Aml?$P?Fu7$B`~Zd{eeV8!q6uw814|D$$ImQo`<`qcI}Jxu1`OZ%n${Vz=`J1BY%j zqC>SsCFgfCug#4tFKdf_x}lXSU@xVXl>SbCl*7`l1oBOd{a-5M(*W*A&2{doTpPyr zDUPC5p9yX@kWA3G{J&nzelAF`Grh7I?=ED-EjhLWJnFiFLxHBf%V1k@F9s8%rx2h`y0)lDLbH+3zEU4u;hY)^H7> zuAt}Ht37ebO$<83O%qhDp=j=Z|1D~-89wl01KU0(Oas*$0?6D@gof-uPuSX2O2jh+ zXpZQF%okH`>+!se{;Lj9%F)!z-+EatH59{ij=kS_Sz0s_OBBa9e|XXTIOwi99mpqM zGz@r!an>ZO*le(MmqJq(*1fozX=uD?P&~Q;8ow^Rz$2$6{P7KSm4b)6c$a^V3FJUs zaDsicg9UgVqFBjZvqi2ktGDf2-*HWSIEuFITV6@4PraImFwf4&Y}Q4fd z7S9AR&M{&!rYndN&cqg4wYD}u*~$=%Z)U3(>zS>Z)TZxRh}>c-sAuR>YF~vg^XPG?mvguAE zPp=YKu;NU29IbDOK1*muH`@q*`n{!yisL;SU&$D)Lz~NWj60;IQJd7urI!m&@`$Tk6x^$ybNurw4rX4j(66N^@k3LDF zxvnv-PZI6jR-tPXPc+vMN+gS}O2y{XFIj~68jyma!fMdoMQ8;Siz5-ZMTekPDbV8mzTyAA=T5jfuWQ?xQot4zmIE;{v z{f61N>AQGtI#p{W#wmaQOB-5=dOC&fwGz`@dh!)}@GD86l-8m`z^YcroNv~`E(HWP zesU5sn`0(#c9IUZ7G<4X_1t*BMbN$0BCO(Ra3s$fGrrcAJn@}*(SldX6Eoj{Dx4ws zlpDS*8QTaqWqViZ)JC*+saw^~2ehRP+AY2@!&FjipOSN1k*GY0p!RJ=YUEQltde<= z$%akLmu4f_gB^di@bJd#vPie9cB9(T>$W1ItOYFmQGaf#tQDqG{dVF9sW-ThyV-?L~l3@c&4Qu0!40i$2Q0I6BZ?OpaKNNeMCsdTq0erzt+T z(4j4;^JdNf#Yln~#micNArmac!wnM-k=H%0603(d>?*wnt-NCTE4`#o-_J1>4i5jv_`m+??DyiRLbo zW3-NFLft!w#%1nSkuDS6oDpkIwQ4H@xL{ZKgiiGk!7dH}XC}vM?wv3^dbK8x&Z4U4 zDwJ`s4am0(m^%s7s)?wXVItDdbz?fM3g`)vjG{$?xoCN2a1ytM;h5}h7+|k za=uzbTiXafs?kLZP^Q+TC0#^4rDaXJ*hT!Td>%z>KSdo9qUidkB3{u&QMs<-av5VV z28_9x4)6DkNYXt_9DHenjATx;{~5IEfCBPp_n_$*?-4iGf}0fsL5)C>3I z0WF6k84Z#1W1Udcie?hK%#%nBdlRm7k~`y?-)88qk>uD@MCyA0b1#TPqu30U8dBq) zqL$J$f>L^lqsAYfcxgzO7L1xn*%msj87h(0J{YG=Q)=KkV$6G)fJ4bQJ2_$R#;9-h z7hd8qnxmn)9rv&qIya;AULxE0Ti*Z3ZArp^a$5>GnK7V)MWSFfI^1u|2xAkn^N08qKWfXKw+=7JuU4cW;^@JP~<+U)mN;B z$J(X7Fby@xC6|6;NKhBZ2KJiNJY}_}@Rb*apqM9Cn%_?}P-H7j=d!PRJMF+rTy@y;*ebLNlFXK84S{2-K2IyMFaPpHw#LQ ztwT$OicXFTYCBneA1aFJl++va*Dz7my~_;%@m8A6@O=zb7%sYbJe`T+cn*>Ktu157 zJY4is9z@ft;i5&A*x%5Te5vfbXpLd=7&2>cFz;UA&NV)tB`<&dESl62qH%@h*JVwY zHv2z1+cTPujSvy;odIw_+iU-=7QGuGD%EKVRM~Da!?h6UIJoQ_Z_#N7ee7Jy6LMvS zLpVVPSTw+);j~VGk}bi zDZk(rDo!ywxp6U>FCnp8!h%s3Ftk22T)YT)ZXm6W@hdnoJaBbtOyOWhwwrTd@pG6Yq7U zo8OjAhDzXoEUrUyps|xgjnIp?WHes4vl()2vZ1bNQGn$3{S}e)_0sOXbbpfg*1g*) zbhW?i>V_3?AB#BabFC)Ng0W*xqP6u2Z}IvPRE6?1X^QCUejSna;rJ++UQ7`_?#DSw zW0s@AgDy>C+6+*|pvY$;vaMoM>-`h<3k;upR5SAbOV}YPw&UQov%% zgQ)&gQOSL9A=H2%8a`Dtca1(SGs>)CAN>wNba|>6p?q0_62BDD^%^7Hek`J6Nh!c; z;HI-+UPa>Y4&)$2O*!~yAtuJV5H_MD?fOz|OY3s1pn@tE?<^IUpmk&xeCpyk7E#D+ zw#^V*95y`M`vFU^Hu>OmTx^e{1=R`#I-hFknJ|6A?%eb2k!%NKfB6>-sL8c|VC(@= zwT)96IM9Qygl9m>{9G`K#uhG5ZK21?lIJuL=01%9v@tukEH#@Zf*LJPx3^v%F4fft zs)cfl457iUej*=qwoHEEA}_8CbeiwfEg4m#VU zANdUFTFqvxQBae(edNhmtw?>wP?;P%{s@)M6khJxM+({_JAk4xVHrv*Mr|_1sOY%E z_9RrY$QiAF$`q;&ES|c5P{ zu+WvEIn%*b-W;X1bcr%_cDksp95_U}EKv6YXQZ+2E`KVYCBnlqIg0N%$mKUQ@>?QEENR?XPw?PJ{1jWel(77|1)^@3#upSyJ`^`o%yIYI4_vL0|1!|c znXom){y?s?L}m9ToTO#D&_MCC#LnP#!|fU5wgy|;v({&HWbQG?p8A2jXN$VE7W`0v zI1$nnvk{Z@XCX!k6VtsAs*5+J%@#rKgU1%oSJj&~&K8s1{Xz?>yWETF&k@yXjoVj{ zeP5Uf?Ac!{(vO_|CxuWAy=dti^o&bk)L))-bB-tzK6$T~L+%As5?WU32W|BWQtY+g z?MXp%#n7}p6$@x-1fyN;g=iz?Ebe?%cv9b~_1 zo~T~x>F$Ec|LqY@H|Gf-*Vh&~3R+f>x^+#TJzBU_g~CxW7qHFei)KpO-DI9G{7OX^ z<`asvo%2P-Qf?fl3AI1;;dFmKwyUmKNWVag8F=e;824vxHb!P*RwrKf=rfM7U^d70 z-c{JLMf}oip=Dt1nBD`=>`3?&zH=-9us|7sn~pNB8=*bxcVYdTuRQ0L3a94_METO! zI7^lpQeCqky8J@n<+utTbry;c$MN{+xlojIN&JbGj0X3@XzD_-!tuwy!zheI4P6Q9 zOd`y21j6YgQk54vdPAbC z1ERs%MfM16p{rd{l_~$0`OGJN%CSC}Ykp10tPaDw%}^KWOdbo>sYTFn#0ChM2^RwN zWRe~9=?faaL>wwLXS=;1c$s+mv z>pfu(c3yw|D_Gxl2>N&};7S6t3~YunS9ny!Rsk?hkDUc(#It1G6M)u0R6Z!Qf9#ZB z4s*bjO|y|!6=}#J>b#UjQ8~GMT8_?a5H4wrv?Tbww<7B)48t-m?;1kQ@-bko%lpn4@gDc01%FoD7TN*LXA2$u2G?bXeU9-@!z{x$ ziut+6VDw6Ed_yC|*^RnQ{|uwh&7xU=*LDRfm*$nw&g)A_Z=6Fr7}F7= zaq4DKwOBNka|_pX^`=vsu>$vbOm{Ymhl@f|w&^lXrL@lLY0Sj+olbvB65=s#W4tv)9g1TahehU*Ft}u&)TR1kd0qz) zPnLjazCd4QHv`!rp%^%?G|H)ga_Sm0(@@Z7NP)c7?$<)wo&p{O!we9M-n_A$3530; zj->lIOMkpnm8IA3aslD*xqQvxx#BW>7IAnE;CwSb@YAn!9Z^}0;U1{pd)t4Mo4)_v z+hs<z;{aTT#ScBZ3(0i1ePK8Vbs1WoeoMlcnep>02<-e+W-ZDjI4 zn9;D;z?Im)2=wy6v4tLeZ29CnaYU81+H%Ea{;YS>dXG{Z*{ zI}RXq0~2I6w3JcUy1?GZ!mDAVuPtT;zl%)HTw74VYIbjUq8!?J#b=Fdd;Ve zdqr)fK`qO@y~60I+?Z{NJRmyaaLH^-#zAaHD7($H>yT)uj5pICheU+S<%?l_z4KUK z3OkG)$E{VU^5x_d_4>Gs>Qfcq5y3U|IPQ zcmM8_v2KW!I~ENzs2>KAiJnC|V0GER=Ch8M**%GGg}2Ny*Ho$ntrnTSVnPuMEIpVQXVHqQVp>|+(UMX!gEH+5TUFV(u{E`Xg`8lNgTqVfi4KU7zG~=IsJj2kmXG~J z0}ls%dAm^K9a?w{hv9M;+f@q(ad<1jhqQ3oaE@4s4$4Nhc^>$f${$d24Jtm+J9!=P z9mkPL_z2<;s6vKaGw@L#9}*tdlpB8IBv?8OwK&fpd^F(?-cf3c4{k!Vuc3$h#%3zi z=Lp>{OXzh`9HU{MCGs~B@925}bE`=|Jn(oa%h+33XjD9ZwLJKf1YhoO6$A{AG`=X-TD#RHxA0wv!P|`mTA#NiPlaP5AG5TTD_<=}r z^DlsTGnD87oDnu(rpFJ2#n39G2%mR%6@+aPky6}8>ef|$PtQBtmmb_*$pDKnU6sIwN+Q{koat#U@TawTMuoQZ3%LZHXR>>5I|pNVPxXZ&1{(@?B>vT8)- z@E&QOQaTj*#`?fb=a7q+7hwT%>LWp4YLs*z!q9aY+GedKp>0+RL-ztSJPgfFQ3Y9l zHHgxlqvLc1F(-!5R*pG$I=^_|A$03GdT3@rjQ1d_@Iq8`>rfC=a}afZ0j!XMn686p z#tTtJdHNIWc_HcTbG0B;@{pXGrA3unu z{w`XTK6et3SxF)GKCdu{?*A@&>H8oWSJ7&)_Xk?q{UPFYex*;8$Nsn3J>;aMzMlMv zxpIOwz801I&K=iM@8dc|)XhmtU37x-ICYLa^@^67Cx2qPoS-UyBDJkOb&;0(gO=)d zf(CPHX?yB?E%jS1^~!PD$f@U!Y30u8FU3fzmb&OT<^3sEC^s5Y)*BI>){LcTzhXO# zy0f<1@@$IP>Bw^cbMU z_R(V94_lJ{5yy39<{ps1zvswNQI&tPSh96KNLAm9T^@(`%k%+SdjEs;>b;1F%+z8_ zXt9ABR_-fJ!VS?97xa?lmp({F8+JoGz?Q?;PA0tzqcofNEO8J|#QlnWD_I_rF1G(c zM+d86CtkU5+WU8ygAOh(IJ!Q~X)^+_j%W0fCUmFqKa__L;+1^y{;ubO2Bya)*Zier zzZ7Q08ah)}zo!ty$R5v0%Mn#Diq7qoq9z}GZjU1JdKDg^WqBR?H;guoWpT0n_cK-7 zB~`E~7E<*#%u-Gw@R_m^YxAzV zSdCy=D0P{6pK^Vw@SROn-*9c7TerP-a7d5s#2KUgTj4hqx>f9>-N<)=%(s3)zG&`9 z1enTCQ2(T?ig2f9NuBa3?I>>gwB-KZwQRn}PMJ>$(*)&0by`ruRL%3wcd}gz#PYZ< zbg_gfEMT|=#RP6-@(;htCm+Rp{|Xs6n@lB>5X*!mAmS@$Q*`yMyMWOH7w;c;Qi@`{ ze#wNT?8I<5zLT4d=~~;F9y^=rre!11AqA^?r)Btpm`s-=3hDx?T7`4GieYoyX(Cb! z`fwxySYtXLNsm|jz~ zf>c{43UW17b^BDt@Pf8~C+h5Kf*+TlpIuF{9!s{%!S;K5IdU5Bq%tK-YO z!%j*lX&Msv$2JMOWTzasi+9L@i#x)R?XK;XpG%sq>pcG1B4K)LmoN*rS$4ad?kSNj zn`HD?TV(Wd{>iG8>xKOhH0_g(GSSdV(lBSU<)nuxQ`ftJm38@)5U@iBc#dW!h4@)( zN9y=?$9~2vLlUp$vjSuJr`|RFl<&~`zw0L!3o%YP9E=|L=Mk2^J_#Yt`qE8qBLPY# z2mKIyVaznRU#0DyrV4TAHgMsFi<_9Tn9rGptV-Z(aPjKL5PJ9x%f;9n2Hs@lXq5)P zPJ{of6?u7?Dk+&)DAvnVJv8`Mm_uT{V|*tge^Uizekc0T-!#WJakT_`E>jK9)<~ezYiLv%(_r5d zt7Kwug6xbTTH>tL^hX&}rB>6l#7RwMVx*SXW;HU%Z?krHwKltS8+T;J!8e!NtgVK2 zkpZFO$)6bi)ifx;6z=P#CC-tV8NSi-yuQk^KEPB%@jbmlk`uW~qME*zw9a2i&jU?C zz6-TPZ!NL1{E6wgl0wUws`z%$60fY3n9t--OxcyxzZ@{jX^Ac3WJQi>i4RxM@^YpS zLl83B`?=gXjJef>A!7ZS(xq~y4$8r=scMj^qH^MEY8zzoQI>v91A|Nn%DpzUHOSN? z`a&BohnD@kGmW|*ZFkfE3f5;}djuIcEmABnz8VsT4p)8RQ z{gz33mo2sIs9dDy9#g^z$(4fk(39^w(`{zne|SlQGtg_-Xx7=9l_iQs(V$J>t8A-xFkGMQK>u<};l!Yie7jR52+! z<(mZhs;UV$WF^q+s-~}d>u1U842Y7Hem7fICoh3@Sv0+TBIwCX3G$$Z1es$8DL*?= zcOkd9oNb*>X&}Sr$VM5afuPu#vn;P8O)GRBrOh&rQL`k##+jBi)lK&mkHG0Nx_MQZ z_2DcF)iiyn^Z06-j4nA{LYJRTk0CZROH9xbuWO0DIM!@Poj_OPOd(4DD6++wX8P6HgXuRhs~nCl zV1}4FfQjGbRDKVwi#PSGziNE``ZTCKQ;7wyb=*Gex6GCkR_thGYJEL|=jVmk$SIhg zGj~&L6Vnj)07N=ujwpsKJ$BQsCZ;dLrdc$tAsxT^jDKSsST%q?v^N;%h@lY-0Ml4YU^CM~oyW^m36MHQ0%VP`9BFR4u2;g= z($}3#zE#}Uav{Djv?pd>V%@0N6WL%F1q?j~j6ZGP7mf%6>24=ebm=W?xH&T6|2@_j zM6X;!;hjyvUOgDzrwDxGfz-9LDJ&v^;Q`nXzj#jtrv|vC&0?Klw<)FYc2MPtF$KZl)Yvvt%FU_*2GyZdpd%Rok8P*z3!+ z94_EjpD_`BQTD3+_&{c|v=>Na@AUktRqkgg+rzX^=lXQ1hWTJA-RWuaa@*D;zqp>o zcJ(ry)V2F(u?F!6e)SpGeKqQy-H{m|?1qf>MKbQ%i}}pidSeA9_EQ?r5&&Td6W3Gr zN8=urGJQC=6roajjT5JU^_DomVySY=4lg3@|yV3G~Cf^|* z1lCH*np%U0#?%~R)&d+}w#g;N>xE%BG>-fBFuNS+hC(wJFqE}64!Rw8x{OpE!0m_< z;|OC|UI8O(YH!Za9P_@rqeE7kV&K6qyHGG*vriMq%6h*Q=P_00VPDaVW#oZ3WpVKg zlOtjJ6ou))F#Z}zfkjAKAVF(O&<{zfz!+`hXpPN7D_FY?lCULzN|?&H|DfaH+y*+f)5y-2 z7-8D(SoP){z$IidJ72)3KBFwQxtN`QzRIOKt!U4};4m8OyUrHRfu^!bjfJx`luRQJ z@o5O@Ghl9&Sd%YHtoCj5vHbt_q@IIKJ7T_r9oXSSoF6+3@VPOdu0j+FDy^X|y(CeG zw8=*;^RK6+*$~q!#~AZ;4VO2s?6~6*rO()IA4@6cC2q5%eB60&JZZ=XQ_mWIXKJXt zpNb;$5Y%T}vJcq@=Oo&$Px8?Qw5GpDm_j1684KVmBzPJ;qwV8ufreoMhBnAg!}02q zktis@UJ%M*OgD*n6PVnX#Wl3cE%OUn+sblvr0I8E<+opICB49}K4T2VFvCASBMIAQ z%!gm~!jt~|%oJQ>3&SU8UP{P}^FT2xC0Z*eN{qHxLmO0p_9V&DJjE2HxF%<4#Wc&H zNuy0ZZtl(VbN;raWzA^QPdblIleL^PTX44;C)32SrZUZ28uN#Vl7KqP$>2Vg=eYqt zGKsNwnu9BQ*XKc_Lk0z0`Nt%1rOqm0_BW@SV@bftnJ&0|y!(C-+0gdCZ% z6Og4b(=3PdYitx^nqfp^mW;}`GfZZTpQOx<{R9|s)4u0<%z8v(sEncCC8P2&(%LY} zxl@uRf1o7c?j%N;BT;HHN}<4!WR+u#^uHy>qh^JYrkBKUV2qC>&69Lsw0Imtkir2Qp!4uC+2Y!iE>6^XJYo#Hc0K2BJDPfMpp<4qxHeSyb=l}BDi z6Yy4gY%7k^XS6xVB|4RFlHaXc^B{F)Z~ZkHy;Vb|$Nrg6SnFXp)Op-V5B!g7{VomU zy_q~UnYxWJ+Hs@~KhQpN zy|vz2&RAGz6=3~ir84QJK(C>k5LYB2NQ&F(5Zx z3Sy@%^-y@DB89)cODYYaC+2PyR;WHV&j|T6=@bNv16wu*L0Yb1H;ZsJ4(hx4z1zSqLnj;z|xvUPO zA+|U8PeW9IXTfo&Bco+Qgnv644H2y=nxFj__Tz#wCO8G1C~Q>vxI7e z)DMQ7Q$vP-r>!|XK;qt_%=y(l`X<8^7;v%ve@JXD^LvykNi20H6}hZs71kU}^_d_1 zoOKdnb*jb{lT$4TQ%yY`UBgDPPI zjs&s!bH5V;tRcP8Ha*VQsMkRQ?1SA5(Ug=}!;m8ZxVJkb}|vGTO3fhAG3* zC8es2ej912H{0}O@yG|EGQ6j<1Uth&Sm2NRL-N!~E%7HUaeHOUlZB=cIuB!r#OZTE z3XZ4<8nOuDBtVN@942$x8fIC%$kf%*Yx8wa))qnTg=cBMUMmoyIi9s z%P{J`xJG-InIc{GXi&;Eda(>^xbD|1VarW}buRj=8u%4jvcgneiMUGpR{(t16&Y>2 zZ25hKNmZh*UIJ11+c>+2J+#?!pUZuAY~4$-b}O}kvXx7PTfLu(qn(e%CBI1iK~ z_ZloYRA-YZq>SDREU~{hvqaxkMOdO^D@xsDs^eM~k<3xvt!URK(|)%oeSQvfkkU3I zhZCidLnajX0nTw$v2tO}&lI|vlFY|(0Qjzq#}<cVh2$ro`hep6x;ZU|2f?3gurpf3}4%CcQyt9cp*|1I1T^^j4 zN}g`h4CB7;NqllO!QACxWMeKGgmu1D?K$m;*LIDa`EXMynxCoo)ppHrj=MdWh&wg9 zx^-SVVGHkq=xhGi$fSk0b;&uu1iT(V9xqfOULtBEVF!6AYFgM`>V`ZgV0{UD<*mrW zwSD1dm80C(jWo^EG_A3mY4HWDBc4{7!Xmz#7@Vi+FH`BvsV!Bu3R_l@%UMD2_oEuO) z3OAAXB2#=`THr)Dl6XK&RJeBv8_Q0CYEe9SLL6cJsC^FGZi?yF8Dd!ME; zH*62Nx=)kDi5&^QU(D<@^&jeO;s$noe^aaW#kRXNlclBd!t#(?> zM$qp&T(9B|#Hm2z!X58Gh8AcxbJaQ!?f}-|D(@mq4`{a9?eb%zQj8DYl=vh(Dr#hS z(6H%6em>t_sM*BL>Pf5%H7$Mi^hEQ;zd|(O zp5Wn6hIi0j8Q_Z-gs}eOwVLsr;mLc5)HtjY73H$w@Eq3-1TVNFAQ zAY_JISS>HWon&q}D`ir=lxVzj1E^Wl4$MoUq6H(&`FLiPFamX_xY8D~1P5!TN{~ZP z8QjsX*0?Mz$~&mByP#bjE}3CVMDOAF=65~B-ow1b$RnD8GESRI4jxQlK%u;V&(H~#Oj#lD{lWb5`IjR!)?hS4UgmPT5=BYKd#YokJ}RQxF(x(Z%ec% zG~e5eY{Qc4r8*)*sSn6{wLi|f!0a|+os$}K8TZ;nY-niPV5@l#xC}du9GVGk@2eWG%tD7V`)Nf8cp2I@Z!}i4f zye76b^^{t*4h>v}1Uu2_2^dlrD1K>4Hl5e>@N3$vu}RVhS%O1-FvZoS>*Dnmk&3Pi zMdw+7v8h)}WHf1j>IkZu4ae;dveHIEDl491h;YP{cwWGKG~JU7zo2PUYfV#BQ1FYs ziJ?~p6k}HzHa7|LB#STLQEG06xbXrTgf2HG?JuE2pWQ$LE@>ulI5^|VCCyGQJew@K ztm#_kW|rLKm6eUZ{hZL;p%!xKjO(NtC26^%ROK#dHf>=gmznr=@pHOUXM4!i0=?? z@S_YcQ0JZo>SYD0v1)n@xC6X^pJTu|_)@lzY(`!~_%2WkSiIfB@-yNh^V!ItXJwGW zC6aFBRfHX`82CDu5V%=tkXy!J?G|$O3JOFs$>S>;J-1>j5w2<`H@cL}GW7hq-#)Z> z{Cj~FOjZ+$%}}tKNK7X3YZ!=wlSzwfntrv~KA!_!fs8{(1Rqs#!1nv z73bCykDoNHJctw?d7J4Pfrpo8@WAi^JSuC%N9Y`S8ti^T*rCmOvg#*I?al#`Yy{$t zJOR0d(dGg4b4~aEV7M zhe@)g2a5-xy?YPwi>J1jS|loVHYay(YwWpin~NXeqYCG8mH0k^v!hqUMNc%tW!xi6 zQdFYx;3n21s;3%vj-DmMpJKYxF@wZE)x^|2lwRGW#LN4rFWbZ%yF*W~>(%~p%yZl> zzCSD0{8>}Xac5HreXgA zg{B3!%!1T^iM#o6d(r!)#!|*%bB}?q&^;~f#E4g#_QD#J)HkQEvy6(=g*K67G2v!p6{<|ID&q*)@b)BiUo zlOpU&i%~++2fXy133`0H-<-Z(%pc>XFB3<}_?{ehZ68UI^N+c(9%Nx9K9bY#U7_IX zbAfwF0|npQ{FOJR6iLn<_mUwBKF)ma9x3P=L~bhh5%qUt8O$%KyE(QeD2zXVU4MNX zJ-y>US;jWLrdZ@7CqpXpd%3EM#2TjjJ(-+(%f)wQd>zjGJXRsDDb9A0le$&;1~skj zb0!JL&cE|%Ul2Pqy6ZK?8fXSq<=yLj1;dgZen~wl`L!>M5w8`M5b%s9pAIg+>OY{~Jy8&~?gD4D~)ULJ?ZWK1{I`s58_{kr_|s$BX#@}vpx z%*Eal?VIunS?x2IolJ@+<3${<$3e!o!Am^wJ1-N@X8ee%BMNc766A@BK80jeGk&iA z=^ZB%6u(})LIHDGiIdxLJXidB31-+0AIhRT-SCF8=!I2|Lji&b2a$*C?2?ZjTbQUg z4?_q%5)>&=zKE~phy#uDBw@75JCE(cTe@g88wPA5obbv@fsMU>imqB1U({EYp!f#n zh0s_jN}eSu$G{9v0GOAtX)Mc(&XAezP!tV8W)c-6{x*Szcf_a7`Gqpor535QD9#R3 z8=SpGi-bf>J)OB*C|-=|5yiWk;&(0iSQ$6{2C;GBJ8<4NNM9FzkWz62g=Q0x`S^h{ zI}1Be*>E4(?!xP;?cB?%tr$II`gPL06+e>m#-7ft_|>(;e?qAehd8Q|2FT_XBq#$A zW~mJQiF9=3UAUB=$aq)&s>|KelJn>)iHjGaQs!Qm9EA=R{1++Cqg9OEMJBrOooe1b z?}U8Ji-UcUKh_P7C{AA`2i*8p+=E@@l^frTeGlZ&8mT1gB7Ix)Vb$z+v3x#5K5t(k zM_co5)o$dM<~;cdsnmu~u3G$~u_A8&NOIcnLu}6G86)A#C&XO*BdOJvcQng{I8l+B zN4m8|b5FS}zHZCA$l44)?u7hcr*W(2I0Z~m7eNpw191Ui6Y9#K#kOea;6EHALp=C4 zc88B487rTnqN;dMP4rsBf=L$7kCC+=d~N4t2o~{(k;h)mmNdt8FF3{Wl^w^z>oE(& zoB~D(iUG&SBM;u4b2~;H+VSnoJta?GFOQP3?f4d4rDNplc6=x9$Vqan9p8oXJxa_y zc?V_VqqtH@PW3^GTr0DzSqwfYcJ$;Qm~!)Hiw(N+k7V4N!^FHBU*BiWVH6&7+QK47 zjD|>h83aYU8K3Bt4jw=9!e1yxF$Fcud0ZI3!Vsa`VG`PnU*vrL5M=CZF1HJA*nrk+ zJqxq5#*E>KENp^sqC$6+H0{nia$OIRe%*OzOQza;48_M}dBP!*&>e+-R!9oE^Y~5I zA#$fXuWI#+)S+8C9#uR8$Nu4rS}b8~N2mlFH$V4|KPoF?>rzNtUr1W{@NHW6hO+(5 zXbY@s#vhgyEr7P9Zv_Q^Jt3e^1wI} zyr2p*2-&qa0nJAp&=*z*EM6aUGKqgLiFJ0e53j6Y1xtu*j(T@+}E{`VKetdnqacf}J{=}qRezWiyY@A6i ztDYAVl)cxGb^Uldw;|{$y%GoDqPq{2I*nqGBZ801Ix^yeROukyvz0lb{!JR-=( zLHuybN{H!;J@2f25*5FQ#Bwmdj{7Q|WDn+>a6_Aj*9P-pGVY<1*lQ?1M#k}}Bz+j} zLE{|7GsAdujvLyLTpPh>a%l}n!bpsb#XZR3k^GZ}51YtMa9TE-K`Gv2kqr}dAIEnH zNq@gmY;8i$kK*IGjOS$7Xmr99XA(b}@5haHCKpHZWA!&OolHXTb5yB`V*Eck`#Y3| z{Y&WPdQ_1QHT#Fh7I&~vFP6fr!Uy4-%t3ZX74}9Od<}!~)dC^bq+f4EQ&gV9LAuiA z!F{QC&?2d^UxTLj<4H>K7+-7F(* zB<_K{A4fBYK9C>ArDup&0{L52xWQ{gF_dpwiBq_WcO!VcoLkzD1V!`BEqAuWJX>1T zNpv4GnS3A3*VXst|$-DwxhF{CE9aisQo*-A^n>`8q_hac8=tbcS)Y{P~p z8HGVC4ndiQ2XU{Fh)|^N!8|L5#*+0he2toIA>)w7e)gy26091>l4CJ^cb}h^8B2iE zm**P#rlL^`V=WXPXE{l7zvR!-dtP*5E2Wqp;6O0szT^NzQ1138{U`BF^|eqolV~L* z(J(KXW3U}4*+S@ngoUG0#C9JRy${YJaE?z9}N31A>Tv6SuP>xCi6ATUo3_} zqWj&&j*+GSc`(;CgI(Mb(H1V^g_;lhCASxcAa({WIsnPspJQrbIz1dBe|c-aV5L3Izr!!|*-j^{v($hQJ5BHH)m{nsf*Y?kzb4UozGcTtur(-fy_vOTqGF*jzR`50 zWHJUV1`aSC!2xN=NnwI`UC;02YA>6P94=rNCef=tP*JB)8}Ay}653 zNsGCBJNZv<9Eo}^-?g^s$rLQc#hfciWo;f!c18hyyj=9+L<;t39aLcXRv9gJBL-%>sqOuHmLO@8iRig-JTPms0rIgmo7pQ=c|!$;}z zusKGN3zO#H^_Saq?4AG#Ret>7NIL((+sR#jb|hJgc&CnL&x|!GZLrY08m=vN5JqFK zVYo}M-`7rmh^rNIsu1!$DDJ!Af1Y~{JA5kx|H9< zt+gWJQofC{+ZD+KTN_{lBWd@KbI6-EvFOfCxy{vs1n-H41b7dl1 z!8_LI4=;x6{6Q)*f7_9GuHc)w>8nbqjJYVK;x3sDy|QnZ)Jku`qg7R3Y%I#o9>kvF zpI;l>L+eM5Wa|pPk-X(wM{;om-@M6fl+y4#nt=T^8Ys{(aAR?k&M7*I$kNZoYd;{4 zgm2;G_1dw_p%?6nOKEF}b5*JvegjRN#-rs$6TVB6W`FQI9-WH$ZOaZmkdAwn{D#~o zKM~%=bPie`J{~7shIi7++vcT^&BHq#k$=1kUn4rHEc`@tYvW;Fhseb50~?ZMD|w}T z+Xs#&hL=1zMci0;`lwD6s}h#qrd0d4*mQbwVF^U z^=ULIX@7(E_n7Q<(aWK~;nya3RbKWmkeXGT%9OkGL@Bi?ip`$QaH15(g$LjB4LV$b zn=jRAFZ;WG)(4lTwD*+uGkTkJS;Ys+JE6=etMD9aeUF@3#n-|yybo9LUR>?l#Bnw6 zJT&oc*%9$HMW)$}Ha#tA@{rnFm~j&pSG01(!} z{FC;w^8-upvofz{%pyrS>NqNbP2Z?eFC&lF^K0Z+s0~?=%x^N!w~;cLYeNpC@U_X% z6fD)n*^o&o$Y~pc&RNoV$bCOMk`F1o8+Xu}IHzK%&-x*ml*-qyeVb!V;5?dHiI3*! zTz19;t06^qYm$}9+jA|f$%$0{y8PrP8vG^tWX0RqQw?0xv zu@mhj_mz&I*bSs&Yd^v&Daj0a{7R7BHQhJC2t)CDkU7`=t$eZ5vimYgW|3%5T4dmg z#8e|QGWed#^LCOy@5As{U-I`2vU4qi@6UZ|E;irDd&=a!F$$|U@hUlei<;fUcb5N( z_Huj^?;&$tv6;Utw}Qdd&A21BBFZiNN!#0I=vi2hzuX? zMIvV5U0DW6Wzr^#*O{kCUCd&cf^5j*dvHS)&^Hb027QIOr) zc+xDcB;L#Bk7w>pV35xw&Ly9&Yga z(WGbR@&57;V9w|9Uh=(QZ1VZZ^30DmN zcvVfCk2Wm*Pua*6d-ICZxPB%vyHIPj&ym%;ur~ZUo0Py(-Ud-Dck``kntqU?n6W5h zr6}%C$-v#1XZpYd*{FfNV!T=ztgdGLZgshY<041Cr9!EiVhzGPq`9A2O6F8K?xOf?FP|anvSG^~6&ub=PSPQkOeH5v zn2;WW^PYc@PdP7M-p@Cb$tS(H5kD00>t(V8vicwvpQ^MZ7T0(?uAqVV>>$?lxVJaO zR)_g(9M|%?SmhYsO(rMLZN&b^aqr{y6q9);u~53On20BNPwwZnH`yP{Nr}=JN z{WWC&X})c}K96mT`$G+|n^TY@i@~E)Y1iHLnAn`*2XeO_k&rX+ne&J&Jp-RsKajI$ z;IsE5^6MEa$JBmA>Yv3LN%(3Keirgg56OzNkWYO`_Mb&s*H;nwIizL!@SoF~^pFfa z$B*aY9*~3Q(A|bTAU~f&`SQLejnBj9>-(hdc|Mezd7tb)&-dYeTuIC>pk7SxllB+* zrYh}y_+~c?I>64br&&xbzr>DyiR>BHz4v`G>jE!Sy8ojiPJsClv-vF#NzN6%2Dj=S zX?2m$FSS$h#~2VU9me_Fu)g zb6&|>iM^~UK_jUr`d;I$IPS$!GVD74l5@I6dfnhVa}RHlgd6-|?&uA2>jt05b+|#s z-{c!`wl~N(H*w_-T_<;M@^84)Kas1qaP>2OBA;*Z4h@zrMDpw`672ESc$?I!^$?YA z`&DC`*zq?1M#e?&6&ynGPnkL@RUkFmP7dL`kCaN``dlK2(zCuECBjbi?Q)ss7AY*Y4<`^CJUm3LX8 z|6bDJ3GZlC{cagc`@LiW6Xun*e6)wGd&1jt508=jC%iAVMqN?Duddn^o3gPDjLhE4 zNO}pcuey^n2xh*MiEdB%fiiiO7cz0~&-_Yx)qS63lJUIPGSciNzk^$ROuX}wUnN(a zk2TaPuEMQUsYpay(Bc&@7hOUTUv_+$G+_+)T`l%inQA4-))iA&LQ2t!LTs=*tdG2vvq(D zImN3+!>wZ0;%`q2ss>qqaCw~k?y{xi((EL$l)-2A34_m#%x4CCUO7R!$l!FX7EbL; zogP?3l4ODh=e9yTEEBTiwsA{YCGI$eRkvN%xc5j8__;A8Q6bcBenl{}ix1edk_{B` zN8h0o8zGhMJGh6#T**vv49`mu-ujg=!;3bVyWmV$+h zbNg9*V`@tK&|F+ADSjFS zg|ifeHz+n561nz8(N9t+-$H`=!S|D>pN7*}R`DZAseY)kx*>djB-|M)2gfjcp5|yG z4ps^?WZc5}p-L!_aTzhBUqhiW z7Z)Q=YADo`>0SMPpOR8#;Ut!@Mcpql>>o&#TMT07zJ}Ik*awhELa%YRq@u3$N8Su= zWTsxK==lc4cT8cZXthC+XiyL-27cUv>*;UMyfSDSBOPN+w=pP^4T?7`ilL%w8x)~&$5RBaF2{$4v%rHot1I~odZ zSSL~&tX)n@*26o%8kbnlE|_HQHPoOP#x#a&+{U07X;2I`MnR0)21S8Eaf&Gnok?a; zL>LsSn8I+0i^j8(d3Bs2rQVP!q`u^>JS%CGO_BYw7i1m!2QG*WEPo$r78+zq$k=Kl zyCzdclFRLcj%J=n5vM0+lRC`=74h^G>TqvPh(kSvak9EqF?#R9uUHMk?}0Ax$kt-9 zI+_$Bo^LOd$n@KBn=>rjvX!=7URd?}^@z%9&OXD-iXWI7!?D4`DucSK)trq6_18?T zC{<5~8vEN@B`h~+)6FGah_j#2klQPLJ(?N;Eu zpps)Tc1A+^EnW!3rMJ+Fo6&_#=`Hl;vO?wzq0bM;;a9^hLLMZNqLeFS^X zOefaqBY4X=&oSax{(_6FYKD(gA2{RAnUKChhzaNc^yUid+YkOGz}*ZC*nfE97@09<{}xidLGNEpXm&n9gK3%=YO zFEVwou%9dJL^=);I&;_ik!eGOwiXWc3}X%^HJg0No*{w{H>4loh6)0=v?B=}D%9g5 zJCcP%g_hiqOtNdJ@U6AFt|1B*GZD+whXf20YCG)CLgO-Q2z`gKWj2JK>_=7%6Gn2| zgUHKaXuIJ-#ACS7oa-J$f`$ui9U8l$aPfz%#}=|v`0!yC-hr9=vX8SYcE^+4;ewme zJoEQv@z(eha<~;SA1O3#;D?VtE${^}DtQRLsCBWZ?Z%lz`Dm<$@qIe@SMC3rj|)YWA=3$0teotkdkT4j!U!vC@&- z%5k<<@lWj%tqh|}l44x|QH&CQYJT>lN^;8-EN#qlVaNr{7B!i=lv9qb2uSn9jum)O}4=Z9k8yACzRma90| z)s~F;N)RmGHI)jbk`9w46FUp`=EjRUZ8~}VmEfv8vElcOlwQ<%O^Ig!nyq6~@_m5N zz)6XZ;&-8WBQ=~O-kseFlHBVfo^kkw(4`Bx15@tfJcHL=X@BuLsfp+}PKcE^^s<5j zHqOPIqsns*=*dY68%cHoKgPs)0uJPEu;3uK?C3~x?9>ioy%3>`jLT|I0@Rq3$B=ln z(7KN2y1%6GO9QE})+AzZkX=;Im^-?uFseQL3=;XjPypL>s9Njt-BU zV$zc2#0xdVmhtEcO+AAGgIarN+G;#J)vjuAu0cWV+IzSLsoS(^8{!`1-r7|i*w(!@ zITs~3mf!k(j8KR4n+0t$(BtLGH~KJcwVSu$%0dB-ABllZ0x-F;TEpr~@N|1)@q6RF$KG1@(kL zVj~J2$mHq5Y-U=E+=&*NlKdG$uw{YFxxl2r)Fny?Z0+trOk;$2;;t9gv}zR@6BQlY zs@&xtctEIS8W9*Bt&R*M4b}XpCF?ko`8Tq>9=T$Nge+|-#aG$A!73AHQR_lyjT5Qy76sIE`K!CKGt*uP#L ziMkDr73P!1aYBO?Q-xnEniguH#)^wt1?e)GlY7?ZicX)n8pe9ZRO3Iw<<7^{>oc5Z*Ji5_G#&Sp_{BBxj746 zw$n`EJ^5v#VEtbzj|}@-urGh+S!J0U>U%<91nV~S*`KE5LZq;Y^qnL$Cd(!X4av~C zD9bOCgpL)>)9(%A=Q)Czy=_cbR7^xfcx1FXL=_tt9W1B&wA$ z7y@Qs@Dc+S3WHVUE{yoo7GY|oa+{OVtclBWgkJLU$1B*PwG$n72!Z7vA;k?lgXo*p({DmMBD{E_!kU~LQ{BAAkz zhXkAd5-;?SVEtd>6&?}>{g-%S4hzl|ir1w=);Qn$$bTu2vVy6wIu~yo5nM6ZeTNY+ z?usyqsICaT$$)!81M%?{!NR5dE^+1Skam@|)hiUGd4;U&h_@0aeu4XE>w&2 zrvJlTg0*FFt(o?hHRaFb?sRSc@>|PJp||*DXe-c~Ff*#@85JF(4gW7iD(%;`%Ad!X zTebDbsEyhV<&Uywqc*bq);3$U?j&)O*0TIjPz+6ltd%5Wv$jV0qpaPm4KBZR^DWv2 z<+o<{29mf%TY=dIqBJXi0;o{m@>}Ejwk*FjYQ8RcwpD9YfhbwpdhMdM6aG^llp5@! zOzrscrx& zYh3rXBrr!?fr)8mj@G~Y2}n10&uv=o@<&;}O*^{$){eQ_M&-9=1BXXwWXyjU2*sar zwH+&PA3;*BNa&7=j8SMM4rKq1ii|o^{aWwT29&=Jq`@R>XGQM1hI_zHt!?>JF+{<1 zkYGA!N&-Y}o$^OU6R;z@L~Y#)MyZvjt-y>%%A$~`?OXm-(Ch1wk9ig8FQri|Np!xp z0&`pz&tpP($bV?6WaKVwpYj(ot$n&8eXKNN?&UAbiPIIy6B4N7J|X(Q3@}pO=ANm@#N@ykZHsc3M67vMd#M69 zTkG@Mv*mV9OfP8nmEYR9pi_b8AGFyDJO!677?CX(wdG%yBDe$%Nv%s2nNmtord-l) zt5D%<5$)xQbPP203Oq|#{#cQlsnm@o{-~|M#gL*L{!yE*Uw(K!t#xEQU1y|>j{;u` z7A{3unOMfYRR0*V7gyKQJ=fOL8K4YN*T3+hESbIo&Hy)n0$H-2R_2mvP2dRx?La33 z{eUq*S708n9MA!870I+m5kg#JijcS-o#U_Y^E&Glk zj2CYvMAcT8P42YSxw>k!>d+9CHbfm3tp$%CPXF;A`)Y;#W4~l#|5n`4btUKAbt>zD z{kXhu@~-xc@870nwd~EENrb!3m8@&6b6wE}HgT2Kr#jZU@ueA#AM@RgHSpM}k@6{Z@W+5f52@Y| z3Jy_g9*z0V+I{-H(M!j@@yK7<^+%lLp&LljTy$3Ao%T9cPE>W$MO3zS%~>W4pMUVl zfSN@GZ{00+lXcy6UGa+v$+6FUzOg#d;SGmA2J6dZZME|_%DjMcSwon;EYp_^KR9&cbyH1>#mDa)w}sk$dH)@ z3u;~&Fg|zX>X?;t6Z&?xsvohi)2A1OwMK3eRX)0NrmB}il5Q?m^jh6ej5}p%9d-H2 zz1tIxH!=oH+?7nP?!(ZqA43P^RaB}- zbwqeCve#7SW)`KMq>j|0l{_=msm!|f?>EG|SHHnHt4ue*j%TmvTtb2-sG|cz0;2;% zwPET2byx^lV}>U8sIsmpnPiIo6(z((hlIz5MQ9_`%tk%g@M0w(KpUow4hV?AtH(fI z9aX3@G11~TL&U{Y=V>RfbRiDXhJ|Ru_$Wg^Qil+YnXajL#7t)`vkMImA0H5hVY+T0J>BJSZXXrHFd5s@o17RShiveDtug`PC*`y)-@r0@^v*yS0>94FxIFTS1?+q6330v z70ATqi*&c-;%d|JmCW>8o}g8fB-1=2Wqcy|Qm}9-%F51`u`ktA$lgBxg6b~}{3lS0 zm$-?1LOP$5=@d|gaEd!vX`o(}6soS8LN|ln21WoY?fx6As-;jzAOwiENTKhoQfR7W z3cUnm+N4myHig!N?3s-|h1%Jt&}|S{RZpR}YNXI1P(%a$5bg`(jgX}xd_#Ab2y`Q;73eFYt|8+4BFti%^-}0sU^B1_xB(mjJ_FsL?*uf2|H#Iu zVbBRcN2e4$O@yF&lN8z(Gyv4TX$tKQ{L&27-aLg?0&fi}2U-KMKm>3Matc(1ojuSA zXasx(qyVdd4A`v%jsR9IQm7d)%_W6S2F-1mLU%jk`d@|MkaG&13VI5-1jPqH4cT_k zR2V-3DuJH^THH2;I=4b8UD2@IkTAkr>lAvjO$x0G{sH7wAddkGz~@6Y4|-S7^|0xV zxU+#u&^hYCtc0Qx6h5G{Kp%tt|Aw8rAq{;+5@M15kD>otho4ac3{LG*DDRO%PXiOc zul7VWgZhGI06%v?FEGNhV+vIQy($D;|B6NUzqP-jNA9l}al8{+GcX0P@j_$qMq}$% zKDc%Nce`Q#px4j*2L(%b3v@PLr_giXq|me^bT^>)!W3G6F-AaOB;=2_qN@U@jkr>V zXR_m!Q|VN*RC>rXl{Nw$0=gBn2pDdbs;9B$sWcZtA5b^YSl}F#mY~%^Kf^&Hs2TJG zSPkB8NGd%Bj0OatXmBbW81P@9|1XjLTb!p+sq`<m_q9C7%NKC&8 zNQUeP&<1h-3!WhEpP>J<;6FJoZ)QFs1Ahdo76^Yusat{C1HM2tzy>8R0G6O%y9`tVx<6E>4%GfI47C$O0=oA9yu8=K>?Ib7+?lFcgF z%yEbwf7paCL>o1p&AKBn(`D1ppzx3>!T2gKRIQ12iI}2|Mlx&)%chOKK(7fAa2gej zv`yHQ)g?GO3bWJ6E;?XBbX1fO9;*tBQU!&FhlXm|oBH7B;Ls?S(upOT`m!mn6G-g7WDm*4y z6|O-7F<~K5ct8w{!AGa~CR0iFE!6pufZ;kjl9{R-RU-21_p(yUBV-R!z0zwGevA*v?b>W;$O`^8yT9B8w zB5RU>NwJ-5gF^$OqPnv`C<-1t)FA<^GRfH6FmuV&9UNfb>&c+S5-|2N7YqpXxN2X~FK=b%#n z`^9N=$nx@`ZpJ^`>6w#f8~)y4PWs==SsceDX;hh=MwKMl}0D5PNU1$8Bq0yFMO%2 zENuw=eApia@_ZFGY}DhA z2&@CH01trzz_c)pb_VQ$CV(~26Br7(0!^-_Q2}rT`TzvD3akTm14-A=PJTj70#U$G zgv-L~K^paZn1)3Y{AmfOfX;vq&>G-j@NaNoGu1Vjji11>;!`wesHG$2eqjS$33?F-IKP1;2I=0Dw(E2wl|eDu z(2$r2EH$a6PjV;5s3WJ4_sP10WL3V-*E9(0FN4$?awlJBOK6I23R$&Fr&3@&C?=FR zh3E#8nIXD1@+vc?k(>}+OPLGFsTSFiJn5&aZ-YA+Ta40T=|@_fiXto3x@M$Mty7yv z1V%fkKYyK0t$|y>eT3^Irc#tz`%CLYl5bt{)JtjS3HAYpana zChKme(}s7_=@uhpydHchShy5rWrbzzOZBfIn^ybHlXSWSaDAChKfTna)4eYY zNO}E*|KW2w^`+@_%x~%R4Dbr!E?frPCC{KQK?j4*10Ac(pz{?ObV$t%NmH}4BFTs zgWdq9*UO+fSD;M>ZCO8qj&{$W`Z=l$`U%W;b`UmzGmi|q2a#@ozt#~sZH1fzXN=Ux zK7)<~HW{g3ZIq-AY5}+ezBX_N{2Rcpu7Sq3%b=m28Pu$O233LfM0hqbGTALdPiwYD z!fqLK5Cl!TWY7`7yKWhDeb)?X4?fu2K$)xnJflSbqxHIH(E9+hs|IQdgd6E}#Gen` zHqxa&8MG5Hy;lZZ4Llu%+8M3Sp#4TO2DR`NoCIW09$?g;&!A&~WFs9TpcFu^k#31X zO#zjo4Rq9$4C*^IgB}LG2WWvHgwF%bre)Ax(=(_yXg;tVvb6~RqQ@WS_zZe^7Mz2Q zo{>TO&O|sagRTVcKPQ8Bgl-|I4sr!DTnxGCeDIJxNz0(^)BlQ>8x6AL4Ak!CKk7Gb z$)I%*XB^^oXMdLE8UiPCGHA|r1FsSdl<66Kwq;NYkBu}H=nTvNS^%nc8%fnW+Vx~y zKb^Zm>fEGYrq0f6f;L3eSrw`#u6kYHn#Q5fFyu<3Z$}kz9jyx?ozLs)ljWmzEir_3 zHmof#aM6vE6V+F`9(_zqPNi+4H#csg6Vf-)xQtD7$A(Q*adi`Ia%~fx0-6hS0Rj;w zz|Y{zJ^_^GBaHaZ(>bznGBmFJ7P=QmGE&A5C<7KQMX8*HHD%Q>7$e1Y}WU*6JCGvyclqy7Bb82;Rz1Ngl;@5X|K{EaIjaz6%@Wg0e zN?Ba(CWDVyP#JJ>&lc*kuUyd6Ise13*8abDl<*I7mhf-T_5EcFt^aNd-T&(r8u4}u z-S%b+{T5-r_gm;Ogx`UN{HotV*MS)hWi=T7377gd3p zfqDsB>1sfaa4qdt%0u=MVb3dD>7}b%=_}B_pesOKu5G1X1L+7?Qe@Isz;qy^GN5OF z03GnGQ6@d*_yu#p2bBpk-S@z~A(?djuuQsaDB=Jefe#1|9iB;hfM$a}otQ~CMP<_D z$V~bP=m}Jc&ZJ|2!I0;IR#}os_3QB`bZI7?VWbC*l*t%61FFnSdMhiF{s`&{It|n_ zJCiO3<|F(VwAJZMy5~$LeFEwSnh09wY$lxqq##`IB$Ez!`UTTU;PXkQp4Nge;&UeT zp$1I&4JAXkHo_ySWzjX(S-6*F(L&&qMHY3i$-@4ES+p+Xzg5qoTP(BabkIoXnuA{r zT}|);>~|vmd)R%`D~k^G&!QiCXVH*8S+oV{ZiH2R^;z^d1kd|s(ZvwX1$_(S&{#N~ zmPNU#S#&>eZ*ms(oSsF01sov1JtK>zOv$1%K;xnF22Y@K1V0Y;#}WS;?1n-wFQJA^jNiQ8f#HL?D!vc-7Np! zPxjx-Eu@51R@sKn8Qz8J;z&fOZk*kaK|}j>_a5RK9E!;-UeET32@7TuTXFIP-BpDd z)}*9~n>Zj!*Hxy^{Avw7QBFjR`FGp|*Bz>?cNmaO z&jRy-<$xb>eB>8A1^*+U8kJ4I{wkXu0J;O2W3#E(xNJHEJfn*PvZ*}+}uSUL;(IiNsw$Q#bhrmrA-g>Vq)K_Cb`HwzKBA;P&hIECRs7GWL|1K%3_ zHbm@(@F3VU1i1O&VH*iqGLu6e$r1)_4X3lAD}m33p!Yy8z(%ndHh>4v2ROASo6g&Z zLhsF{_4Qzef!PUKYd?6jByrbRO^s@?R10{AD!2AG2v);3Dwt73hEsz;2-U zHWIp%O+SEIfsTHZO=~{RriVd)1m*!&HaWjTUpDYhd_7}?Qp)UpFv>QAx&tY-bLfOR zIhg0>(6)fmA%}(njzC-BaJ?LAS3ied0(}4^JLS+ZjdQ%IW#b&$05}T)Uvt~?N1_3IdE6})mF3t4Kr44;@sU_$> zgy)3j(vx9-MAU>_`WEtZ$bVa!OMRE+(mkM!K`oc((xJd9@E)K$zRjfpM(hFo0k{m* zNXu=&{!lI@m%0ISfOs*N9s<09O#rEu6FH37=-HpT@F>UYA&`ePhu%N;_4pnt&x~ zRgfCL+7EM{s*Vh=B8^3=aC}P=8aPFINN&|iWe`TH@#GA1!#c5~Bi(sje=>KVu9X}Q z*5rsr=UxLp$5ls43%=^8fB-Cw2LzCUfjY-}hVMdDky>7e?ms!$M;pk8g-1negSBB{ z>PWHx+k8k9)D@Xv1{_{!Cc0{Mc``OrtzR7xx?{ySDmXlXEv*M3LYJ@b1;r2-T`Pq& zOp<`x_>SdVPGns;a3$H*##@sDtdf`7-Et(B=sI zg1}JFX1#XM`+f$r0sk5NSkOl?JLzI0eg-`QRGzdGPf|Ov!fikV=wM(05E!?UM$g$v z`^E31nSk5uo%FkTh%9#q)pl~-A$Z!?~ql1S$haH$z({*yCtY-W zC!J!XjCTfK3KlL!Sy}%w_NDr5kgd6~lQy`ylO6#51Y`r@2(SONldkx@lYR{>2Ic@) zKJC=AKYu`tS|aTaGzUC^y1-aqJm3v11I_}QfIMI|a1$s24gf1{MY`Nhq_cqqz;xhy zU>)!cPze1O=*tES{76rc<^$if7im{u3=j>38D-Cm)E2U>!0ZlR&;a~FqpZDA9t4^O z*mM->1yE1WXP~n{d+Ixh^fDL@Gz0WAsMbrQ^?-B09ALGPXJLk}z#AZ-vq<*=wSa{{ zsxi!D3?9Hupg|Xr#+AWukd=nJGG-jmr*9s8<8Qzq@NIzcz>Pt9bc+%9Ko0{S09W=^ zT93eBa$~2iH@O^)uPT$)>AI0?>vZ$2F{G*|t3&!>i0T7Airlitr=@K%kh#>xr=@@X zNRzmB#7ACXwRP)k#Bu9&2{PheS?6GCT)ksojkO@_D(hT`pw_un#_K}7EhHZ`c$9Zm z>uOilMzJwpw6CRm!4WHa-4#-+rS81=vZb!7%&?Gogx8HAXE*9j)oqGXzHZ_Sm$G<_ z@Oyv^7>^suIQ+5Oj^H9-AFvcS2E0&HDh-FtI_J?+_!xP|E_u`)m^>8K1I#f}#t$z8 z7A{4poP}4Fk(cUELKbyCkDk4dM=gPsz{T@Ez_sRl2ca_#E9$8Jk3qCpBBK$MHrn#*bz_*$CGI9>B&D;VW=>8hje~ zM&K&}Z{V*G^y$KUdg7aWYLS#rP2l@3!pFe-03MK^h0JLxN)e0*9E3JVWFBZEU0`Re%WCc!TZ*?GKCsZUIvvQ-l5j1cBcMssz;+gK>hhDad^TpeOjLaJ(LrLZ^a} z6*9IOB{%^2I@H)Bgu4Mc=+=Nv13ZwC$YXVz0_1`k)M7YFCEpr5vAy(ZsLw z>8gl-2OBGx-9uzrj)wIf^nb2fe;b zG5@4Bp z{3rbdV|W*+6MVHa`tzQL>)#~ouh@kV?EeDHcY@K^2Hd3n>~o_YLvAh*fSmQmP2?!} zj-WFTX2u6`xBGLXzY9CT?pwswB3>eBlrg>=eEpq$sZ3wW{9n-XNccLi7AU7beR&Pb zWp@g-;ENLPMFwtz_7D9l?%^i(1-SlS0u|$#^BkkIGq_DNKSAM`kRTI`KM&v60XDfQ z0iJ`u3z~+EWr03HChzJ0kek2Hz$rN0i^y*fDHoK2vIGi^^uHE>#nYEJ@_QsK$DH6O z=#~G2gpKL`KjzK@zNYj4<3{aGjoQ?Vz4vVGy@^dxv2SeU7&WT)x^~gpwMimKkR>W1 zvk|4GwQII0E#Lq9$vsC-ZmuL+?eFjPdgt@q&-0vf6B#!*TFgn3vWefhT6gkqwb;~$|9rCP_0M_NOgW8>;D3eYhA*KbpSTu(!%Ro( z{H~4QL=1aL^Ck2*R29z9)lt*CjA?3E5f&>057nAnc(YB<8zZ+ z@h(uaQ}Jn;npJd+QcnATdsdH=t6*{d&#wSg@V`Sp0(Y3^dQ>{-HA&gXW@@w1R~k)& z7Sen7V8`j#-qFe84Ap10VXb&0f%qgAn+46lRo}qd^qXrnvy)VNELZCuiw}aiY~a}^@tM!%PXbz^MQZr( zy!)wPYq%;(|Kvk)4c?EHodH{EJA7~K@8D;c#)s`P#183sRTCfXzY%nya69Cn>3g)0 zzQ9Mi8g=hfHM?EqW?}W+47Es_e{*4dUCsV92Uz?g?x~9J;{!>df$@35{eKrQo6rCC zVW(KouoKe9JRTCtJIXENL%gH-lh3&SjuZb*JcvV{?{7lWog6&E-vw&;IlhWkv&QH% z!&o#6S3t#nhUDDhlK8yzlW1S_<{0$hftOM*O3FqWt7f^Bo%#U_ zPK8XI=tq46N!fgC4fj9R`FnN{CzihP`G`+25=BcwksAINUr4H9oAvsXoVo$1SPnkY zRpRvB@B^^J;4Um;V^#E;H*G{SCz`X!J{t4U^dnkQZ*G;y5c7<;w6%Dbs1OV9;Z*fF zQI>yiVSQchzoQ+$Wa0a~8RN?(g&J)cVlPDX@C}CXMYmQqQ_J`A^aJXlG^X- zQ6I<-p{o8nT@&h6IW4h*q~u-ML~{K^-bq|opQ|N!x}+?wYBI6-&-%ixxEfQO$ z>Epxwj~`4priOpX{Z)Mh_#TkbVcfRGpzOuO_OPM8_#@a%JMMpfGzDLLb|O{-|Mi-h z!(G`5d}$~{@j9A%qOudBsgiZ`618jDY0 zVHfVRD_7y`?(wP4mHK;d|ILkR_@*JYSQ9p+a@$$>XHd4g9{Ur27)*f0;KUc8-=lrO zJnv2Ruhe&<)?4GvEci}ue4y)_8sBByhr?(c^b;NW4;MBL(c6X3|2A`fRbU+(*vx+t zVb}@LxaUz>NUc(Xgz6K{asSN)YGAXj@rfS9&wJkiYIZjsV`|oI9%s=SMysKG84W7f zlhaS)mPil&4=xGNYVYK-zG;)m*+9&@F_*dl8`#Tc((*-Zekja` z3OBH~ps(nwNo`|3JwK}vSOoQ1=qbTXd`l$-_64*)Um2^XQ@D|D4rTF!sky^|^2GC? zzoKKII{*7krsg!UO)S0u3bLRZv%Y1P`tr_&-bi#W_|kI?U(3#p*H1YrD=4Z=gA)yB zXx_`ft`rElSJ=g{TcQ@4=b&rRYHa9zYCF>7Oz#|G9?bcKSWE1t#O_dAn>F%M8_2AQ zl0|pwZ|_^d5x50Q?Bg0DlyC8dxUH7^Z*FR=7Y|2W zVGvY>&tVa)R?4NGtgEuW#XbusVGF2tH5GT$wT`YTR!+Cuqw0B~>Q6%_n|q<^Fc=Oa zU?hx!(e4+jj>)=IdvNQZWQ9MVs*bYX1>;O}reL@K^`Kn48wX8R#ShvSy)%D$+{I<` zT`kUT5?!dLK43E1*KU~I$>Pkqpx4IRK)tsZq85m|0qfd;G1H2UDRf8W3bU|_Klb^ROuOh(;+p#>97ag1%xThWEjU6jb#E;q6 zyvhIUcx-P5FPmLm*X^d9(XScC|46lXr+*h4InM*pAmMr3|AY$;NrPj&OR8Js;PM&0 zp?0^>h$pI!?!Tj^<}9Falv6R|v~Qz#Jb3?CZ|sL2=K*NBc}PYiLfS;~iS?7$;w)ke zR66el8%rX69MmPPU)_J2Rq>*`hot7cr19WZ{JOX4SGB*eW>JlkhF}~56;Nu}WymQz zrLIQqO{(e?`E*T6pJptk7FU#hbx7V%A{iajy-h#0nnWf$EPtd<8tx8i-=<&H?uYyE zHM~lu*#fU(9Gm@2x6kWpH zHLj$LdnHuN!GM^UarF!}*V)GmUujS(?!Pk3i`f#fizSp(3mNrF4ck%tvqFcZy*(O% zR@q|{X4@!s6iZ6Zs7XjQM@jR7X4}3tp`5B!`9x~8_?JY#M2;Gbfrh5cc39dLJS?eE zh1oV@39A;{oN(b`>1tZYzE&+p$uDWDEo!nI&8=k0Yt?be>VX2LL>=sOFs`PJ=d}*Yzkv2{41J54-2b;ZmAV?L z-h5Tew#wzzuaprttFe}cFW zt$alKprQPJyAiLV)v!6g6+9v{YQ-k$21h*-eegl2aBE4w!vm$GZM+!?!Qr#n7(NN^G1_mt95L3 z-2XgZ+Q_l^h$MunGw02H_O?-H^cZTyU#sR|(R{=dj+z1&#NEW}K@R35C8s5&NY&g} z_-+^0*X3!tZ@F`IWwOjaB1e}Uk((P5=IElh`J?n6U22ORU4p%w`>%%V=XA!fqbv1- z6|pfRmzW-1b%bB}=LKOp@{@$yjh1TW{~9ZM%W-<*51&HaCKGHy;S&=wkAJ|eHK**Nx7tZl^R{1`uG zW#diMsHl@{>Kgc$G+zHvc%bx{Rxud z+t-*6sd5IIeW8&s;*NYr!^|{xygYH-2Vd^ zY?O-$#QH}3U8$O13UX$3lgl}DG>nNE-NXl82aTkzzh!xIZmoHkU7!6tM{8*IMeoAD z$6*vnn5D@J(+TcpvNbi%9Je_mzS{kbqHf~lfU%~S**0n&#cVn5|C^JJ7fXC-+?ff@ zAFG*mZVWc7YEEc?xz^rx)Mv~yRI_hNs{iyuHwmk4qk3I8afHeTR*RccF;#oXrkcY> z`(Z|t5npUK)QBrJ=ghXbCbo|IZ%z!X_Y@2J8olwrmCsw1+)M8F*UcgbKikS&)E!S8CX5bH;q7VNh$In{3&~{SU+VpP~IP7iVqFIQbk08(HBX zX4Fq4=LG{{p>esrCw_ndCGMO~on zUN;%3GtgukG3UM9f5WlYQ4SjHYaIEO1p0cLa)XtvnF8ivL*EcLY9^8&mq?9KYpJbKa8bPW4=DtF2L&d_+x@%Lf}es8i9S|m*pHgGN} zvor?1?FItrN$Lg)WObKXS!{frKGrs3nqmB1ZIw3uP)9NQ`Tn-eXs>r?U+~}f-2bF5 z^rlmNVRu=Nr%QNdl=+GkU~AYyVGcWkwzoEMdT|D3fc*AL^7HIXVW4Tu9gEP;h z2$Y7a#Bab|xCalR67grKb$S|0DeJ>JmiC%wd3SNGz^m-TUO3s>U5frg{%V!98Jk+W zHxOqQ3xcd|xBzXF*4UofL-e$1ZGBbG(pJmd&RrCv+S}BymH$sM&+6@Xp7xIyNobRk zJ0x}p9`Okcc$c|t`c;lIb1;|1QNmG7HD1?Gr1BN9*X_5;oFi7be$Xnz4_W0Wd>1#X zbaJ=K`NLNE9`!nCb+6FSvN47AryQdzxc?P6#y9z@ZkCMJseLDvaewx)rBT|HpZh zkcLEQsJMFU1U7Em6-Bx}Fm=50XC0L>CMt5ny zF*ASl0%IHu2HPhP4ZB_Fs&7cbm;m_H-4jQZeNd2Hs3iq@AvB2d+ z_ZKdf;N)7>Z5~p7yN49o`ahwKgz1a7-QgkCPkKm$(;o6Ydd}NJcA>8LYa#0?51Dn& zLy87*9ndSVA8O&ZhbP$9_J<~Qa=$r(kN;Y3{`Nxze*>Z}+6zwJjZ6ApZT=Ya^g|Ds z3)?~2O0Bv+LjQzRk38fiy7!)kWVr7kU!%%a+DiAUqIcocJrBN+!AU^rAu$1nZsbVnP*0{9_ATuNlL`3un**?1uU7z{Ji>*p$?s~*^o zdV()(gUOkB9TgY_%2t{si%nl*yJIgzXQR1t@`?rT^8yc`?3?H;I1J-+GY7VSAMjU5 z2Mrv{gUq^lw54UvWNCWxy1h^f20~%5-W_e>Pd2A&2R0R$u+OGb=fmH%m1t2Lzd0FJ^{GGET{@8cCrk#gm$}lwS{ka zsR3Q25Aos&ZU=b{DJTJR4)GcjPy$>I z;}gq;K2^ooZp6K@Z(y(Wp)P=7{=CEhjQ1msKmH8$_;=ArVv%T5bQ?6qzTwLYE|5Qr z&QUJ~y-gJcGMwgI*yFKNpXK!^C`t|f%BHwFIv8@1?@Db`G#5h$lRM95QltLpDr$D# z<0T{R^Wq92#Op0aDNrwrKRDH~zfZcn+f&r^!- z_ml<1`=B2aZ-})Au$K)?v2cQdTACrURx}l$-bKn~B_Z!BiH!(#t z6l{D<&VF=EoM0fVZG4O$C}KE&jDuc5Ur;n3by3COgk1%7CO@(V`CiAQHmrq~J&%cJ zZ?q4!_}=I)V!P11=w!%??di-s^7Bw<>L;tZzO2(*J0^G30Gwz@Pf;{YYx`kSoF1(W zcW7{;E-y8Ah}VX%*-SL71s7_r&*k*1IVOFd9+TWq45IMw{CP}r{&h?S zgA*Klbxdx)8E}vm5^^jvy341$wmt0}JW*L6f6vs97CJs~a6)zz3d&lK$ zs^j7W17R)x3)DF^HhKkpL2N$iqH4hjyEf`fes96!lDFt_nOx+!c*Fd{$0Zuy<-_B0 z2mi%~8V13$1e{S%sEz%i&~e#C!5MTi4Qo|HF=E*1vFBswEq+{DQj-^=l}&9+bTT-R zZ%M5)>cq^+oPN;+fIgqmE0S(OgNm3wy?p11|`JF%z+2CRkyXc3kRCKQ7umj90K1zO}O3 z)yL&0G#PqsEjwO^k3NLs>)G*!<8lH02}W)|E~R#GBJ>`tgYx^RgW-Dt?R4jG>rR+4O~4gKcWv{6MT64xb%f&koDGa3A}S$ zvSBMN4x@>^k2;}!LDjqv&hFO8DV8hRv+-WCZGxBdgkg{$c1`t?X4AanG5QQ`G2KhX zYc{=s?}=Rne8R@VFK*^+>00Xo8u)TVK0Hl zXl8UHI!zTree7oFNa88hq8q#?YP%|LnZqGsr6 zFR}QrNyq>j@Pk4VL8Blfv7&dJPZk!tDQ$W>Z>gHyTYR&6%Z+T_{H1PhIhfO1a_07y zOvF3o@s=0F2I3#hdW@BA>TRf8Sdsk{|zpw{*+T zye!^Q0Gbu>mMYXvBeoSLGdE2R??Td!!kY|pCRrf8w|or3rb^Q=C$F`wW@&8Q#9u+R zR!qT{>4x>Gwc&J2Ywh+jZ`rtuq{qeV{V(h`# zqp(+Dw>&`IVQ)U$=JG&^L&T4uZq(ziL+22igFZdz&8O}t`Bm6W$$x@Y$M34@+?jnu zV=*-V#|>`K;0EJ8uvzQ|v0T{xG^D2PDeBLVT%eJ6gYnRmnruIEfSah$ zWxCF2)L=FCCYY!3=JO0EWeGY_rzYx%OvIDI;5sLH!p=!~iDpN4qRr9aPz;v9En?G9 zZ3;#hIODwE^rSSae^R{g&#XKtTXn3cfhYDU_!gG0Iw^bMBPhqv8)uwyBka(BEJOHAlf0`%I1a z7l9OYPfI~`Fa9D`3~jNyq0@+4TAr3yZB9#l*a&S~)7KVlcUpq+ebBwc_M!#RX;2W` ztJP^4Nqzy^hx%!%u03nC)mTIgz_|jMDY`<_#tzsNXGZJ8eHzMBSCE?f#OuR$Hgg3w z!bob;O*k#tC!XfD22aaws0XdSI4vg@#3jX-r)34i#5Gegz+~Gu{ae2FZw)r)Msd>W z^JA|-N|Q}Zt9X~kXf)KggsO3GR-ZTGk=5FAfn}Rk>0=+s+tf!qoA}5rC;$uaLm)Yn zZ{{NdiRDI9qb0zNTgY6mRY$i>bXy>?QPsW=(xG zzEJRy2PAetUziBZAsY?fko%~!jryp;t8PA0w7ZX_gaS|!nv+-5dIMF7$A>EvH)wse z&~i=NFwI9w!)mAq_on*D2k1N~3}@&2NTx4+3(prWm<6<(!nT0vJ8dv3Cg zI(uEK_uIz}e_k11=}yfveXRkudOm=e8dZVWS4CCJ+NgjU)W>cEA3<~I2(@$yqmF3H zA>yOO4@NWKg-(FURyuQgc*W1r0r^PH}4C-9dS0>l7VYZFf zKh*`bW9QpuasOAv3)&a?%4}+q)$o-o=s)EghkQLlW8jaqY=P4xlIgZ8h{S(V*H@a< z`{&%Un{!E@sfF1M{~3QpeP5|$hqXccn0?L2B-GgTXz>=ZEBIIQ&l+(5-^GII8pfS7 zL%$MxP#@D($wytEMtrbpW}}a0a*W_V)r<~is@Mt^Kh-oYntdQCet)|`4H>Tb@}SLh zTls~sEvC9ntlJsBvxP68Pues!izqu`xc>;|pS zeGj|%YW$L`>Rakr7Z-O;Wi7wf^0Bq!Zk`vmZmp$&HftW&=?h7ydVdPqyYE(=S#SCo^Z(iwx&zOc9gH&GG>GS~} z_=mt>*Av8*U+J+;mHiY(5?`q2p6U86+Tu4JFVres@R<9r3^Tp(e}Ig?+v0U~RW(1n z=O+bI`^o84e)167!9e^SkOuC-RAP~6O>{6U#V&$gg!E7i-y8C!@srl6o&97o&U-kO zVK)UUpau4CEcU*WpZr*k!DalUXIVcPg5EFhCs``^Nhjis&~J&|LkkeEL;Xkif045; zIr?dC_YXH(W+c~!wDpr*?fm3B^b#z9DP8^KAY_5bUD#xIKPil@bXGS%i6CY{7o)zQ zYFgpn>JgtZoI|k%9{2QgV#8-_VrkVKi3(e`y5Eper0K=r8RG`O6Db&~8QiWm;jI-p4g$OX=&@QI~HZxr{Jrr6tv@57G7 z&RZS77P*?_(5E&0WpHg2#^VpG!$Rb`qWo)fyVZ36xNUb^WC}_9}Ra zKL_89hB<0@J!}?pBX$E;k&k46Ke~!xH^}vbInWexQD45Vzuf8PFD+p)c;e^n?=O?V z6F(Zwt6}WIn6HYVCAKr#WP-npg(c8zqQCfk<}blh{iP2Kf#>)Ym-vg*B7ga7vA=u< z$H9NKzgX7z%S$vb`WUUYi4DM~Fd3Rdy3MQwd2;f`2~vIQFP--I%ZKRaV4bnVlHc0v zYfERXOt8QF1S?@HbcK`Q95}*XGQpqlF+!wm?Vn%V?a5c4XKVav7j8q<~OrR<{X|1?>amhqeI{(k?)@;h(`D)FFUxc?QVy zz5!Cce}MS*3*dQm0WuC+HzV+9LV!d~3y=X*17smucoM~ElNkZB7yWE}fP6ADKvH}b zAcKijnHV6;CI?8v=>dEXG(cw2XWf+ArMz{^R?BkjE2=gHNIob7Kj2^48X)Pm1&A{g zguL4W#2p5}kR1VXbZ3Cv(6uY-gujj46YLJCD}-RD{Wd-wh$(`h@}2RU zi^!EnuMq#sJ3y0Q3hxsv=ocWbu=}V1G_=K^Mp0We+#lN;i0y?wosTOXY>^;Tz^Kw!FSym`38Nlk$GR8k?~L)zS~6qo6%yD!JE&>Q7{SvOtumGryAj4 z=E=>4&&U$@Gm_KejC_m!1;1ifJ$gnyLEk^>zH66dS#r&pz-)+wwa`55j7*O_BVXvM z?0*q0-tf`-!ZXokFV4utWPvf5Z6o$ib=V6nb^&A9FczJfB2e0ckvEqG7IF+kc z=CeAr%hoMlze0nO$&#(j6DWR=>w`dvMtkNDl;2@RzCif~9a#j0&fp6}iUrDPSPGk< ze91s@1*LJ(YzfE7*hZC4jJSj2F7(D;EeV6)*qlhOaq5KnwSZWs@{;Fm^$vY;`g&=@j*6ev@nI`sU=J@|mdQc~N)%o3G(K(dyiH|qw$W$aAk zT+k7$t#Tvkv#*bBTxW5J2&{f!~U!b)p3;~qml7z3O82Fe-m0$<45FJZLA3jS$+AsblwNuZ4GA1KKN z21+>GbqN$FsO-Z1A2T#ie!~eF7AUpROXwtGk*M?FxQzdTN4Fh4bl+Zs7V{3Xln3h) z-hp7`Cf-?KD$IwWBgYQ1jI=)6@M%%^UO!mAO`*-Y6DT(!5Td{nK7W7@A3qC}VsNU+ zS*i75!sI6ZaUwMiX7G!{Z_)Hi(X&#D&Acp}Fwx}GCZXK^S;JH0*>AyPcD!kRNhF1E>Tw!3nbWJSQi* zpOdW4#Ieu!I41+pF#Pt!hoB?I4Ievn^zcE22aR^maMRKwZON%=f~0u5AgP-+NcKV~ z{>aQh(mzX(JVXni-((Il{c_jY2bR5Boq|E)4-*Ro$x8G@0UJk6_DN#3$yWqrH`7(w z21kkg4222@$(OJm2EaA=j<_O!iCFewXD%63OT8cIN0PVySPPP-GlHb+^dQ*;6Yyu@JIxG|%lHe>yy%RDHr`)k(|P!c zT{=rqh4s_}OI7PvX1Tt)z|}WMM*9UxVc3st#7AJ~ zJrg871A^phi1rVX`uKAo1ixY+mk&LUKaX4$Yzy{K?D5!LP$&G+yMm)dTVcxq&&ce z@GoLF!Y&FUq3S8FYgHCij5!u0osS1eG57}Ch!4ho?iD1J;0ri%f-B@5B*VZRU(meh zPIMHxl-QlIJ7f34u7{?>|MX}qTMu!i4hKnhjWgy~_-k{s}yhld` zNyaGd{{kE%af<=1@WZ2nWG)=N7$n&*1xaq~cJLhkcXT*f1)7j^!!M3K1A8Fsfx^GU z()S9Fogdj0EWtK%ow4s<)qVy9^xoX%$ut#Gr!tROI#phR|^ncd;Jtvfi zxgOSYFD(qkm#3M|OKw!z=b#TP2G=6zr82B9 z()_#}$EZ+^qN?Y`f;NGVs-KsTn&;&bb{6ag4bIE=Fb38^cSzgtyqt%ju)d+YTWZ%D z$+dP9Mh^NUS+WNs&Px?o4k?G9m$k!ebVS4OM~yr$rAEin9s3ukIp)0dH^tYDXK!QA z%U6(Z+o_C$|NJui=7D6}$>!EipGcHS!WEcM+Fq;+*oZfzelzG14)>7I$VbwqmC zeA>zR&!n}8^njC#&&MJC5}Q8;9klYiI6+HLw$j?Vu0S`!4ww&pmYtVl&0aguJV=hcDm+B;zyPp>hiG!_hX0@{V7DMLLdCbSJ@P7h2=ko$ont=#z5-pAu{h5df?hGS_rRxVEf01 ze@S;gM0&t*Nc9^hcn~6<5DZVqA9)xeFCg=y5V?+@4_sghEP4_mAAmEsK&e0Ie;OjY z;27k=-|{R(?!X_=`gw@l)Cd;BaDp)m$$N)pmB?Ld6q;;Z@pD z`2x;DH0;O_D!0=yAbqIRfz|j$!38G5cNs%v5Nw2lym^P*0WKXa%iJ2TM;FW^*O7SBmRu23lpC-FbX%}|&I`|*e736)*o1)K2qeLyc{ z#;#u=R08vdN_sRe+$zM03(^l$APRo~>;ezSP(%xrt%XD73fzV=3~|ZE6@g&Lhaa7t z-R20Ddax*8sAL6aaDiS$=_^7XS{n*iWZ^R8;4>(MKeR0KAQ-aXA1%j*A#-`&yR{0u z!Dxy0fsK{fF}T2FXkLw5QiZDmm!Ky8>8hL%%>%=#he|4_4=rFmGn`9sLhypg_-9M9 zsZya*4(_vo(clSZK|5Z9M)(re!p;Wlt~ML06Dqx7JARkCJQlzctoSSIaVg;r%*DT5 zKU7MgHDE`>P^ku!VIDO9C{z|SVllV`HSq%)a{x5YN8JBDABReAC<^7_Ve3$7+MLb6 z9=MOcvIX}N{)8*|5iMzM#bt+J{L=6#OoScnLuE-DZX4W%)%ZWO{`yb3B5)U)<3|nUilEs`@n-L^P^k!$ zVg4}pK6zb7CfDX|XPYKMb-UIMChZB8ufQ9Wt#qHR>Gp<74k!RZ%zsy?e7HMQj-kp{ zI#Bmpqba{-_rxw@H`@0aEyfAf1J(A2O2}awnT~{tLivVm)>q^@y4mPtr$$_UO>-*;Zg!xz?#Y7^5?8@ zsW2m4zJcx2nE83Qygxgf=j6c`;qsK&0{joAgiD^!!lgA_-AEh?e-$p(U;(jn_!+(m z=LJ*egv$>2m_!9w4YQyJ+#)^_T{54|Ee)61un-!r3zvaV5qep}r5Ox{oL`2^@5^I# z3bD#iiA|-Uwj-3q&kfb#`h1NmM#FU$P}+)M3b1m@^{^R6!fx0PV_}(BxCHFrs_hP! zbvs#jUpTLQ9xms+M`d?S+mx6&E|fgp6k3+ z`8*$oOXQ<)xedzx5=uh%$Kf&G2i6WJix4xfbbnCt2&tYrLjFt@A^A{cE6t$mAaoQ= zf}T(`WrS#u8=H zwLlZsxmt1O2vH2PQzLH98MdnZyV%Xp{`R1xZoqsHyTw_Xn~>B&f49zl19^J#@Z<_! zg!>wAat`6nCc+%@!%Q_(O?G0v3FV8KlZzb;9ZM|c`={0i&0H&K?~jZSGktX54{hNZ zA(^2(C|hYkT_>V5VF8SU#-k(TE2sg=R{FiJeyBDkLb{-B(E8&e#CcoAV!Y*Zt!&fZ#G7v@&-sY&?R@-p6n@Kk2lM%fKA9xA*BU8|M<) z7Z|?MCM`m(*+RzmcVB&Be80?}l<5>I&G^pVO*A(;7oC9yLIudicl+FlJ;xq}egy}x zyP*ONpacF@XhiK$bPbfo?g8h?tIq{NKp7`t6@m)u#y57cqlWoN8HG>M=q%h6;(3mGdx$_Fw zvodRQmWIj=_y#=S5Ud3iGuVlJ0#56(NK?+JNk~JeUkQ8RD_8;naBeBzUQ4L)-^%^O z;%fG#r93SDtwj==`z4Ea9yDU~aBG+16LMIy7N2lfTeOvLK5qMWG1j=rTFNeTI8;j7 z&Sc#k|Ai}KAFIr};zopEpy+Scn6G62 z)O-uig!22R<{ONu8x7wJ@i++i3mR*w zE763%6yd(V!UT`B+HYm}c}Cf|IEvZVydz(WxA;369B;1ULv;=l`-Hu3zajg);m4}G zXRQg_oN{cg$Q`a6CjBb$<0N=mG0gPqAO}C0kX>8eHcZaIFW?Ea+u4Z3ehg>$VPVT5 z3Db^zdA&x}uO7kAjuW6axpDB>h%hata28IlQTzxEejOPm1<)+IuVRYiu$i1Is>lgt z;X_CRvtYvLFzE!<#>8b1F;`+v7Jg<0`QQT7aOHilhU=R-O>PqGhHPWQWC=Qs#_GEI z@o2!V@m6>h^Lcn--=vjNBkA3zZ(4V9o4G=dh;0eV3H)7m5cs0ABH@+?!yKk8+-sopfps1TF?kuKnLgn{WabUg)uM*X22J) z6xP6I*a-*VD4c>o2!V@m6>h^Lcn-;Z*#KmN51o2!V@m6>h^Lcn-k0Sl1z{j3P5ov2i2gSClALa7;T^n^oBt&0>;5qm<r^l1)w;TgKALknD$7TV6=fQ&>IH92p9)bVKyv;m9PP}!X7vRC%_Ma zAObGKb+`vl;3cFw&ITYS6oBGT4yr*tjWr^a2c+{J$M2yA=L>s06C!m6o+z94N}xQ;eMsFdN>a9U{x3gQ(-nNgq5%Xw!$7b z0w=%^f*=Ae!*#d^Pv9k_;^t?9oKOIYLpi7h^`QUQv3$RAY~it^3JhA3 zBL;`dOIQRxFn6%^ez%GDv%ho+mkz^t&H&Vf?8B`0yHCv3e&T4JJOZEVs_bqily6|f zmA}puH}c;Ri+Mq(vZYE_;0Y)_;I9cbrcC4`IDEMjUIppi+BzUPf01iBwWtG zs>R`wWf@O3f$ATUNXFAja+3%q@CnK7P#C*4x`TLG^aORE6Dv&Z<)zl@JtyAKQe)3r z>Mh1N4Hz5CThJDEPv2p04sM+{La2={<;Lk9(Q^wM}aO)6-jdV%Gn51OFfI|9^Q!;@tm! zb`QGGc_7x(W0Q5Ai~5-!w14ovZNK5Bc5{}R>Q(J-C`-++i@E=$`2eICw2J4&fdw4`$w5ScuFdVA=z<2L+ zZ{GdT_%r>vI=}1-mxuOf_JM!V@4zP12A1opY%Si7OdD8lD(r2tRnI6pKcP7jsl5Pu zVJnmaRX=z?@0<3GwMZ6$88BZjJgKX@t~t>rx*dq-04oCx8Xe#h24ad=Fd9FCykgct zo`#BVjfrvpRr7tA%X2bIq>GUB8F+pdY{dQ*#$%UDA0eGlWw(S~kdE9@*f=3VR!xkM zt7t~F&7}w__5)8gy%@pYO#`Lyig+q~cXB^PM@Tq+ZjCofaV8Vo1j{L?4r7RIB6k#f z26h_k3+R3HBx<=5A%W!7B0JF+#P*Y$ca>+VUgPP0u>V%#XbBayXGpO-5poVZ!3SKQ z^R(B836tyrPYMN9XSN;m@&2F4F#EhQxT?N@IqxW@^6T~5pW^lKj7;+YdwXfThJ@?w z`xEuGL{2~2!3NYR+q~pCxUYCJt?+n34?@#qkx~U;=_0fwniiD*EA_KL+4rd(3qknW z9tv;kO{4VU_UK}g%PHEX$M)iP!Jm>gQU;}qlxolhvVlKDK@a#1O2ZvU1qI+dJg2V_ zdJjE>UWLEXMQT!DXr#<}hva3}dkM_jW6GTvs>NNr3dy6xBjwh}NSQDqQud;I(e7xm zQIYZy?0_$(M#>mnyOUo`J|*PFR=G~(`oL{CtJj@lZc%CqkB*eP*g>jRqqsUoDvGL% ziIffFB4rjVfz6vEWy&`;u5BR>lfZ~6t*QH+&^xAFs;_NzU+daVS97njHTytP{atMC z!KLpd{}10wC)8j#b_C2LUK7gdd9NW+^5Xj_spt|V!x}`%iS|)as7{o|v%aEabE7D! zSvg8(4~>#;P-bwHY^xb1{sW_A|DY(jiN6Hy5lh=CO0Lz5l8LpWWO>E7)M0SyW>NB? ziABsC{z;U$Rg01%pGL{TN;X!bH9yj#OQqoVkmqEQmfunW`$4~UYL6Qd*p_4#2s zIpw!!L*Y%M~%+TGP~#T18bhS&=IeUNuUZ zvk|4kT181SIK(2exVLKI9h|-s_I4KQ(>+SQ>&h+0-s~JD?Ktq7-rWDZG+ba{2X_2% z58HkEv~`pWCRU(ll#FM%Vl^iy*&#|6aR2kue6LrO9HsswHB<5b);Tms+bH>s zE7rYBl$2-}B_mmI6B{_7Hp|sjc(d7YRW$V`O1d(10m-E-kekITbdHk6G>%}D9C$;kI2?}pzAign~c2I@h4hm-#dKR^T=g#f5FGD^zoG)BK25hd5PG7K2b z?uSK5Ml=AnFO1@~%c5jHq`4j?Yhfz%hPhDYPL$-kOAd;_b^Jq6MQ1OnxB#klW9f_1 zwfsd%3#H&zxr@92=tX{2>7r?20gV3^#qGK3~b}_L4J0rM3 z_3qKUlu|Umnr~BOE85ukEAXBBM~ihp3|frQf8gtYk@$~Pvn{N4w2VzCow_kvJfR!x zpC2u&(b|(@xs0u-u_{_7&5o7<)2L_8$kox3g81l}(emv&oBh2tTJB7>*(s+)%fb!O zyw-NKWcw^yzM(dYhI4LxwA`N@EuWL@345_!=S0g`a%mV||I27uPD54(bS1V8{e(@` zqxLGjMb<=1JL*1QvuXn;=vUf(vkiYhMv?x`*MHrfn+VpE|Gt{hIGopD#Dh{_Y zT0Uh)+9lC)kPS~@L+XmUOpVrJjExjkVrMHUoW+3Y3{t3*^qFg0FbfU8Ffap~D$inF z@MA6^x{Zx&;8v)bk=&NjZ1f~2F9KoHqxo$=Tm28LF^^oab32!np(zG>ug>&<@=JILi!3;2H#E&18-x3i+f2VeF61yeZPWri!;sCzPzd%ZbY zl>WLVS|Zr!6=qfEP%$}8PpYDySg;-&xWeGn*nu=mWzor8m4lqB!E#&EY!4bp~8#v7gvT0OV)zn;6VSi!tXhn_bK)>%*69a0LemB9{!m3=1ndVDFp5 z{a2SzJ?JV^WZ1sk5)UrbJTB=79&qJ(WY=ONy@;1)ka0=XN!6uJ%OV?CRN3m}Uvi~t zvB(wY}7Zy7GY%3W!2C{ zv@}?hN)zQ1DihiJW%gFHYpX%DX{(7YOJSs_6r;9mwbf^9wnm~+ael9-o%tI3-IUw+ z_s6@}bIx;~bIx;_MVl7;BD%f@dosb}uqU%neP%B!*(i&mSVm%HuY^O` zc_Umz%|(2XW}@SjPm4?(i)xpunTR5%5NU7=a}n*>yGg`QA>aP8NrpG7khLu;WE=eM z*G)3DL4}kdQ{!6y*q91_GRr1@B$jNF+YWA$Ykt@yO&eB77ZyC(yh3IY_`?28ax=-g zpHv|m7^{S55c@r`7gJ~^{DHY?sPd;xGKfMCvwjX7PVABco1{^b3hBpq^Y@$Ne2Qkm zZs_Z26*6_2Rw3DWe_d0GkGIug5pBOe6S-G9dBOze0B_U1P#q&ai9!Us^IMiI4G1>~-%kaZtd zND+D;y^O|wQXv=Zu8@HK??_XL&kyUis3JyHw7Np>3hM#p>eJtBO#9b%c4Ccj|0fps zFU4t5NB;lO$kF>(g`@9Y+1GOa*6%B%I{2?j`>z7_aM8xcDc*3>X>jF`Qdoiw0sxj>kyZeg`{E3{} zA0JPh2KpUiC(0j@+No+_F+A?^$7p|WH+72tiCvxervAzMS36c(9X^4&4Xe2+4Qq8B zX)V%TcL6)^<-S1RO~-2?VnEgC-ZUt|L~6fTt4k@9BJeZkGFwUb!tSeS*_F5 zFOfuww_LqhmUR2~w6E)CQEFXVLpxf`tcqT>S=xkkIg|D2pKpx&|L2qbiJaQMc+@Ad zG3`Wx{^^+T-K&$Yz5n~U|IC4#MIQclRS{KV%K%lA`@eQjt(2oasixKr{(Frzd+-?R zwSRfX$2(E&cme;!eDu|h{ryR5f9lR}`~BiSwN~A|m-Rz8ZI;JT8ggvn{zrt3$(uLJ z$Ny_-|LcNv>S=#<#|y)W8rMl!TE|{l$F5UPJE0^0yH~R`+{BykjNdiH{nzQ}Kej5W z7DN|UcHJGTc_rmvJ#>qVP23`}=-Vf^$fXZ&k$GXMv5EN~nfn|awKW`SuooVjwMBfM zEpo}sEi!Y)7V%&!9z?@G+9KC2-y+N5FX*(!m2zgo%A;%#PgEmV_g#}p{vP;RYD;jO zs4ipCs)3c#a!@UE;qj#6`y02)%}8-=v#s(PT6*SDX-5ZlVZ7LCt6Tvye%va%(Oc*f zbpEJqlJwlaCGEr#j~}^>AKJc6UU_DlJUwEYoQoZ4>Vna~jR!HW{Lj*Kd*aV{cAKSCKs8k2IicPq>hq^~zQ}%!cpF`i)w| zP!q?Kiv5SB{bLE2Os;J%e$sDE&T#%VR*m<4rPgjfCE^bsNA#}dg%j*~xi)AXi#H=v8)@miPEI7)cAO7a<$_mjU$v~=c5q>M(JRMEUf)|5Hhi zvE%x3X!SXd=Tk5Icrhn9U;q98(c;L*>(4f!;{CjM)b`q44>A94H>{DvZR^F$Y| zJ2y+C8@1>UgTfvE*W!P`ul?1e_3c#Us_U<7*Quu+bAejemph7_%)5-oZ{H@@?)WFg z{hw&U236bm`FPu896Hf8DlU)sUf*w%KKpAGJ?^80osr4E%69eSsm~XkU(IW0{P0_6 z(Ubq9vG$*1Injd0+SQ;sC(z|M8?zjp=l&ywM}`yF;S1>8aDt2BduT1{jy%kT!o26e zHu(u#={0er_*8;UsZ}N8L(D5Zz_>sD_t8tOAR2)lKfP9qppqG1LRe}%=8RfR zKWDpKaqf10#?*HH{|}4-qqob4=q2<9>ixlXiT7=nR~Of^+0t6R5T=@o=!YoZ-tfA} zoUnep{12c1rt3e8jJ55uq|Rh?;4%W+tlBO<)Go|jBvN)n&qs}436Fmw=B_Be zicLaQtGCN6=#S*k!a>UM!N%>f9DVieZ&YI?HMisRDycrRO1^JZB?I8~@V?en@*(;d zyZE&#`5GyXpIRjsbl4#~FW(`vQ92rjw)WZ~g{TA-_t_zu`sG+^-n({ge>ga@NX0h} z&qc327oP#u^xYw6-nT>6qsa+>DDFQT)VE)4;LlMDt~^G9Lj2t+GT{Cl(i^#sBNeM+ zBC!#=?$>G))tBH!7F;}jhun*vd21Xcn9@VSO zw4IWNq12$+PHBx4Zwd1$_~A7><(K2=ZRV9Cana+5uU=|Vi|P%CO1#YSTY>1wL9e_94JIBS&(vCl!nnYUptfg_n$=EACufTAriJ&#=7B@AHk;nCMKAw2O3UaA7U&VO#l> zvci5fo*N!dB&JK0U(G#@-3V5~yXxfFgq_u2mpjqAFx6O7zkC|yPf ztnZaf!x_3vNY^DjtybNWbxqpf{2J%$s6XmLpbzGy>hcMCD$HBLYy-Qc=rR~J53`ar z%3cgt73s1N%|MxGP_Ztzqt2)e8dIXn5OgQHx`g{Lo1se~nv6!GUNd#M9<@g%~cU{Slgta?q3S>-_aox?F);qw-2!UfNo#axKb; zIKPPUsj*|5F2lo$9!Gq`qUP1OT{VrMN6{qI9UVYTcI#3C?}5Fc30}1HkS^yQ(d7_q zcUTu4_Q9Tn60ko!vT){H$LY%hPX^^p zZ%_u4`&qPsaX$I_F+XQiP?H8Eh(XuA9h9!m2Bim!77%a`OhB!eJM~vNcJIxHr*%_P+opvKp19qO68kD3p6e|eI$;@4c|7>&_r!ja|P_CaA zl(tV$5S@kI%A;u-KZE?Y(OAF7gK{TL-kQb6$U9z}9+dVZ*humlY2ZB;JV4L})!~RB z-z*HuG&CS7C>OKSW+YleY&l9RB{93{M4_!*(?ptk40|U20C`tZs6Fv#v3A!NLCL1h z3~HQ=e?KSmQBhE|w0DB?InGHWY(UW~-VVxZ1oR^KO76QgO`Jlf!`Y!7-N^ju?C4yY zd7ijQs55bA(72g7BfOM`8c}#pa!^ufY%!;~lC`Ie3(7Dyb2km?}+vRs^Oz$IbSSw6koQ5?z>^n zfW=F>TgHY44I9NYsr^gwcFr?&>=W4%A);gjm|Nb~qSQW1;)yxKsF6vH18aa=-)&vcNcfk9Q@iQ$Oa?Yui9_;uTVtT`gNckd)%whAtO-D`q6XQs7RlKfLGpfdK ze(qlxk;40WRLdi`Rf~>Jzr9)(!ZvUi>WZ953v(lLS1@jgZbvtSc|HDH8J~h4M-PSB z;f`uK=dNnGyJxjbj<1$^aDv7!EirCkq6+oDvs!+zR?87M8C}NsSn6^2Z*%1&kHkIA z_$}7`K;DQin&tTCQtJj{wBh`++*U1%23O0+{i|i`{nawPU$u;cTOO#Er|_N4cpn^& zf5?DpY05Yq77yfZ(H!D}_pvsiT1wzq_m`($^ z{~KAPlk9XFIGY8r6cOw>1U6vqIXIr+ukgn)eiPrjBp*YPW~}*p;{N|^f~^$35LI$2 zFAV+j`rSJ17zutCbc~qe6_4bIu)cV$KIc=%DHxrwzH!7Cp{fx*j;g8s=LnAx@Spjp zMgO3W`#)A<)limtbPxS`oyfYQG1^~99^Fu+P=wKQe>CnAuB#m@C^}(%! zU*3A=wZC+{^(Fr+zN~)S|F3*y+RqmfCwd2cm{=_pLw-{M_7d!wb^H~|4*S-J<9%UY zeQP2yrBQj7Xe?9{YjG;7BGCayi{euW7b4Y&DjIF~;^Wl-1@T985%yqWpNaBs!B+DT zs<9SX5SciOdsJh!IO2<55FH;q|0;!&Shwn7zFeb6usfrt(KPIh4_3=wcn!AF1LPkx zl&}A4kQR1EFH-)uX@;Pqi^ql+DZfgr{CQ!jBGL28Rz+042}s4)wMVquEibgwcFW@o zJeSsLEaMB?@0L<@01ZP8F54})qLMSyCAM|C%s|38ufuL>-*LD2;X7ynn$?%M`~HNq zaPW(LcFW1=Pc8m4g^m%cMGAh^>)$DUoV?mGcYJd~Z9}a&(tFYR-n(TN(((IH=$_s3 zRha3p2qmG{*~oKYKE|$X{dP+$bTK*?dlz~OdC}^Jxc{n$NRBq6vL`9)2VYduIPoP(dIDYgx%14is?3S0%6KE{DZ^dp|@-bsH1%;M#%y134>Z4?nqxK&42q&saNto1h26@WMYzepB<>^FhF6~&f@lc|NzH?5r?gMTuyd$CVPk))@Fn~gNrmAMDP4Xc=l|98}wI3ql%ibu4g z&dwuEK2AejNII*|yegnaH5+Lv!kJNxMtq7|v=Kd4jjUx+KKwC!LecYs>q!`CJ~}Sq zi!geQF~1u|7Z=gFn_bOBQ&4A=9Ogsp^lmf^-GTas*_wIvklG;C;7MU#!TfpX8gv%A zILwo1_-w|F(FKUVbxck&a{osM7(b&ONZH?^Jz>5}a3Lx~uT$uaFh65{KKc^PK_7(q z1~GFP7ovqI2fYy?b6>Ll*+v{aoTPqv8Z9SaRG32v8i73sjY0j<&1fqdN=jX8bKW6lNmV@-Wwv4=d0qXYq+3 zZeI%up)9nM`P)w3BMZ`L@Cu~)&hr|yyOQG-_8|9xk2 zT&;Km@a{AA$UMe_P!igQ-ome9hN53jVMq9O5cd=6efJ*m#qW`x(2DNp%f}S;Mh~O* zgZIctv(bh24xGHL_Y(LtBRJ^j4K0&L{`(gVt zxEt+qa{rMa#UKmK;>aO5_96@{!cvkUx?LCpmVj$ zNRuAMGthGMW!V1{To)dzxPqwhLdFudbHgl#HQ})qNgUyHCh`~Y1?YXgy1^XyHB!Ea zB6AV@Z&3SB(nyj>a#g&pR5L2Bu0OK2BXj%E?dW7;yCLO^C^8qZJ1*k>e>48xIFeiy zuPfDzs#DkBQ44o>HUZlicZbpA^~*W_kNG0AVvoE8OFrRWJU-ncpTdPuzT6|N*YHm= zbP?)=lrN&lT*R(Rt*+zG*pcL_cwMPxRIa-INd7*D`DegkKK~OKyoGX`2ILWBM)#r{ zQ5s4@`_S3O0RJehwM$P5NVMGet57U4D*jEl7G2joAWNXK$FU|_J(~rjXY=y{ay0`L z@B#s+hcy`XKPj+q*&So&p3%g1yZDp0KcnTOK+IU zhxqA(wS#)O>Q>H_1TThTUb>?;iCWVbznWK6c4QpkqOd=DF1l82+`{(VNN3iMe8`JD$c3Cp#WusHg`CLo8h7Jk;6*x%li(Y^1)2Qhh9Y!(6i`iWJeF7gBn2(FzAco(QW8PbPei=E=DRz?~H(akr|Ly zs4eP%ET{+Sg$5!OV~3;A1T+=7PzfqU%TQzF%;N+K0toY z(!zsZSfHc6;ehqa19JEyjtzc{lpRNGw5H&@6_tnQ_k9+S7AtuZD@gt+CyJCWT6O0& zC&9x%=Cz|t6F$r5%6a7wagxmVz~Wj`#9)6^%s7{;rPB#@_-N!tc=>dMdfH6+Je4Er?BW!)C1j*Mxx1R8k&nfLzQR`nnRI~&<51+ zq8fP&jYBD@7%fNNA?+}~oON-Hj3F=s1=`d|3yS@Zq}5v}mInSe(&{f%!ZrL)q#dKs zA+BKv*Ye`|H8LJ{Me8rDk(P}+#k5Cl zk$P{ZpvD*1XmW%k*P%%`RFY|o)6uKwMdUzFqesyolz>#sUFa5cE$WIoqIPHzjm?58 zN8V*MGWL=hc|9y;uV$Q&zDF-~<^EfkYh)@Khmz1h6py;24yX+ZpbE4KEk?6Z0cvt- zjSOzj&QS&`KtCY^g)}$?%|t4u6upmDpf8XgZBs!3G#aTSyU|EGdnYVqw{6E~nnFF` zFp3UG&!LGZ8M#m~x*h3gJ=%g)lM7HeT8Vn02T()Q8eNRE4*YU8ibFk70ve1|Pzg#! zdB~2QLF3S5)B>p_!)dS;yS?_x8kr9-M2$Pv$cPR#vib5Fxf7|F8CTTEX=r?x8maDF zBln>XJJs}%j-6^Gmjx=xq-(enG~((Sc^*wdDJVCrvzSwQ4k`b=5apaUV9ePI-LH-{ zw$U6LdcOXKg@=g>=iwBuQUlW*3xFL+-cb>ccWOebnaf6j~1b&a~EDcf9&IlXAIf3 zPddFTauXWPc*3rIIrGkb?(TM}7%S$8Y(mxOAc`sF_oH6q52V7b$W3s&bDq0IE4aOZ zVV&7v@U2T0FJt3Z$>O`Cfx!V|tqlyO^~o}E05;6*kG&yTsvf|GN1*ZRWGPtLz~G0L zjSUQzZ<56heb5bU{$%k(FI-HV^V?)GZfaoAp=DD8&EVLWEW>daHzkV)+F|Bq62cyz z6IhWfsrd~J=FQ16ZhS+7r7~H{u^mvCh6dx-Wa(vSWH4?^mPF`)sZfWDp=~>H&<9&J zXk@TdB}*Liz@!FRBZFy2vbbqy8T zb2A;ma%el8EZw(bA0hF67XFefj;1jN%dfl<=!9#aAL`I-NRf(DVhpyJ6zOs*^TrhE zW{fcyo25tq+Yj5ej4{}or$_=cpOhj_=sPJzlf?|YEmEXqvlv4NPCboar!>L^%&&wG;_5u(U~$R_D+uv_RW=DKb#ma4`#f7o^ApY)9J^DS`G2DR>q3 z#VO*$HpiyO5$J(!&t?53DbfS_pX~| zMOHxL4HUc@|4nRwc|T0R_T8K!Yc7b<4Eil8a)1f@ttnz^OCzv5G~SjXL!c9SSYW?{ z#{^AxrN~75j(BzkO?R_1=z@j|vF}NdSZKSK$9N5Ky;7tXwz+qTjDmjXQuBSd+6y%t z3@VkQFGqS2HZ(&=0!IqX_i^{o151?Mk2_a33@E!lZ{cFr!ETB+t^n%y6A$eV5T|U| zGM0@EBo2n4O=I90lp^Dxd2oujp&usEx%nZEP}xw2wue)s#U;Gjq3jHXU;=a{ricSN zA4!n{=y;S3LF+I!bSZJr3_W%>1Z|JeAoN2mlY#SbjuKj);3%OE4edyr#8E=)lPO|_ z_NQnJ>M&K=Pp3$kvZ0rT^$~pBl>Zq%4bU}`PlK7fqxdvH9onING;a)=p5^mj%E0$5 zj|@7-@W@nvgGbh$1uzb}#`4IZ_cvag0^vde4%wbAK%OHzre>A>Tn>mYg`?4 zPT(q_=S8j#+Fs&HpyOo%T3$}#iQEaaPvTCXX);#=eXtbjuh1ZLIcX4Dr_f*r{4frN zUgdE?$7}4IlkiUEiYzpg!ehg4PUW#dKdgklG_Iy2d9-xyhJg>pLvIEhLr*403-v6H z6xy>XKtiXB0@#*Zjwn=!K)8e+HdH=S(_R^Rwt2>M*7Y^Bx+9&NpcsdfwtqK-=3i4z06! zv@ir)T*10`I1y-nmxiHv4sYNJKL6%Yj#fG5a->(15L%%9J?O^s z)4+T_Ezs)a>j(6Gz$5LBy@V5h-g4dsw0y+ly8(MS8-nIfxqIkZ$<^J+=iju7FO@e^ z_)9v5#v|HYdFa0xdjmFf!Vb5yV`zoWEnETg?c~Vs zfO~mk(DMUd*fa)~pVz=-S5K>6R=&d5*p7)l@I`l!?C8=@%`e0iZ8@e=A;-LpRpsgJZ7GYmTT$?5a z%jLwK*TmqtJXPbLT1^b54g^9cEP)}o2HHEO$`R;?=JT5vtew~q^uh_y+&NWdLl^Wz zW0zEkxuA)`0lPpQ4urNVQpE{d$YVgCBXEilg`xCGDFei9VQ-kIo%26|LJi?va3x}~2c-~`N#eGc;yr8(Luqj)L|lQpFAZa22#BrAi2TVQl}V2J@4t{2Nsw4ko}5oB-_; zIC^;2i)^5)^!mY1~CY=*#6TK}Q~MhIvaqZ>cRCf~JdzE1&@w zf{XFn3iU1b|udYt_C`1(lE5o;_k0vqaHs0EpKAMTYQdhhqJlkI1;_X=NVew<*09_ z(>Zhw?WH^_;;ip+GRi-XM9N>ri9zRlPVg2QUckVoc+~5y!*U)B6lS=3^r-}QgW(J?)FU<@M+Na54cs2C@qBS!-!QcQ64{Z5s zGlLf)&S~Op+}vR4k|xe3GzM)=iHAot297Jz#B8Kf*d1EBrb!YsbxV`X zZZvW=4Ygora2&K=lO`q53s*t2g$+O#Y;!Uvb8VXRfDV`hb(jfl*RgZxgH_OSeVS-3 zPGN#U4;X?;(B3^wQlaUFG%=h;V%ViM1z|k&!E)$_tFCAM#x$8~!VhC^z<(3#&L$rm z1&wiO{8x2z!zFMcw8QvwSr^Cui0ZzhxnTj0HCB%HX0G5qIU2`Yi=lkN8q>6WGByEY0_#qCkEr8?Jg}%>31P?%CLYlNtzfA{^ zz|j3^(o5M75Dz_Y1@yyScd}mjVF)^)W*orXF)%?lG{Y|ZexLVYUabm*W!UziTmiNV z2C;S6VIaSsXJ@A{l*9&@_diJ^*xun>MNj-NaS-`O@rbb!G z;TzALx_8ctAHUmfjIBW zoB+0IB89On(C`rRP`^u~&?H_z4t+8o!*~)nIT7f9v+-MArSQWff~ELFuot%Nbv`A~ zp3GZO>ryCyZAsM`*b~ksM!J~r4OuleHKU}Ps#iP52 z`5eArK(8xJJkXiT-77nvLWvYC?*^);$#Bce}m(ul5e8A7qDi?)YbH=s8S6=xmlQ#>XfOeZ-lYr%MlP51at4 zEz+d|e+ZUgyG|qiaqM%_Wgzrx=cP+q8xt3h0EaO)U5-HCCFv6P1Rb_dmx<7E8S_w& zsF<6!8nbV*{p|L$}t$M*J3msU^Gza*{V6{Gp1~wy;CnD#7DMAhGyu&H6Yw-U7|J{}4@;LdiX&)XI0c>|G4zinF?5Vc zmrAIor%SgH6eviS^)%ptI`qLKP=_t=r*Rm2Kc9bdVY*mw*q{wMp_2p_HwmC)NxC@j zyWmvlTS}wQvMgOf&<4%V(D;Wm3e78M6xw_g7)ktR>C)l>=08uD;fkx6hh{i+B%gol z>U3$tS2P>!FqH1$*xS=36&iQa z7&KL<%a9ayx`(TSj_=c@ODZP-lb~Zi^|Vne_#s_9IDD{W8aq3{j`8bo1$6$H&L7~S zfuDFp(0q`F(>W1ngZ2>XMpN)-9u@RM540Ulmn!8yLg8oe!*b$Hzw$;h$Rp{Z4Pjt2 zWJn40!S&GFAVYeo1u!9#1{!8aBDSSbhLl1lTmya3qvjiDNZTytn`TH*`C-d3_>CDd zP_bEtj8gL_u^xJ$2fx2Xh9)5fj#D$l>forL9a>suh!Z+sfCc{6EWkFMMPu1C+$KYO z*e<9;YugN2jNjKWL%NM+16?v?0yJNr!A~xLH)e?Cc~1J444DXhw`GW79R7HXoiT94 zXUI5cx;sP46nkZeX*`L0(>XNt;bfqtF9lv80h|b({b&Fh`)5cjZ^>q3$Ix?s2ESv% z)eOmy0&MR?tcT{IoXiB~6Eh?pnzTpRDFfHA3|Rq+;N{SJYpbO?1g9D3f%loilkM&Wl!IG-znp#_;z0KHxs~tO{;N#AjH+_~VU1s2iPW<{xt_m8pRTL;A zU^RCQtzVFcKreJ*8`tn!p?NKh&S!od383>UP7He1lXwAlz5zcpeuE!c{rDGhV&8Hy z(6NynL+2(M^U_!aCkr*t=1eJP;NQa0E+RoC6VSR9KeTV-O5SJTc3wU7RB_i(-$COa z(D*L=(5ka@=%^-sF>$-O8aCqJ&jw)V7d~z?iEElA6QJvqELk;^&%dX2mY8Ny==>~6 zgpNzIq!j9%vgC;JUza6z5*p*PWHGe&%@V7J0t2!n6Y4{=q!K!w$dV3ka&;rK!~yl^ zvt)(h%URO$EgG1bB?F-`J4-y!UYI2Z-cnziOR^;HZ6;=C$y8{Ym&G3|#{M8nVtJJI z#Uxllfu&gz4^7Ln#0^8AWJxdP&7ab^dc4q~;$et+9d`MGb-pa=u@rumB_+_fGK+8D zb24zf#=!G=mURD;g{ugFei$Usxf=g63Vgv1@$1mKhC*Lv$wX*clO+M@hY4%h$l5G% z60fi0q_8bt(FpXfCl0@T0}Zbu?&~b^Li0CSqUj7A{w!%x&Yi%7uPE?smb6tijDvnS z1ll)pcTk5O;=C1{%z7H#mL=n$54u%cRhCph%g!vBjo*HHwwOMo;F;OtfX3F@;)Nbl zwkD>JD0nUt&<|bE-i83h^Rp#(Ie`~s%Mis2v-xAo9Q{Sv(qRQB5Q`t$FUgj2=x?7b z-9M(m4%y;_rYo`~<`XsoW1;EFZ0P~@uGumH+OE;E<%kNnHe0NpGSNL-RzUxa*<$dq zgSc#ogXWuA0QDaDKcitQ+`!J`@nf6r&6Z3U>YXhf=|udYA}EW&^(ZaplMLHbo-j48k{XjFa%4W{$RFLK>v_zY4r_9{!q5W zL+``cG7)-)X3Jv5L<&OJBb=0p)<#b zZ2s^yJ9~yJ!0#BDEjsj$VketO=pbPQjij-2#W~rs9-8K5i+MA7%CMnjezvsO0+(_{ z(6o`p$?Jh-*!J((=oUWz-b!}x9i3HWiyJx)&^h!S{d>o zX^t2^$8XFL2e!9aj(DJ}d5#32`SKjGF>i%Q&<-a+C(MK%==ofu0E5Libhri@JLE_e zw06vqR;xHVXogN`g}zQKgdymIrp`H10j*tfq{V6)fmY~;17Qdbho&oXWFoXc7qr3I z8UuUR90@@kw&Cl6v0IMBZ6oli9C0>pXmDPgBL|@Mnj9&?@3iDd5ZbQK;g9LC(;JC{ z_M38KJ+#D8_$2;>)a^6~&3EUBsYOGBr*DqLRI%6`!@r#OTL6(7ox zZq>vM&C$fgz>~<`Lj4gE?Pf>A*b%hbX$*QF;|lh$@Nw=MT9ViqbUewOs+~Q<4g6IZ9}I zj+21)=Xn!B5{~0hLF0IK3au}*)9;y|lp}T+dL>7^&^Lu6-w$8QkzUZ4%*jDN3@E>r z%8q~F1kyP==*%P`v}cp>0Et~Bgyy^)DTBrWUj2{Qg*g%rZPR#6&^et)tNcYI=0t2W zc^fCg**P**@x2`B@)P;yaTU;1#tA}AXVB^(JD#5-L!fg3uM)Zz@+hHkQI7NovGDyI zNrlc2c4F25OjUUBl?x2UdhQp+bZ%v-)bJQ5cegG zLFXE-6o%HaQA0z%!@?VZ#`U}rgT~`wP{9%TzU8qnVcW=KX+Xdx9t(8AN@%X&tu$=N zKlOMk(7Kt#&<|TSBJmd93JgIHbbiOj7Md#gxW$kkPKCCuJZh-JSgkRi({1bwTDSAa zps9+xZ^EB++`$f@X=jeCf;zOGLPI)t*_6hsIVtGdO+jc3@CKlHFDGdv-@Y6vAYKph zCZKOWjpKJ5;EJK?2%rCMrxN%J0XTecB6dhP@@6D#;F4ZYhkpF_MlLb8B(9B%zg&Qw zT;LKX^A5NM+jF5y3b6ffG4x#Il9Jr1H?4+AZ?9d7Q%v-KtIODeGaH@U>E>^PTn zG1K`iE-A-$-b%c(d$`00Ew{V)14}e|2aQ07)x{rBqET1@{dZFMO!C8lm(fs9HmYom zK^z0iT`nnQB6N>ShG08-x%h)g`1|0;_Q5qOE`f!uN!X7aL34i>-*hG+EQLCBs&#{j z$F{?m4#Yp~k}7QHP}X5v?VKRgd`T`bb)@hM?C>lWmT;sdUimDSBtpwO><~H^5qCC? zmE(ur4=D((AG_oT^ncp3*EhMhzET9<^NXB|5}m-QRCV(6*hN?~XVjkIaVfB%0+ zr)`+n$`PK&PPb72TB=B(xYH#)&Syc8JEKwi5BQ<~N0)T#MB)&Mp!qO`Vdw}O=*-*s zm8*mfI12iN^~!&0uC(j|yXQ(`7e4={8*`-;hdnM=4p7K+TdtTcV27|5wB4R7F;~Dl zawSgDnkx?I>6t5DXq-yID~V4fA@pbAhtBL=2|-_8uEckxK{tNrEy?8%qp-1Axw2Se zVE5#Tu^XMw&6R=BR+cNVZAmX;`wbau-VOLrQ$o^{aPBTxK_ca#4HuBKm}B-}}yJx?6i#u0hqgPP;H zJTcu!ftM)^eXr9Yv}DoYO}z4Hd18a6nKS~O@6ZSgmC;BX^B>R%w0=Y*Q1{Wu&BT34 zBhdFX>nN=I^Z46U@o&tNUOm}xMV^%Om?red4+=gB~9#}9e30^9jxo*3@o zUhkuOdf(r?R`6)*(-8Uxeq`4WH@ zXtVq3>N9gQhuba0qeB@+ATKzRcIeqXyq`1&aH)f`_1R1<={NKtj-ePJzTf z%-wY;5VzvZ1#$q|Z3TR5m7P6Zz_(V}$bW0NWlLFd7xf` zABJXV1yaSpIIDnf46;K{fsBLtn+4*7*0%~IKmzaE1^nqZ?AZl!1UjLqABkZ%XneOo zCO}hZfy6z+{M-VW4Ndb4_|tGS2wOdhy?}N7SqBrLU0YZny_m3g3#1fB2p)m{MeNMR zQN3RvUgoVIuw&?jU50T2iwh)^c^|BV&Lstsgx|ERKswxy4dbD;yg6$zZ*5_zL*;8Ue7|WAL*A@nbtyas|-%Ih{X1BACd$e{F$Suuba<#0EVu z6Z&B(G=9a-p#|#DyuN_%vhgOM1$v-u0H1$9gHbq)8w&W-t1N)Cp$o23{Fa3SncqUk z(6_Zf7DLlccCH$QT^=Xlt^!GbW}Ul#fkM^ffyUk3Js)!&+M#m~4MKaMKw>5^u+-4W z1OoT6Kye>O^&$(P9r}Y5gqH6)3FU{~UZS!61$@he2Harr9a<)G;yh zP=~Evp~1riG7uV%@YRkUe^R0Nn0K`( z;(E^3#Ag8E+ygX z?66&-SfQmoJAuy23Z)eKFXsfHxnrT2l4+zscs4=#4LwxKs++l!-9cAr9No5tHn;7Xvef1#8s z-d`v!(`jTtp$vi6K{N;*4;D%Vh4mqv+z9GCOkSuDE#wa>@%i^WLT3z&!wSWaL4g8} z9y&Z6t>WB5IRf>ih5T(GoWSSALx;amLeScEn#5W*o00GeIX#F|a~d()&q z@v~_Xgtnd2_|7ON*myeMs8pZ-i>6Behw+x_66YepgVQAw`k$FDI`nDNrF$+{uynel zLi3*KQU#su-4d6_Nm$&H3H?^L=*qT1wV?;x(&ZUWZh%{opnZ^=pJ-3qV7F93>kzlJ z9m(h4`;c1(jwJC=x457;(Jkwh{is`7j-sJqZi$DM$J~5dp2Cm2`6FB0-4kwE13gJ@ zIRd>;y2U)2hM#gv0@R;&%LEu2?v~lm`ixur${ypE*k?IGhnt_@#Xx6J0_|hz42Gb2 z3<1y4ISj#4=zQKS2cT)3TPzL=LkBdCcZ(OgU`W|7Fh3R>I-p5oUfD3D>1F1j6NaF1BJ<<0VfTDCFoig5>#J@_g^t%bK^RJQON;UN z(};(z3^t=PKbyy5n%o;OKGqUd?x()k9;@{-#?3D*i3*el8n%+bxTseKvRf9_!z6%T(xj zmn(#>Ic#7a`Lt3xV-T9_mJVf1%;PmfXBm$Qniud$=5qxLIT7gfx}_TpI2XYMH1NJ# z3ZU@=x5O+YZZVIJc@Ip&b}Zp!pmix5^|Bs1pmUj9Dxnr)&}I>xmQ#R*mJi)xnnnVc z2rVDE#m0OHmMVLNTUt%$1mFc_2d~WICCJrXzw|zz( zXkN)%ar5~%e$Hbczz5AmBwodxLEmb2_yHY%!49G4OLl_ayoM_%#t&l_W3T1w0Cd7} z&<|%r<5#>XXoGE+uzo!az!1!Y)(xEe5{)DJ+AYSV6oeLN{>CkK=zuP0_0uu*e#_%o z1~;-J=-Nc1&|JZ%L)n|TGHBYuRhDxCa3J)4$5qg<4&B)PO0Edoyp6irhb-Jq05n(e z2$`_L5DdXG{MH@#p$B$&gPrYUM;~GD=9Oc6_HZ)Tt^ilJocJR&1Z}@kUlG7hOQ`*#7{`ntw_Akc}z<{m}jgZA4={2B9uio~#z`3H-n7j!n*n`<(eliuhIx@xzKV(HZEE z5x9!NPZsg56dHM|NP4U$V0aP#Cp0^Lrbx_RuyACNq(axIA_>9JXd3tu|JWi4KtmDpJ#sR(13-w7w()KF~ zIaxrU7gj*e6dEa^z^g@Kg~r!78R9~)0{UL3@%8L9xrpz4a&jp}Vka&HO&eIBN*!!l z8hN2hW01szF{?;)93I$h1`A*U^kf%_8=7*8q#W8^>}Vzp!YLL-TaIKD8N&&r)t2#j5H@GV3D4`+f zpTU*<$a)V~0v+0$+zErw+uX@dOuWO+RRTB;+w*R*#J$BlELZcT938e7x|AOp4w7gt z^VrV!h{rb0E0zh+Q^p40W@GbtBhUs1LLIJQUb8RY(clQdGH74Oqf!B|#cbY!w^&++ zxa&pi2;1~QvDmP^FjM)Lv2*DEkU~FWFQ*XHKW2x}{%NtaYR2Cs@maCV#x}2HgU||F z9OCouWYG2yclkLxCeXWzgwVg5J8jNiKJf(&!VsKDobyXMho&{$Eq)!gIm`*HrLmK+ zp&i?`js~$qa1Hc*#Rgi?$a*%)yln#~cZAQsgMpg~>(|^l^uul^Q}`P?Kf(_Dyn5(> zQ{n$nbpL^oi}?e`>vRcFN*M^VRVG%%a5Ua@~>KE4@6 z`-MtI<91pnQ)ukGE-ty-Q~CqfNjaLU*NKPT_3I=jn*q11lQIm}tdmJpTh@uX0~H!x zC#%t!SSO>Xdg~;6N4}Ef<#kfaLa|rZNk%4#POamkne=~cow%sJzfQ7tV%`teNh$h2 z#@9)Ti`c?CnM3pEb)u;x@CyT(!25Nb*u=3v*71={?r)M5hj#5xNh#Qw1TvE{N9-<1 zN)@rac~V?dvy!6h!p62q@;xHBT~azQ*f}YgyE1;aq!?(#cV|K_tlT8u1Hzf^nUstr zOt4o{vLo}7Qk+Bk-bqf%U=wK0s zxDw;~K}iXiP(L&&t7$L_nGh|U#stozaySG3506Mn{^slem!W|xQ9UjxNeoV)KZcm8 z(Er4wsHkE-#&9`mXegV;GjuLi(_ml~nwUTfEf$O|Px5Xx3F0U!CsT5aVFrIrXz0<; z!w~gTSztHjK?6e^J(O4{L1GstF?JdiI+XAK3AxxvgMJ2Qx;yciNvS~nY$nFU3QCyE z&dy2lb{h+xM?z??WI^=*!h(BH+VhjVOGe@sP&w3%q~z?$_zRO_VsKGXQmB=X_+E^^ zn4^tz;a|dzF;+?Z+uyLjM{bDs=zJPSCn8Df&L# zzac4WBX3Od2}thWOzHQf0=H7p$lIv^D)FSmm&UlblcyBJyLlMVzb7dR=-$T}??|jk&^7g0SLrE#aL=7cI_0c46T66y~DuwP^5=8Y0cAQVYfAJ8b`6Lf9 zTF)f;{QqGr@GMU=CZ6Nr;fAx0og9cs+EHoX%rR)>Av}l)nn)Hd=sJ)hjBA?;Rqx%>{@xOWK=%=saSvrYC(L^UnCD5p+A1V#p zUrs_eO@G^|m$*qo$c2qY6Auf?`gf2Rs$G;E zy-3De-Stv%GW}4ym>s)3l*D#_y;KwHgY|LV&u52&^^$oB9fs;RJe7^$O4MH9VM7aRna_K*Uerrj=QWNrYJZ*5qx*WC z(r^*bkg2nSH+U#e$0BsFl7Rtspz;p=N*VY*XNlgYyc*E?ikIVM>};L{(EOQ7q5B(^ zisC=m`QI47NrNQOUeX|8WPE9Z6#t!!cWsb5w0CQe1x)PGAo?m2+N(jTG01C>4e0FC zAla95RIvszP}#phYSB8NL1xi8ut5s1pppkSh>gmj4KjnyVGVre3-cY`Amx!qHi(Du zkc$#_8XncaN2E!ls6kevdu)S@M)Bz^coh{f8u%9t47{*`UvVbUiyCAthJU3WY8N+1 z=08Z}5)#MoQWB5i%Nk^wan9e^0I{~JL6%<4GjjzSzna&-#f5qr9dI=_{Hq$IfY`g5 ziHX%~8>E6b_$P_1#_P$PSi6zX;*{V@i z6Ng(jO6*J$-=`xYyN}vU+&`{Ss)@DZ8^w=YMohbQLL0tr-pb4Cugr@^3GZw>HWoDiu70Ye^VQ zOx(^9(ck7slhL?J7DDq*9!6f~)?GXsXy45|n5b&x>$jpC_tK$~jP7gX-vLna`>6n` z)$HU>5?jN|2c3tR5Yl%4` zpNc+5MeiZ8hA06p>?TTgFNw7BFh#bp1614DDZ023oz6zdy^s6dOo(QxQKryvNz|tQ zAP*&a!z6_E7!UXTBs9T8jo~B-pyKgrdVmC9;q_k~UA)fg9JMzo^@A+tZHV1c_GsX@KU zkq+A3dmY&rwO2c$JxTwk9H~aP&XHMk>K!S3iiF;C_~i-K{V?vxOmy*uBLz>hVBpAN zv;Zz$!%n_(`0^<-M-6o>MiWcX#&UGfMh|N-zz$Ttc4QJ`=%bEvXkx}gjE{4X&4q(8 z^soqHb4-XDR*{(b9TO8Lup{b+lNjPgjD7D&CVy_|sG^Da(Vr)1V2Go9Np1_{a0NO` zoA@{{8^#SS(d+-uO_CQiM7C1O9h&%1Dg!W$+D=WfocoH(#F)SiG=>2($QZT#n9SWMX3HpeC_VDQFTeijQEP4j!^&nj}R(rHBd>o5!*N zRF^gJ{a@U0Pim5kPHvPm@$NMb%cV_Hhwf!f{Pqd)4J^<_ViiqNi{V{OGLQbfRHmB+ z?&D}8?`LPvkzjR`Oc6U+Mx0pF#P|Q>tmmm*cDz|^8q^(|#h}65saa;C4tq9B!5|CgH%nFIAC+q-Kc?v4Q2yVxn_Ovn0{doB1p#_fKn<;$hm)XqGy3&mp8NCFBku9cbVrTIi#TbJ6{)nq}!231U7PxC~u1$9Vnw zTvXB!U|rO)ik(J2%1+107z@yPl0;B_iUiPqnjK-RmLotDLrkDL!9tDAVtFj&uw&Gk z+2G6EZ)uh?^jb;yVH`fP27ly|20cCl~8_TJjO7-iVK~K z%s0q*^A=f&>b5Q7Vr=^sS@k~#?m&#j&MlJl8>QT>MM@+0XyL6C`tRE!IU86AHFR(# zhFFIBel4ObGJgLSnI^U{i2Ci{5|`{ZS?GWkDMANVMR9(MRHJbS6EHA#91H%3ogUvJ zbr@jv?<}yaMW%?g6I)~vV<$1sTO_o+MJiA^xkb{Er?ki%CNN`~ey6rb41?3+EmF>f zbvhlR8^tY>`!*At)go1BpVJ~UETo)I=EUwLEt2;R_b+RaO0@sZPB6BrMGD@ff>*Wh zt~veJaNp-XdXY6PlKmcuKGGsO8jsNr{rEEs{DYl0w#Y0EZhMO?f1d^VTBHUoOrkf| zA`56tk{JCHv-JCb2|sBOpIG^v9TU5lLhG9r$($knk)uHS=N8Eq#{ZA`h`mMT!?-6* z^dV=oq*X#xH*b}~k62*mR;fc}msXjH%x#tYj~TdUtE@$R?^c;d^Qcy-Wa99IR^An7 zVZBxK48BwL%2uf*j@{HMbEx0kDn*}=5So$kR=zba`u^XCTV;v{r>0eMK4sxYTV)lh z|8A8OTF5q!nDgoNBv`W>c zd^zoGt7u;kW9b)h9s(|WZp5}(FZr2#{q7FyrIrqsx?TeGc3v-)v?~X$=k0U`qK@HV z>!lUF!u2wT?y2j=2%-wC7YE(b*UQqcDES%dr3iy_*Go0JChL67Ja?{_iZ~bQUF#){ z!M*Dx>l-RzGf`wU6Gg6BFAJzVx?Yyeu^?8WzIHvIa^(mT>-m%`rGARUzNP({^?b^e zxM{siM~M%wmyp`m)H`%|M#8s5_f2D-ls!!<0B^coFlPO|*`!?Q1#~s_mKy9}+-bJSpxor|+Y|l2{MW+&I zppC21-Ls7!qU1u^t4-FTjt*Lww;2iS*CrO4{LaNx6c@Hh?&iceN59zNZIbyDp4uiW zF`>6fGKx=Ygh(nP4qugfwnf8 zCfXLnsWvIcL{A$Zy`;ULBcfk8ip0t|-IMe8-8G<%hn#HSUu1!kN`Hqs) zuK&QsqV^x#WIk&DiArwG_@5cKHLrjF7aj&0w6INzqu+M;Q#&8IWJg=H%L01awTr$3 z32oob+v+5OE78kt7aP?b+9ib=dT5|Ue-9_o*s)!f?#RMBwM#jMxE5`-U1oQTzW<*K zWhWNgrCkhEcWal{D9&x?laLJDg9Xvvvt7~{9@s90(T_(Q%z}KczlY0?BC&#YsYI=? zU8c}3qCa0;?&CDZj%}B@$m7~2mmj6DusF^|f{RiNG4B{U9N#V)>R7&v1auZc<;-@e z;fu+Qv)UzxFB&(`rb5KZigqdDzJ*oM7m=Ss;wO^Oxl|6d^JvH5d=f#&Ac2#Z=c0E0 z{amST+%M{XkFSa`OC@t8cKzUYsnnd>)7GROo#~#aTe8E+NFeVIM=ZZEws_S ziw&JdqE)n`bq@((aBn-G1*39yJOBRwbV_x9yNqJ2nj?r@(=Mw~!%8$UfdRHg_aEX+ z&!GKbDuqrB6~y2X7RK0PY$&?Fwq5+_{^L}nnEOw(^A(TWPqfPlG@p#~uyEl##X}c$ zc$!j29iE{QQHOuCFkh{%Jjs?Xn7$#&*6lu$) z2RlZ+n?zSo@m?NIG~@kbbPlCSw@WqJBP@)GaUQyJN$iDoanbcS^YbX#t5gL2*V`q) z*ajY=m5j$y^spR#v@yV146y^1H(3}}^f4aeVvY+9Gx*YY6SL967<#w@mH)6qRMEm1 zR-=YVbZ{13TtFYQ&u2q#@oZp#HagR62=%w=cRsIwkBhtuD9Jl?!~|BN^)97E2Ltpm z!(d^b{^(!{s_&5qCNPc4`}9W-3oayq52y$#GbD;SdQm&hUC8U-dYHf&R6phjF@~9Z?Y@O7+L(_CT!t>1aV|VAD$qv{tyx~L zd{4js2`?AaK4qed+41K*6R3T~>za1s8+Jr&e%~%3sz33JT*~+bUKN+H;Lq$F^9TK3voc6yn{^Sn6rk92E4ynNab60Wy^bRRQ@9YklMC&gdqF&Ct=Xdb;|Ix+j z4!&rV39jpqrB{&I4J3s2jUcb!CTi&>q~$j)>K}J7gY> zD*CTxp?jI{D*E5oA(_{Zh|M@u?k6GI6Zgkim<#oR4w>eLiJ8}u02ZQB-62*Kqw){p zH7pdx4|PcFpPX?`hvd`lVKs612>prGM~P9xy68SGaNkl$MCfd(aK5SZFYpgI~_8M z`bQ+nI0qNd|Cpnp5nu^ApD-TPPnqvVVywC``tLs%b2pRWXY9bD!$uyu z$j^Dm=&0dL6#vq}$7q-s^KZfJI{A)A65O>@rcwL9PAR&T1a|L~sc0O|Vj{OwR^CKq zaCPJ!oqXDcji7rIuYYx~P6=qRF^@zNsG*+MDWhoObkvUN+gRZ6PFY&P1V?mA4GUUF zc1kg^u66R=ksQTQoxHQbLRf{`(aeJmE@JE$61{_dMV&GeXMzM5l{5s{f$p)LG9Ps~ zu2V8uP{&*n^>GEpmJ!pg;aXzv1SUrF#7@bt#8X%pZM{=`3{kn0c~0*X3*BNWgZ7!7 zeE$a%Ib7&>(J_)ZIIC04Xo0gk#YPvaxUZepDGsrLbHpCjRx!~^61|6woKK0-ynu>f zfZ6xb&miHu8Fyi)7{meE=w8&xk92dP{FR02pkqkvW9&Y5TEYOdE@mM#FJU2@`5(N9!sofB`zFT^;Y_C&Za}HAjH@HJ!YZ z!HsJavqIVKu9W%-jN(nzIIZRQzy zGW!4j>)F{;WYo#ipJ0J52B6l>#B?;VlKUPSPh*OH!~u>HJN-OlwN!9`1<)I$GH48O z1oX3TW7IyvtARKir4o!Y$LRkwFB_W+?HMv0rz4t^oZ-KTU+k0waqOi|$*7|r7NPbs zC68is>1Vyd#OPs&`l}or_r2Gt=ySM%ibVd0XYEgc0|bC|#l(S*1WeGD@uaF5@ua^9NlA{V-n2h4CH>qxU{% zi?I**gQSxkqKU=~4+|69n2g##WT7s0@KL7}(w@L_V(Vj$4nx#wkIk~kEElIZt5Gic89%=I(RJg@&%TsS}Q9}QH0X!9=q-XE1N86|eM=#tD4CfK=4rcmFdi@&}n!Ckwg3YC+)qJ<}UuWnh7fy z@FF|Do&GN|QM^kk(5)mxWB%OPfU?WC+!V!7VPfg14!80CHW2$yL5b&3cT1Q7IExl zDnT6Ez)pzux2OcM`hAxyZ6dLGj%JDqF0df#zmX8_vESJkv60y=qr{0VyJeBs{c|^; zxFF8%=0E4J(Q%h%S9Xim#)4OOOC2iLbc^*Ljs{ccVSJv8*tOk~{}!b| z69Y`5{?BfiMF(@HnV_s&jL7TQ5eDe6uzEcSMG4>rv@rHIiQmvIHE5T2ONh>m-BS7v z3*Xc&9=bPo$N6h>N^f;b0>fLnCBtXpTS)|!3MNGDc6Ra}iQd6ZBIDgMgKA~BY-GUz zm%mTHJG-S4^}D(yK=p1a^8xqKMYpP3a%Y(L9@4@DQ zc=(8&pSnd4DDeV`pz%w$WWLFGRMADBc5jhq>MP>kIZCwuU|!TSQWB!JNlFU8=Jl`q zDJ5lJvk(XZw^?pp%^v7mXdbkJgUd|Blzcwo^)s@3^6+WG%*aPDwsHF?UJvU68cz znv#s~Y0pVX5vu=}5(@)Np|X2QlzA4!<>=+6q!QgdQj$h{Pj-yXUMVU5fjBS4n_TqY zoAu%}_%Sl4LEAqiOL;jO2c-C(UOM6`bg^K8g$`n3G%<}Hh8R1TQvOVTEJL+`N}`1U z`k40%^B+PbF@d#_ho<=cUlQ=R$o!QI4@=2vGzwF)NV|xXj$Xdc0i zLi!&`CD77RlJj5Mk4o{`FUB8DsnIzmB`g2Of<+{b?y=1G8x=S%#rJ=65gebA1+-5{ zNy#D;ok;0XUCtRt3uFH83_O{Vp?3-iV&YUP^al&-DLw(kPEipSJdH}AdODRr8v_h6 zFJlQm^OKTlREy(e#D#t)XS~T0engg1V{jG^6>4XvB#Aaoqq`y{>YtYI@BC8yCiIdF z6D@SnLHpd4%wTvPmD?0ovUBwRl9B|P=cmMr;tNuemAQn^{}?<}Tm)FfPL&Ih*h3%H zi`dbUCA{y?1l;#=c@$sD(@boY@(d80mod*~^uLY-(72w8ZBF}*Yyj<>SWsb|n|Zd- zyOnl~>lKtHy0I=Lbz3aqpGXl$3!$k=zpBzmkpWd6JFnFe#%3M-e)|N+tdCf6+|^i$+B$Pu>uocrDOy8 zU$gEGan9%)7T$pY-?DJzca(TXI)2YXMkS1SN>8l(K&jEe!kw7lCtgMv{LHiP6G!(; zO6G~R|FWQpzwrka27jbv7Q;|L37iypo+me=)`Ju*q`VTk%xJ(8b8<+6JC4QUeJx`*El zqU?)5Q^B4Y9IkkE|xvuo6w2qFvvsN7fR1ShzQd<@NB($RxCP z55I`aeETpC)mXeornyk|Bg1{^h}G!t&yiqw07nvIrw6hlR1cyO`;lM)3q&5$BOA~; zv`1!H&^nBx+n~tH0NrDHq#T2y9&u4SmWeQSTn|4+$Nl3;7_Aei5E>`4K@2eSVEUa*rO`fxN;7}l z;i8oWj+-|P6rDd!HzpAF>2lH_(&#n8Gy!6k1W%;Kine@ z1|vLlN6|jcLxiyjN{OOQWzfG z%Qq+zAK5DP(8pQShI=Kam;}*?93f$}Mth~^B<96y=fQz&dot*}++KoamJ@^d@`7NB4zZS#~zZ^)<|_!2s2Z$?%Xq zSsi(3A8*0Zj%nh=VSSQw2?G!BlUdq5EF)Hr=#x6skL;5L+O?zmWS%&2Y@aN@lzzvP z(8*K^i_l%hIE))7^hpO7-iZv1I-Jxe2?m(w_sJZ_j6Ts$!3+CjSt+Gl)h89i=H(!4E8Cz+>`=%an2pUUe$;KHUs zeWH(_P^H7a`ec!gdV+~`c8&$B*wIscVi0>zGcmF9OrO*eJD4E0o~43OT!+MoCdOS( zf^F>lG!nrIVy&}JN{M~kaCw}L-DGqH2@Fy~Zn$U=Tcey68ZT06+Lf33BqR=AX2R3) zl|CuHlJ?g~h}gkI6u-eZVr2sh5S#Dy@xz8x@Pj@vujX08BnCJe=R%+1OjnZ;8mN6p zX)%EtB0u5?t|6h1`}h$$7Q#AWf3}Zr!=wKvoGI-luD}qN)2@EjC#|S#?BiSUNZ@lS zfch7GvJtf}`}qCuGHwKY{OTn)z9Nz9$n2XwsfwKAY|;OgQb!AaM*`PVq3`>o2JLw& zg2oS&^akenu}?g-Fhu7kb{-{!Mdi%Dz(b9RpLrJ0`=u`~3tXtb_DSB23s*-*_f4_B%&)6%*mAo9K^q=x^$ZO1q!wiXPox;!4%cB#0?gc5?ZqG!n;A^iOd4 z5=+`ojJtfShEkm5$_%>8U0HrB37z6fD<)2NrSvvB7Q5nM>`Ye*Dri63l?0kAT*!|SA|;C4q+crOsAu*|b`@v5O+W8g;(EGb04DpfPN`K6BFp+^h3PXM_qIOGnz(<4E*z7CxT)7~(8?%li4ILl!up zUpg?rSyWGA=l7Bj=KYHe;!0FbrUIy)($D*UT-aP>B^aRh%L;T)@0U8%&*+y8sGP|W zJjq07kth?|SV0`1i|St(N4t7HJAI0Z;1v2-_VZJ0B>D(P$^F=)96>ERe~ika`gp&@ zmp{XRr}`yC?`cZ>Zzito7Y~hR`=#Jn2G)@=CU6@4=lc0X13phB&|cRsJ|^lp8WwUf z|2cNp&cbM=7>B`Vzc}3YUS$09+<&D%E)`tpud!3KHc+Z{+?Zx!^xkDBsLW8xB#C^? znMZy~g6MtOFFE!2H6_2F3eELP8L=_X4kLdhkq4+i*e_}W>tqbbYSc0Z_!cZGv3Yzz zvK#5J-=DVs+mES?VxhKI3Um95^7W zh|R(QnIv|P;C?j~(gvg)jiXr@6UPjQOTSqBB#}D@WFz+-EG1U%8jz4Ufkq4S z+&v(*wChy^lG(*P_fUE??xmt=-8Udb-Ms!yo1JxY<9>FK;>H7HhQWgaG80)%LOoPy z4JBh@=OGdzb{{4ov}*=rMK9wY;XcM59gy5U=D`){JT|~*ML1%dME!9p=yLyw0V#HQ z{d-(g&|oAe6{=4S$O1Z;*H6c%2V^Dswd|yoi7-uUJj0GrtD^z~v?n=|K`K?xGlW(H ziPPUl4TDCWA&faxHtL5N@iZO!$qa+f2E?X=x^X~ihA8FdEP%?FJY2)<^cx<&5f++b zN2q?snW8<&s?k5t8T;M2}*TYug#4oguv%zqHzyFJFaIuI1c5uVk zWRRcXV`9vlpyRfKVxYCdAa5FR|C~X|m?V*_24y9>*9?k--i?E@7{xaaO2G>xcR8pCyiGK+4*pcKAFg`7dL(P|l#8T8h()7RX^mRnV5?<7Dh*k zD^dNLjlaa}-{GRnr}R0o~;?rLdrI2$$6jtsH44YTFNoRwQ(-Y zrD;h;4LBXeew_{-pab*;5&jje>TGX={k1kGQYzM|;0<&j`cVs-exH5`& zVtf>1d@UEc$^_`+bQJH*1W^af{*VE?Faf%_GKzO)0!-jqRB{-P7EWV;3uyiy<3A$C zLNs<`d=z6jTDzy?vNmeioe822xl9nnxDdsAFu})^4hvD=GcBu7-775?23U=5URvtV z-kW*Q*oULQ*uH6*!4T(B-!Cnhvy9(AEh+~4$J3I}g?m7nADd%>d@@AiK%NPV9h8;| z46z3Ff;4Z_F~K1`Bk1E4nul@L7#5N^+DGurd_q5svq$GB9=6D1(y~1A*t8g^#g9+R zYA(EGX{kixL`sUmNjy|&ot&0*bpO<}Ed826}JH5!PMs8poIM;&v1;QsBDp4i2VFX(p%6+nY$ zXf-A(8IQ3$d5F=!i-h=t%)OhPqmL_?C#>QbLiOIXgmEsM`_oeVBLg2IQ`AxUg`HxG zI6x1TN2n0`sQkqJN0~6{_Y^xO4xXl@s69g>s3+61h)O+)ED$%c4hEQHoZZgrKjb3O zo0d{;xGn=xNz?IHI=(Jr9lt#{~&=G5=7-go?3L!L;WLmhyfM| z<3Hxl2aL_KFnTzJ<|iCk#%6q?lCwt#P1HZ*?CQ7(J|i=-3Q1Sha^9XM6m?J%ps|!zrJLMZ{6H1o{`{U12=-rh9rAy7E*@z zU=W$3jmlO-e7z^XE93oioI{)VwY+dpj~dZ-_S(DZMcy1>3We3x_0uYRQmfWHSMp zXk0ubN%U|Ql}m@Da0e`<-~XrxRuNm54ar8d|IUK6J6IasM=ic111=vD3$-hUWD50v zu#=rw=xTO?iEBv!!|R5mJer`KilBS*5O1TS#Rj;q-^RqmY6TOk%zFnVCQjTp#4jxJ zvU^}i0@QIe_uU7Fc>j+Jt!79TxeuXc>_CI zVq!N%iBahtlKEX|?<0XdDY;9+Xk!Pe1C)L*DuT-}7#!li|6J&4o&j_)D~}FCLlWDS z7@PG$C?auYmkXixHRqWJ<9-IsnZ4oNFo zFU2|Y7&kn27Wwj!xM;jGB&(P>_9_dbh0~b8-2F&siiZ=e*Vr-Yuk+CE&waGfdxHwm zU){iw5}UY)?wjoV0CxT#o(KWx9~pf51iI9xQwW86M8UN0Ja)Ck;yyol}Oz zkJ?uZi+Th*ICofzF}Q?&(YVrKsUz0SVcvYE-&HI`Y+OyE#O^i25<8Ol{>esAFB|43 z-vgta4>j~ z3Y?79Q~>=o91RIL4-HF-*nOBII)#L5r~n#|P-65jb}FTRl+vQ|7!Ms;|dK*$hAfwIL=%^K%kjK|)_}#;6NtdJYS3G9uaMGS8+XQh{FPh%BP7j7Z0M z^xJYo)RiO_-)clExp1}~kr1tIMx^vFOo+AUZacy|mh5clh%7%J|2!hI(N4A-;SEa? z*nWhM(J>zLE}(sf5q^-6g>V^$Xwh!(JR(zQ?m~Z5_85_s+>fh!jYxtEBX2}nBljMW zjhNVn438$E7#$<`8<8rE?LWc~ld-^oBjOSphmt6-fBCAn7nFLWgWkiZDk2CS9Ba*zFh4m51x`G?0 zkq8>6kMPxrEN}*;zmn1xQz3ND9O0MDsK{9(VxoiDCil-KL9|y;LG&^EDiS!CcKR7; z5IbmNcpgU*=OXr(5n1{VCd6f^TtKPOzK~2&y@(xL%>sWN5e?OgIWi{Hur-<(>!SNO zgZ|$~WOOwfT7^-9I2(C6&l2qc=3c{*-8>@kja(Sdkw6IxV#l>4u#U6;CkZ842!r|& z$tWXkWXGsG92vTpbsalt;-QUf=4_eAZDAhb#QG5_i(;%oA2YAV0p=yP21nwuf(C1Z zBS3eOjJaXINJ1A=nU{D~MC~tgRyUC78zZuUb{A`+_#H}(_PZQOIXjvmLGC9$q!Orq zMEfPo^D)mz1(bfm%hl!xU~ZhST(g{W=h%%b=UN{8x~?1TaO*F21+ z44k9%k>B#rVSow`tNJ}hgf=GW?`Mok7`g2zU(3h5OGjlHCa?mP_;#bRfeRC}Zev2! zBex&rYxy{{>`_^96BD3X!2~;u^3EsyaV4?6|FK*W;6mMFR4gVAP^;uD_Z*cv^!6I%!(&X4H!5Wq zU>&OaFkzGkZX|Xw`!04A8x;e!{YIr0ZJa{&fKgd`H~kJAl~U9X8kL&6dHpK|qcTl{ zdB`Z=$;bdKs-nZ8bU+8E&_9wLao^WQrScvoJcLQ?&17hmm(t5hm2{W`_?_87xQT z-cjCUW7gV1=mqp)SK98< zqZoX}MjnYC+EIz?T$n%bG@|wL5F<|F0 zu`$36=XVhg?11NJWWPup>^;WA1-4cCK#X=uw&E?9g{`c&BMm{ zuo?Z2U_sjbBk4zM95p5Z>O~~@B$YaDOiEBYVT_;cV&N0Vq!ry$#`rNi;`pg!;&5T$ z3_7P#A`%KwNBwkmh&EQ$(T=0&o-rob&r#yyF+N37zc6rg%G38J-POje_A zP&xY9H?U!1<;F2lp5^tgSYxu13-y*Usl)I#c7%pA#;4ubY4ez5HnLC)XT}6N8pJNH zMXz;C7HIczkvOrQiJ#}_aD_wM#!gXb=UG4t7trmZ;?MK?cT!_~D2xWwo5-+-BS5{6 z9n)@OEBZKvnmfj?V{sp|)^Q&TF@ZYzScYN$7~h0O|A8^S3609)T-1(BlWcI1N+shA z`@4WC9vgtR!|{7?TQO^Tjc#MF*#8_g`Xy7GiHqR-y39a#(^?f#k`Ug}rif5RQ`wn_t%=;m)f3=f|KVsr;68Lyb z%F&%g+C!{~?tj8FfF?Q&^wEp@f6k6mB=80O(f*Q$wTFJe7$46g{)zp(BC^&=#h?PTG08LCs_i+QdxDkEK9w9!A4WWJbxL9avjEq*X5ZafsGdd=4Bf1!( zk6Ao4Audjk;1x_re-pJy`d>-uiG7^K02fd*NobrSK^2+ezV z^atm7EFjFt*!-ETEn{A-Nl<5H3S+&k0$L>RuCk0Ff9wqWgIhGK<09 z6O#2N^XyB1w6GKdtVDJH32A+k*MBU3Lj0&>;ROG}fgK$=AqA)wGXU+gC!`v~6%%}F zm3|kp02+Utkfm=i;iW8qX6b~Kpm!M)qx-iB3DL)_X~ttN+J7f;R98*#FClpS$GDiH zLBr&ioITDG2a&|a<&-KQ#$wd3m=GI%%y^r2%tq}>N{fy*!0;-Lf_C*E z9L-m>V_lpJ>-b_c-aXB4aOcHxoNvvBFl1A?~ zN=LhoQ&Inl3Ca7G9p6F4q8O`*ZCoIBF-@#gPVmu761;PQk6v>Bu8BAwy`;n4EQ~=F zPbq5m(lP3AADO;OrVns--!nlC4=ILgNdWC9S$LjGJryHZ&x#Ji74% zJ4WM27Q)z1oE5sbbe2RghS~xZLl2Xv{>(#$I!>eY3$K5)G3yijl~)VTg85&@|Hyp5 zO^EhMoB_YHFbzS*q^zQ2h@-^Trjxv_&dxF?r4;qeCdJ~uk11kr%SlldSa|D6nV~&~ zD~XNmC;8n^7D9(O*nxgO)4ubhlwcxfQZhefJm$o?@VO|6{J%-@=n&h78^4f1i~+Q} zXc8ORq*M``m?jR6rr)n5e8QxZp?k_CKQPMKpE@ZWsN!VQUnfECC(dD=kbWykkap`Y zlM=6@!Ml=)J|hA2iH(0uiuPXyTsO&kwG6Nr$bIFONiopCGIY?6#$j!A|5g@86aSy0 z`w#46z8^Rq792VYfAqav)?Gszap(u(&_y%Q%vLZMZ zQzYr7N%QlQ)5nf4|sC150te^u1K1YDY6K#B@l8#TZ^Y5w0TGX4E_*(+R<*NQE zGNQ^)w@JYdO!!QjtfU-aIb~-RfntmS8pCa}P#8B#CXnZ!NVu@an4n-gJ|fA2XiRd} zm`rmdm~Lw070gVKa5E9P{|O7D@fizkMZYgt2!k&Pfch9i)V`wsw%hSKe*#D2zsyHJ z2UEO+K(uR)JE=3v{#6_r`(Ju99 z;8HEagk1D7&_1)BHgE|#MAYa*k;xPTo})~{%n;v!OttEjiHWB}!` zj!d9&ZM)>Dck2m+vh^oYzBBEAZkO^M7>8A;|Aht7!UlA483veH$wkaXD{4zgDVn$$ z9V{fpDLQEUl^vmtD=@+U^}7g&g-x%W?}*ZlMZ4kM?b1y-Mpu>ZV_wQG7VN@??`zLU zEf+Brc{*6j+ofc8B7c@lVDKWDP`<+1($9ajU3^S%6{fGX^GgSOn;oI?5AGrABP`mL zz*@OCl<$!V6 zyn}fDqyM%`swz@ad=L}ls>`#BxF~o}fGc*)+h@~2^RyClD+Rh!aT-9TM0j|LmW3+bZ zkS&;C%>nFmR~Ez&XE4DzRW9z3k^?zftiTwnFx;K`j6q6Dh@6T9SE`16I;2gNF-CJ= z7D5lTGA6)MH1_Kd6J4yv2pvpt2?nJdQpnN7sAGcVsO{e&3otmKLo&+=AUTYQ%h_2u zJEUwJPHNG@S&Xpw5RT-C4lyu1vO^j$s_2kbReucQ52cJH7@pK2^~~p51Vq_BlMJGM z78_ILUm@*&bqD|Ze>ef0+aYxrUrGR|U*5roMDdS!1Q9zOvPhM&9z9%zA+}+HIW(^z zU{#Mc<6^8s^U4ms7s|rej0w&=mVoOr9nwODeszaLXw{RlBU#`EQiRb>9ehxf`r9~L z43^Mdp`Kgr876QWk#hP%hxq7qbx0HSW;Zt-WviC}Df@jLGXE&*`?<%^!(|v}xCpt> z2RbB+?jU#R(KMirMwUR(MjJh>#}GZ#hS(807-584&>ZGSj$uA5!31rzM;M0zuEtEt zMd5Kw95VqGA?DH8z`*&`qm41vqc%!L&_o}7EN8qv#s*Y5M@E0fJRcLl&l&eAPY0%7 z5GW?I9FeO3mU)lk`PT~8O27oxHftqSf$XqWtm9c|$F;Ho6U?JEZ>6UhXYp|;yvsY4SR(Z&_%Vt^jDW>mxO><}HCRUJ?}i4ItT z0al>3$68s4HrAqp4d|kW+Ma7AL=(Hw#yt8siventwC_cLXko_W!scQjI#`PyHeiZA zYI~Cb)Nu`lIE!Wp6REStDvWRurnm&Peb_OY*vwJ+2N0M#vV%CnGif*0W@L^E!yt7F zmenkf~stF9;%Sdq*JGh*H)P#Q|a>|yo zmRGXV|BAIT|5O6Pg=k&5mcPzVe{4e!WAt$|CRlhH8@-Bq3o`*1jjE!KyAeH1F~WJL zbCy_!#?@=35?!pp03Fn>AuzPDTh-$XrZ|VzwQHr=A^7@cQ((z zeiMHXoUQ(>CpG7>)0=s2DO=cj4rP}!#sG_}slSDkV}uLnAKgYKRQ>I2M2)|bn-uK_ zN%8pv^xRrLLdgcY*Gdkk1oOl$`Q`9*~m7LG*eEu#?^Bk?KQxEeidQ~fer#9TzGL3OO40rjII znYo+^j)}zlBPl#Cl4Y14&kh!|1Cxc&Ie~>R!j%|fhzWM1eIgrFWh}dl{wGCJqpXai z1vAdiA}MAe7Y+0<&qDgoBPn;702iW z&L?2{huA(1$|y$|Vv5~pT+hN7pg{m3 zR$_wHsNWFDVzkgj2bZCb%@|?}CfJR71LLmFu;Uw<_&Uz+=13~8X9AbAznX~ASLItG z$zp`XHxR(BktDR6w?$%8j?hDYNyOVPSOANzA>iBD5ake!2HLsTY;^93WDUkRm*K+w zGpW82?~G&x>UVJuV2TAdF~B1*4DV(qnB2o%eKQN+%MLJHMru*Jk9k-)#f50y&yit- zdD`uVxVc^Cf0)cDAB!Zjj1B=8Sv37fO0H!>OzEIK!9=$(@JSX%uZc55<7tlUR>u7w z0i)4O>KW(aa!jxlooC1trdUzW_-8p9)Sn{*7(5s8{F8cPB|E_YeN}#*r-F&S7r1M0 zBUL#2CnjD^K$OGRNjaKtu#hTaj7baqPf_H1)cbjS8ySyF z(H~@kcd+m*8BpcS58NzNL|BF?nrH|+MH6e$LKj`!g5D0Dvf$6OAJ8ew(K@PA@|Yao zDdm5me0rxW!Kk`ZBGk|Al;XcK|7D$$P&O{_l!B!^|5|;gRCD1rbV@S@*n%-em|zZ5 zoJHftPH~th!3f=(nD|b1f(Axdi7~Fchm71zV5qySL%n-@r_8^X`To=?i|$qb{C^)E zY0&TQlvZ>wi{S%AcozefvqJ{>4>FLl_E4wPV}Mz-A7)~YGCHWQU_SJ54kIkOn|_aU z%0e_A?Gzui$2w)^ZqCT%qL>I%tU}lClm-lN8OE3=favi~zGBNnPjvDTS~7sEF~v62 zpX`(vLtMR_jWiM1eawS(n4pKoQ=L-yAoD)cDXTK~6Unpe^Z^Fq5;UJvkb7YT{jdgI ztiu2sG5)ZVzyHHUN=1O-N1al^0tr^3(?&fLo9*oM30zBNC`VZI7y(DzM3`W~lVqfu zh0y3_oT~5Z!+93BDdV?7{GQb?-Odh8KD^MTq6c+jDJH}!bkJ7ijV$zkEbwtB zuV!JQ&uM>}iO@zL>oG+i?Jo!vBTP{LHvuxeQFvWFJ>xJb2QJ;t~cwK)P*?bwPD_NjK9d4}iT z{GLdkWg#p^8x3@@5?!oD9~WbcE+)7PQ*1`<2matv<$`rGhsIXxr06;3#WHlzL>CvT z`mNXTJHZSKa?7mqUlx1f!ME9s9qriJUoLSvhCvIs4#M-P{x zkG^W(cAd1Ki4j_u%Wz?Hu?1Z$B((_|m|~?mg6-DvQ%l;lb+Q?Q?bq?iWiqh?J3((p z+R@l`oy?%Cual~PetWKy3i=y+trL&3x%WC=VncsyL?1JSFEOBmi0I(#yH1K+)JHg@`W?1TVpV@QDOVn`PD)-TU<}@10gN$0t%dd@nGZdz zpr3!_x{OqD5l~@ch;U$gVoY;1=v zsl!aLUl$*PW`bk8WDfP?x}^9uCcttGu?n^0yJQhYCwK7;2m(8$OY-Pp$?F7!HriEP zvO<-syZ9CaJN#XjlyMJO7k5c523K`SH9HS(W}Z+z|Cug+7|6tT(D5Ax{H04Q<R{k=U9yIqCRntV1y+(uw4U$c z7mX~0D<~T;bV(b!IIG(K)+O^J*1?DT2!mXgEMs7d1wAY<(Iu-WTN}w78hMWBBLbZy08Fr& ze#ysV4*jpX__Q-e`CsmtUd9!4i-T!Vrdwud@OJH%1#L`J+Rc{?Sm5ApDe7Z_if&nq zR%JJD!6&fOx@9^2($l;7bsP2Pb@Qt>`d`{DE$H3a&9|Qk^tNu%2I#-ETNYt>Kl8U! zzr0%_%IVYHQZmSTndiHuo(tz?CZ>b-3KMhI-m45mAKPfx|4u*37A{lu=#Db(H5SU! zAJ!Uia(*oWGu-O@yTfHRc!&E5Puj`^?#qtEF-#Pe@` z(JiZ}F#p}nME_pKlrdE0|1dEIsEv?PEWrpXFvTj=zU=0=UMz&I7-Ckn zf5nl;1UlO-25SE$qcP9Fop#G=DqL*C2y>|ak9z|x)HcxmZ8smG;GV#o$^fsHXrOFv zyldRa9|X6LOJmwxe&*YnT+R0KP%m+~nl{>ge- zg8I&M#AuiG(nd#XS30UPR!-Buc)b*DWZ~V`^UVk9aW(q8ua_;Dph>&22La8H0jx%E z&-D_bvDbP@l>4riO!X!r+i$%rLl;{yDqSx%OyC|sU?1aw>t#9RxQqZPM?YOJIm-4y z>!mDDJX8SMC9EO^LzS&G47WCGp8*GnD!!XxOP5J&|XK$N3Z8~ zIh1iR>c@~;I+~cp1WP|5h4We9Qvx`3y~OCAMWCNC;iW7@dw3a{q3qqljwz>0$PDG^ zPwS zoB)(hvJ>h>-n@YenwzlGVXKwVe%cBLhXBY`V|2b^hguq6Kp|y>mISG zced-{OUXR{*1R6s!iDzZ9+@}GLRhNYwMXjF-4p*y`(8cLj4t+Jw0DoJp*_U~UsJzd zkHnO{{d**!939XjW%RQT?BPqvq`HiGQvyK`ouBqd#J_*#KL+(k5glTj_dil~aF0}C zic8Qkdc>sN!&Q`H%%Ois4?o4C|6x5+LwkU$C|l*UD=T_r{&y^hi%~zON4haYkN(>H z9$tEpq2sYVQZdH@$B_~mlH(ab**>91e2gl4BtdHdJN=%8(fWZ2EmDjAIX(O~i~z6! zL-a7gCe*8YWHp-DhBn3+V^M|)taI7n)*3G$=@AE`^H><&^GOk!H5?JfsQ*YS2rr<_ zxEKqCOrehUg#?cNA{IpbV&1o_Z5! z(YT8YZc7K%ZyI!eALt`LXsP9L4`(A0L>|+~7JF?Ke1h`W#KWJhcw(U>* zPkN=IlrlO8kfA+##ig9=$$S|uw7q&|CA!#3hiLC!*{nL0FpvqfeR`z|&3${N0Rvo( z5pJQsSxUeMQolcep??4YqjzAhc$k#+N)u`a^-5N?AKWVm#zwDXO3RqwkX~7cF4m!O zSg$mqk0EO1y%J+^IDx75BYMU7DFL8`aYZjbE@PgfSP)$d&^d+;VT6gQuk4kQgP3;# z0it;Wyfat;tuuS2;9v%x#ZjPH-OGD=Snxb{ig8V^lp6%}TT+N7`sn?>SLQIl zqC@D1WoXqh54z~0@dq-e%70{?L&?k^Gn_paw!@Avz)IDC)vErAURjI|y6EFFj4?pt zO3oNP%&T^s#T2!}XupabqgF?zFvJE`e|4`cM<>I@D%F5(s9(cG7-La6JGhpeppOeM zz#2@j9;140Le+j~q9!t=^fem4Rw(>?cg3(R%$Bc6q3mict_i$EJcvw$4 zzK_S_NG5oQlu>pcA;suD+AF0M?9itl+K+SQ7+@2cPtYF|j8ywm1b!4{4AFX~S6YrC z(C4^m=kxqqFR-Kebod)7Me8Nb_&751GB+V6t9YEKcVFQtIF`s?eYy~EKR zPXK74{T@#f#%-MaFBsp=kx({1WxUBoza&8AH#}V@@cbKdM1BH0+qRE4ov^Uh$D2)< zV8=c&Phv+q^+^+YMSZdv{hj-ys*=$_lci5jfuIavDkst$1fh3@Pa-mJcIIYSeSB*Hf6`|lX~=U zDJB@8_1iw_#sIgVc440spGn4W0jB7naZ#Tv!4TUpTGS_*qO+KY3j?Fyu`^Xh8}-^g zJ}AOO*op!6VR#8UUP!-7SqSyTj7J+6po=#8Scd^NVssfhM$KWPXgGZtS#vfWsA!`i zyrNHH)$vMFjq$al^j9p@&?hZu-pEeT#yq+>i@{BOQpP;`%>;H16T5vl2HzaUVOy5JQZx8@2oUBt;$PF^_?zXlA&W&xMC|7~@JzFvJuqSs;0UvsVi(=gco) z;Dbmx!dbK*BK0-g3lI0n4DA7y{)UBD^hq=I9xhVlN7xW$<57<6*F68)V}1N|f&o4Y zqWd^E9UZMFxtTC|ihIJwr~70X#?7Q2wUq?^TgE-#C!X?e?3D5POMRjffbl_}S=cW{zh|Ls`eiW&+xCl##?G{(R@^TY1ma>H2H1!ZuE1osetwif zzuo)!`#)R+d-RLFm>ui=5-9iTmpSEuOh~(4)-N$yKkXNd`Vgxy#_G!m=wQkY?T7U9 zCM7m>c)$3Rqa*sITh&)E{&MC+{f{hsRKJv?dsKf$7ITqO(WEMl?w2ekIFEt$G5u0g zM*u%3FicJ-Ftl%H;j8I?KMSJqWWS^sJlij=*Ra4UGIT9xjs;gRKQ2V?6#_&3)qa^l z?e7^HRL8&fi-!@mp|P5Q^$fsDv|eL}SJDq{OwdL1btc3Vm($OEqn|e;k(ri$301$h z`=$6g=6kPS91PkC5WTeoOuLqe*y&Xa?4&|vf#*NkLdv;_{=RQx|BfTMiNNOidHW6JANr*kP1J5-fr0_venY0V9*{+p6Kz2FS78O= z4g*qw_D=?62|Bpk<@pb|2vrB{#t8GM6%Fu4BvQXK6DfBY;EhNGvipD(E+NHxQ)Yp9 zp8>Hcr~3}b9A&L^Kq_w~LuCU}hw(uJ(yGek0}`u#l><_KJDxefCtNa2_^SbFq{2pD zHJr`F=vNc@pV$!=qjxSlK>NG_S*Xfbiy^w0;4;+D9}tIm!+!GO%q{Fw!~sG`CC%>dtMVFG)AZ?muf7W{?O{&qlW(7BL>)I=B2j^V`vvWohk zmVQfF$e|zVR}Ju+66$dwnssD=`Vw_`e-@hF!cvymip-N0V%tO z_9xgVMo$l9c*PPCK06?5m{5C;1yINGd+CTeM(CjV{D7=P2Se44-RQnJAWN4q{w0nO z4QxRhv*==iKF*>3G8w;*elN3An+uzYI`ps+eO!Sd1{h;2YOf5)W^~cGpMKbc@v8%} z1(UyX^F6=4(hKnKkf0G1L9FmGhD<}SZ@&-`dGT00cbqL1g+d8lsllk5RHM6|4WW;Ls_$W4%$U8Lp;`#D53@j) z=W+#+)>kGnn9FjAmKq zQJ(+gzYL%v{CYrUF#d)Ck1-HU)KeBf?|&?S{g0^>4VL|kk^=R^iJF%V7EDhK&733utw7>MD? zgVKunDTA^ZJuG{Qqx;pM)S!3vptzV~6B^ZnvIZ^8V}SFXW*#iZ2r~=0(9UBZ+SsHT za1F+oNAvtaDf~bB)eMS-Hr8W`KI#_^N`w|BXkNuen(0?JC=1ZLn)%Vao}HtA!=Pj_ zZWzo+!83HcWl$>6$7&3)9*re~(v0ElgZyZOi5mwc#rUp4ej`EvSdET1D2?dfGswH5 zDBn9MF}n8=_;bv6KRd=0>o9tdK+t$7!^B*K4-d*5CXdl^B@;Y8C^iO9Ffsbgq#hH@ zq4^B`(Z;gp$-uK@M)@3pV}wm;tR$1@;1&$fc!5B$7Wwagq>u{_Lrk#XZ=~`CCPW7v z4AI9FBQ*a;CeTGAAV9P*KnElAFvbAGfRv;CBID4Cl zc!QL#A``d}{T7Y@?KgScFu(}Sw@4`_Sn>*)c$Rx3+bkN4-m|&>t|H+QfdY{MV?@Wvp7+^I<=wgDtYG1>dqxS(%1G*m$O3`XE z_Yr>{pxs8M(P$?V87}mWL21DlV@xo`Fd`GLQIA!a;$qZ0`SSoxT!}Wep^G!-jO8GhSqd&{em0xpDpz%$Hh`4Z52849Pa*Y1R&Qbf8 zouh{>sx0gn!-A}6@37ETS*bv6>#W$Sj7!nOm1q=ZC5sl$sdg-Rm-=n8vJkBd7j;~i z+h%2js@N_ot?1!q40dL~KL`vLqO)sOJhb=9N{HUxS(!nrBrEe;nP;D@RHBWGFxoFG z%P_@NsF!A?8~p=B2(pAG5*OB9G;aL z^p0l77@xob=$xFD9BQXzrQm&bdMf?V!-bfli{@!rX?~yQUq79QR7c#54$fP{LRgNT z#ZFZDe0Gj@4GW=z-Kripqlfc8V8>XF#svh3!EXoB$VKFHAF4_V&N zMa89A-p@tHKW3#K&AO~KVT5Z``_);QRqfZ%|07a}m1ta>j@Bb>_+b< zQjI>Aw6WgJtfO?Z(wO1Gyfw?`v*>^kTDP$frl_~Gz>=((82u?L4yNd-`V8aHz9TEK zs{ac&We1u0EB!IOJ1dJZzK5g4=-w=!YQkmAkKuh;{{8=2BDtTlLH_~H2EFB^N|hgE z2N*uY01O_^N@>KzXrcKS{n17r6KuusaqbmNv9y!SJ&~0Km|`vJPqG13|74c$e{o?p zWhFrywRJ4?96P`eZPcG9RcK%{S}zb7YA>-6#xHY|b+NNmoH>TC&>ziL$q>eW=P^WQ zHT}^=y_^1Tl0u6M@2xEF<061HoE>T(FhG?t#Slx@Q~oe3RcL<1J%ZX=0zw}nj3P3H z&L}(YA@DKg$0*1A=#CQ*S`$1~sy(xjn}Z7*OL_?;&j5_j!C;a<7cjvV)jq{pqdA@B z2TJS=O>{PqT1-ABwdj6AfAlej0nTBFCH>5gm6(Y>Wgr*sXWZ@RVJilh!w}~%!jb_N z#7azX5vI5Vwatu29b3`B9J*LINPwSn4`7InDt|%d26_Iyf3qVhj4k|uk!2w)!x*d3 z`jS78F!_p`48vLOV)XvY^Na4+oaGSxu@bHC=#R58_jTnd3=`Oox;u?%Fk1-aE zG7d}8-)~5&&?qG^^l&+bxEg~42~3r74kIiXqyIrey#JSrh>Lnm4rW0#4KjchCg|h5 z9OXlX#KaI6qkZU*EJFuZVT@VS4;$k9VYHW%fpHc(d`N5zuo1N*=&w9-D8mQCNYRm; z4f+*BQZ_-usbm68T!J1pt9ollx-mGD{^*>=0vj2BHVdGKE{5o1gdxTlV}dEBsOOpQ zoD4hSLO+KI&_ow)^wF;-6BwV*K-GRR3r1Yq-gj^&AO$*AWOB4J@F%k(&nf+d0D-c5v5_ zEJPC<(D8<(3GKVd6ngh?=Bj+}kd$v?UbHc~Z%CG6#<-t}xiE1CgXKd~{4qOzn41c% z6-GcvHYD1=NNv*)zx`sKmE0T37q~ajePu{mQGb<o7OfFbJdaAwMPx!XS>Vk}esV@MWZ&^ja@rnnlze{z?rGR~pCh8=%O zfFE!#q5UC8faymZ0h%2g?WY+!tmVw8h$4>QGj`HBBo(Ny#Kyh)7(a0S|Dj!3B5F^BqDBU11^nL*=wp8sg! zh&WXE=dcjQ7-3L7BC{BtN5>!35sgSShUlVIGr}v?=#PDv{(6LO!Lic|2uN7?HzQ(W zvS>s+^nO1gEojz`2>&mWpKOmvVL@hl-owd&g6;XB;D{_j?`j5Mbo+>`L38PdY{B5p z5h>Yf`-0dbLl~|g5KMgnLHlt6NAC&xWAyBZly1E}|4N6z(Of+ujmp6$)krw+q08ogjcQ64{Ol;gnI^~Pf4X}|AL!q2kN(w84R%soi9hE4uh}A zB$HbDycz(>g zWDfP~Vp)k!1A(iu%e)xhLg2fy;oHdcuG?pLDQql_RQPwq5}@^GQiIyESPF|d3N%o^ zpFmU@T}*H_h7S-p`pe18Zunr#XZUD;D3)4uA0{&x<0`ZsVSecPH|v zi3n3{#Gsi0Xg*mE_S?Yk|G5Yb-yjA1 z(Qy0*sY2t#4N|W>X@fLjT)9EI(fip3*^JuHS*Vo$zuX`e1{ZALCzaIO8>B^fG5t`l z-5{m=Gw#w2vIyg=H}H)(#$C5TLRIe05Si+*euESrpi)8rX!md6%Z2QCaDz0XmL&jH zjyLd5H~M4wfh;h(LFzC@ACs{Syt<7Y=QhZUa-2ZR$RIAjI8T4HC+Uw`W@>{(s>93% zNma$B4ZN$39nG>cG{4y(OEAQhXzV*GS@cRrrQje^eA1{?pjkO8i_ri1C|^7zumz(M zqIb$DZ_i_X)DEV975z~=mHwDXxmckZP8*drbWb0ZS&UFOh}0UDD)g}d!!t%@C0b{V zN*`(qNBQX`_0^-i8;%T~KPol`FOEu+Dz6@uZnWN|pYk8{JCylb>31m4zxCdzG*aRH zb5vHL{o$zO&|fzy<%cmr@2J$GmK_xztjY=7Mv!haj!Pld*^zaO4_RT0SoueToRcL?9&W~V$??%PK7+o~xMtON0 z8Nx{UJ(*Jez{E$=4;NxoFeVoMLPhlOJ@A8lNe;Ue5`Ojf9h1IDBc z&7Y3R3_1soNzqZ14;hn63~@20hmDDkR{0n|sbpcCQT2z@|7hxu9OIpF1aQ%ebi@?(<5}Qpc7|d7m^7kx{g|vq9sAJ1RMp==046q!Nj17Rj>!@XZz5n- ze=`ANa&v}=xzKMJladoiHCADY4d~y-0Og%5pxW;mlY$eO5DiQ`7C`%90>b!_FR;e! zF#Ow?BtqtOH^wB74r)JVTnhnVMt_TlxUjJit&o(W z`2hn^`;Z;{f}LSGYHjQo^>$K<9yVhX5fFym9MuBm>0tvHVVx@Xac^OY-RSmnvx5KMm{a!d+y(u&?TIhj%QsGZD2+vj8f#yjR@3Hm$bWFswn!m|O79E@ZrxDPFIhn7#C?~aOEy~IA3>P*Rt1-r$YWN*H zJ)H?I&WVLCI?CFdtiS|Y(7%MhRe3RiTg-DA0ikz!PHc4km=jM~&rUJK7_|lhI)i|3 z&hh+n5#LHg=-tKw=q$0&ROHK zK$Wppl^2f73e(CPsIT%wT|7RsTIl zh58Td^dj1~pOA$Z7EkblV|Kdtgsj9E`!G0YLgvssY(k7hj4Pku~MD4-}S%uDJ6OzfP4qO!eo(V5!06Go>(5qtsRe$q@glM}HGONnBOo(2KOD3cW z<3CSGJw{6>qzUc6PDm>zcM}MD_b~4tC}TBx_Yw%Y%O*0iii_xh34S6?>K-PLOW4Wt z6JnwsOh_Gut0%Qp$WI~o=u=Pe+joLOF`KB!M?z~a7KeBFS*Nrlti(vPSQmfo=qbyhMzfoGz zK47D4#`K_#qB~^7*vL<<3E+s0Qm;I6qxfi4Y?KgfjL|w{qZD01`#Bq>678Ce;-GQK zMp=Q*;*GKfGa(lgq>bPe_Vk21{T10j0G^tF~4$rqin%sf`G0i6H^=c z>34?I%xn~w3TF!wqy06Jqn?uTdL|MAQEr{*?R8AFOqEPBQML*!&WrTBw)19&hyi5=07Jd znMGXq)dYarxp@iE#yke7-Nb@uqI-T`>QMi6o)-}^;e~mLF}#R?Z^lLJ41oDOy1YcFU!Ug%ghY-d zw^F_#&x;7@=jNqec?%0FZ)HK{ZDi&)=FN~1bneKDkH(#B5QDpEC|*Lxd-Jjoo%{2$ z1dRvSp{ie=mo`<82}W3SJM9mWYSbUeO9Mt2p!X<8qS}2j_a}CUCdN;YIkcL{G@36m zFKU_B*^%n-R-XSDgzpjvn(wo7b#s2i#H!pz22>dn|Aj>!OL zuIjUS*{sS~d{O3 zDq}Zlqj}k)>aqAQ)Z={R7z<*WV;maeZ0s*Q{}C5471l&v=FrEorA)LjFBZD_ye!2O z164msfT|u-RgWcqCG}GTsGKH1<;SEN{ZI0ICz$y^=jl@Al)&y}qur3 zjp$)BTDwh3A4a=R$}GlvOiIaJoavsEQiaCelTwG)K9l^)oeB4wlvYei>5s+%lTzw& z1Z9&_gYm(W(umPfld?+HA3e$658%Q(c2bJ(#^ab6!{eD4O>IqVembLqH@ zv%>}GpGP35*RVrX|7&)L#ubyY8Lg`(rRY8utYcz~(MIFyNpaD+c2ZVifNkj2vjMcO zBQy7tiR&gaVsT;IFeyvW#b(qRCMCl3CU$_~&6A=(K%lphA&hY`hJPh6jP9a8Y99U3 zxtj%+v(UYhvJmzAnI9c&#sJ$?`GE{O;UZqn0uSPfNq#oZgpW+hQna6(lom9frTrlS z!iDI)%7o~&aF%GjGszc?*~mXw_+bKhkE1~w8_~gL46zL(oWVH5Md1q063Z~fDvbX* zDGnO%Pl|`u8Un!tXH@+MjC+IyKAhxd_yqV7M};vqsd5_`QRVhYnN#JCNiiN}=W8dW z8k2}4!i=_ll2>we94Ygd6s(sjsJ3UVt{#!zL}JHPZH2~WE3@Fe$;V=Di=&i%ac6+1{YZ>jIE~l zwlfQDJtbvLbSRvXT6E`4$#Qgdo#N;BEPy%HUOXj*Pf^ASjL^me8`0Z+if=u0q}Z+6 zb>>&?drnE^(_~=pDXGhFk(N&JvwJG`pOQ8-4xl4y2TqCce`MweCPwYZDe=(85cLWI z!StvpUg602W2R(2TE|VvVvNv3`}iqYji$*)(L0gApP?Ny1{c;zQ?d|aY{0N`igz|L z@o%RjLifTc*@DJJQ=&gh|3y=>0G;1WiGwkEX#bv+sxl_ZOWDA41ipBRPcst$Iv8F) z#n12Qf5lWry19t2U;)+P$|)&cNe491zJZ-68#n^=um#f_rzDTsO;b|vJUe-SBftc! z(R_#_Knt7E#x<%PXE1$uO6I+QD>5v^h4u(3Mgv_mA7vo=k8ySwyvvcO`VXe0{BQWt zlq^EKZAzA*(ZRie@mdx@t#e9>0y5OaS)<>>S)CgwsL;--6%RBxJ+ z8uXGWS&G(YoSiCfVaKZcA0mH=f!{DOI^UC0G=3luRWIChsBJSXIgEFl=4bc>yz8`> z=6zfFY2IK+U{^EoYP@DzsxYi40JLwNmS$!1v~*+g z@-#0r#8uP0$dJtZoeg35+O)J_(!z#b%MkIK(=zXMQq(ssHR#0CvJ~ynX=z36vuT+{ zWAn6>yn&yyBebv=ww;lpw+LLD;aBmv{S3c~ryZA}zQYV3r)C^> zqm7%&zVnPML1UL02{6V!)m}QoN2AHef%HcmZRHU&d?T86T!q%p zXC#La7QDj-elf$V9jV7EbZ{}IznIC$GA{IA&hSlVCcrGln4)p=4Bv8Qf>UN>0XkLe z4DC~A#8>sF%}5)@xEVc*h5kXR(L()<8D8ngJaseDj1gurxMoHQTbbwD%na{#q(j4u z)M9ue6JvT46D!>r*@EsZO#B`@xNU}aJmQiWX+)dxt>`w+$P9XSa1{S!zQ3?jwC|@M zdJoJUi9CX5f{U^xp$P|46kHly?5yM;r0a?_gzxUh5k>@9KH8B3e?(| z5RG;g{)mBnER6ObJ3>9nS*!XXCPZhL3ELPqG9wij;Sx+@`k|d;=V)!DUpwpM39y~# z-Iie2QKjUUYV>3?!I=Bk8&pA`&7c-)*rT>>RG9UGn zn+)x5XJk1VbIgm@_hc0PA7-RP{UcRSut};ScCytbX+Zx+o1_`tZ8k}_DsQ`qU$fJ` z-6kd}{dhd|b>FYo`}{xm+;h)8_s$`i0@{)e$vm+1 zj6<>#3`{3puxaKYnc#evoG&5+pgsGL)IjT%hh!6&b;BVUP|Z4A=w3%ZXv?=$lyc72L>xC5NLNFl8s<#^&vS3X4M{Ib23CAAIqUECAd8dWg;FB>1;OvIPu+U0~tMB-{!QW`eF)56M!6 zdk@JSihtvfYz1v^9g>z0i07R{G8+uObI2)+I52-q20-s8WB?3&N(MmxXXNxlY%oLN zfkUzw41#XZ^~E9C4CWprBcKm7e?)v=9+K&x9V`F?Uy*Sz_YfIZe9s}-4|)Sms) zz2qFUfd!x)+zh%v9~kI6B-@nUe@Gq&v%tiEQl(%z=mYaX+W>70=7J4i01SZULDKmc z{ojXV78n4FLA&!iO3r~7Yyk^{w2{L9kiaJdI8Fet6f6X-KT{I0P~s1^M|R6WuxVsB zKVVEkqq=1_XdB(lW^=-U^`Lc3x7-KX!G5qEw0x@0|JZK6YDol>yZOapB1r6(ZZLRy zH=E80a0Us0S<}1up<)7FNCIH)MI-?FFYcB;g&E!Q7?^cww@f}j29|Wo9I)xiZdn2P zbGl^%7`Uoic7egGyV+CD`5#!(Ez>)2ytZ2wf$i6IOAi>xCC6at^+XKXS9Z(P&(T+R z%L34UXEz@hgcyG7N2hI0WAkYdnDt<$^>;&64cFTl=od2#Z z-7@na#Dm?k7|eaBTkZf$w|2``FzD`wqs>u#rv>7fKc`T*z$^T2jcQ~ZyV0Q4OvAuwR^>hWknI#vb;D6VIw1z6T2LyoCrkaDiQ*|JOY-VSo&+?vd$WVIdg+b8qjFUaro1{p-0+5r=Npd zMQrMko5A+Y)CgGmTWSOhfR0t1is$R^NNML|G6X#ARt z*HV*UA-F=>qDPj3b}u#bHRr$mF%nV&7zBM_@;3zdJyivITWC`-2sVMaZ}iAku>CDc z@GU%;4O;y@{7g3ygC$@t=mJZ@IxqnGLG#-^vIDe%gWoy{z=81?0YEEQ3Z{TwFavA? zS1A2mG6GunkzsHam@sn`pHdCTihq-#(E-^Ank@mD1#cT0;D<`lCkFV4-6(^1T7Vxc z9c3t;0pb_11^E8wcVu8zfKRB>2hIt|VlX%-AQNn(*d`y~cfPUD3&?)ZcRmrrTQ3Ok zyWeCCEWs|k5FR@>Js>sE5Bd~d6ySHi$rv~YW?f9UAbwyfSO_{ZIVj~I7j%I>B6KYv zAlMF?&l+WD$_U6z(7P}oi|{jFLW!|m;4U!tQW8*@8IWFd>!N_%2l_z64;1jSfJ_7Z zU?Et#I3Vjl@8W<{Hge#DXaSqRl(Q*e78wFd!AdXyHiNFq1F{`I`xOCpxf3Cn0h*Tt zWC553mVyCr5Py3%C7wmb!7R{xB{c-Pz#aGnuXF}vD+ku40okQ+8Ex_-5rgSq0L%eP zbBGx9f|X$IRWyyV!7eZW#vCUjSJMYbpb1REZU+O{_T^+Gg@S--@V09Le8!IqIj;@K zd_{mIU@qteeP9#l2lpxaI;!#~Y6wgK&A9=&nFxYlI(F9e)DU(PSb*IQ9>ezDK*q5H zpzR#|S5m`26F!d`0iD5{aNxjO7?6I@b$dVt!S=F%OqTGQaGXufH&b<>?YEQ=EZsuM z!6sKgTIUe)gVYTE!G{9;vN<*RaDd%#_<_5y?UnR;rGqgB!aWj@cEc#v{~smC5U#D1 z9JITsTF_TT(}1=bYDPu$J8B>T`*CUj41jj9^eMuF=4S~H7VacGX!{#g4+eJAsZ|Ew zq*H_D4`|}lQOqsr)TvGq>Y@f9a*q;mBsfS_fu+Z&V$lC@N&p7Fqr{*+h+h;L{(%TU z^M5#Pl?{5qc5oM%`y=PQiuX7Ps|0`MY?_N-#9_Ac5zcs62Aw!&nGQ>vkp#gk&<9p3 zhv>ty8T5gb@P4op+dBF%znD%5K|g2*6V4@k%wgGq9R!UdhK+z+yOR$ZJ^b1SRMy$pk*W_1b3(i#~$W;zC;M-fu*M%W`iM3 z5Ra~Guu1uY9iVOEVHsmmBO(O`eG?BmWjY6bh!W5~>9DK=t%--_F_p2Yhh^$KGB)e5 zECO9AB!HfK&S6=PZJUk1nF!}l6Uv@TMnPW&;nLtU;n96y04!ZZ0isD~F--uvvYdxy z4hN-I9A-x&5o8kp^k0ds_^S>}4_J83Vc7yU<&rTlH}5bn7!Uzy2hBI)5BhJS2EoEr zv@P+J-g;Q3%tycNFu$-)xL?zRU^{3YO?u8k95@Jod0^q~v^8kIgEj_(j>EDG%+d%D zLxf-|Xe~P|^NHBEkqm;iP4KaV2b;0|&rtQ)_CJwfuoO&)Bg3E#41zhJGuTK%9N7MH zShg#NU34A`IewK4fi|!h^n%BT*y<-iZ2Q}XrBCVa5e_uJPsG1M-%mJj^#_Ng7j#AR z%6(vaWUn-wr#fe^w2h@E$Mnjy^Em&53B7!Y7)NWb+zi?#^~yFdcXF>RAb|CZUg=dh zorpl+j9zIxjT!@OpzTZ|wo_tTugu17n%&E9y~EGxmARl7EK>G@UVi(XbQbo?ey}OC zSEk!J|LvFcN^1fo$ik6`3Uhm97IxP4y=)dGKpq)Vcw?_DhHnB5=aa#k$S}4KYyg8` z8hqi+y|NH&2kXGne8QvqZzbd7z=B?x3O3!=%luyjxETFo6<-)^rkllqv@8Zb|}Q2$m|ls#oSH z8{C0y+eVFH`@uSe)xFZM&_l`5eYL$#c?=?0OT<vPl$cGmM$ zQ3pisPO1jN-q%DMmKlT%dvf6C20GSc)(IH02cm>f=tE+?VuMd1bts|R)Nj{2Mx;M5T^xb z@1`jdsS(f)7J}JeDVPtsz!I<>tOJ8!Bk1j+#Gp0ME6r2L2$%@k!Bnsb%mdrOV$gh; zvu6tDznudwL?PG!dckJU54M2;FaYNE67lI|1WW{7U@BPHM~RdTYM>uXyaenggV;f^ zQTZJqgJ2VAn2HW2Om&hY4r~zCqqHgL1G9<1^$k@C<{qPs;a%WyWq(J)mxABZ`9N!s z0;q65a2kS5U^{62kqjqM6JR3f1v5eOacTg}1vfi6DCJ-W=mVR;AlMF?e^@ls=7No2;cO=fa^Qk! zS3+u^G|wP`x!`5w5VT|a!ECS{%m)Kt2^a)jp!Hlb05*a9z}$I#?Da;s_enSCI-eR- zHh2v5T|mY_v-3i#elaxwW`QoS6wJM-Pga7a1(ZB%l;L8q1$%u4H3I$#Oo3l|2~~Q< zD8t8K1$aRwO$0u$h$aE+7Wc`-C6qji0)Q=-QvkxHUqJyt=SB`(9J~lNfL-8muw)5s zcljtoQZ^A2aMG1j8Q2YWf`v=_WK8xb!;{PU_&D__!!tSb5%9;WXlwY>mgA3oDcA;< zfI)EL3fdn1C9p1=^IyA$h{$Orm<0O4bntD^kK=-C>BZnV*P&lI$}l;X5`)J<4XnPN zst4!aKtf<_9yPXfl;Jrr1sriR83eD$Cxc);*Z?lMg@P<~jxxM|D-kc_>USHR2ORZl z+6Jt>owES!16#oNN@zRqoqMU$oKc25e#2=5#;+$FxCPX}75DYY9pHyxE7)5>U%G0P z;fDt(82D5rRS%ANl#Dnzm{7%O1e&)I@ztXY%c}ciCU|ZQeF3bjrG(&1;BIinQ}hAw z0?@FWi2guRf$3m6IOSOq0MGanRSaGUHiG$J3%D6<2L?9(v0}O&eF#Q?|(nu45cCZM{0bSsqz+K=^U^}?rZO(u5 zwWAD=yhA|nwf6}C?);FPgM}Z{2f(b)sKV31!xDe`YAD(3u<5?=mtx{9iR(r z0)yZ_&^kauU@jO0y`W_!83b*h^#}tMm<#5DUa%OnJCD+)929;{X8~>BaNdFe(2xgy zM}|OekSYXi$7y>o_a`F0kqrGzTZ2JK9|C=berdgla9}EEi|ChGV4=BR)`9-$eztnU zkM3uSH|Kxh_2UnZ|YujrQ^(6ymowt%@+ z{d~Nd8hff=7JvcJ1=@D>%T_RW|A4gKN)0#$c`FHn zUa(D}dq5t)jq~4JH6Tj}Xx}!#-fc<*rh}zm4%iO*(1RZQuZkOiQ( zZh&7rC*k@5wqld;lLLJHns8tzXx}j)jkm*tDPZXz2V}03gLV!y(Ci~2&x z643uKB?kjwC79bmI4}ryf=yox@I&MH|7$>Q2AjZrYe_gTz}9Q{!viu=VINHfTKk;? z{Kh*u9~h7=VBjbvT1UiRQ`HK;AwjV8Tl~NPxLe^ds#@W{2iSrQemB5xz0-8x53m)R za6xJiwEi$4GnM@leFwBh9^tp&sd}&tEOm}NB8~SFLCg`E2D%cD$O5o%&JlhKo|4Tw zB6owiX-C+KP1XPE2wSnKS}<4H=N*wQ&~^S1xeLs_=!onB3&9}hzxaquSx=LI8K8dw z@q+=d6m%9YIwE}>1TQ-xTNPe;gzt4zBTJ9S1cl4U80ZC8fYzKNvJkX`8t4P-zyR0? zny)${TfkDVOX1aI{62Kh2AWqKkvU)xEV+;K-*ycpfG7obDFJL#j@KTM0k9B^xu2Ya zDWLs2G6I%@gK4aYQOEI|bBkl#;kCV}V< zlCp8$yl7#$V&i!6e3BL`8Xk9AMMjQh6cx8R%+3np)U!u?78PeCh4}^70ayA9ZV;~I z6i(&K7-cXN!;!5Me=5#I_@LD-&kwVSU@DwvqB}_@E0Vg|6uC0id|9kzAthHenuCLJ zx?9|Qn`6=RI*igY-ST4PyjVG}jWsXYYe>axLfd_oTfB9f;~Y~zM((-PM1kWP(Rq8B zaZa|;!1tWp@-h`vPiLK;&LScRrDiZBA(h~lKhG_;&eSH3RvGX@WT%CiG`b6;_&m4V zEmqy;I9*I#T{cbB-{vsRNTu>J?QS`R5TWR=r2qy)I{&iK%;&qMCZ^?=$BXnI9m&y^ za7`Du<$ST~&yF+2)*l^nMgGEaqiGPqbD>+@snS<9lrWP2E~anPI3}9XFx(5>VyaD> zIC%v|RBt|q4e;h-x7-%0$ThRH`O%h9xU6-{ z@5I^~D)*ULW!Bhy<;?WS5WL4N8$!-gQ*drp&LyfE#y*eprWEbU==9N4!@X{Kp_rSh zO%wk~(I!P#z_t9wE$8 zN4wJGfwOF)VN$h=qT4ZIHoN7MA&19Paaa*&Fl0R77DrRH>rJ~b8n@7Y=3*pp(kDIS z7CYx^H%6CYqfe#G3c2(>aLNp*|fk+8iM-EQ2*cxcDd!VVy$qD6aP3@J7Y{1{AT#ZH{9|sp~l}-;+QBp z?{XNUTM;&Y>y}@HDljjtY&^v=sIw>WGKDgOB6Y2nUaDhv!#DX*$ZZ}2{<7SM>9m=@%Wa=q|8iX?z zR0-#G+F7Qgc#PYs#P+Ys=1o$cq1-C+x0V|dR6!bWtSo&*uH@MG__XNS_sS=Y zrv#X)-03FzhmzJ4NAm*bMDd`hEZ$-zMC3qekCuO2rmNSwYR}cAYEhl3wNF`I< z@&;9sl}PSa%2O-`svrQX8p${d))HBkVo8UMNeZ(+PK_3=&a#xaQrKqL*Ol+PQ^lSN zN3z)exMO}?CyH%aSaF7x1r?;4HGzglDNteN5mUt|EhTOI#{=_e7czPeEHl2m0BBe z3)kyrCCe$%B0oi&F49LkCx~US+Qe~s%coD|oUpm&S{3tGI5nqhwuE%7Qj~g>t5t39 zMX3xpQsPx_X+mi}+bu__h@YDxW^Qntm7vc4V+bo!+|m>hqD8i*rN?DV;ygdcEsIqW z>!t`tonwk`+6cP8DW1(nV>oi(gP})!)REmkj-H(?iib_*ijL5lI zGg=Ijsg&7n`HIqe5~D@I#afCOtlD6-WFUE&CETwv@d;Ai6&tN0XOknrq9JuKPnfFu z)v%bf90Gka-Q61v{c|= z&mn&*0y#NaWZb)PqWC=#SelWtu5!y4l=KnOVCRN2gu}tfy=I{{!eTj{*1VdzgVLW? zL1QYcBIOb-L8LxIUFM+smb>LRrLR}vRkoYZEiNSY3b(vRNz0M+Z2xeHHrdjOY;oHHi*c@0NF{rhc64rDihN6~T7E-mBO;l+kPkD0fA}wiD>NGA(&$#9C zXtA`C{;TBQB0@ zj8Hu>Dm6+~2bDs}8_7%taVDRE@5_uF*v7@7Fx1*5s+w$zV>_cZji{vRwnj!}M#S3a z)4CXnqm$D_#ZgEc6@Pa}%iGj>GnI_REYT*NogMkh0Pe_8fCd6Iyc%vw6aM*AwMpXK z2OX2*29bRKaLd~mFpUQ$sop$ZoYSb8EzQ?#GF6UzWW=M!t$Is}Ro;!(vDYx7kcI5& zOpe;za;!=GT*jQ@-3J}xB7P^ne9$pjA0T(2Z$99bhg1&VBE^Em43Gg>%a`Fhu&-bn zFVm7ECWyIbluxy!Co>J~CWxw;sG(E#^!<)lk(9+L>qhql+;X0ZazBA{vb0GN%f+2p z+C)nm;*LIM|CHEx5YdxGN3qPHWe2#IQ|wTB;+Tr904eh*0aR*x36OlbmTK7nn>-k1 zcMh>#u*R?5GDG>QUbsW~CZCD*P1twKkZ%EO*ATl-u~(>gVOyAOeW3DwGpyxREm^p( zAR|GfobTO|T~~UC2u&B>ze1aB$)0I295+<)bxkCdN18d!;)b?>bOsnF%kxHVvX^ZjV{b@{7GP226 zBYJRFm3&s!T{U`fww7)=4x2Z-N}kU^W?T>J{+9j{TUQnv@oVwr2+qU7!yAkV>KxBG zi%Y_lRkA_l=m3|D`oEVYi{iSnM3M0iCKfx;H!rP{@v0oppby5ECyR_3s!ot{uBzgz zo=BU9q=po(8-ouxxJiGE@Y!c`wY{b)bhTZgE=eb@wkr8u$evs#gDUxN$!Ta5zCoL* z-?n9*!|1{+a{p~*i%f2e%yOobEL4o_z-V%cyPtPlX0kC&O{`#+xKUd)vIHY%lP*Lz zbG&O))v33>wV!M-nquZq=*?AP=W54XJ)JCwq6ezf9p^&PT*z&o2c{E7ziZuvk^gYj ziM!S;a_)yqtRy#u+@vO?a;{^<={LL>jTrjf?+T0zca>OnJ9msFO2JS!w$aTP$yHUu z?qy@<(wx;*>h|+;QyxaUhgmZ>wx$LQn;=qk8@dm}r3`f&+K=H=#=B+X^t1Q@U&zAI$c<$cPuCa#xk?5mzObpDE(+c1(%xf=hjgW>dF5R`Kdy zN1VvN+p#;kbskmPTqS=HRlnB8iP`(L(PGgW$J3^|G-iaaSBax*h%$hoy;&t+5U<@) zK10mAV&epHU$Nu7=z{r7g5Iu@xnk8aI&xf!W)TOA9rLI85q9sd3axdlkF_w5HxP-T z6aE;z0qP=j(;kJs`Hg1fsUFS%P4k)*O zR>@aGiC7cEiHPQMRf}Bwyq{EwJ)hByRAyZe9T3kQaGWzb9ikcHSbLS&^tofc?qN8e zA*NIJm}|0QbbLv#`J9!MoDO0vfw6o=uj_EkH#K6c=&BMcmC=vkI#eZuGHe%6qV6j3 zkunM}3;{is`KEe|w8K?m2~nEbF|=Mi_48+0FQk?Gs^ne+Qsw1}{F^kx&hY`dgeMMng0osglPKL)Wjrt<}bl$;4FC zk#QVy(jwZy984(H9rr7~3l?UhDzD?}$RLq~_#i*>`HtZh1K{>!p z3VNsfJ=fqJ)E}kYl2IGn|2Df4=xLC+$mfq+zL3ZAo`yQdkn7FQk`(329hAn z85G)5E!NN1rZ`hD{Dc~Xp#D^-j0{dFIIbi5pW;?zFmAxbtFF$U;tDgE%Z{y<^H1S4 zIBR^hT!OCtl%G3;@Nh+n;7@ToG8newtFI0%afit!$Oar$-&KFgabE^q5-yyE&UM1= zfQv;}e;NEc#((v2F0twsN4$tx&&s}iAsK`lYZQ+*G9;~B?|2}(3o?H~wR})Sm{@^| zD`jX-y@Uv@Rtsk-gRdVWWn#5FEi|_EmO9RiPPmk0CsE-c?pOCY zE;g0I?VD6B9QQe`v>xzgWhj28C$%F--e zSPKhHwTfK^d*TkT_CBT<{m6NXm|H1%`l+(;f8b!1=KM0VC4*eJ7FWp~RJ-x}sFQJw z5zpMGO|y8=yRxd}i%NfMNI!C)cD8=QA5aC$W=>{A7RVXOjRFZQ@I;yci z(S+n)Q6)c8Ia@L$T~~&bbQ%5jx+*y;3Tf7mv`0xUq{i#3c4Ur;|t(h z@yWfVO72lU>mox-FFQxGsAOjj(}<$Hj57(XFR+xrrWI6$mTJC_IAy7(1Ep?Ac`mHz zOEsB{oh)zeWr?%SXf_Snj9_-R6E}5OI5$a%KF9JM#1^ zxc;K(>mYj!ES*?b2XVnR-^oN;wcZwknA6O{(LTtAqN>p1$a;gmI1;as)NzDN&2lRQVVyi{hiXq^ArT}**hm~W+7SYC5v zB$QyaqF7m$)NccyL(x}07CcaPxjuK!%BJTOSIMVU))pgYB$Q8%v_}Zr0&Tj!=2M49 zOi7hg_m##dDQ;e%ofFp%S#fVwXxVVI6puq%60T&PeLoYUk$4{XnWWT8O3x;GMk!Kr zMU|X40_hc`)+*+4zfaKOM9yYMTEb>>+NFX&SS3rw;Ik7SeVYDoKXa_cXPhI%)CU~r z#}zH*YzEkk-eW)$>S=sn}}og1CDe{%rY*F9aZve zy+&cR%Qhye`K}=yr61)srJRRi|E(k0QVeVRyehOH5eHlJTStne0XF4}DtWdE-=3e$ zBIg4~a>P3E^&cG*MEye!vqh~qm=9IS1q_tN{UML>;^GD^He#t*o3g<=f#Tz1SiyhC zzTu=)*}Rkl^t&|sr)4}F+($${<#en{Zc@4lUB9F8Z*e5W9Y;$4H+LJOkly-9?7WO+ zl2{k_u8CK%vhnl8@{#drgwDB&yvQo~oH|V>{46#t($2EDAS)ua$s~1JPbFT8Fiy;$ zPBV0}KoQr0o^9MFpHS(J{#nFbrX@wBh}6q8>oPT0vRzFxj@%~iQ2IfBD}v>gp_Q1{ zD76y9a>D5+R$}z^h*r{4#VH`{G1E4=Dw(h^{Up}xWSMI8usi>iiQ>r{$|p=1O&b%U zNu`#uO|Czi5R>VfdT!N{K|)jhve?$!hXwvhMBJcRC)^%N&AJ@CZkzmA^%E0kbk2um z$q{o@V@Ndwj ziygB0!EK?NUzN=hH^1>g{3?+XAKoTgR4LvygjQW%ZJ?x!A90MIRyf3k2<*9OCNP?-8SX`d-sN;;N z+h-fZ*uPX*fBG1@!Y)aQAd)+gM93XZSpzQ{a&I&q+HL6`}D1jDRG@h zxy&M0sa!oQ#hhQ2%@8G99WF~=*&A=ADTksOBR+9E;^S_bZO94L z=4Oh2qxiw?SZ9g3fxE)@x5sc^nZ%!utAB8tJYSu& z>Z)6CrIx1OM09aJq>ihWtMCflMC5Z5af5yn!Bx#*ypjKy`mnCBP~^068v`{)2iiTp;^$oKkxuL>n1Kn)2qeyovZ|SFgh{T6tQB_iP2)K z7JHVKO*9#9CaFc$;^5M5GVUB9gs3$7wi1BUgvYB^VTsJ)snL8zD)hf$fgP)V;;%S~e1a&4mM9Z$c? z#@FOV$b3DVfewtOx2xsTp~SjWWVTy5i$1KD--L>gx|~2IaIK$GWy>i-Glu71AtO4b zfWFvQEw2rEC#=9b7p~}=YO#L>-a9age+V0c7$pWzNJ+nq+b)ww$ZK$OVRXcL#MW!I zX*m}1z6+v$lIO%tnd1C#yWxgzpA^>t=Z71*T~VjjE^?SAw@IJsTc-bpKHRe8;zPO?UZ z^SXFg+W#}@V*CU6>oe~2{wvl{(k(cxFo&H+|CiHvs)wYlzXnHD%bz$l9t=|TrDe&N zX2Twb{Xwy>utj62vMcm8{m2(i)^sJcoUH^Hihq?RJsC7p}) zz9H$A@4~s!XXHDO+PIa}Q&@wfr_c!-gzZu(EEozfr-JNf-9hyvgr)EQ9gb_+s4|wN zUS|=v5#2hzIy7hBiEi2B$h^>aClPUTI-W{1KFsftv8tp$REmt;A6{0fSCyZ|zf^pK z`08`}HUAb5TeOQT8su*7Wc8{2c|@Dq>_|;ucFcsI1i4*ZsG^>UlJqoH-C@}2hi0Dfbo-K;qV7nmQArVA~kW(dY+P=L((Tk z?>(_GUR16uOR|{nqS2;=gRUMHG#9CuTibI~&{Mx3&yhMm6uc3Av* zD*n4!Hc6_M=c-6vMAwUUFpsR|B3Zb&=(*lHB+a=I$%oX56sFs7mNXcb^$9;jE#k^%4en-@1Jr zHGEE3UpJ)db&+^0>ATT`W!3U`W69qfbbZ$Ij%y*Bo6p!%m6ib{Q z;Gy5qGHtBrZ@}$&qc$=Q5B-eHThH`5+9RA5?5p!(q>S=#jehbLHk2I*vU+STWq=vw z5eK(9E*r^l=NOMZ2Qt-TWO8ZcamzyCo?C8=?uW6R=HU~Z`ZCqcA3Nq>m~$UuLos@R)pO#RN+R55xTJ~1tRAE6z{tVSAEUHj6e~qN{^-K!R06&@)lch+P#+~et!oyrZIPDDJB;}`bv*ja0mP8S}-#5Jo4ugUYACv zmND4h=#fi88|5CmQ)9bkzGfTcMrgUoBabZz9d~JI(fJij2Z}vx z0sCNF#hGHk-P)As2Dq$yJj{Oe5D(o=Vzv#`*1dGgyV*=wfYJIJkC?nhJ3o2{#^8F7 zJS4vUlskixHCj@17hHX*NA40|ui@rXUo`O^<%w3u8h&D@U?UyL;gKs(Q8SCR9A_7T zVH3^8xu^bAhYiAM4|w!_28y%tFJ==Nba~{fiiq4Yv>8zMx1sf;{V76;A^&tZ*FzpT zPjOKrBZdN~_;b-39`?vTL>kUi{(2-uDuG8inRSM)09L?l{-a0k6?5;am?lmuahxV9 zKHgxAZbnFW#v@0I54LDYk&(y5&O*mHlVLMw+UQ5bo-a6ek}!^q(T#C)lZY(?A}y}w zM9kWZ+YPPiuOOJY6jO*?C6IYA&;bpE-<9AAF_d5g8dSqAJF2IRDKx zC+5$JYlUn31=j`F`3o)x*Z&JH<^ihy7hDpYF~7z(ERi%wOa94(Q%E=qE-}AGI6ECP zrmCEjLZqy!Iq?i95v~%>wyH*Kzn31{fMLhjb1%C=V}47s-BJ_U<(PtDyJdL%8E|Ph z=(`+?m2oTau*>nz28_a6Ylb~VX@)C-8@jbn8ES*8_yre$tNaCb9L{?RrzTaFEj$Sz z!JB{57LHDW$b%@ntwy~2H^-Fe6&NkIoy>t6D03-Y;}1M|;Mln3pm^yk_MjDDcn~~4 zhUc~`4~A!e`rP(7(s87{Door7X%vgd1!St=cy(xbdG8nDP-=ZSZXeQaBo(=F{THDb zw3#er{2caQe5${t7|Dy2r~H5XJUnM@fZYxIx?;XhQlYqhUZJFKJItcvhjLrIx@;yrNW-I4)0d4Ob%=KZj>|2j zkd`QqT&&2)ao5vde3+Ozk%CCYDyC&fS{Q%YDV^(b zg9>o)fWFL}X=zmfOdi>)0ti@AI758#x3Y8N3vskR%=l&ITpxkF=75+pi9t902}fGI z>fSq0jjU?^hWbHqlT!H##<3B{|= zX(o%Uk_K7lk#mi>?IF9Cmn)JZxcC0M!#aVnFO7&bd^#8l|DzI7KJIrM38J{3oq@ZM zc5Dtys(MaJ{YXWOmR%~;`XQ-Pg-U*eHu!B=T0Tt5L+Tik<_wcONS-a>P@{)Q`;f93 zY4x;vs6#y)dRoRuN&dmGw0D@4jnxe`gAdH!r= z-KKJYo?3~XSmTjXXlCPTbbT4UlcxchDP*Y=+3)qp0o8uXK0A4boue;jw4A)b&M{=; zW2xfG_LwJhTQe_|lj(vxvI*nz^)-Zi9K6RX<&7%B(VvN%k2t2qxgq_KoJ%~q`&4W_ z;y5eL51I5+n0!|!XXxGCu#Ev6L62hlr$VYIQWg1gn0)xtP`vq&K1e;@_2GEEkOn^c zq?1cdiMJIY7eSAAsvgCZ_#B)K)!aQ$3IB{zc_su8Od@_Iss_TV&z2Q96x4a-l@oC| z&`u?)om=|q=Y`^fR+=d%shmd=xaI%VBacnd%dWRYa)e8KS}%>ct%h^(6OZKAzF7_aEWB9!wPUu$h0=P^BReOf zm`I0ylN{j_UuH8UZO(NX<1>k<1F!xcJ@PNAckN_c`o=L|zjIHnrA7=ja;eJZCY8<3 z4bvkwh=l`X6U5YKwGnY8$|IsiUZIM*L?6=56n`k!rds?c`NkT);Da(Nlr&Fe=&}*- zs?}zz7+6+ric1s>N(nV`k&1v(Z^CzusZmisMTi&f+?X(dbXC(8;#4uAMt-C6v5dI% zrTtDHJ%2Y+%ETHziG(ycTtbodEYHC@moau)ysYL;uaOf~(DM$6?Wc3&l?~YsxksHY z6Nd7pR>Eti^6;k~xpZbtXlM0TpNTnBwP|s!Dk58r>`*oT7F~X-HdjEc38s$bI%kW3;)BrK%M*y!WHO zl{Ix2tF!9e5z`6;_chE8Uep$u>M;^>Lq>EzhWCbHK}M@{-u`>$9V=@@U!FE&v>l=l zqHYbL_A-6nfnix&GqjoRmA(AZ>mc0b;hZV`2`-FlYsA4fN!Nu@bPp4x7K}EGPK=#z zU?gp4+*=nmiZJ{`MiWMQNmwz~Gh-Ptaxjwb4J&mRUJN~*07lGj!baMYw0+1}sNc#} zKpguG4Iv_K{PPmfwHS#}V?SA#X)LYtYll>{i#L^lu$Is-?i?JJHtDjea zj$<_4SHq7>Z1{pkJbHyQPKM+8k3UT=v5?V$FW-s_0gXmWONPJ7TV%d)cG=>9}csV*RnQMlJ~LIh-?4W)aI< zxxK7FNZwW>$BR`z;k;dO`{07rHS!^`>P2o7KX29ML?=GSxlvmqTSKpB?Ywkj=rzki z_}zlj;{%GY3#0fkRpolX8@I7zvh4$IL5&TZ!H=u5>78%-hoq7N*9F%j7Cy(8%QbMO zIyl$w>B1k<2my@5?O`MB&zuAGVWR>g^Qo}052O9*uwi|kbL|fyBf0>?@Qi-Trc!w6 zV8uA${zNlJHzKqlB!`mT-$r(h!!`Maai*-Docqt#hz0+oa2|}L-8J&dP#NOl%0jW3 zIuTlSQ~4j0>6Avg*sEcq6l33z(SnilkB|~=d4U}Kqee!Caxmu;a*zk--BTkAL!1N7 zC3S6RUl!Du%|V8}4<;!f&~u(AT9b;zj4$b2)b^kXy)8TP+&2D}wcXER1UhCVX2 zVkG!=BQzK#{*89_*T{c`>TN2N8ON`bKtZ&!IpF$&2ZHG2Hx(%)w?uSr+*5B*6 z+s~_=X)n>M-l>r*LQ8JTKW8<>3zzpUBi85a9u8m>?F$?BCN7!pF|d7s;=-tVpK4G> zJI3z)WLz1xmoZwyZlxFaqVpI+pJ1|-? z)DUAD#7OH1he`hj1Lu(8!q^=)qT4VGpAR32lDNm*4EJWZ#SSqNRlpsC8ygyl9y`QH z)DE}%3+nO^BT;HIedS<{d@s~;%eonfJaGA6*07-V!CGb;&vx?yYb#vZziQ-^5O+v% zF|W}RzN(S)Lfo_-EnZ*G%z{tss*zLG^5bNYdeC7tZH6;+*N9~Y9aoM{<+uYPtG7nP z-J_jjvb;{~^wo$<@6paT2u$wTEVrduR!-18!{Q1z^B z>+hNM6p!Uiw$12mX*Keqk?8BtPwt6o?bGa*L3G~*HS!NipN_7-c5q?8HpP-bj?4>d zs6LmFfWX_mU%-aaIK)304%Nob*&JT-Efl9nLp5#BsNgat?m zjQl?+X~K}SQ%Sp!TAm0?U%e0xC6#1+1L;i`gH))UNcu}=OOKFNF_O2vMn13V=4Pam zg-d;DL!xkRC^yEn;IyN&CiE)Fg)fNhi99&a9DE_+XhHdixP&+9pLDekR2-%k#Li5% zlU?T2CRy@OI{GPzQVuqTX6WM1b9wU9fRuiuMy5t0J&mMKnmb-&LAYW!kJSusQFY(c zNT&+w7z#?;j}w+;UPj76w|`qBubzm0DLPMM*c+5o9vg8>=msKxxfXiwWCyO9F}2dA zlG?9a*O7nAQRdP?r25!enXIJkjbg!3ZH6Vq&!9E7R^Ft@ttx5t9tumjY+g$!=XE}G zUwm!o5#@D5S>yh5qW*?;%)1P_lWKXNVcM@Zo+&05u#q+wE{3kjhQQUFjhDYwHq*2N z&Tx8d=!KI(jFzdjp)X^k?<0X}wV_8KE{vEnI30N7#MFk7Iz6l;y~o{6a>$4-!tf{8 z4o#W)^umnjCb&9Vt*j1Bb>xriAThpA46|z0%O;^OAS6Q=W)q8gAEhFw87CLRw4~Jz ze_R4rF`r!Mk4rEbF}DB2DsKyh`}|t5=O(-MCm8D^B5&o5f%3`G1s|w+T&-l+;2LJ}DZLxUk60L+A2nAz`B&aC$R{yBF4~ z692huq~2pR!`-cVcM#);4Ga+sq1=rPaKs#&G_8b;+HJsrVp{)l|| zYNcC$W)T^+Uc7MkP?&tQ%;#!(!$c3W+s}*Uveq#ox>}P|IVPs;k(txhA7*EW6y+ z-o0UB#4_>Zp9$aj56u{DX=Bdv*IIdA=%Qb=jWmC%B{O*lg@3DMcBDUm`*8#pn`XGo zmulrfH6cAS;!W||I?8(RE|QD+Cu9H1wQ`d9`n3%+BO=5HIb{>Zl#=mWl!oS7?sN3F zYV^m3`Oj1sBilwot!bR6uNQsg((#30~p6@^NF_s>R^*-l%1J5;>nCChRRACz6Vt#)v!g1S2EY zh7x}54FtX7^FCpad9zl^P$e#Som0F))j53sTeb4<;a8T5Sm}B^s!VmS{HQC$s=HZ@ zdHip@jQF}(>vDwXR`o9AxH05amA#!g$}}P7>~c)hU)Iqef-{6z(8C3P7e@Y>LVDGn z{RHtyk2YaUDxvz}Z4U^#+~>O-6Q&eJp7JU!asmI6KBd{V2$?EgyO-8gT`7`Eulh^* zgorD|3on#S*6+4mxTSc6Sov4Sd}j+rm2$EF2~S1 z(Z+?frQsJ}Vm@PL3xC=#_+{|c}I!SMJ0j;ILU4FAi7H^W>02VVzo z{Y5+$qTU6c1ka)je`y2;{E&$!$Pm{NjzCWkpA`NL!X^J*lwYTvZAv=8@hd_&Z{IlI zl!uXvp&pUwr?n3v@l_!c#n_w5XGTPY&d#N^j*-#FkOub%`BLaM@A%vbKEa}fwu}zi z@iigWhvqe{>&vWHCcsz1dtV=3B`ViD;OpU)kNQ(w6I=sal+MtVzDRE3~_*qX~=vj}AT;xFNGq}Y={`#{aHcKEq7EtXv|4$!D#^UZLldBtaV)~* z{Ee6JjR)yd%wp5jgkb_9)m$dx58_LoV{&{3iR9vt&kS~^a)>8<98a|BAY&$nrx8&q zz4o%qxIx$;Y>_@?`d#QL3_H#fMe81iF)p){bC8LIzPahe--R|exgpyj_03I=-|3s1 zcmWu;<@DOnQ=>a!d1{oHpphNpm;9Sr8+vnLF^c{m=>PlGg}BWG@(@Ukf5x82#L=0@4vk5v*A3)W$J$7>tPeT&#M-5kCG< zL`>B<1-6PW*J$G>4AMxdD|Zo7+TL1un`+-rgxFrHjhh)2d18FlH`ot3CmrGf%4JA* z-XC&y{$I{{IM;Edc}B(jmKe5^-)#!-m6x!VcWR|qh21keZ2hQk*!g(;++u{ARC`JpSaUYBm_PJgahNLb5wA*BkdPB@+#+T!YA+hLcg z^yd9e9E|6ssu_vAZqbPnV7_+MLKGAJYO7g7QxY#zBoAA;KO&xN(5&N=Lv3b1OhsHT zGioYo1Q1WPA}o%mUA74j*S z+r30nPrh!OLcWrEnLDDKs}iUt2J)3G8mAC>2~x>wA@fz_>qF8wao@8Qal&}ImZm?j z>BOh7NXQ3N@CEn`-G@#PpKmBPSyKBL4@!h|N25;-M;|dmJoyLaQ~H}boAL0JiO?r+ zMh~UJdlGRGOrFP;PZrHPxMgq0EvG_+o~3`n(5i3MQ*Pq9D5gbsH#tUGQu?Wm4MJ*q zbK|5xmCX8<-D?-`L$5$D-6UiW*Hq)Wp-9GjNLr)CRY!PBXmt2g!7+io;7gU`UKJ5f z=HjANRQ%79tPyGm=zICn=!Mso~eq>HKB!FBz`!4Kk}fNl=WD@FsatH-~PFObmT5T_Z+8$OwN9pcTgRo?gm@qqB)A z@mtOU2<{TYpTR4F3BE5vcZZc2HbzPP?yv#Fr402kf_)f1jGeD+m^iW@Bd}jAU8+qQ z8FP&BuT|W=RJ&qi9>;-p@eg&}z;WSMB6bD1KJQ z{Tvrcu{1}!!j%3U{wA;3p2G@dB?gxuaa0){80J}CUU0xj`5tASS8P>=hLM{dHuhnZ zF7k>wSED2b3A4;AF|uy?Q7xK5}>!biif7#fNj23GSk1GoKZ5hPv{;^2pEyF_-nU<47rg5~26{wuYoB;)Pso zf z5!!wAN~Ip0mkvfT%`L8A?&(3_j9x${jcY1HbLmz{U!w@kr56xLpG(_+riot=p}F+8 z4~L`DH&fLkl@3YI43o@UA1ihVxk6=YB~ryM=L(T}8Y6l!lJ6z%N7bcc7Lt0UEb{6o zvG8dpd&Qs9tnnFSs};9W#?`A;4Dk=EiS}f1xJ;SI$#T^Fr~DSFdEvj%z_m{=HYO(X;ko=oR1W3B+tQMi?@- zd*z)<>2VP=TfIa*Qd|*I%2VFZEb2Lz*grv=ufGSo3t`vOUb#fZFEI1PE$5s&6n=)Ua1Rd1Qnk^DVEufvL1{XDI&U|3}#Qz*Sw{{~z$` zg(UASQBYAaF;PjWFfA!DDJe0iC@m=|DJd;!*`&A^&}g!xWRv_5;=TO2pcPG8Zn;HC z%O*`rnzU%Tq;!*tl9Ej}S?BkBpU>wi*nW>Mk4JXjuh;vW&-t8lKIgp8zxOdRFX82K zq0&AqSHGi0upRYNyDp)cI1d)uoCn~W+RD{wiS-z~t|qTsm_KsJjTnkSje1Yqh(PK4 zW;2XS`=s3bV7H?yJ6Mb}6PdFwhH|!-t2`N5t%Q=q1+Fz78%fo8ZbP2kc?6Aw!vvO3 zVmTUThc8A|v@23uTyepH0llAjIQCR zh)oxG(75j{rtmPjYz4DPX&YK?UzMv%q@MCHu@jPprLogK-8z4hc8mW<&_n)TwC{v< za+0Lw>vHwJgq_nb?4X~pD;ZD5Oq<3@ZlIU*O}WYzJrZl3w7Njs*K+vlzs4^@3x_*b zq1W(jxq3@-IGhyg;dZjIF3~;4Xnf6Oo7Id)#CPRtn8ft;2J5&`b0EoWx1QJxrT=32 zH#Rgdpg844qjs-*N=ODmc`9wP@%ihVY4~=pd#Zg0*s9xJG{(Q@zRBK;&~)dE#%d9g zFANMga`%hIp7-1%Lb4E=Z>eBc^t_LmE=m2Pdu&J}SW)UgtPd$dg2P<4$VXk7t(eotT zTVa~8E`(kLIm~KrLC9D}Jgj1bgk40K%!+;%>ZFiNupY1u)78jD@3TVE09Jfog)#Si z&W`pXlq|2nQHb`=^b!goNf!qObb#$Lv42XSQn2F>R2U~e!A5LEh<~ucTDy;}GI=E6n-To6hCAdR0^Z$v#i8Hh!^iH03jnKZT;{f)e zd`2pNgyDXMAyd}@cT`9r#2R-6t6J;6fVk`f_l%HMu(EX(>P_RK@7$w|{14pIL!vmM zXM3iClf_!^1N5Bnq@axtJD9lz+XlDU#29fO($E$|m7XqF7Y0MEfYPmH5tQQyREjEB z*GZ@aZnI1N!L@!E>nFGNxg)}9_tMA(N%HKF7geh?VDlw*Ij1!4pnJNr2x`~GFRFu~ zP`5+rCi3CiITBTPkf*o9$BlncJtI$F4zGKFHlbp9k5!XjR37O8tXQLsm{zDlsNK_F zRNo6VnNYg7f25T+ZG+0X>P2;hq-Cf?C_P`Rgo~VQ z-a-rf=mF43&x`8!l9f9_^@?W8pULGYwjLJmcu^(Gvv=xePckA8k>E8jMY~^Aw_F5s zRH70m6d$}qjmQzc1-|h87uA2U=}lhpUAyeu3-5>=O;`Wfi)wDT=FRfEx6M5rud=+* zKP|?Qe~!VcEVU=J!hGi3{2x6tVhqjp*a~$-Qfu)ejUgZ7ZE&dZ*dcnX*pIQAR-smM zS%nhM>$cTIqbIMjKPFYpP~|f#)HL@--DEt{rE?(ity- zDfBb@O$H}DpkcJmrIN^3K=r3o+D=h*N`NI5=8E-JC_UAjFrF%YSA{v%yw-Jgs+psN zFvWLQm~(Oq(bqNH{i%C_vk$6aDc`(U6e`b}@ipFXU+&C^qF-EAVV;ye0TusOo?QYO zd4GjD$t$RTlJ_QUp#)Fh6LeK5R#m8f&^dz(XB!=F=Glfu0hLXpM|D*gOAFbhwHqPD zP4^<_2yM~4)8>i+Qz%(rQDs*D(}<9@#q57V`Vd;SR1BCxNuET?46{$NmmwsUTiP86 zzVZPbls)!RDy^5eWyT{HA)(glAj%Oc5wzdM-3Xz+3iA#D{EY;Z`YISn>-!K2t|>6@ zLztKb5mr~BX7P$LnuDEO+91AGvQg{nSB}NV7H~P2EqCLHp~S6oOy>-;=*%{?=*OuH?w`tJN-+W5?7nT> z8o@@0OO2H!e5u7?2Le6DrV{powIGBBd5nW11jq5^AUK{wNJelWq(0}m)?R|pg0NYH zRs`E1kMW5JVVCo5B8+(+VF^MB!ZHym5!w-IpC`5>2r)M6IgwZJ;UG-h1e1YKj<8&W zN`!Vla|9t`u$9orD+%KlG7!rBLM1}GUpRsgF~lDxo-lqP1EJh6R3fzdg(CY z=n)>{umnj&$QtP}>}3e65MshT#+79(R<7ciM;@wB6bn7-3Qh-!tNN`%wL$qFu279U z!Zrq~^+9}QpDDwSRSZuISXrTt3U&BFtIu2f0*CfWp>m*esn2RF2jVcBlkHHJaniu^TGZ9F;($6=YO{=K<$xK04SsN~2|CLGqjD z23Rc-ZN1M}Sjh{f046+cJ#zVdI>pXnz)V0L`;|Y6eJ^y8Kg=LAj3eMWUfmBK$}}ID z0C)gUXLN0**mq-sf^IY}I?R$}K_wNf2#xqPmhHAOliC=|j;3SVTqB&#P}xvAhvz^= z+18CVA`h<%F{XK3A)*NMsZJl-@g04%G^!4bV^S zHO<&Iz#ayO?}`5Qd#(B2gn8shUWM8(R^d`X#r5{eDn4`{RD3~&xtU|ky|S8kgR>W~ z88A(nlTYp;f5gwAHZZfU=Npwlr9`z6N;?nJTM8s0d!g6-vcf#X`Z%F zg(cw83}bjLU;`@C+kQz#PCO6&ASP0)2qHwpQrQ+qNge*eROrfUT7k>Lv-k zJ;OLAU?@-M+Gc5P$}qxxfQf*?UQ2UrhOrRPxdyPTrb0a@gO_C)*6>I#e;zme8|LiB z$Cp>x1Wc)oG#E3Wa5HR$$i+p2P9c3@EExdj))_X<(9Ku7&%s~R~SdLyl%PQ{n6 z-~lu2E%u-|8+iXn%+szfwyJB#qcooe9VP9@bGkDJsy(n$g^q?gDU?i3JY2$FhpGnF zA@;(Tjj2@s9SOho{(g_|fp$jRLdCnHl8f4*Pu+hm=3z$itMIGfLsKhNv^;tFaw~by zz3Q5QYg1+jAE{K=OVBfZ>gS(fh;&QPuvA*_XRV+c>F)KL>7dzA%`a7|KTFVw*ciId zp8XY);P!aP4$W3R-rxc|!lyot)6$jfQj5HuYF68ee0`nTskVc*^YDNy`;5WvE4XitKM7n|< zO=x9!wy8s+wYi^`(bY%+Id9{2Ufrg`ozO=gHdmJtUn3b=Ffx3*OLm&fS!qrGrx~xk zZB73>ccYOuuS8udebfvz689B`8;4(`ckP7ANwTOh7UjHAjIAWJ)9)|`K|7bPzgF)q z4ygp`4e;QWUtgBj_c_%_Pr-LWtP1jQ3*SYX*UvRN(hF^m#NWxcWA~W1H4+_Z4@d@U z2p))KfGxRTAeIBR<~*!O;scvXJn|>Qt#UBe7|(z?g~o^S1`iS4aUOH-J#I18`x1|F z@;hcqTnG^pJu2IrCBN%?EcfPIc22iIBur%Zb$LFM@=GYSXpge4T-^CR1L0h-$Vnb^ z7s_6Q!bzS1Qz*{6=r~c(hZ8aovL{>GI}i>`_ApR==OjdHOS&- zg~YpAL!9dAw-PYHbcpaHzrLMb-<}-P=G07Rc`#jHCm-2CFWq>8m^B8aZ z%QZz`Z&3=c=6bU6H1i*g2C$o)G*F^Q)Ep%dXvgfRyYvcL8W*jv*97TrZj%1l%2y$lZ#&MjD^fJAhA2At|B z2V2!m+E~`mdyjVmRHD>>Rl~N7T>6eA-NFH?XoR%yJZir9P9J9+y|ln-oRsq%YlQfo z2g=$<8A2n1tbI(`g%EYhqc`kY>qu-0H!mYi;g#Fq3*mnhWppO7^io#6ynYfw;lC*> z5we6qNd2yG)WRZ!#9;4$JtE~`O&560D@}QZtVuP1<%W7qU;2-nTRRc11+F2~t5-oL z9!F?KaT-KGzeN_Yl>_c$sC%K;*mK5p`Os+O1b}jYl~?4C)mfMY5OBz=PMSw{C-yO~ z(FkTc>@`xo`Sa|(2q_4g`dstICf-l;df0n*<4QbHrh}D&DGnrYAT8v-uHp6)uq8*l z=9cjmgc1a{jPn65XD0NNmou6Al*17xM??xY;%g}bB4$t(H=JmKL#!i=I3*Kg5Sg|r zW6!heCfQpNb`7p#7LTu^4Z~XSKN6xtj~yDri!=lD0BlkmmhM7mzo3d!CvrL=;%E9YFZMgK@dwq22l%dr_K(vM zk_1)=mUs3%LkPx$Xy?GR4Xt`%E~k|$`P~~@#R?!T?MUOOVoy?YGaE1DHG96=roI$& zDerD=t6lQKU)|%JJuqe8Y*Pn?nGd7w&#{55sU(xiM&{Yld5UfL-G(E`eVL;Ou+0x1 zyUPUS<$C?#>lWAt7|GO|4*s+WI%ku@M8FLa=k0gV#jYrbb*up`^Q%s@!Z!lS)SV80 zYKi%zF2Eyx@Ldyh#ym(h`=_P3c?s_zK032N(?OLyYB^|HK09;{fJXjnn>m&W2i5Zz zvN7Hnzk*ai?UVrf7qeRM&%(&@cl!&69!_d~4qD}>x9R0!+v|&EgcxD0@VGA0`(qkl zn_$P0BHPo8gSSlPnPJ(qfxX+*w-WIJBG%6=xCmdKE~q{zt#7yVj|1-d_cqm~6LP1e zzwD-hna-4ls2l&W^f%sV#=i@&2XMSRG}W)KeT7Co%*V_uq<-{pKcydC1QpMu;}{8b z=nm^C<3AxMx}dtCt`o}eQ>hn|;PjP@SN^+Ad4*c)r&=DzCi(DU>QfVZ-w)OUuk`c! zfzhk@zSTChO@f|Ew;s4@9IsUZ)r5(lllg|9(xG~wk^((usI`7-+4zFXIZ?||!i+~~ zLI!!v(f52muRTlch4T5S)3;j>)Ku)FBok_|q~#T;oWT@S8EE`qk2x}52C8>r^?_FU z(cxw^OPpD&X~%|O5=bHKyN&8~eL<|V3UoH89@`tB6!j8Ea86)Am0x$^Z8B4LnKKEn z%@2-2HS?ar%N&61m^ykc;pVJHEN9Of|> z$|m|_(R;^wpo;v|Cu!CLG?noan$n9srn97teo9xPy-;DpEo#1>!sYBtN9^O&urMnD zXHq2rQ;eLYg@cDdbU^4?n*FK9!3%h4AE0f7$GmBEYbuXlRye@{7!4RBg|QZ}aAQHd zBVrA<^hir}DG?8KPjDs!#sS_Ss)NA>QMJ6XWSDQ5)oD`Z$jxTbk8*Z^W`ojr(7}QhCKbdP ztzWV2FnTR5+gOh(khu0QGByq)E7Jjw1B%gZ+b*#@xh^s+5vd5qHja#tn4XrHHWE|X zJDdk;fl3_jG0(j`4yBti@jW~|laO(_bea<|StaDOMaHHP?pw9L_cDm+OFX7qqoIWB zYAlS`H})QZ!Gbnx#K{y!8c~#8=#!KksBFTq?N8~~$~J-|!*;-y+hDg6##v3?JU7z4 z$k;d1?cfk;+&#vvj}#2ouhWP2ff$cEA#rU+JL*CARAcTa_h4hwgo44&#HTQZrg+q^ zWEfQduaDU5nNaYAqXVjVHqC_ej>aPM)Y6y;4qwORVd!4tQP|_m+welvqtV9w5$wURp6if2*P-@FsCKCC>-ij}5Ne~J8amqjb7zU0uX&|MRm!tcX#>o+8cl>9Q0$d z`kD@ymMwg3Qap-wSC`ypQwnZzELl&w%0239p+33IjIQTH7J4o_%HfI~(A_V3)E6dg zReroIhX*Cjzx28W&VB5W2zoK*OD38|aT- zvU;_JX58tfA9EK}7t}FbP;@%F2JeifZnZ)czhWixOq!X@*~PR?4Ia~(*&g(D!9G06 zeVwxkD&|!_afvF=-+vvR#E0AsRs1Wfv0G_|8e{CgykLqW>F0dNjUM%qG#t058A~s9 z-|FxImH}=SaCn;W=B0FN9e}-n)1@Ask#0>KVCXe;y(*UcEW@Z@^L*it!s}Pbo6M_J zLY2H>wWz23K{qYng158^+6>!9y58L$^`RtvJ3PyVml)T@Fk8P0%Jn!!&h4{V4Jc_vg7V{`#8DMl1UAYwI({wp{-C+JS_hs6)?SWqN zp2u7_SO(2Yn$9ncG+(|gCF414^FMm5djxLrZ$9O|UA>Rt*tzxgs9XHMWsOc|8OjNp z*}}kAa&-oA7e0wad3a-fh@%Cn5GqxuF}IjqLClJRP;-G#8$Qot>i>xjQ4)T}YNk~j zS=c-c|81+FT^~?MCHLR8=dGYcx|FHD%p8&8Akwyz7fkyW=F%RH`8VVU3!=rp0jybfzz z=yc62NOwAafnD;6Mvz}pY7fLB7EURAKY()|q$h4|nEJ>i8Lpv>g&yV>M_yc~vU;wXI6^_7{ zeCbhidi3A$`h(JLsw1x8CE{Kn)4sBrC3TbaeC@2I5UQA2swbq1z6+)2{F@|H8`Q3E zJt|OQ-RckJxRUxAy@gNiB#nbm%b@hlOqo{_Di_Mh?2}jO=NySpw%&7iV3fJH{Q!K% zDUaGE#pHnZuWSr6g5wDvWzY?t=8Kc?hm);$SrpF}%r#JZ|LsvP2(=wbM?K5UysbXg z*8=bCqizWQI6Sl5(S|e{!Rlb%AZhye;d;7^NF%&$G~2bBR}_%4_aKLw`P1vkAN#D(6zK>1AiL zpXw1Rw35#w#;Z<9MOY#6`WK^&RWq?q?!DT5fin}O)}$`=3Q%!UGQzw zy~-ui{=;w7yA=b=jQec3A$nvq%NM~iG>`YH^^*EiH|VEKG#>nz8IH76mu-aJES!N- z!Zlv?xg_Nil+LYgo!QC?nLZiXh*J9;uX(bi7A2j}pIyV&Rhx&|ifg^S3MdGvnNT{X!D{5xlla9 zXq~`XSszLnw|donT>(W&cJsQg<&NXo*Ahuu74LjI#V4Qm8~ykhP9Id*Vy{XQ%G;0P zHadbjdf}aSd(~HV_zg+sdXK*9F8(@VPN}B!@AIm?QgF2dr}U!(4;d4ZIk`+~(rBcs z(1}^$Ro95lCLP5jqZI*}2^=@%B=+HHPU$bE7q%G z4zNc(XInwAvl6A)$Gxgul$H-je7i)_4Zr)RUiFOR%R=F0p-^tQFk%zolWHmOT}42_&}K#_KVz)9T2>4HmVt zKjqjB)d+P+Lan^sIC#DLE@xCd`o&)LsqQ5G`b*~%RWVc*lzz&wdFP&DUDDGnL4WR5 zL*@0n@P6COVa%Vvv~{;D-)4Ms+--LzZRe|f&a2*&3cUhNUF{xAVurRHDs7Y3w0q{v z({96pei>xQJWI>?=jby{C5+v}I1lG&!V{VWU><*f$a<4i6Hx#Pj3fFjJg-|Z2 z&=|Q~s;XvMa!+s;-BVWQespLbG@IvX%d@+<` z6;#PKuL_j+UYTf?%i#y{iyYOe2?%yBpqF<5ga%#;g6|pG&603w?zzyUwDP4xloj zcD&?OKatOB0o1t;<76^V-38zFE2^M8b*?;B&Yzw52X={evSw>wxUkb}4&?^H>&lP+ zsR*NELte0B34B$fS1pqKc=tLpKRR!so;JW#{@Saz1lx@3tdI#eLj}Bw3HCY-xdd7P z*Jx<8IFa_;dKJW?Y8t!Tq_%lMjMYT zbcZ`*e#MaYcV4wdYWRt3v4XQmxpT|9Fr#@P!_92?#5c)w;rICYu7&Oh=MJbsD0)l= zu~2%yuZv3Tz?05RvbaH|Fu1Z8O~tjx5TBnZrm(6v2~!jg|OEuVMTP zw{oixT3Wp7E@Rzd?qX^ES6-+-#ZwO6_r6y=A?_%cY#gqj-RXWt49)55ZT3#sj03DX zoz62?x}2|5`3}+sn(M2}vRHW$PjE9bAYxj*XICekBp?SY9!#GWL(b&CBCyO>Vth64 z9(yOksy0@*#E(JzE?OSHP>8ViV~aU}koy;}xwahf8%AI4-T~{tanBSCjs#Ep^jreu zVYy(DhrRu70Ai%XlJ3|e5SUwFQ3;;=OP<}H{#z2q z@|$*5um+*|Gc9mj`rod>LjXEDyn2N>qyu5iF>k*`SF#AxeMsyZ6u~j$mea25jm-b7 zv(3l_Sasa1?v*6y({_&z3%+5P^M3zQDX<*gszNX6gx7SM(2TGKK`+gAi0~zrkhwH_ z=n?l6d)#iO(Z2SYr`pmG(h+2}w?LN(K+)G;H9?-qpI}8`&ThVKjhI+DSm=3J16c5P zwB`Nun!#-6VQpXmVEv!bxjQgBAo4u055?T?2R_*OJ6`ELEE+5atp9^&gRSa^#gM2J zu;Px5#=&3bPteZGasd*)r{=WhU9N9h*#*$pu~Bd9mA8;kEntH@4c?*^zhoP#Mtg0;r0rHk6S!uOLFy_K@C7tM%USfCX|g z`V)NwMq-SFOP*YT2S7M=;d<~&^D&fOEZ zlruKf1DrRMpq$+glZdOpvcQt~9dPq7XO(b$>E~_^7loigLQHszuy;@0?mOps|A6Pe-BvSsw zRN4*JH@(_eZR0>s@H>1(R}q8FbwfcqLig3xXT8Bm4B22^=V30ePB2#H^`FFE3U(Z< zf8I*?9pFd6sqp$wuqLo}zb>+bZ+$0l2^-jd96=|_2mCmS5a+ea zBpaf^3g=WC;cw(knw5qSmT*o%BFX}b0`vC+Szrm|PYPZ2ByDuWnv>%Tqquc4%FVb| zvNVYfxf@m6v{=jF-TJ+I1ib|VSV=B)6fbN$I?bY)LpD8QIM=`z(QRi)@Xhdg4Zjg| z2dG{SUG5L1S8yXFiSJ<+OMq~bTH+`KjrgP2Tn^no)m)8~ZTZe7m_8W&fUQ%l2lPrT z`@H5#=}J&N7|_e1neWo%_^I)JiqnRs1@CBqZ^d$WUlMR=ifJ@mmgk!0jND63VLKEF z)WnsP&IbfOMb3k<}>@M++Cv(67*#0pec+Jh{ zCu1yLZ$8h4YK77{RO_d74z)_C583!5S-$uzbrG><{*jwAX8(~|fX#B76li#iv9UAn zGCi!yg(yAbH7AGFWD@yKUXt?wXcwq{(i@;Uk@`vT`zZT1IyQOIN{X4z&wxKStMcYi46CEfT#1rKZUwi{&0wqylO&zE^z6RJ>5dGTO01B(gxPnF8MY;# z&FM0Q+=xcY-@Mj(=B0W)Gs@8c+6n5DvUr+sV%mQ05>H(3^R<2ERlgJ_75&8TUDHNr z_g$G#vyWQTn6s3munKB7R?NpylEO*WC`5ME2GxWc)eGF=;g}1e*@I@1tx8=kDfaTx zdOVUghW(exf5P5oJJB(m*4UOo>Dip=9eCHc)R^#R;^<(wA(2#~d0UJ3l7f@Tz4Z%|RR+Va`(Xox0Dsv;@j==-A9lu~qSeG9WZl$b?0Fh5tsY_(1?bBw2dgt)4 zpt0em%ebib8FnQeU}@xqDwQwq`m}tf+?g48iSgKSW@Vkv=bH|u%g~B`sY*Fzm=#Xz zA;YA=S7k@)cxNYM$E#K9K^vsB9WsFt*k#;zrF)D%0h{mv-}Y-&=6$ONQIPQto?}1S z!Xm=Sm-7ZYO3(=XZIyZZ?dECbrRi}m=TSjm+F_oe*lf$DNe)j6wBcj7aE$&K;y=WY zd$&r3%dl}NDKj(fdwfureniaBt=J5|z}=E`)HNAL(2J0c$pas++YOuv9&knWg0>ov+qfNuTlx}LT^ZJN=?|C zDsBm>3C<-lJO5>sIq|YpVi30})3n=^g-~%Mv;m#it|~PZ?_sBd&0+JzUN;Z1k?$L~e($4^zM zK1ugcDx98`KJwdj=Io}k9lrE*mD(b6XnXt>QoA!hEslfFdOL{yGhOcwRcfImWIu}f zkjuuuGnx0WWxr!of~l#>2c#eIczzXh0Cq6`hWvz1GaA|Nm zw*QoBbv=YR(0lUi5Km0u82GL!xSn&5wkPvDbQ2JiPu)E8_YPD))Qw3SkiSU*YnWW z&oKDsR;y}b-M^@5uSQX;dg^gI;)2*QuUfpShy&vJ+wrK=0@gLZ8t3fX3u|4pTDsjG z84`9BdnmbD_KAz1^zpCom`t!;H!<$m&SM%7Vipci2-C`Z(Y31Jx?u z_(h|8vfShx6|x2_U_K z$n_0HvA-g=T@Z;Yt2w=(iAR3ro-xe!4|?xa)!LC&z+&V1v1}1Ifby_(h^jRZG$#SK z7~k4(=Q?I5vmp%-OMY5yEZpf{WDht&AGg+gussbSEVtS?Nh<7B2)-w)jk%2o-3V<@ zky9chc2bV6YU605dr?RkLZ-V~jWRxObYC~}?>ImD&mt9Qe?tINXA*2Li_TU`Dn`{EkyYf;~UPzumCx<>8gq4FoAL?2kxq#E^t4juUPmVE_b zAsv9W*c$bUZp>cT$#SYb>=64k72~p+vm?e#B1i;Vb6JhC^8`mCTJKxOc5;v^kb{)XR`2ApJ3@n_6^s)=|p4NZYI+ALLH|9X=}F0m8>(Y{wQ9Bzp#w^Hzfr$~YJy7HP_6RBcsqEp6)N)gQ1VW> zCDrOR3AG98oaNslL9^hGms&wn2L#;><=kRX4nLJ6v4(y_!??9t-6P37c#(c!k~0%D zxthjA&`l;9ZA84u`r0n2p6%7ja{*N9MP{dY@0+-+3I3L^>DSfjeKG$W1i~5`VaRz< zqnVJ-4EUzEE&glgkN8IT+54+iyI39j;B}5=Hxbl!k{7TYbVOf6sg)-?vi?$XW1u*#x(Jql~IJ zw$MT645=|UTAaC1SDT5(n^pNEM{|uU^cBEKTyyg?|PW1|}lm(R@S))!!aj!OGA8CB}j(h3_GUw3qFWz{H@z*T(-683x zX)dSMs6O*LCcTSAF5(EOgqS^}roVf*A)R2camI%axUbhIN#c8nXkm?UrH855OoZqZ z8XQ?EXh29_MB9kFxR73ihD9|4&YC6to7cFd#&jW9ieS5~rr(0WB>NGt-RU*vk+7Km z&`u!`_dKKOfjrx!H6U?!)F_60mc4eTo{y6`rBb*exChJ=DtCF^4to<=%N-=h>z;67 zB1)YADRUCcl}i zObc2%ELiYd@UH)Xmx9MH8K^Hql^x(IcS*d{Lyqvf^RE8+K59sGA5He%##axyF9}KE zcl+Hns?xlx{i=uEBST8SA~I{#b`yIv*KOV3egL8v;$@Tgttf~7m;UU3DW^bW-}gUa zDS>E*uwwb?O7}?n0kDXy8d<3ru^@tX=#tP`HF^)ab!_Z5$#}7mgMXl$;ba=h;+p+N zw6>w)GajfpcUw_1z>!BNRyiA2hLHGZO~12oyFdybuNiRcEkIEgPf%~9gN=KQiW&tH z{bbFL531z|@e~ot*|$=JA_VQ$sS%+EVf91Q&OU_fyc%x3FnjKYxK}ATASfWxh4xuD z*5x1xZXy#`fx8hZMG()`?FdaG;3H69?9vO+_e_mCWIXne*i(@1`P`51|O;Rr9W}oCb%G%jIC8t zRb@_oCFLs5CWlNC#M1wDH4F+&6~A4hjm0m{O~5HqCum<}{ig z=grf$P&~P?k<)(6>W^?QWZKKM13$uOJ=+}2Vg9a$+5sP(q$~n0rGcYEW&m=cMp2R!PvTJegQ%?hr{`EjS5tVH4Rp6ubvx5c=GBWR7A7O6*PJX?AB0E5Azp3T)pl)tD z&owt8(d@x*hI-emFHii`J$%R#u!Oga=YQ&+KCOh`EzPz4oP)9Y$lA#5*1ZeT2Q{M8 z(_E{qy@D=;n0IPTtB!&!pdiJnv#|>;6Fh|9@+VjXSOyqVV){?8Sg>rcOZ#DoU`6L) z>0njoVcGVeY*r5j26my?4%Yu6rC`U;!+c=fVEq%g8*J6zYt=+6F;XKBfc2iJHN5xH z9mwJ5kO(Zul+Ie!X0E}1xe}u(@pX5EJqx0vvzE(@S#7ICi1@Nrtu)pZt{ZFo-0lu# zfka|#gGu|c);Rb&Bd@3s-u0_mwZ}T`ybI@~tH64}+-9HR-sK)+_klHck&Img>PG1K zrk2)IACV6E4S^DH)V$=|T2}Eib_bZf7%cQVO3Jx@o*oah0VMX;o;_%)!Q(Dk84VlH((f=xOps{^c$;VQ7%V05zjPq0F;crgD; zieP16abOq<`Y(-tQnYD3rB_1V6RWq+*@PpqaC3%+;o)p!#HBBWMfgA0+1;pyyVPA{^xZY>RsL zpBANkRTe^(LuE;*wNN}f*4YGl0Q5aU7nlK-IHDQ2_gr0Lu3DYxF%A|`BAI{@*VLE? z_4fBzaqEM6El^c{>S;fvS7JJ@FR(e|F5=@zFvp&@Iq>?>R}#<6gegw65*!Ybmg^p8 z?7zZwu~E9sZ8H+rG3&VlrH=VE>Jur@Q>X0a9BuA8DpViTk%cwp-tN~=8Q$N!Cm9D| zoN4%Y*_~QrZrs{QAm<*AN{w9azSKzLLZ$>Cd|6tJIXD0GDZ5xP3%FzuC(8$|iN-4V zq~tScMYs6Y8uK8_3gXgt6rR1@$r+7nc^^itodC8u@F^}=f-?s+`}P`@BY8UrwB*aY z2@b#=fJ{#?pZvX!YNB!XYb;B6o_1g22n`Dgh{d37kU-nNHx54SzQLIZn2P~BTI$p? zGf=dVT;z__w>9m8?pa!6-qy4jTA%Zn`;43GnAmk%a*s8*IQ5-XWA{Aco|#w%m9?zK zJUX!FJGQo|ILU*jPN@4>vwbDALkvMrh zg$N&&Q=<~(#g@Uhe$8S=&QDkpY=?@28Y@HSh2OD&o=^@8^ddqO}ALP|#StJ*pt*+)(Pj6T}%N~JU1>>CuATvBQ$}LHdSIF5( z>#R2|=lq_Td#RdHqp6xTHR?N=V<Gu46mr8r0%ntx?sYwiPup z%DQ#cVP4a7_SV%FR7>BeQ8!8ZCrQ$I`s$tQokq(=^ibMURpc1z)$eQ65K)r&^jf00 zDGGeX`0fH$u@XsK4qC}?)tGm|O5$4c*N-yZDRz%>?1CzK+o~>$zcaorW=CKzptHF~ zvG+??gc&~vOu!ja$vZWwR=`=`8E*(!2-y1v3!G?ydjYfd)~H8C)A5~g22E%1SYGG7 z8g-ep*(bWKZ#Q!zRUiXu)jo@QeE`)6mHfU%74)Obv#Pdn*ogaU)E1pkX#HZ(q~?c( zgGjj~qfvOEMm-`L6VcGQsAqWko@DY{=VB#Fl^@h728g8XB)KT3DH`^Wwr;58gEi_e z#AU06(o+>`57T9d<1w*5sxg;%mqVTFw9}diS_Z%OP>p(AVjT~!tId{X_f);w-3i^@ zR-4oZVTLWvR?x_C?c?53z|p-o#@7WPj4;}Y&dD8K2OQSPT&hWRiowzxPo+Mj%Stp zSI@ggJ9D67dTP`YLM?zw{5QV#z87j2RMtO9zEG3>)K#0@qaD3at*2|$^FrBA8p{AL zy((~63~k!=+M$BJvmi&T=-HRh9R0h-^l*0cTf@7_J#xVd16N0@__Spxwg%m{|J0~k z_-#AMopBDVgI-N%Q~u*PO1|=AfVQ~FKpwt{n|IK0$~es zJX??xCejfPs#O6J$3lM`7yiOM&Ab__6h308#SiVr&u|`x+IwNGYSfSa=A0?Sg4i^^ z{rG4q>qQp7>BoE#d{LOi-~MC14gPp|tvW98hx_@}udQQ}nuBi6=t+bhWATT(tmmKO z*T6@Qw|MW5`MvOaFR`A#+|OgmS?LeEl<=2Y{Nx|=S@1;+N_G0a{`yDH-zDKMv%>H6 z^XKBXO(uMt75;HQe=hy$@X_%WKkLW54}R|~i$C*KzvqW7q1=0*v*s|;SwQyffz~Yn z-nW7;YQA;AR);m?&CM0JnoUSr3`M@a);!j}keaToj>d9oln*MtsMa(A$DibMBF1++ zU@M?as{e4&e}Y>Xmo6{3*m!HA%N{0!pXe#{ip7jTMCT;csOr9g@y4MS*`u8U74dwn zx>%_F-*Ri2`&wrcU{h(WdRWSSyXcFVy}H7EwKHZahVEvIk{au$W*W||REskBGK0AU zX?RakZS;clhS#O>yUk@Y;tsLLs1v2a%39SR@$W%t_j=N^{7~Mg;c+B4ei~KJTdUSf z9It<4tljFKU}T=8tn z!)g|^jhIs@^RI7{1ZONAIb&|U!{`w2p^s2b{sX6Ev!LSl)|%GwRzG!6sNGQA@71bb z^8nj&KehH7UBNM z9UZkQRfhb-M=kPu-=X6WIve`H|E4pc%b>fVqfIB~sb%co?FDQ4ygzoiQ96}P{27dB zi@#tdVJ8!kI}n<_s8xH-3CW?2?CEXJ#wQ$j;;~xe>lMs8hRx&~L6C`A{YvowIm~TD z@ND^$J}Mc^#jFNf;zw+G2p@6T0I|$i41FkXti1#w`x{u9A8kNrL12E=oW(oxLUSkFez@?Gfg1QXpDR{STrP;&{(MqT5kq3u%Lh zVm9P+YCB7iJHh&EPMr?=pJ=LV2Ji z?03lChZ)%{BNe7o#8KBU0T@D3?sYv7Qi9NVfsZ@j%{*A1N0c0nVUFVd$Tuo?LeyP}N0EO%XD z51mVDqkM)V%XLRcHiB!SPyO9|!JlWjriJVVi@emQdd-+V1+xby@|8{Y8ABhy!CVYN zN{r8#CPK0>2uB}vU4CIUMO6e)IMt^Pp3T%@#>)@5t`F&kI6h5Y@up8%n;-rl9`oYn zk?L5V`o_2@%QeBc^Fb0=0u~eJGd4Zwy2IXyP;!OO`1(QDO(C z`qVfx(s#f@yTQul`qV$^`b^b}9s*8Gq9L2_Q^9B9`yO&lzS7BCmqOWY^r@$4$>mQl zA6U);pBimp(kAQ%b0zzXojI-%%i0jqZu05dMFIvTW0^l4^e{UmBycy}-kW{;_&cIG zb9@6<#6qSH`#mHA>_Cd|>^Y4TbYsDGFQRrno_B*jDBu#{G0J=k4O<*twHa_ZE zcd1dbj~qMtNZyd(8yStA_Hix7tOiv3F_R*{M-U-Wssh^RQyD zBj;h2V4de-jbJ_JVSB+iaW~)@?O>r9bp->!F2IQM6ayAu1)hh6fyJGN#egN8hb4d| zpNFM^rJuzXnwg&kn0Xd7v0SiK=V2vax&1IP?W(|vz?PbOg$^5eqqYoo+2(hFm)}!2 zV5XhZ@Bw?5)u~GeCx4RLVPG9#6E$M?DluSv%j%4|@$wZSgl5(m%MfN{BSdD_Dctnx zKZ(Hw76ZnnRQ)GiXDL{`1o~~>gfvlV0$765HKvY!bSqd9n5?wQpXeP2EA1DV%o{aP zE=S(2HHVQy|6(^P^YY@HeS%`8QRSE({8$^B`XumF?Ho%ox*c<@i3n`_q2pY6QF^mt z5lmQ1tvMz73XGl-Z3ayS{e$S|fu5UQmF#HrQ#1V3+0FS*dD+&_6225Fx3gC5kla`XCDvi!L;8^M$TYo>(~e5o9X`d1IBhN}l@ZLkMyJq7E~fpI zj2emj)Lw|;peK*?{=Q{$Ah)ZTX5-h>x(gD<55Bby^ zGPidMHEp@})Un?pY7t3Z?Nj@OI%>wiRBS!e8mO$F`qWx^@+VN|=4C2{Z-Fn(^{HB! zUO8w6)mL&33#>73MR$gabL?B_fPO|}D`}L6%gE-uP4TQZ<3Bp0k@?^>GAM^2WQsBR3 zllypQ7M#!HQ$AyJ4wK9Gz-7-ne}e5d}E{WRlN3Hc1g2w z$8zBinno}3jZfVzv)I#6I+s6fUNptKv@;W>_V0b_aXklx5+yX!xb#`3ZOdhQdx~rp z--VJbkoH0fVUwv8Z4^Jts|MdnpE0CPeJNFT<$#B{^4Ld|`AT7k1D#bv>(rEZbQYka z-|NE+4niawmv5w=G@_9_w@#(Yu+*TT(G{R*BRltPx6y82U#I>Z4E6M1`c3AvZ)8?C zv4J7w5_AF<)|vZtrhQ`8LBn~H-9Qac4QX|1s^s?>Von6rR(<3h3=EdkogJuTlCN=K zWlLy1<$P`#LSjapxicn>jXU-Zq3^BJ&X(m)*zJrq;9}SqZlmZaURM@ zLvKvjAt7?>1~|6XgBb9fCsF(%Z-llG!j{m(0O-9KnF#4m)eYE-(Et{lS7+X!)rSz{ zs{8R~kCeM0+;!&uj8zE5BFO%X5`-NHsR&~m5IWs;Y6AHve~J9p3})L{*Uwe$q-$+= zWBlH9?hvYsrXs)&RMe@b_$>=V=8dV3>sd`PerCfbcFrdbKa;)=?Ize_3APSMtYtOx{xjnNnw(1)Dy!%mSC7i|S0XU@fR#0D3zZr~Iu@ z5vg_NigPMd)$j9So$}62e44+OR&)mFxn;B>DBEpyrh|z6AJAe9#=D|iuI-dpOSrwx zG$IP1v=JeR&H>fys;QuQ<+c>G-H#6PqrIS+cUZb_9I$jV?`2pB`UhUnwi5K58PWn3 zcc(>-A3((|qbGpU&pou?imMznho&W6-fRPC-1~(~908dW%3XEl+Uer`{nk#?VUFL; zrziTiqpwYo)o#LCpu?+z%Pto?82&2%{LBR(v)*%E5spNu{2Om1ZXrr%d* zE}ho?N%suV#<``_-S9ayRC?)jEqsZSPTY1sR^#$I(>Lo}3Dukz?@R`b%BnLLRA+#? zDp>_{kwLjInK0dwSwmq+)g^jWH89cmMQP#4K{pKJ^4sURP`$aDxku+4Oi=a`sGC3y z66lrpF;V2eVajp_$q&@2+ob7V4yva$mpT|LcRxd@UGQbub*e-{Er37p4&Bai&>qk= z^6poHnwHETIJTMj$3mMkC5u|}Se>~9pjzzafNxSH&L5nsv7}1iX;c*@i(z8%lcxmr zTf4whH(nEE?cxB+tDdM+(ekkEC|8YhjhO5k7B%nOyj3Bb-ftfK09o@>OS$&{P)OoYpY+qht8NY)Zpp% zf*}PuHRWiQ(w*tIS^WP`&HqY_G_CD~Z!D=(+ojgc?H9f@NRJnA5l?5MD_&{PpvnD? z*Iil~6x8pE$WkJZw~NUp!e7>@dZr<4lS#VR*XLg(!<>(&AVTg3k?zYykkD7^)HCwE zkp`oCUtWT<1vKqfb!v~4(|E}PakZ7%!lxGb5Y6i zM}*_IMN*YRh#l|Hn+FqOt(hwG(5MmozL>}GZSU8qC!|dF$-~6W*)8wqP1Q^65fAfr zt##_JlEOXEda2=CPr-%8vyZwWMm35f4EZisp;&T=?BuupE;A$8VzWbnx7C@$6snvU z-wDnh(5!Z=+mJ%e{1e4584|sc2Ig>`DwWjCf)Rh1L0vXI^f5B;!Z%h9D#>5espX z)u~@d!R(_bbr&r@yaI1GzAK>wd4VhN|g*`9)Po>3s4bRzkCTby%DKRSFM5Ewf-doP}9ITpI_3CxW*+Y_I84*9!ns=pR7nJRqdNl###kO&u zIg)BK-r2`!Ic*1{mf*)Qe{ZQ*$E4JD4k(oa4Wu+JDxahJD7o&aS7lP!-jqc8-I@hH zYplB{f6SI^>4X~5+I?5Oc@kDy)3~cx#Q)?0?nBh0>(y7WZe;t3ObGG5vgbpp*xC0r zdT(PWmFAOkw1{G+dZ-JH?-hhN&{>6r`Moqqsq(bZY*WaNVmK4< zI3@VgdQ~ITGALaW*#|P+nGUt)sd~j&&8md5xxqPsP&=SCWHME4hvjh%6ElJe9}LwSHV*A77|0^6YEDUf9d@0GE;w-r6}ia zs8_fG;()W+%&{MU#T~uj%l7=BO+kZ(owxRnuCyHo2-{a&Z+dhltR4vlrWm)5;cBL+ zCy4mjdb9oyZ80sVR|c_3har&w#JoT(5p7{3dvr`7pu`P)>c^|1TW(GgGb&D7VQ-A5V?9}CuAZ#;b1eMvz&zq{eJ<109^(X+w&!0zEU z`=#T8mJAD~4G$;;PugBTV8?G6SQ^-L(dAEOrFVd>0-GX$9>d7dG;_GI7p@S_@?lb- z#~|&1E>~af> z;{Rz+*_a*OZaja)J?^SCC^&c2D<)&CR4+Q4>QXoxoCK0T$wi;mH$FV#9<5(ga_#_F zGJ5_!(V`p4f5*Yoz%MjDZ(AQe#FiHnkhsH``ZxE~A@Tgq{DX0~{LbQcT&wXczeCFS zozqH{GCo@HbcFHo-`tLnyXpZMXaD-Ju+5=%$yQzDFvT#tjmN%XC)j15xrYvk za4{_U)VS_5w$9Jycgm;6y`Q%kxL*P0`%| zN#EC>onw%m)Rs@qfE_k>Q{!hXJR}jU2kbv4_KaXIu-&KXmDfB`{8k5NTn~T+_tG|+ z*diwrsF9y@gf+5&+~})UoXh{CD^6;O*gzSe$^WfqoAtbF3nv>5qS*kpL)EMCvL42e z`g5Y~1IrEEPLk%Grd_=D3$Tdh^=$KJ9c(YH=6-Ga;Ij@JtZ8{Nn0-@4H3LsNFs*}MZZac}O|94k}EyiLoodUMde z6wSD_!g0o*{?1OZVyMEu*Q<@vx=w`BBe{Zqu=T7PYR3uY6nHh;*G<+idC7V{z_04n zo%*SudJuee>{+wLEfSBznO52f5_AOr*j5dBa;T~u2dxlNxq$0(nvLvCT#H2)}VrfZ9lL9{Xs+3G9 zsmQ~Uk`@~X7cLk}Hz_G?vdNN`TW(p>q@p6FMM;T%e$R70pR3sR{oTg{J?Hg0|Np$t z`~3NwV-64-lnj!CM;TnivP$(bpvkSd$nAqmSSCLCnvd9yVRkO7y!2k&n3!!uy}a_G zc#(230jyxTc&saLicdD?qUGY1uDppp<(NB`i_=|s;|5D%-hw^$9%-NG-;KHCo=Rha zd@Y^J3w+R)=YNwoc5E!|DA6pSZ{9$p`>AWfaQJBLXsWj$_=k8{KVBP7wK>swb==GIGhk1Kk z<>|c9{^ejDKf~I=N-{6H3qOOq&}ZZdVI^yfs@m^8lQ(=!5?J74mBwaLD}OS(>0rf= ziEU@dZV~2Q_$G4$_oFk^=y9-hkBbXu@^17GWiO%e@k*m!th!I1FLn(_CYE>e*-F0L zWY}0~bek79mJOh=UKyS@da#r;osdP@V%WE|iJ0vON~gF_nhP)&6^OcT^X~TVz+Ako zQhvE3k5#CPN-E#vyASOP?(bakpS*GY3CuT3iz>O)Q9Z22bzh_XOTfCpek0zO-|lz( zCokHk7cBO9G48v(&;2`|qh&N!8oxCC%gdF#U5 zxwn#S`&H+;!~%tQtWPgk(;vjR@A(`q{sk)Vua(>v@NH6VxG4FaIIF z^F*ckjzSi)ECRn0mX=S589$Ki6lwlk=$L)0FeePE5=Vc?o8{Y%CEveF_@2cQ^diC# z%lxxkfRKS@oxMtI$KtaOODf9dS;F$$L4O@9X7tK%F}IEvx-?f|=3a*PF#GmmZVI92 z{>Za!VFGnBq|n$aUhP4orvI216p#wGD7?xziUc&jLw@zgyiq=-z$YTaTR-MK;A1PL zoUapC{gii&e;nqp>#G=&Df7`nujK~k;-45miy@B1Rq^s2br>$%f69yT=>>~iD8`-3 zyE`EH*NpEsRT(2tkIajqE6(Lb`qu+jtg7OIkX4C{yR}#6*?08ig$8)NL}T4rWvoWU zGo2>^`zHZMzgWdE&DV;2ed*d{7v&H>1G4=fvZZnG~eHfTSKUZyM$h(`=Wp$l_F zOO?UQ<_^3^hLmG(#i9P|WVAgJ9u~|H+vz^1lj)D5P2_+D1H|=@W* z;{AQ(M7?cL?yblAH$kMV+>HjyON_xgYlDkeuibrkwB%dsZ@>Sv` zn?A+A9CP66-A18Um5u;XAIo;z^iZE}h~!_0k%RP?0*YUw%JtpGdyFtEh@#$Dm=V>xNd~7lKnqa^!mEKU7zy4?Z#>j_urSykXY!0Urvi32f2J zyOAwdd`lMy@WoxgZz=Hd-L%D7Ey$A<3bAiqjvBr;h(!kPdb{oy+29jH1F0kXS-A1q&P|AHW z=fHe=!CY=J&R@UJKedv!)VACBv-ot0?6*&`Fy!*5Zki#oj_*dv{dPSb?Sly{pbBC8 z#cpGznd{pJ)0$Gjs=nTB_?p@MX8&+*-X6t-&2!30^vahJ-klg#^TtichtTwJSHTqPErGjP8sAfP`uL0K#)yF!jz)sAp zHYUmt!4ZQD6)Ex$6a62iE^&OMr{Q1CyG({7G#+6zrIP z;XrPYe(tu-!$s;aJ#btSVAg_aqglEd7^gn@mBU2_c*u?7*lp=VzR{_{y$N3Fw9mytmp2XQJIt zg^$xHqPRNLuU}Lf*PCUm#@%8`6hm~ZtLFM5nmxi?V(nn*k5(I5{RZi%EA&v`C^!1Y zhH7#0b-uRy_z(0DCvbohxZ{`A#_u>UWNx=#$mQ*Ywkv3K9dJ?4RU22EJtg`|J<=zx zo`jZ&2d>nw8k31R|HW$KMp-ocO8CnMYkpBwU#UO8Fr_sG_DX7wX(c8w6pFkEt~NC}$IHi^-Hs zG?B)SW3FO@skE3wk|}^`n^di9;$AHPsWpdlPa8YbiJ?-&JWG<>-p75=R$5m=m3&3Eu_Z z`fjB$hZP#zRvCyqA~b!BKGW{?27}SR@!G%0i^ou~$uP-3!1OQj)**~M_21{%b_DA- zdlgRFudOl){RsDkXRVE&pxmKiZm|BMmbsTkyr{~UKOXv&Ct2@J(1-3dg7r@aX!(C2 zio7Z#Lmms-SahkJhl=6j^zSv-AJG#3RAto2A94S2l`&^J)LOi% z4UEqZI?&d}%hbd7`0qCElIz`-MHg4lhlvhotqMAB#BO7rTwvdc_e(Vr6AIr4pHQ*e z_!wo*_8mMWroJtlp(QmTir?LBY-WqZw%Ajc^1PL3$E&>pKK=A=^TXDS&&cL|lhzBE z9Iy2&r5Rc|Q~^W8GD&Us_==i{uO6t9@M>e1 zq+ak)`B3&nZ&53Z6>9b_o{_Wnv7&7vFT7^MXj`j|Kgz}_QxW=nPT)|v>v5u)0qpy2 zTqdDz5iwq5Fp^5bJhe<3@-`z4?Kd8ma;7?v1zfoZ7!6+kGh9v;6TmycZ#8uXW0F4B zw-hYFi}ipoZcb0b(IEh>yxN@B%H!xh5$~|r`&+RjOuyMT3v;=5q1cIYpE4{lmExT+ z{TiR+n7gXQg)sfbv7rYUb!w`Of6{p6PYy28V4-`fd0_g}ovf^9Pu3%S^TFB)tY)%4 z!QX{t9}a$F9(+DGS)VglJ48U9>rh#RRqB0L*jpMGjbzU>W@|nNAz&jQ@6`T?=JJj2umGI;p>RIY@t3VPK_~SO?9U0NWr-WmgKH0tqupY2Tf|H&S zuS+l&FyNJGaez2X%kGNQ~5{YoX+<>er}B zav<87Z>7T>i4f0(DrJ0CU$1U}QlEbQ5UG!`H$tTol&m+~vV1XqI^$0-V0mS=kt>B) zIPR6aR-$9{Xz}UCZL8V0aV2kBx=nRB>J5=`JYNJRN z@M~Md;u-n^d(>ZO%1114?G~|9f=zq5fnKYs5|9 zN7V*1GB4YxEv9JY)Apfa;Y{8OwH;vNwwt_fRa1Y9qs3XJ7swR)XclD?SlN8DR{uWR?^;nB%-^%q9Kf zSRx11$R+*JzP(sdv5dc3zsWbgjmp8Y_G*2sx#}$q zqS|~ebb^14`CQTpXw~M2MC)_x9Z-j$?w7;UD2Ljz6N>?h}K6d zDO?KB6sR)2T%;|~ZN7aVEjT%P4Hp;I_>?NIeH686;^yp;$Q=$)1fR zWFW6w%)wHIr4$Rk$NDy7>G4=Pu*3~w@iiL8?-;Tb%S`Dk5=)g||M1g5!u+jJ^07Fv zD34WG0tT~qe7!!-2Xh8y6<0TwLs(QQ0{>22Lo6l+OBoiG*ig|@#3@ia!q1YZn&WfB9F`u_VpfKKI!My+Le}Rt-N(%Z71!#4 zzNKK1ffV4i`gGq`EG<}6adl%U8EFL;cpNp~V~N3%Fv?<5u~d01PAs+{i>bhphD9Z< z1xw^;t5h)eO0%qW?Vrr_SjAUyMq}w_Vf{=zObV7Rmd|I-qaZOanq&oBj->*Na&-tx zd6;6XHrEMKbXB3S$C9Oxe^HFnthnN^L`Pboq+=;IEu+<)egyzs0Pa|QvTq}n(3w`U zj$?6Qao$YBj~TO!ivO_J*38rG6_qThsnayhD8-{DVEl`g<}0}iWh@g*{0&xdtWMGM1U)Zjzy)XSNd6q zMb=U~UNEa1#9>K}w=C&c+C7#6EbDF}3-P9xUd*bT+8}yvwgT?Kl5~q@3Hp@g-b$xx2Ppx$G}Z)uMhjbE1r>uOAl34nilx+JabgL( zw}1bv04WDKatk8lIF{V|tW5MtkE^Y`hJH>`*H}TuW9i1C#9aoKrgW?OV-C9CELAls z`qzVG-A{8iM}o<>>h>Mvz`p}L>Vf|FaNnRWsMH6n5{Sj(mtiF(4NLw*{Yt<;A4FSw zx&M7&t!o)n7SLWXr>wKm>(xo0@>s&KWIk*qei4@3$JiIS7B83=ZLo-9EP+{;r4dUz z7S)lCV~Ks-D(OBfF~78;3H_46)nkdrlC;rcGO(P$q6(}COI5a+8P)Xa0ivJyA5E_Z zLVMD5X;ysDSI7>K`3clk0+y5qV&m&MV;C*Od{c$;8N&=4_Tq8Cv z(Wm?8WAPet*?ehiJy;l+XTB7Hr$b;-V0VdCZ{&$K>{H#&uFR+@3DRC+at7 zUNZHcZZnTp)RhnUfr>28M)Eyii?@nYnrxb@? z9ovjiQXM_JMtCRbH`qG>1HZI@lxiqZzYj{T$rK@ z+l(7z0@kk)UnJ?%?fU?WnQE3vaN!y;=?pAwYGlaRc(3qNm!Dx@K)UPSULMI0BB_@ zBwM!pNvsU46O2Vd^-JR4tp6a^vaQCkbfq;jjQKrlaJ#Q_FNKpeOrM|~ zf~s&9ns<#mJiczY2VX&?w0Vo=EAcX4>!7;wEh_jjsu?QiX)C}l?zNJk0u1~fiSR3n z+Itz50hLr_1-KRJQVN@(a-Xq$ExpW__5%Z3vE^&hWxmp(4n1d4XHqW(I96yY7FQP-@<)*4$lCUBhaOKT-X*?NI9boL!GQCv!(7 zN3+jSiZ2wJx5#3bSQ1z=*fp5tFP^`2usC}S8xGH{<2YIQz3fi7z%sNN9)MuOHAH-S zuRhAR6U?Qtcy*tCt4~ZHLr9>w?>_xz|9s5O5jEV4Fu<4nNM55467F;QApbUqj*&I& z*L{0m?g%k#C0nwA=TR|&YK-4n8z(E-_e}=V#?%D#oShclsn~Q?@KW&o=6R3BtB5Y%AiBsJ zBS`KRjuac7Li(tBzuHWjr4$Hn>47ut0O``qf zO*a1~kc`Sgt|3vcq};oj{n3B{-d?>mg~oPy_-KmQwMNbs!D1T_7_0T0{421;y-~=M zCdxsy{D9%**K>XSyCHh^U*^+4cA&Rc%-ervRepT2YtI3ePdj z-PhHK`8E0tQ{)k>5N~g<-@IFB%*HJBfd_|0+&C=CJUJ8tS9ZO)?|yxvZyM%~So%Wk zrfVJLSn}u9s8w`Tu)iAS7taMjvJji#S{Bq8h76FO#M;0bZ>+iO0m%-qda$X|5tSp4 z4NJFu(m%K^aeflhY~Ehc3u_Fi?C>*RRqCV+nn}bfQuwXE(^_=e_yD`&S-960ikBW> zUAhc&M7(J}ggNdeX`VC)W6I4Y95~3E+cv~k62@4t$b{1|m)|O`d(edU-Dbj#z-^1g zHVf`dFkzd#G;xXemxM6}C7N(D=BOm`WQIP`KNoZMJB7yU;>{nO;{w!S-YT$wgNzm# z`s5kMuoNFGH10R^sq)?l)&(ZpnEXZY*9X>;BqsiXHTWoB%H|GnH|7cHnCtJTxwt**){}O5-nKDDI!M z7W3p|D~@`QV>vZOl61mPwx>g2TJGi8aj*n17M#^jy6Xa4^fSzBn73E_&#*wSxSwGW zV6nM1j>`g!1&sNb!z8fipJC}>QD9;H5|a%k6UcC*elmeYU^0RIGgl54_GY2M`Zd2a zGslg9nK-;&fhN`hRtDBT$0xu_!90;jEx#MA1WXkFjeM!l%}qdE&;$ z*yBpUoS+L`;suz?bW!)1ezi|C<{n*q{uoob4$PWE4BnvMc1`pZ^i@ZVdG4AtdR~W3 z0N)2b*-AU-OT9udr-QdS#I_BDQ-Hb8A?h~B@G-|b#pfG{w;OZ5Q;f{Q9C{_K$SH2l zBFzbygEooBvdBvb=HgA_k6HRWpW~PVHj4|=z29(euhh+A#^d_98zV7iZ??*ww56(1 zi0>>gFC;y(jFWIvf*W;i+)BTcb{XKV&Ena|^{XA_n3K0$UfGRc#b8mml|R`iTBIMa z%jy-Z>StItSOr-BMrI479>F~IFT)E0YuZ9fHd~Y#NhIJt9QMcJz#73e$dvGto|C~s z^Dj?K23QE#V&%$omkXBiGk3*c$rmw)>97JY36O1T^^=k8lL78!79bHyPhzcLi*VOJ zf$d=NV4mE`65dCxMKQ*fVYYp=;L_J_ZRWS5Pk6p|6E|$s?c*ayczgK~gmmutNnJi3 zy#5U}H!z>Y&IITIkeWZ|#m#RCU4WfMX!_#)`#C@V?M9{~E~=r6Qd6ICpNq3J!lO=x z?{<03b{~8nF{;h%gYeygtI{0Vy1!jkLgZ65rl!6tPHeB@mBann?6=9{Oem-^wo6I= zAf7ncKS_kl=6jKoIr<KFB4PJd}mih!5YA9*Uc&^60yz#sF3PjH4QBFNh-w^S^7QomD+A4ws+ zVCC)9yi0i-tCc_ioFI_=}u^?S)dGXC&%mQ{$TYjFUA6k702giHF*y zvmV|Uf(F-K!}DlwpXVdRp9smn2dun<{WWt-d;EiWc2-c5XmmXURPt$!@ve;iK&)8l z;3&qTU^+R@Rf{W(?O!XUP;H;lg=P6Hg;H0fL^vtBE~vh*YK$sL&4=(VDEL#{=e@6OdZnzTvx z(PGBZ@rSsK+htZ3%cRLSKmW9ese6H>hPsSHvbcg{%`f(@pTU{asLgzRc>=n1n9KNw zq-FG~paWLrjS$YwdYEs1C_>a_P4erp^hk@GzMsGnQ)_l&5w%6PMFdV@s2}Z8cM^E< zM!FO=QZ?%y$G~ZK8Ea%5XRh;nma>KI;JERKO3h{bPG)iMb!O_?wvhP}n7|P(tN-Fj zwdP6r9QX*Eh};}4Zp&vdNtWg@;>mofItz2!SQqCk7yO;c+5UYj`5gl*9`7N*=&vgJ*6J26^cI4%w_ z{V|uZ634ua7d4Q#=tD84fV&C*%jn(_q z+t(#f$2`>8ITp43W$v3WhXVN65SKcIWUGOfFD{7U1V)KS;!X8XTP-Ruj^w4nxW)Si4JO!NeoF;jYa4aPjgSE$cP2nnYPPIMX7l3Et+8D&{|R#y_7 z@(i*7OFZVASeKUNn}XlCFtd(qi3IC>f{(91D%Ab80;qDRM45x@q1sCs8zUZc1dNux zz0$!C;U;T}%Wz7$9gG|G!I%-X$-Mf*9vOieJJqGuRcxnbi<4EHYDmIkDnu*9KG|=N z&bH1Jep18;RSy+2!(|*~^}x0tN)^xiXY@Pl(Nme#T;nn}%OvfbEp|P_@y86nxVbLl zMkxRtxR(!Ci{58gmu-TIO>mhPG|!I~;m=b1U4W6dy9`4PW0Od;Iv0}rte$Mox{3yN z7g5Xb50l(ysgr$x<$%j&LlcCrj%z-@F z!6)y&BqB2L6AzZOTRilf{>X%Vm}9G5hHO2u_i{6)m(=yzq(AT<#LM3~Ld1yY_2I#O zQKYHn@*W)m7ExpM=mcqTA+}EC%^6#OCDrAU^`d_H{96W=0#+^8q$Z0C&+B&!XZ|ifHjDms=RO9p{@j?kBQ8H9mp39KmDcknX zlofuHRt4Br>N1}52Yit5lwK3_A}yc~D&#jV<67AQHhQ8ee32HHPCj~|ct(<*2c<^i z|GdaDN6&0(qKQ154jK;@KVg$eH<= zz20SHBVY0-|1Skg_}itSMK%r>Sk>RGVWbsH_6OE5(j^%zau^8oPyD&HTB5>hcJsp9?4{VY2WWw%=tO#%$Y3 zi7UA`_m@bRq?h=D_5^$p`~k@)dUz%A<4JOdhw}DN?jgKtB=Z?dG0GThdr2%;P^ou? z2H9aUVSXW*tuW;qH$~dR$Z!Q{o;U|m1ZU3>M}JM-RRL;mxeU29 zWE+D2wCqix+Hue*&^C#FF&(*{y(vlyx&~bl^cIQk1r3dN@Z=_F=mD4ctoIAkeRs%l zsO=?rz!t^{vp|;P7Tl{vY~v+;wAKVw){LqrsSQvR(?l_@f{D)B4!&NJ56alX<}xjK z+hvr>_-0NQ+jk-klL4dOaT))ih1e#^q}B7T(?>fQOl2k$gnD*)JSnl?k3(F zxt31y7c@*6R*v-LWIpB2rHa*kVVUqLN34NpCj7;LX!yl}$c3B8zsi9~411AGtA%Pg z4w|zZj0(0wkV#zMnEiy3&_L$~uJhIJ<7>d zRz6izjU?DyC_h;`XQv`3N;h4*qa-I-jr}FCVeebR((ACQfKCqPf~~Ya@fuJcd=b3a zI;X1EIXG6P2=H%S;**#33F7F#H;uMC0ek85MY14LJ3&L4?qB!?C zI#%BA*yMCxDX%NTQQUEt@h9o%b&n%D#Wh-59I5@M%XolTY&lSUzhu(wtk5-k8Po|V z)k{`{i(M7;l2*Wke_6dG5%=c)QTXO-hs%L5dI6%~W7K{b-1XsN?r-sv4w(Fj%lMZB zgYnZ9zBy7W2kiw_J!d$m>N#zoF(<8_V@x4G7kf_Jji~$WF7sx$8YmTYWhLA4RZ#6v zf0r?AfYM&r6l3oO&HmJ7bV_vT6mfl(zCbw3a%~Cm3u&4EwgmC`DL)s)|EH!HLr6~i zmY%YD;V9y|*b6VIgH5SarLfy|$=&F|vJ(W|$^d^_%&S2pq+#wjEuOB?XZV+3 z*1mIbCA_*C9d&PpIKLyu*QZx{=n>;w@_WPEkVikbj5(&G?dNi44M`=?X7G};F5z}= zx=X01h(i}6zk6M5bg3EV`Xj6xDUE;&MQu^*V0&S&!>`oVwhVmK^XROaS|<^wA2}Z) zYi#QT3P!GFovhWz4l9RhA)Tvb6|4}?R`DIx%sPfRKhjWs)@870$SZC5(Q5|*qx(0FK#S%yQx8a-Z!Mp9qTWpr zuetTWb)c~kKpR>swvS`HC>}#&Xe7^# zlwFTpOj$GD(wHB)XVWkdU$2KI_z`LGfLc=>f&bYvZvS6(Bp$zk-nFK>v4XIbz+x7O zP64$J>Izw2b|}@x5^bC9V*cnnZljjHs2fLqKDFkg>fE^h>O~p2i6loSC=@lR`Up3d zCRJSxx}Wc{MAU#Gwc=cZe!qWQ5;L%&wS44!zJU|N>XL=+d-Q94s~{qatc90jSc|rQYfP;%30(eUA*>@c38YzRUq^yX?Q6#oG#r^|C(KaU9CyMf zeL3y-QeTFO)hud7KPn_JnJ|+`RPU9w#uytugP||pf+ZS%uZO5rVkxORmLf8PYK;`J zAZ&A>`1beu@R@?Ds*(M@q3xrgQHcivu1y3Yd43eAtjcC7LBbn zRmLr&#m2o1DeC|u$JH8(WgB}CkEW{hB)6Eg?`1WvN%G@sjbBNA8oX*csT!+wiEnHg zqy@-ah1MDiq@Hmm$eLL^w2zrZ+1(84VYQbne)WO%Mv!5iV^x|+z+I?6I65rDABBGv z{@Oo~r!1&IiXch`ePEO&N&2qgyiD6ZJ!r=dIW~?bs9T&uQjjcHS-m?JOX)PLcc)4Q zi|pN61kQ5-s%F<3t1yf58@cnk_>a16oUEZLs4gha=>b_oO<;b};*&q>S2?;cFN&_c zY^}>{IW6;NSRhy&SpTKC2(SdODTFD1GQL=_jGtjiVEO(0%GMpRjLHqK^;1V9Y{4VN z_Wk2r`ywo%acabW2@ToWPT;2KDobb_#LY#ap)}xl z3Zr6rt-)jST(vhs)HHEsEE_O$X072Rt8Bvvak5FDpw0lgAX=Z|ESl_wD@KUnZ|c`N zWEn)SV8(KFtuYg`{7GyP*aZuA^vgqqSkcR8rPXvvBZkN7M-s({w}Y<2$$FWjSMhP8y$x^aY0--pmTf7eGL3Y ztufK`nc0lbbzs>8>WmiCC(rMM`0fMi8(1e!Hgm?swvy7Y)$z5WN&>gBj2v_mx2*D^ zQZ1k8w!g)jIT^U=8dN8?zm11REK$C7##V8~eWbv-^)tGyTiyOYtg7Y*?MV5Jcv>{UG@{0k&vd9q)0dpo3X_nCah13>9vS5vIkXC@8+RRh>$uBe)5p99Gyl)}tN};F0-eR#x8K!AdB+W0By&A- zsO81xPq??PYUeovpO6oEq!jSv|! zq~V^BSnist1(A`qsa8l|`&emB2JP{n`z_R73>t(~{F4m7&_@`pENip^o&Yo?xDlsG zx-&(K{)OBpsWm_JoCd0fR{K$gwlb)YNQ*i-$fEcLbdF|wh;9k>qzvl?NzHdeYH^@` zNL!Vy4WM$aG@0eC<({Rkn>{P&xi~Gt>HDe#JYlP7JD_YcYSrcUwzD=XTE`XK#+I-a zUr_3{x4kys9pU7l5ilNcK2DYyZyhKwWF7JVuuFzN-xG-Ocha5@l^=!Rk(A+Wh5z6` z*dsj#6*$X^`U4N8qKcDQcKEYl0BD3C8K+70O`At$|?d~2l}o=H-Hjf ziq->q%;Pp3G}D(J5%nk)8)JoV*2@ZE9cV153gIv)Atal-O2?oopwt3N4wQ2U@6FmC zV;BI9Pz5E?LI;<%gL*L-D!12r+-8G@g5D(Ejsi_OirXg8$m?s(=R?@aFnikifV|Om zuMM<$#!nT@DMO5pcTCWt0AoDh0o;?05YZjRMeAu$DNvzUQpUM~R)mmcW!;aDR45-ir^jb+R zHiViy74 z#vHiHIYs>VPdz+lfjnf{|0dX|doM?B9u^fxIGu#ke5=)k?4(tNv;J1+XnW)@Y2qjh z4R%uCZ3z3T&Y9vb|I)WFFi&6CJZe`3#56 zegeKVzt%WsgP-Ot&a~?@#giZFOSFt9803FN$#P4pZ8knt{hj+*|5D3;k{!itF;p_xz$#= z)mQ>$z};$W8gQ>1;YW$Y3VqPx_*}+`npz`M=4id`=Nj)Jk0?Yerg_X#EXwGe_|0(D z8oHdqZ&1-cr-kM*o;EP|mPN4Tq9~j0+@u|YO8$MV`DVgO57qF9Q)(!2R;nCVa>aA3 zPV0Ju9xRJwrgZr-qgUo19U)o;RQf)v8xJ=FS#Ebw?tp7#jUSfvlF1mj2-NEjwZ%{@@vuc;*g zRsp^%Q@m43$2gE0EZ?fRAdeHJ;{tqT=9d7j0meUo~GDs`)%fD=S5%ODHrBMc%9)R zb5Kd8smlz^`%)y|2A>^OXH1p6Y_95daJ`EAy9zp4FbNSnB^{Pe%s12-qopG^jxODB zgQHL_8$RxqI^$QeIld0BQubb_{=63T6fJd0ov~&V)Gbh|1fTwr63T~){zaYf0#gc` z)B;qf|N2rNHXu;EpW_Ht^6WUSoR6ZT%d8#7mHHIp`mgj~sCA8OvRu9qjZ_xOgV5?G zn)klauTqzO%Bf1guaKg@sk>|v6$+O8Gb|b`6RiK-Xc1UsecgZ0jY=TA-l+SZbEA5= z^tbCSn;RVhOZ!WmoEuFR?O*G*nLQwy-F;cI{I+qNWxU&%N3!Kl&XYpGVnW?&A~hoe zOTuLL#h_*Wa=}u-Jf5P#iotqr5l?q>jfgu#kS3oZgp{t%8^8OhxvH zY^73FJ^||TpgTQi5GiW|ReGRo(Q*IOBei6ib`N^J2bDPr+G&L=Wq}H}6*Sg^N)1V& zexxhSgVtE6JsvdQgXRnrM;&?7H78&dpi*`h<5cNnouF}Lb*9#7m^#lSp|sAVBBbog zRIU^huMaS_$bEobuhf}3*+!3hr4NQaLwP_cRdlZ9z5p<8mleo4FEfyCz+OP5iXK3d zQG@5qJ&tSa&(a6V>x@7dr^9mI3>fojo%!(g!i&zGC~^_oIY9)aR7777rIJ$&l}#g8 z>gi$6Fs#(mUZ|+o>dYfFg&v9#dXAP>OwyoKfQvnp)KTp%P|k`vQ>YI?ojT^2uGyYL z`}wWq{wzv}%8GKo4yx>Tb;fP972Dnm;-eq=xUUMZ9&nJ%ptvB`4{|QowC8EpmB>5k zaxsCZ7mu<*n?co-#W-&Tee!Oy-3}E|WqEkrq%1vi(F;tZpl*?2JqRWBOnWwHJ*d(% z7oHc6LdP7f39xIorBmaJ(N;U#`kcVA_J<~ zU1zG78~aT4vJ@~BVWplpS&Dn5UWy)lwEYBBSp&UC7Vs^7rg|B_1D$G*)%`~Gnd+qz z@Hn7SFTXg~U%hOh@S33epxG9n`k>85`n=<|f!@o-s^4=MHtZh`weFB67OZjxYXyQM z8O*-DWz?tep+@SH=g43yRLFj-8yxmfYk%P!F}$9*v{G6fbiP&Tn%9tPJTp;3ZMY(su zbisH@=art)&za*GF}#r!bVFCYPXl2Q)mBJoQYl?=L4RU!)l1~6uMT}woc=_Q5c3ip zSDR;Gw4IE2=j%+Rav7cPl2Vxlm15MHO65`yg;F_3Yk_L^a+}Ryyr;_=4?Azy7M0OT z2e^%4Qm;Ge>3ohy(Chl30tUHFy-s+j|E1TZzs!u)$89W<8GX>>a_4%dbxx!ez7Jk? zwwWfMERx>IvDu?vVL0=3o27T^f=GPSd9xj?*w1bDdO6E5n?Z*@dWSdK*KugC!ESSV za;r&C6#x2+eXX=gC;ExE<92)IE=psF+jv3t$(a{KjW?3R1=s|r^tw^_^yqa{HEtyH zIzH5GcuW1r8%mw@n&;v^$~2pUOXuKAhP%z9+rux2?Ka1BdokdmK(}#FMsn`FI0C4( z0-gYDmWA;(U0i7%k1XY~;mARR6#6QjM!Jo!q^E=D#fm|MlnU4fcvylp=Y^19DPZv^ z%jM4VVzCe4F~ImB%jK5y!Y)DWH>4g=g}K3UnE+Tc+HFeX6-X6-TowRkj&U1%WkmDO zi$Z_LSkdl7vF?NM3U(Wf{AZg>x|N}$C~RR?S%#BZKrIQd z1yGgcC>gQ5t54NRy&q*+Aw5m8Vn5R-PRfwl0XqRJWEMZ{6T$wD$@akC($2!I!Z<=_ zlZBB4xG2IZ40oR_j0u_(@HpTIPp=JqhP|pbe@BqL394zT+c+vi-rgt9;@;i`ST@bd z&DK50n;JFSlK6bN$2H&rPD3vNYIY}wG)7m zGu%d~Ovqd*KIG%M(}y@X(qD=2f>MEehipswku%?$h7kTbDA!CYsE^LcA{i;(luQ*& zT$EK7ySE9Tdv=(J*8d&m;mt$FAnLon{z>2{7>&<0g z$L}DE=B~TrI>+STPO#JmF6pizq_a=Dd(bV;oaHEXh>LaFb?!?#a&Uq~_FPJW)Q8i+ zO8QKF_(m6&(t-7t>BG%nVOQ2)+?td+@-eXT!1{|ivfRq-1WV;AgJF)5zP4Hxu0!j^ z%)U(vd=sz)O{f@9{ho$P$H|2DITS<_0DjG6M^7rH;eMRjT3X zKml&b9$IP9!+#LQ6e7(644mgSwagE{w<48ltNjF28I*D_zBk>+*V91fyG@08g~$EM zTRHvif;s`E+y`6k0~=^LH1hGXx1Q|rWJ+qxxljoUtf*h}P)cL&fI0@HH0CuPN@>gq zd(4(UPX-w7p%Skoz(%O_g;s!Hf7dTBTFC3vIMmD1SB;0-J`RO4A1dr7%h!4jrIOJF z<$_YaW_qZV<5>WUI?2(1WCn%CTP&e+?mt$99DtvGDAtR_kfmwX2{As@~ud`5eXFa2F=;ybmzA!!W69(H0n0XX(Fgv z7d{4>?m@>}s9oDj=k=gxy3Jq{09ygofPJ9*Qa-Hj5E|j57tXEfTe(HGV=Oq;fSYswSC1&#OVDrm!)p=P}6IKR5UCv zL=83RFoDT#^E|HOwD`K-d9B?AmBVKb_obf^9ZkxKvX6GY)uTB_Me=|>u`Jr<8 z9nkMCOIi#e^h?r0P6XCL7eSZW2<@Y9W`ve4bI(*m)w^1iouMfbhS<-k3ITePi*+(w)% z+7C&F(rO=^=omXV87ge8+wk)ir$;$LMcqWl5_>CP)5C6aZeMd+N~OD4FwL>~M;~DM z_z@~yygAh|K|DLjF-FSc`shwth0M^wZ-rxu zW12WR3Pr>{nWD&t&U)ExaM=`sfq=}Z*<{D{Yk%rgXGR1&<5^JoFp4ktqY+g-?~ulT#d1wc}9j6>cL>x|bqD#Wy<~D)JyR zncp!PkknQvRrK4#5sD>H@s)04y`+|TsG2Xi2E=@NHv-kT14rpqZo^luGhUA)HS5Y5 zoA-OI^$@M+Ew?!#b0gC(O~^vtrI|Ln%?VkJhjL7nq8O^pS;1I>*g3uh`7)uDN-wJNB%zqpNMGOEL#VwpdI!AEPMZy&L` zvG7n#CT|tp>0B073?I?TSV9c8TfP-XBOSp5mWh?GDhiDcuYRiGB#Mq)kkRdtsVk)ZWIyGv;DK=!>~FdqOKC z!{4oZ?>uAXyAd$`A8vEceREiW>R}W1X5+!Yk$1F`Jg20oC2^Holi_M0!=!QAzEC zD*rd-E`2TbP~)Sa{Mx`icblvE7k#alR0>pcr!_FF#h2O&E&y!@RjWt0fU1Hx5Sce- z7>w&nYu-8ynaT3c;E?y}>tDH9&k>J|M)0zdGuoa8nDI4YO16WqzY%AAHwD@FG-Bzc z7GH&vcAVTJb93Yy@%0?XUG{*#p+t7EW+MIV{6-{R!%!6q82gRect*P1$nZgtP83hh z$a_*9B2HuM9;FrYdSe6mXnxskik^|_A0S~5)F8O8(|#ojJT z@iZrZTyq_PS`kze)ITM)9ZJ=myk%JHfr{>CoKgi0rH+B7#>lT@jh}^|&00sS> zp5&okgQ_p*1)j&Rb&PtrNeaA}GrXIoWZ0bcc&ZEZAx1$keNwSkrV4C?@^2kSqZ z>;-e#>Wv&R>rsxhtK-{RPwpVU<20G!_5Eg<6NEguc17{c)GS7i$Eg5k=lxpPaTu=O3vYF&UXkN2&eR{A2yLL2CFvL!6*6zd=2ZiX{? z&JkKVO!SX#b9Ftu(`UzD^+YPy{ zR5HsO=2;&PrRIYnClO9iYFfJ0L&<5WJs(PSD=DHDcbN6n1(-@7QoBobk9##)PHsol zf!ZZy#?e#az{}KEeKDshnqWeF>&<1}1E$?Ne&rzuuf_u03VW zG+O}M0o6=%Jnq#@GwR>yj)N`qgMX_~m_CBmgQ}V4R#0>L2-@L6mszM?JB36UVz~|f zw>5nPOaN4oo&8h=YwZCwLN!9EnMVzjIdcR(2C8O`TR*jCjvch7p_bFN9;YdwQJ`w( zc+02e9#9!z?67)sIye)js_&|kM(t1$b}N+OpPG9>+Gpe&uv0edGwmXg<45*5KtHYC z*e+}AXuF(uQH_AfSJa!Qg$}lhGewRGYKF59qMZdUHLLru9Z7QW*ij!;E)p?chVE{c zv%0bBVd%)uX)X|PvW^S!gYX|Mp1MgcM@ni$y>%rAk;|h;#{hdq)|(P$MZ38-q#cA5Gb|OrGj)nfxCiFx{gn0I4R)V9>afwFjV#gYh;jIqD#+mA^m><4* zkQUWRX$04sU&VcJl99}FtAITXDkG%cJpAE4Db74kK@|gL13r2M+1PpV;+D{65jBxn zK=PM}uj%#X`J)3QP<3r}{-^@#5Y$t$h+ptfLzYm`0beoKnNe>9G3MCTP$f$oZ)(Q@ z<5KF)14j!z{e=TZbF{dxk+jR}jdXdUNOmPPp{PuB{8fwVVx)hx-dH7LIc#?GiQ;!j zj%&0ksPYZ82DyD9pwzs`_YTJ@E&3aD#3$;_3B?B<;h-iI`B3ps)|(Q>ZBaj)P{`v% z30*ks!=XA(wBa+Q>~eY*)mGhd@P8gBlCzA6(=_bddgGNrByFQ7XM8*SV0V<6=r$cJwH)*ASY1=H( zwbWC8#$9Ndolsh#)w0GzT{>12^dHnGD05FyQu2iE#=DuLuY(FHsyB}fojN7lccTrK z0VV)$lA732WSe<>rXpvkc(Q<#)vxT%@e$*fG1uR`;rg3l2gzL<9g^x2kXt_IXSY9O1P`3N>U|I6)mil4or?^!JQ)|Ef2bT=8}mW2wsjv&%_( z0aWOx^~M;P^x04J3%}hH z^tO8(!v>UyRTFcAMv-Qj{d^q8_aZ#6!{O_mfIhy5O_<|Q&Z`@Ya8>ANPqJj~+Lr!@ zuy8E^T{;ZLp=t*2$uL@=itlbP9+cHI6Y5e~M%_zUh5U$`m)>ApCCh5A z)X#6G@$uHzFp)3$O!$Je4aP5J-Q5DOs!6?|d0g^`;2YOB7?TED{#D}~cc0_ESv`FW zHn|O#>GFQ(kv07=IY9~mEBIBzf9c|LGay>GH~ddsJRi<$XT!zq8>x$zf|Zpwi0%1~ zslF{(*1g&w4&*yV1b1LbJ9#N7GTL6SqHh}1>8LUB7nl!pUkWY)2Pt3){}FemIi~pL zV=n)R&>C|__>^O5{7Jl$=9uAs40GJM2EMi!knWi4A86nX>l*RtD#t`Ica>wbe+pP@ zUxP7OUTr%(lIu#6yAf1v zggHTDK-Dzo7W7HE5!MOV2B;_D9>yrwf}Kl^+jh9PFp=r z9hTED#^%eNrqf)SQAu4I-+n22Ol6<51EwA%m3=J(D)X0CP|G}&no_hu^+KsB#V8M@rW7$;)>pr=!JHy| zbW)Df%e8XA&L^y(j-b3LZA+c<^7A8>CmYO(L*Yqt;t&rQonyJ*=y9(mQZA?>B+4ME zH_bh1P8hfQIVoe-!LCZYoE)UuPYSSCc5096Es^9H^QSEc@7+5&$M_LR84i z%*W})DS_4w6|>n2Ws)ZpEr7lbs?>kugt_ffzD_E%Fcn)G%p+jmov>6*bp)&es^_T& zQ`J9r!aM?Y3^4iW26N8vA?}ruslG4>8cIvvN^_N|Izl9<`I-~3v!KD$=y&4&qDCKB zPjNLt2O!jE${}ejw8{w|6I?5t<<7~&|HsD!Y9mG*{VHdWJ(Cklz0Wq78vMo+;$*pV zg1sEjkKtgJ)Zo{gkQ)4}>f705JHlyKgRxK!vVx*fTJ`=7DCkX4xxZ;J9+yck{8(;} z%&~Jbv)8`Y8jP69fDVd2jI&uAvK+4tZifkKX)x>q%`Q>%-DNu?YW;oD6?C&dH<;>u zji;@KIfAt z8tdQ_K4~z&f)0jPO5YQ^opy0d^3Cum9Sz2MdE{@%MgQM&96cfjK5!H%{-MEWkS)QR ze5#8eb0w7l)!y4+9G5!GM;@>0IC+>nPCnk=go{?&9)p<$TGUBlj-b6m@*MLA4CoOJ zkDxcN)*aUjI3qv%7%4)>ZW^Snb<5}T`;zf{jB!$3o=F=|`dePufG5SQ!knOyq)KWu z$8nQ3d5_U4-OT=21Unp|14nv`hS9mhMUuneXAcWT*3I5y%$7-h@M8`#GYEHQbAd<- zOnvko<95j)j&?d&eJ%n_oohM2MLD0WH3RxxyT`l$bS^2$e9AFKjB~P2X$wIpUQYu2 z2qYLv)uH+hVbQyJL8F?{S7ky|aF-Up$M}O(Diev!$-d}Mhk5JrsMF!ERX}Imy2o5` zSRwQJe$J$U13$P_B#WX5r59&j344q;WRk~!?3uUTGOXSF>hsX?%D}jY1@A(;P$wZO;nr{KIJYeWy61#vY`R3%l89N%z^IHp#=Vr5Z9k$@mExUS99PdOnqc!Pk{i|$e2By7>;LhB~-pMcz8D0x_Vq@RLGy!w{;iUKsAz%!u)b*agCP7Q(=R#cgKE{r`Xr3{v1@8R4CGs#d07|uZMHk@_ zM{nuymCV3?9tO;0@UMYUCHW!;&Y)hC4eca>3B5& zJJ388z167b%ub-yK)KF5+bDD~^i`L_gQ5`*fQOL>$fbq#Eg{<5P|2$GI$il{HHEp2y%=SWI zuPXpp@T?NZ03{Ij@u&;WofKO0{AWraTx%wSY5~P(ig&3L*P2bB643^@){Igqt~Db9 zP{3#kd^+0mvtHBDMPOoII4M+Rzt4oKYz1gYRU*0Z-`<&s#-_+AcuwJ}^2C3Is1e~9sQxPIt)iJg+g0>j0}oq~j_sg~n@$SpXa>b+lMNFQBTz0KS2hUg z7!S~bM=LpkL9snBva^*O@K`6&YfX$L}tNX!{56C(n^Dtc?#;!CajH?%v=puo( z0p-H+^aU|*oDDEE{iM)<D~A zMU%A}`%5QjG5U$@2sYK)*|j!5bYrN z>@5}KQga1A8P*bn&ZqXIJcg?LZMAj}--8)IZ7^i=2PcL4yQot9Yf{=ZQI`ll@k6CY zKYWp$*n=`?02qG`o{(4VTGdm}RA}Z@^!Vpb3UN47<^N6`w!<>CUTIt|O5-w4Lp}iV zsnB!0+AHF)8cgS>N{u}s+KN#Z5Q5?N!bu?xSHnI@n!wtpNLpPym>iXv#~G|&23QM_ z%f>OE3E9{I(9)nZsM~7WxzU6oikObN@!3iFBPzYKf*Q^4`O>^e-vKJ(BAOKqf=fWD zvT*`7XM{5R{G_~`sF~!8Hk&Pe75Xd>l0g&DGr)z+rN*dOF@NW4EmVF8Bu%+ z4SUgg?9D?G+Cba;mB^PV3WkE@(`b{Mu@pWAye>Qx1G9UHypsgtmCsmu9v%lW0OsgQ zWGg9#+p4FL=aH_D*-t*$Owsu@yj^dHS*urxe2dJE!i;Bc@oPv`#9atsScx1(^Sf8n z5cU(53o3h3iTn^oORbDL!}ajCugto=`zgVoV;7XjXQ|ANVN~FOF4&I*=WN2Zl~3*OSac8QX6jmO{zFv<%r5$Dn6+*%k?mBFyC|4XFh9msR$B``5h9hPZ-G(F&JuYA)Pr^{jQ9<| zCm)NNL&K1a{Uve&73P&oEH&RecS_AY0N*U>Bbyq56&>rr79kI9z%olqNU307Kg^{$ zh0Z~9X3Gwm=ketMGc$1AZZG*;os2{2kfZ^YK56L$)-}@7 z?K1?KcMnHPpJkDoS=z7+&vL^dvu{7=hMfwDBSL87CAu7O)foRp5>ekEazpm^N4wYTNB*Y ze2ct_Ijo1qeROl0o;A^oCEuGRU_Mkzp+%lg8`z9vH``1@u z>k7@|8O{wHg2k69h`@Cr`uj$U{4*Q07aQD7dBZ%+VKME7GuUZ?hvbkXcErif#2$r< z5U-S1B9z+_j4^yFF9T?SiVj!NYM`|$dhJtTtLp;V29zr|+owXgg(7X?uPMRqP)zm7 zK;u<(rHU2<%>;Ue%75agVs%6tKws2VF6X>I6>=^H`LY1ya_-zG%7Dn%7}!C@9w-rV z&io0t*ZD6(TgXSPquzGjCqkZO0dzlDBIJ{c8VHwX9iVbRad~#OUQ92<-iL?lLrNe! z1gNh8s6AXFk3KW-TI+fz`EC-ZVL5YxO=g%v?_@ehNP<5dAa?IT- zWquLEJZ(p-i(dj^^LmL8X)DhQk(Lk8cvOjMVYjD28^x+_ooghT1o(eb$-10pQ5J}S zP=J~@N`!p7`LU31`2Y(6zE74%KUU-~6CLf^pAl`5J9G(|;qKp{)Gd~?!)X$He3 z+3AlIIpw+(5u!KY>g~WsLcK)-3@cJh(^XRw&{&{czODL5$hT^M32!O4b|Fmpz{uAE zXrpk0k1L@}{D}E^n@4*9O9sZ(n+u|&)z*C4F9l5Xi4rmGce}3lv|lYK{mBv`NnM_3w1)y6#&$hmIyg_81{YTTr=nZ&|J=~2mSvb z=W3QAGqw^T=T_GVIoAO&10a`kQFYyNZZk79VtI-60kplk5+U8zqY&}py?@4X4DY{` zK%242`!JmOZO7Z(Hh@jPDwWPpZ3?K5I3932YF_iRn9fR!LCw5XBF~{w| zhuz}S)R7hFUkghG=<^2# zDAFJc>wCSoHa>*M3ynq8#EBviMqM?~OrWc&)?Pj@X7aiK+5vJ&cIv#4WYH^7za1>1 z`7pJm@R_`PP)0|Kkie@!^~~gHjPM1DOR_PbdL&s4s0^(|PNss8AA9Z;R1lJ2zd&A+rOR4R_B$Vbq% zO|pp38)vJOeG3Ff{`OQ?eLi?=kVW21q57%3>nVu34p1G_FgTH<>33dC=9nHy^Vu5) z5wd6{I)a%N*^fM4MHhigL$Jc5J`Yq8#%`QdiAN@ts#}k>ay-s<@W#6>@~c!{Cse*E zYV%O`(kH1d`iNKzP50tXlK*mrcPM0!zhm~(=YnisV3D!12@4q%a__r{whc_yVvF2C z&r)V$Fh|H@hzfrcO78)SyqM(RR#5Z-pTlkT@)<1T{QWTE%z;J2!xkBfT`*&;1j|A; zLv!&ow}MZ4)FMAge3Z&ZzlXrX9z(ow2%M;CDiwJIAG&d`+29NRVUZ8fcN5;=xp@BJ zJ*e0gP&pecas{P}GQ(BjJ*Pp1u0pY!EwUFC(hg9(_AQu?3E}+Hh&2m*;ENXd4COXe z_4?gu2+FB!-4L|zfi)16%`j_x#UiIuCEfrtF6?L3Acl}Q3{`nZ7f~105Oxxk2`cGz zi~Km<`gx*O3}#=~n5XEPKs7 z4R0f(nWf!ImlU?T>wpJ z7d((t0@TO<3(pN`3~iLu^jaklu2<_o*+Fr=I!~pzUJZE)73jYfp;!B<6xXYHpt?YD zy?XYnnyPNSs!fCzLJQ)%QtdiRdUdi6SS>IfQ);)LK^tdyfH8cIftE5p`>fEU`ln$I z@O?`2jWdcSO#vG8g@PVd(Pp5zD!Kxws!8#L^9(A*C5sTH13>X9#T1|hpj?z*eP4)D zJ3#j)#q`|!ifJ3rK%iWd9(`Yk(xA2IA-+^9&@PyEi&A|uDF3gNP}ZxV6ab9?%4KO3 zBBaHrtpKwxTZF=#_r91^^i9H}&({{AMOVO*<_{wQngG5_g)!}YF{x-GOLR$Gg^qzG zO)9nk47p+vD)eSeFBLj49-a;B(DQ)4#O`m!yD5HTg}ru@vbP9rq~_HLvrL%Pe`^uj zo|PaJRjm530iT)Q)@S@1{lNDYag@PI@P-X(L%s3&M>srNKsJEvN4Ik!NM1Pjykszc z1KJ5Xy3Ha)?Zg@(Y6I3o5CVLB5d3)82vNKLqIrw14UGHm7TNVqFtbtqOU)5%L!(2@~n*p#OxKy;RC2C9JGmH(Oyrz|k8AfkGDKm^2&!c99 zlnOb!4=tfb&NhMyno%l0NppBxLGe=pBff%`2z&uG@19bzm@yR;7qFO2o1&LMEs8`o zXqdhXij)&;BVP@mYL=DC%{ZS{dlM8X6y2acYXmp5xtGn}KJB>Uq?7|vQ7v97mG@$N z)7o%1x!&1w8Ir>UD)04DAyj2__ejGK_61S(pj_W5mGzYJYoNMkA01Azucu%QA`%yb z;QdWVQE{owLv`n7rFFfR#{L!-)MysGcE4**s)icS8;JnW%GaQlw#;c1|c1s3opED2c1 zpRf#I;k__QOD?d;(Ug{9Gp%sv_2Mb)I$(vsGI(n6bt?WZ*hEjIO7-*mklpx(`Z9n) z_EK>SkM2Vz|A6l0{wrqe^NZ|-RiWspLPOTJ%n-Jb}vp@(27e!r>Bo zyf)QO*8tAHx>UZGxU(NHzZUa+Jr#xFy;3=lz!DLbKei!6l{10rI$bIU6LmnPFf(e< z)q`q%zf>ICw^fPe9FNA$KMjvgXG@{$?k+)!4~#GkVmSy;mjWW_110_yB7R*Rz`%1# zg6H)~u->p4Rq>-zam2$!H38Y*A*^Ij$sa3Wc`9Mm0!*q`!otiSy$~0fAI@u*Z#r(x zrx1x$ZdcK%P>C9kVw%yTb)$y;v*C$O16rze*XMMz_VEzIYkAW?-J< zVyRq7WpqNNQd*J3SWu?VmDKF(ofkk52XYH!o(l3ekO1mMBHym@>Z%rQS$T>D4#Bcn%9SF z0G0f^LQU*L1#HLo{|AO0%9N){k8MgKYJ~E2~=;K@_=Ms1co_Jk&_Xa+q$pd9_K~zLtJR@2F zDv&pCAI)GBT+4*gJ*%+u+flvzvmmzI%H)4gI?B}~=CmV;DWKv8D5*cHQf=4GzMdqH z>OhALEE7tUA%)m8PZ#nM-nawgI%~-pX8sA^^5g+*)0N5JQQC%|VY9uYVa(xY^Kduc zY&1$AWQZMn0@LqHe#QdjcwpJU&a-znZ}w!;&t~7d^MC~mE$coXkGkVxVE(`s!%fd~ z5gyU$j*j#AQ!PCt)`QRSEt6YW+OHeO@5P~q?!Krbp?;!JS-#GM!_p36=lT7O*dg8s z&K^)Ek7tkHG>>o?#LPF%gPn7L76-CdZ}`G-LANOUx7B7Ebs`p z4O7THa$o=wk%(*Slro%79Q=ah$uc|4dgokVVL`&a8t#T5c80=iB|Mm2hu?ATyD{Go zTqaXNi_C>1bC2@OQRV}|2TY}LZpZ){vLkVwJGG1*h%-;%XTC@PTBflrztHk-xKl#P zu(J8(#VFC*Uyz7KVD8h|SHGBtId{SxtGIn~Fn~^HBYy=R3b*eJVPk|lafZNC;BHdz zT(|>g3fu~J-b`WN2zQ&}?u0umRNy{)&^svZP`H~Ew-N5ZyM#>&+@`xkn7MGbDQ+v= zv9px$;VzmbY&zlgoh{rxdlA0k4u!jGwy-zC9T}#C4|ks8&V{=zOi4f7v3D!s!(F7f zJK^q9+&&E9+#_s4;m%XsM!4JVVUPU}<(5J=bA&q=?xH!u-U@fu91&L|+=<~z`r&R0 z7dAe*D2KU9_;BMDBEJ_#xZ4yw1@5qW*{0tRS1#OH_p<%JneQ~$!rgRl8Gk)Q^@c=A z6R_NQWqk846>A3;6VcaB`yUK<^ZVNQ0ILH=ubWiAfxt2s^u@w~MJy~c_c4qCs999T zMULt@0a)05eIqde>jc(2l5Aj>#eMAxfF&*IYi9)(7^z}rk>fgmt=*u&nt>JEuNn%h z16bVBz6o@F6?ZhMZ^XX9jL~R4q7Zng1_9G9E91vGs+o&01C$;pleeOqQ$ZuJuH}8P zL|`co_QfP%9S`*lFb7zYu}tph6<#5**hl(ec3_KQ(S`SpxE^55qkRKx0T#ZhFV+dn z9@jT9?)%V9KGB2GQauAO%jzTn{k%ynhmvvY(= zz;b}8Gki45lmjdu*nMo-Hj^)V{SULZdp$7Azsuxg6e0_M7PG6(f0%uo-CskkThIRZ z!#u-z5!^-3vstovs+$CN{Khi*Bzt=e4jg=0HhVkUfwgX8$7S|) zMFT5fJ*7r>F&3U}nIWpWXlx8_X1XH!4B8uo& zCf|fxyQGv&iI)7>7mm^}T^*P%FwY^0+G&`69gsGboqG~HhJ3X(6(1bN!-oOs3UI>E zPQGOerc@5&RJ_1UZ0|{DS+VL5C0AW0Wd}xlYl{;WcB-E=N^b=i1R*qogd~H{9@({W zG+yR3`{I}scO*Db2N9!%(LNY)1)HFi4Ei`wDV}BGG}cu_(X9M&tu$G00Tlz?!o}?v zP<&!Q?<5&?t_RW8j46}LD7{y)GQ>$5;TeL9P!PpvrLmOR3nlEZD?VzV943KiM6=}6 ze8)<}G+!e?>G3i#7qY5EO!Mi&521L`TDj!%#3GS3&|!rD2KMR=)|m$_2fSXX>ZRWM4|%D)4cBrlI5h-FL_Exmc;;kNA>cocWAW%=6x6#}JGz z7hpR&6pRj-w|JXfa6>hy1sI-GCU$!ogJAhCCFA-_BlSKqp0?Cbp{Z+$ixv^kccbSa>V!(Y z_K0abe-bymfu|Vse>j*Z2T)M_;233sfxvf%)XI(H_$1yJFzh?=R!0_GuiCdFb&X zj5^yy;I+W7!mV)(_s|ugHLkd6ONx5e|HeV6-*w=_-Y%2(&Q9bP}q8m9%O@qO(!A0y2@m#YV@OOm<=qx7e=?V09dhe zd9SykVzZ!`?PNTl{BKtSx|d+0Y;bw^%S|rDfJOM0_pAmS9u1^nMR|7;D7tuHntzsy z?64cyJatnHkKOebvu-C^o*$!*fFL5+f5p&@$@lCeVTe{b|8E(sQ_L;Xd zLr=}jP==;bUM_zNHw$-{d`3Bb@RzPl69+aHmR4B)`Pen~vX3-emw`NXT!IpYrPli> zv-wD4byk4Us35Npc=HImL0}s|JHXupUO2+mjsR#tvI4$Tz!C+F0hj}jd*7{i&jhGJ z&EnvC1+)W9RKev6*bcA}AdhI85~e?L5rIB{d-qfP@aF3xoml{Vn-$9o3Rn#= zQw2*DumfNRKpxTV*F}y3%5j6gR@13i#saKU!Q~tr#pO*lh|q7!(bI77MuA9ckQFUa13ZNQ3q;m^>F-GTs zifmB=+Mxtg574TDPbr|b0;Afuiscdo3gfyi_sz%JEtfUxw)2bl1k;{6)C z?jw!!XaLrrV&@1O&C9{H5>4%@QVz!s^K$SA2UrM@XL&0RK$i^6t5u19&0!wBMTFZOJ_m}29ax1*`lEvGBM-D@ApvMOP`=Rhse@vnYdye5 zfS*v}{9wx8zG9*443BqE{jV!?o)_}P5`lbx2{+2bi*=T-nDZsAuBR|GgW^jBRtS4t z62QWCC6M7tAdLXyekv171TG&?0^v&p!mCk$p!kdX11g2^XXwBr{ahwi2&_^WzCu9% zF1m@Eil;Fu#lK@u0aXl&FAlh}Uo2*|08H*s?9cD-oeAIf&=h}Bo*Q=U7takD0F!@J zo*U-Do_}%F2DC{u5TF)dey3vk zlxo@uv>GU12(a)qu@E4v25I|WnerVOOnVjrNT960DWQ0ZP?Y2Eia|Ak;>X|J$m_in zRr5Z|?RW9QO?xX(EJY0hm9PO&7wM-4;&d~^Sq$ec<)V(Q zR6X)T?czbTfZ~VRMdgXN`X5e|w(A0GaifNmi^VfbUK3v))gT$kAR@fW#gTPWP|AIe ztPA@9{^pd6cmCJZy3Ri_F96jMUM|)wl&ch0s;23UA7Uh$UoPI{?^db5dy`)cOVw}>>FhweJi8GUQu658C0Qu@v z+X3-9KNeu!edVG(>{go!U!7V6Dr|{TOw_=7R;Rju1laiUf*t@oTKU*^ukGY8e^J2>7A6qFehpEmHFhG_$@ z^O(bnE0?p$*UJ60?a$r1IDn0-%f-tRW_`K* zJpR+J*w1t!(%pLhdW?Edb(s|1Dny6Jt0C-%A*O-M9D?l;QozTV%H?a2YuY*JaA;j( zI&*jgN0k2GQ~iI>aeCKBXuG31&6~R%`@P8CHDR8{HeLFsqhks?xNzf8GY#xMpQ7UID;FC&&)mH zVBsZ5uXxcnViZv?!f;_#FA{Lkg^M{KN#mU14j-g=sm4WydeMf9TJ^%`bLhQ0wzC`S z(#AQ%9St|nOA;<})Qdb^G^!W1xbXGd&JKhk7P#Z!KA*OEtc&3bOmyPn^16+|Zbn=* z-C>mvv5#Cfjbv-Wq@km8ffadK<;f^SxA$G=$jic1T)?7P{aFlnk=8{F0fR(XLq zg85pFG=vSkTk>;`zJ$`ckKJ>(v_Kz+s5AGJ3pF%_3B{tLloqL|yjSR+Mu7DIGpKJl zzZXLnoy45|4mLEShm=4>=gyRnjw*f<00sf%T4@9P@UDt$B|E6dT*W>L6xT|fK$BF| zPentJ;vAs#Rz-VbkN8q3383*mA!s2q-R>|wnHp!T(L zA)=njQR3wrlKOsy?iy4BC4Qnx@m&-XLHX`iVwQI+zW5T%22cf{_!r%$REi6%u*-OB zazOEwu2NiJ6@Y31#Rb;9-9lhB11!u}?5C*qemLUTqZy@*j)u?6-grgmt73pYhn3;( z@GFYG^7$IFT}4*_?OA9kfocV{kTN&`6xUb9KwZ%}aea05Wv;Ju9RMRp>wpMO`(4GJ)diDhd?e^oH;6 z6LF!0TCp{}OEzDHfPdHbJd#h_c)GSg2o{x#HNv;D z#WtQ109ycZb#*qom%8F-+e)Ba-zt|ei--xBY*Iq}v;{8~I(*|D%;i1|V}@^0Q2k`c^2N9~2F>OZD?t z4dv_sL+P3#p;qPel2FZHGJjO+)I1S{QMc%O+~n(ugu1o6mxRgyQ=l>zIWtLD4X_a) zmr&)qg@p3@0jlDLQsZ~2m77bbcu*lfDH3WWs2&Mb11bX)mr%n&2?=G#P9r|o&>7z> zmpy2P&V>S`4}6}8k|ygDLHTweev?ILD@n`jT2REt? zRNX-I`4qGF?w)O3vK?tZ(tdGl&@#zWr}+_slHMw}QQD{NX495Q!MbpODZ{K{soj9x zZ2dCCl?*W1*D4Qohov8~LUB3#hs_;}FPIQXH1bgko1_Vd)D<>2VZ&Eg-tmAm+3b5A z(qXDquB7z$%Tbg~Jt>=q6**Jy;P`X5GYG*&!%yZ6#gFd)%})mWc!gT!kEvLP!w=uK z#D|@FKpLU5f-DBPk`gf>hh2L>3Vu4|CrF8Vt+GFS&<_KnW)U#=h`v}XuuP1Oz3q~K zX&3gzGJqL?VGT0>k;cjemiM5wx5N@(Lbrg>KW6>gxlhf{xaI%q(Lki0ynyAev(nt> z1eXY0=sy>VDL5M!8BIObl#K#l*}&8(&TwE>U`1E0vKxE*r;P^oK@6tQyMU#9ZIe2mj5O7G5!{K_*o+6I$!;dNQ`)TZ1?KQLUNUccQ1W)R0`tAjjyx#c z>D&r;+jaIC+4^>%m%3?{H?n0Jcq%1u;mJc09T|KI~JH16B0aE(HxmgW)Sn85rJv2Nb7{>JcQhu#sjeF|FSvM2r^cEdaQZ9zy1+9{tu} z!=fjQct2$XUwoHU2$tC@&jm{nsJdB7c%Gnm%3Fc9tLT-T3hMt0>g{Z+5G*A-g_R1X=ay^8g@uxp0h z8&pi^Uc%!Tju7+feFO*Tvj$kj++!8;r{pEE&%6E>It+lkgm=R$%?$isK6-&^7sGJ;g*2; zFL~Oadg7bK7lTSbr@>X>5|z3E)ug+PTMmXxvjHlz_8)k1lt2~Ew~Dtd=eH|~xcCo@ zy!D_Kp~K+CsaatU+a9RE zOBB-+KxqqkT?fz*6*XrnN0`YdF@QXPD46nzxm=+2Ku=S!{Y0>17*0|X->RcybJB!H zT7`Nn->Rs`AfQH|Ts`jIDrV=B0p{GVm~K!_3xHaHa{ah`tI&_F0IQcOZD%%2g`%Qk z#R46iG(}NLD1KYT;bgG@>s0Xe79|x~K-+-wZ+9+k>Ger@J($pCR-rV{!JaG4Qx`Uk z&>I|`G?~jWj8ld$5Y@e(d^{-s7^@JW$F>L&ngcKcAfMvf3i}=r+6YSX5ZWA#aH(6w zVu7~#<^_j6DnnWO1oIv2rvz!-U4OGtW#6-e7I*|{!V#K}lanUy zQKhA(s-fXX;-R`&P+=G=4|*V?wJ(Yp!uOt(_Ul5OoizGHr9Ydtg=Prv(n$bQ06s`X z;k|`UA`bRyLKHP1a-K#_!K8v#R-?E)7HOUElyq2M;Nqld$+3!g!>ytPjA8OqXszz9 zPMWSgC|=BbXqQuyDaQ8Xs}iNx^bMeL-m;3Ltj42_^ZPsHY4{3rgRkRO`7RnlZlYmw zs-SwKkE5~~zn2N=Du7u-u`-feQQI#+g4iFQx7NIE6K|E2l#1X{P^@X7H2+q4&pP2~xfg0;YN-H0J)u zco;B6iFqZ%O8ze(QlLiI6n)oMASVK=g#uNDGznx0SjNx21@hQcb--Mq9#tWYHm7MO zyI=oaFL!}Uaj58?Go<+SgJBPB7~J%u6(0V;B-aXY=4}Kns&N59J(e|J+oT#Kw&!iM- zid!7=__@KTXnUlJHjtymM29d?%axFNp5XQ=};8 zaBtK$Gn@RpG|O3n+h1bOkh>c08i^ezH@Z_zMjE?L?r^wUHnYhuVEQl-?u2x5j~*C- z@N;q9mR=#du(`)JjbkTXkcPT71G8jQ$Tw(Hq4CUp$3`3<3a+TYu#t35j1Q3i5!YNaaSL*($nLL3Lh;*`lIzBq83T0X71RqLzgb zL8;^$@v64~RKq2!_+tEux#x>*%=NB}5Q?IxjWa{yYP4X41c(*cBrOYp4*hmCrYEc4|O}!GVZvdtJR-sPyrgULY3R&nA zoam;QO+t(o01QE=z&o*N!d`hB>ux|#1&YrWUQJb$Rv1t_P_Fk*rwY9%0qks5OpmIj z#Xx=VcsrjWOivYhuLGd}4~pJ<3Z~t9Pair8gWEMFlqfZn1fW4cxwM;z2-V$!`WJyQ zwP9|?kzL;|1+bsfq>;KVfJN68Cx|-2uv|~_vLHp4tbqq9rV4Cs@b}dNWD*gRgKdtbizhxC4 zV()_=`Ve~vKE$R*=;M$0I+ek7>kHyb>qP*o0rKsG*1sSW*V5f)W&0ravB$D1`PN@{C8~^5dy1p8G zV@QP%H8<3j@OPpnU_8X>^a?qXO7xU!Y2JZIvO&4dtPoXdhoFqQPM}FZS5qm@*(8)p zNC1XVfI^>aVo~d)1v+5ap%tRt-csv5KOM3Wlx9|i+(eO{2F2^n_?OVF1WrH~F}p(U za0hi_BU|+nW@}Rb8t$u*aWn;9gutF_3cjOr{Y%gkb>t}$(IZ$bHB8#XgryW=SDEN2 zla__=L_cD<6EYyCLf%0dEp;PXz7u(j2bla&g}j%1uAxNI(K7a(2-FJ75L+Q*4h(NP z)kyhP;QC#dfOMaT&h?23c^qY8{YExtmo#B!1i<*!6*Au3P$XQ!@DI!SZP6;SB^t6> zlL)(}#0oi|N^$i@c3_t@A|e-HK1Q$6l+ESvEau)8dgLo}j*jXAwz+_W_Nk+QUZ{{4 z4iXV6@~j3M9xj}U8PTw8EM+$YqB`uTkZ+OlKev&8H(8#NE%`eHTc+SKW`ECUO(SM4 zu)1Ct35+ISxo=kdbp|dV$Vn450=A)Qn>-C}5>IRBaVSWLCZAvo4udM?7UJ-u0uKb%>RBo8 zCjf8nBCzH=-EpHTB^?}%+{tZ?u#NTVwteRR(>Mdh^*zS_S6fPSF>GtREB~^E4CS~E zT)^&1`3Krba6CI&g>9PRe%YvX^O=gfky9xTWb2&p68+Ivu$TN3`_l$H&-ci7=vPO( zwGU>es!dMLCRnEAvS)In#m;qbcfQKr$&qHvbDf3)-d8D)qrBrs_t+QMB4C~z5m*qg z7+_-*%z*zRfFKDQTwRL7|3Zt_42w~{DdK)GyKoua&V1gOug5VFA=rlJSO z0gL)@q}d9JKZT#$Ao~8l-&WcLQ}_R%hEgNmw?XKL7JxMXxsF%`|2$}}BO*gk+h0}e z{SZdsy(01vV+P3#U;|kXT@|@C&1`@#s2iOy>k&a6VIdvD};hbe^w}n zT7c1ql_5djU7P1y-I8d%DQj>+QaK8&J zxIi&orJ9BT%>&AH#q4CEDxsh*sG zJuUr;pN;4Bw<^RGka@jW`eg-J43N+EEI}ZBmwt7CZUBw$6z^9+^X&aUoJewFhuPU9 z45==u5Kr*q*NX#58Ue-u45kslb3KWS*L7KU-S8%yhP0IJ6v-0~kQ#7O~YE_`3M#sB#u zJn*lDK0XSSTSL?z6|$bF)Mwf4qnNmE2Ux4Al%J=vSPy$lk@&O7LWqut2oz|)O7S*j z^|LIs5F#cMV7ODIjH3>rR47680`*ne9{C90A(8!Fh?Q4OFl%zHl+9H2qMl`*Z%A|X zf%DM;Vr>16RE{^rMW4&yKojga1F8@sTR2fVP+WEKL)osa-h^{B5Dtx84Y11@Rw?6D zLR6_|*?>3c1^WVwFC#1E6napgsK@yYVL8p|%061Nb=Q;27=~PZ5`7b>$c@`PX;h3o+71R?7WI zg587>&(TZAkfR(>1<{qVk#fX4YAUFIE#K_nFo6AZO!CvW!lL-0N_jOEy(~QQquT`EFNZU$&vjhs@M+<1<8zlH33-G_pXuatkM>|aLW_*C2v z??VtIy@kq=06zR*l{mi?yvqi5;4PeUl>I5Dz${=|o~e}Y=+7Co;OJNy{c*{cZ;cZX ziHDVRHtV=F(j@`z$V1!Ms^ijFmmFN==F7-=Q}dOh$SopE%R@dzPIjMu`;ztOdA}D$LbncCA>t zOHZlOXH<$0C(mKTs1Cz-aZBHp?$k#jfhJH@WY37An2)`U_GAIo0E(YavqVt*5Y(Y; z;z{r!OK~4wtdzG8PU6WS6MX%)N-^2c5B~Y*1&t>qFMTyA>-I{yiR#yF zc;!voPy%0GQ5a2LsuZg=uM$Nqk*|SFCO!eY;gw36k=OGya=Z+lf9RBT7S*`jgoA1q zm#6g~-L(u-l2ytP$eLJExQSL2^4HQn4?x>8=3$YhPL76KyT6hzlu(u)?pKz!@qQ}_ ze8}NSzRizjG5cepX%yRKkw&>ygYbX7QZ8g)TIh&Pi{$0s0nG505^01-kQq@zk89kQ zqb37i0yoRwfF+!MrP3g`aKOocqs6A(t4gI2_hbP}IZ-Lkg{jc!LFtY!2whIIdqv<2 zRUT285Cu(0)&Orl!75Af`EMceE}@hXfNM{$W0z0kRDqx&*j={;j0MaoVs@`nZuqX( zQiJ)?Pr68Cz#RhIPW-}k^mXf8rhgS*p5}u|0rM;|p6l49GQ1vX1y~F)iomN$?0g{h zD4ku7SuTGhCuDD>Jf14<*(A2A95pc!pfB#pE^6hwl9U^dUJEODL4`tg|C{VLfWq)F zhWS~s;6fjRdB(g-`3Bwdc}Z-E6bD&<2|U2m%M3fKC%hg6-O+JQAjgH&Uu8WYMt~V;>cqC41ej2K@zbSyV7i@O&02B14 za)YCu7IG~NX~+Y}&4+iJ--X+r0;(PquS(YvMOCWbgRR^xU@T=A8HXbdTcXkt^CK0W zW@!WQmFc7^$z^fGO7zEWw(jM=RE9=ib^UEJ)(i0;Jr*Vc%N$_)OM6`eE)!gqo^7g< z77S~EJLnFZjM0n#{PAxqFuND~s!AF?K_82L%ge^kJEQiqiZ|wH{%-(mgTQMC+vGU0 zGfvn$lK!DsV4=V;r0^ewnFK5ZSU4g4S=o^8__*?@Y@N*d{$40kie0C6FozDwa{=Wm8<5v9Tla1XgcSfY0PFp{Yy}nw zOud(3u&V^fk%BYON&-`ET+#Lp%lAuD>xw1U50a=Z_H zC;8FDp{;_SO>X=M^8%JXrYEnI#x|6*`|fSxytOL{4j9zv0QO1m;X?;1>ezlZ`EA%~ zPbJ_vYscnrR=f_Kt^}$T6mMP|K%q4T>g+&$wWxH|O)X3iLpz=Qz^BP_!x}cchTcjdOD2HR zsQX>LM)`mup$4ic2~+}F-v?wrZ4F~J_`p9OpsTx0K1nGY1AG3;87sP{>s?8GdfMb6 zL|y%-^WIlChq1UvHah7*WK&h8&idFt6`6lL6kEwPr(p?IA@Qhbx#ZCdo2gSw6@cA3P*<)uUXMN-o=qY_|at)<%3i!C^(*ju5Fl+#x1u6_T{Y~$IZuCH z8}5Q#+vJ%P0O}0Yi2`6j6MO0w^(R(f?t$#hv(h-X7Pzw~+2k>_Dai=->se{ml!$+# z{ZF>>feP^|J8YHm{v!zf1L9Hdk@OLi^sVu1RV`-oV*$pFRKWEd9OX&XCmX~f)Kp$+ zR>kv43tcC`c7Xi3+%uj(m+Rw@mR5sJybr#zTG5+RW^cZOzZLe3Kxh`&6pXToF}-}X z_zb-ZU=zS17-)CEqyF90aB-qRH1d)=8Wow+Iu8`zGUV8Y7^L$-S zrlKBTEx-)~_FK(d&PkI(-PfSN{B3;q0PWQ$l-Xh0nJZ7}UBmgH8Ui~n+_3ZH`N|Wj zR5^#pGC;L};^N}y6U=rF?VyIhaW?rYvS&}Q+vlX|bE$;g6VOB9u4NFBu11L7e1qDX ztDnUfV1Zdu{F(lR+7f7!jcl$zbVXX7Wu!lu~7W-;hlpHMU- z4P_nR;=oO&(7=fyNOUXury%*jsY3u&A%9@a+u6vq8^<|E!|lIAxJ_^;2)9ci+(kQ- z=Z^+lwC~{0AFTZ&N$c#Mh|-_Te)|Y(3EQ7UpFaT|H{GODamtOK^D!2`gdkyFC<$JB zSX@t+qt63l2lE_xS^`g8l6QOzW!4VL6&09&TQvm~Z(Dc}yh|60baVyTYI$Z*iPA(0P7(KgZwPU0hF};bk5Qo7f#_ zaV$7E-Dk)fE}-VRJEP&zT}%LLnrdU0Td`lrx_ZfTQ63PV>9+3I@-WmC1M`|`>wbk! zSPd`(FiZ{cAGP{MU|p|llQ+T5npb4_^e0ra7TRDYyIzmJAOLRD9QI6$G|XiY+<`fG z68uCO<&uPpWc4B!7k2fc8W-9<+t>-1Ik(~>b}9Sp6NIHtLaL(JAD=)2hQO_lw#nPr z+n?amnAx97ZqBj53YW3PpJLpX;I3K5p7~T7J+2V$q6cj9!<4pErPS_C?`K5G7y#9R z&sxdK;LqF!_o6jD;}zvxvkv1dFeo(sBg_j}J1~!4SOBoLKVf0OTK|Ma18V_BA5qiK zfWLTPNeQ-b@-;$G+xe|sOvgStHX)t-)@H&e>`4qS?Cq9xp9z--cHde`k`-*zh90Rw zLv|go)(x!k0_0B{+{Wi^-S7K}*Zdprn4V|X!Ot_mT>!qfEC~Tt42<{xFsFDH0juj} zX8;xpEagQToAYS;P(A}70jPPAJ=1`DQ2=+)R`yDRG;Tya+%a2w5<$I03$W&FoBS{K z?nltOKQ>4@H?Q@mZLipHggU>!QE3@&ZehSWfj!P156c)cA_13SyD>~42>MYUU;^gz znoa(cd-L3>77#^K2(Iomw)el%ooRA%8A()hc96-!TzkvNrd&ja9)MhF(2;QPDhG#p z1;HW)gg@H&woa+0%Jnp`=z!NxSlCLeHx%{e3UYlL75K;GLI+2kK6SvDhE*@)1L0K*sC{wksK-cG#T;EYyw}k$thH)qEOkIptyok&<~*f z@q^3NJMerHf{X`~laC-NvN=Y^nvmcefc}SVay#V$VVp%6Bebb#dj&QbhWtnE+6P$E z>o)l=_V^R2p3L`4Nk1$CSUWJ34gVSNuMt?=>ukZ77$Qt?`y8Qeg+HNXz>jkgFyj&S zDtPC5xGhK6J6}q}X0*dy16w}|3DZH3;E{&fxvL4y>`(Y2;0E9gN0`S~(t>%3a0eXi zNeA@{5-|TiVL8C^fuT(Kk3W6_%R6eri>A#pg5j4of%ucXnTp>W57SHmSgk-&_=)Uu(t{E^3wgEsjQxV7gWM6K<=F@Q;z@g6n~ zRKOub!CNS(xCT=wt3HSc>U!|HV(_&n>UHFG!-MR~Wog0!|4ryQkly4^r`(kt%SfFp4;qo$JabEKDpQeCfs6Uekan0j?<=LRJ7RSQAk~P z@?wQC$y$H#;TCqs6@2s)3wMfzErxqR4&3?hH?_CFr+Xr?fUgEmFAX(MDMBI&{24J* z(g0pl%HF$zq;|obTZa3}*8L#)c&!ozLk%uqGa6Dk9x&OusB~`@)FQdN#Q=+|v|-n^ zPd>&t{b&nrgaoXmlIuI?eBs{Nf{<(B?y}kB9qiqK>0az3vTX+zU&X#^!JNJ!9XIL} z^Y|7wI0EjZQ#Sdi82|787Pd*iI;z=Y-@-N@ZvWG4Cv076;MTuyW2+uZA3T5*OiM2y z*Z@GA!7b!_;qETic_gG>;Dy9<2b-|>@u}nmKSc-@!J2 zhgF)@a3_1R?bP7Kv7UMS}p>U7-W|Zvq24;eWq)A#!^an3fQ&~ zySxW(jbpk}3zs0};vprMd{1XvzK0g8g}Z2m9m|21txF&3O;L#F6)L+mLKm5N4Nuc4nK_K-b0oAh|nB06Xqw&-_3|4R=C> zUB1oMT}6fe-jSCjMbro;Y@r>K&y*i88|6nd80|JxgC%x(gOH*Fu1Rigfxto|d9o1B zp#DAK7{LTCWecuJ<0fRlZHTtZQ>YZw+lL+~0bx^|1>l3gFJUhaOdsJco&@Rv#yr5@ zxh9RgvjcAF0Xu*7-W`Br0Z$s;1h4I=>&xx(Aq~5}6#4aS!x$F8-1b0IW zd#p{Gd|v_F)eqYFn!}#J9_NA4J*%O>9a z`vY;gg~7e(5j)Oz9W^Q4$LkzDcnY5hV7eZ+)2XTt;a1%rpYAe{lAi=}-+$`w}~TU0Up-e+dtmJMAp$7M+&c zY0?JA07mU;g)J5=E z1kCjnJM$ZpCh{`86G&`M-*mh0!Xxt@JG=b5dHxh5F0xopToivIu+ab5W%OYDNAXL* z3ijFAfllnyn~#f}*U+Wl!nqn3Uiob4Ptq`RGu+AfO5X)v6j}!`30Uu$X4h;C`v?2R z>I=;E^}bjTFfFiu@^pysB7j97?Q3TQmUYxVq+q7I&>GRX&L0oR}W;}?WK%gavvG{v{sxSP`C zVXZI->$1~=RsN%%qYGGQ7klO=+IYY#NN5-KIEFh0Zs~V+=B6}qSPI;2zuRRrLjE)0 z-z;FQzq3EU&nSjF{tvrsBo=dbNOgCj>cAC(OC%0IN>np2t;`|6;K4Vo%Bos8+dK?Hwq5Xb)oM5Rywn{bD!?$V}A zT7iWDJ0(Pa_%D+Ft`=bRz+%Avxg<3?sC|xA-33LW*9({)*q7|!EXlhtE6|S0cOxvv6;V0!7g36 z$i>ClU!}2b0eg^Yrz-hJ5o5)#*pf9KSUa%al~8}BP=VQpzxI57AARdPLBcS{W% zP;q|*-ieJuyKoUYpsKrW&@Iq2Xl`I?<>t=}z=D7UvrpEc_)0q^@4Mp2&b{hy>x~Iq z3OKdSQ5f04l7ab&Fh>1P@*Y_S%s8;;R#85hfyDru@4(*q9ABI#{!jAi@0E*|<54At zC}2K7mqh?gx+-S-pA6xnFL24Y&iAYm z{R|pDZHi8K7H})@d2my>v|u6*&H~`=z_Z!gS55fZKQ3+NEV6ff6>0M7YfrcVcnt6f za8t)Z;Tzx#1)k`|#&u!3G8XQX!ByR_1*zF20ZRt86V%;`nTguP++jYrj(i)sI@(n*6G|JE}M&UDE`x*1w9KxN3TM@8fA&bND{o zj)_&>PoCs623TBh&)ufnB>>Bs)i-x!D*-P6uD4_y}U-Vp}?iv+fyr+BD)r%gLX^(vkq zN@NUdtJhV@6>uv>$yTgQ6T`R!vLK@;A-s|TllD|G`H8eqtL(Ta+@qvwm4`lFsZ0&P zn^hbm2jP_3HsC&ctGXvY3DdlWqVI+A{tsC6UPMxtHr+WI7s-3sJ+d^zB@6CCg>b3G zMV)$~-H&>zdI`sc7vo->O>i&b?x}o^*NSTs+jAss& zPeM3sa<>Y}ro9#9%(6T-DgpC&P2ju0KTly?3ZkzLCmE~&v+-atl+tYr`g5Lpr@I5D z$=j5BTOPz1{Of`aBeei+6etT~7YIxQ*tOj*KSkhEK}?5&*A)T`-(i;}0%r%YwE<}p zSiTwiue5;4Af_Lf;Yc`tiFi3RZMr_P;Q!EdHgHu|_X9`0@{$O*L`4L>Dk>%l8YU$w zDk&)?6-!E%l&mNzX;D!k@Lb)Z#S$0mP*Sp@Vnxap+gKr7;PTR4DwJ$>L6PNh{ z=~i4UF84&JSA4WZ{xv|`Z)?pV|6LL4GjZ7~@bOynW`Ug%YUTx6h=0KI4200>*es6& zH(kI-j0Ywjs@3V6ao!h!H8yq|7fz!AU7*eOFU3V6JqgYXV-7t+9lt=EIey&)W!-5H zczlv(vG(8~n(o4Waj+6ESXenvZT?T*G|zQ@jtMOFyt`n}RPH`?OpuBx;RQNJzpvFj zB`-~ikgGl(uyz8^yvUepM`)TOJn^GLLqbin2p7JLpCu^(ydx4RtUH8h9r&@rHek#^;bDguE{Gv9*cJMq>(Lfq<>MY}U6&&xmZCF;IuuK0N^ z9TPuQd7dh#J<}h*Nb~kTgRAm&t{5H;|NmkbcLa5K1Nx5_p4}#%4X+tad7TxM zJ=^Tw))E}$Z$75~#VA!cGPVb>igNO}T*WoS(=7etCOo7LT62Q}crubk! zy_ocu-IQT3iGPP!ag>Za^@d}*oLXji7P-9Cnm=PC*Z*?-guB(-f7uf_eTJDWDtV+Y z<(R(iA|6rl$UA#O$N8wcPq4zt7NB|ian+8;;9af=?^Hp4+MLmO*Y!nHaTSQZcB#a7 zu{&t-PL|%vM$Z8Grq#HyWFON*#Ld}@M-q6ldhrRa%UlFU+%6+Z&m7aP8y*NQ)| zz^})uGUET>A1S22Jf<(9A8)A-<9wi_In&e+IrhnF+jK3&8c#TFzdzBAYfG*Dh`J6PGcLQbJd4O!hPE}3rYf1x_B=(WgAiDD zgo&18^Dh9#K7GtoR+=m6S}Fgxg|L`w50vEFUd&h0?eYr+S8Fi>^T4^YSrs>EHA)*HS7O#ff+M z6zNfhKOMLWJ913F67AIT9sU@pbyFw%Vn-$`nGCQ+6xA;uEp&LC2& z4Y|juW5)7z%UR@+aXHh~%$b^x{~#h8qV19-Z-l8!X3}1U{e?IJ?-lq$nA!=nCIicW zS4q>j4^Q01)K4w;)i@k@Ce>agj!Jtdrjk0lZK>Utb+APl!So?SC(OKH1zMYtDL&x+x zXzoVErR06K=TDxkd0iQLjEv_~XDCJ+vJB5-x1UwYPy(z3c8##{M34WX z`AdWMz?Z69(Cl)u(~n* z2=O%#UJ0kpG=5T2!(kP$Pt@HOcJ8jWX%WkFVDXVZe!du<3ZLhCUedV&mcvD#Hf^KN z0Or_bb^3?q#JFXSX7vfJCqFH((`(J!PdCibX81=E=E1&W=Dn|Kp4v5sUO@&h_={ua zHke)WJXK6Z?o72cOq;9{HB{6J&A?ow1({B-#U^*hysdetlRDSY*C#(qOUYR2a(+ewwJ#ALSCx4MUKPak^L8lFMx(&Jf4Xto&Sf?RBtHq&re9!f zqT82R$I52))+ThvIrFtCK5@XkDq9-at~JBOzdmvnf_ z1L~#)+D#kVF&E%w^k9G_>lt_%+?~IL4Z><+8JOiK<;=I4ob#X>zffCX9U|E)!^sX( zZ8gmzgULR~l!au=MB!(9;|F(sF8y>>W`^2 zd(ekxnLX$z0u{Hq&g?<&dX;_Qz7bwMHJr(U>Pt;T)r0K zsY=(fi|vO+ny0l3Q3oQU2l)abqjO0Qa)p0v3*8e6M3y|YGeqsV7_V}GwZQ8IZooY| zUV|vVlFKWR^&y)etxQCS`dA+51*TF&&Wh%?k_U{0{y}b@i(gDa$F!nNB-fee#lC{v zNUnp6wdo6TM07))ev|mOFU074n-@lI@~iTe$^8T4Cw#8nTC7d8*5KBbQfF@eeGG4g zo9S`?Rv%QLNute3{C(T#L++^4oiq)W-8eLw(w~NFQ^s*XLbx{DnuZI@ofJ%I(I>e- z{hz}%zj3#jyJ6o8*St2=;4J^%I`jTHoSD-=sVETcLm^U)`XKlXMqBS2sUxJWh=lFG zpX%+){2396Skm^4R2Y)8(_on}G0RUXt!&uPWe=;}OSCKUoLFo)*ep^8G`TL0ff$+* ztUbs=57n8+S0EdoX$r-&A!LKduEgwdi=S28TidCLO6zn_iV1#2P8b+34@Aac4M&z- zQ8zr%Mf$Jzv#nQ0Z$X>ivQ(RKc@{3JaIq2B5)xWTZ}GJd-U~OzZ#Qw&#Q0CCuZS9d z70B9uuby72-I8a0ljy%Zw!lkm7Y5IPZ@}yxQ5PNyuZBzLMvwF*!~5LsZyFf^%!G#? zarrZf7r;}F)a4n+4|rVZXJvxe(3{H;6rbmT8hAT=lf2mFUXru}ZaF%(W;z42jbak; zL0AmzCVBoAKieMTedtZrx5y0$&W&jpycRyXDvadWSa>H~2B^&&*xyXk{8efs-OOlG zHuj!6-9wt&!$B?$ys%Q6`uMRuHftH;T6?|68Wbvi2hC`Awaet7%W%6|^;O%g?UX}@~g|)q|{urg*wKnnujppCRJ||y09zFyg{ZXY9 zhrUg2sias)08a&~9V@ie)=XdxU-UVN^@%`|c@C!tb*vylPDBH>b>`mZ=`&m(R5mzU z!wE*pu{v{0!W%qg^tKLcV)QTZNwl(J+GuT=S3WH2^*WVzD>sj{V~PKpY4Hg>Nd-;9-Dm1%5tnK6 zXQsg%Ft?s%REMy39;!c^f5k0MEY>$h9LSgJf(64?VV0k;K3F`=CV-#eVHYF#FiPT& zdN3llAqtn9W zARJ2GY=;GYHnw6Bb@vQB5?*0kC9Nzmnk_HtpQz8DyJ|i5V7Vt&65wrcquRfDgr~th zzWA~G9C$SR2HxY5HR8S)o)0(t;g)H)|CR8XFQ}f4mdbVgv;^LXp#MA`b;}FR{y0Db z@J#r*{`j7va|Ji5b6P_8i$mc(!|oT4O!3Ab2<&%#djcfwMJfSGf+xbCG^cp)T&=D0 ztbrBwt0Swm`|_f?$-Ky?<5qq~?Eq_sjaKF5GpEAOz~2&woclF?r+nkY@jv5Tl7I7& z7k@dT7YHkc6~Wv!lnqt^D}Rh*Pmc&T&v0cH{Tx^g%stgBNmm4`hRG+9 zpXe>+FbC`)HJ*nYoi^cN3Yw|6>?alXH@GU}dHy4n@iNs>&ywjr@v^wv6UsecEe^wV|b z_B-XLCau?&S*;Qgps{XyC+>~YFm7AV3``oL;vT*-@3PGHQ-{~nhL-`oKd95cGOF;& zuGUyzFKzy5ItJ7l$Ho0$CaVn_q|FBwQ^4+#CiW5B8@;CRy#(sj4cf(?10PV=eycvk z>=pVUHHM{L&5YA#%#6blYpFNa)8nIL=ww&|4z}VTBpXX54&1e;RO5xPUf4nPcCdYc zdMQo|@#=!bc-E`qaoRc`+efrwUiI8|%vjF?we|`vBqRgo?NhHG#RK_?NBOXF*jX7Y znW65wjsc{yE3{y*c5ytbUgf>4UFPL`hEhMfUe&+M0cc@Z5^QSLMj2DU?6Inz^978< zR3F=@h0Juo%46!sENtn7mBrMnwR-+CRTXEqWLW=2n}DlD5)vw8_mYq>c;9(=EZnlX zevD=ynK2n21J4zI_@(g|N!Yt|R}+82XQYW=VJ$%v>tC(T(Xt zUTCY=SBj#s3Ar&QlMs(i5lbAh=aHn6BQ_xF@M1Ve#cPYK#fY*(>-9c%{8@BwGRs%2 z%6asZ=BEba(O%@%OY8MZBnMw7kIHb)F1hsn@b}E#MiU8-%j)%B0&iLDBWG94R=>GY z3z}K`F=dwoc8|kI?&yG}ts-~mduCkGkEI+3B9Nb?iKUmC16F7dlLf+j*VLOGfb0Bh z()@+P;$b7>aZ^bUOFhdm$Hu8AH*0G=dxdXO?{DVQM}0yIv87($ zrY`(BW3zEvv;fa^SZRW~X^XbTw-R$$V!fWVV0bifixzRg5IFh1di__S%5O8PPW=*S z^bN_i~}1>5TqPdk0x;6m=klyVfCt*`!Nq3R{zF4H~2GJ zn6i5FtO9b%JY3S1H@SzBI^ou5e$4yeVQ{0Bc6IXPjg~%o_s^(ruhQmv zhGQ-<%(GPN)tcp!BtQob{Y+kxV>X!bQ8VCS&(@op8L-O-%!egCOSyDk5?%`^0Gxg` zcm5=KRUe=YFdVs+0iUA@;Lkdw(r=aHT%;xshu4&kjkLTY4&G5-ug<=%t?^WDtbTvj2+)j8Zd~{>d3(FbB#QPywiOa_eq5#of@X4N6OB1!_)+j>X zxuRb0ks`NtJmV9ITDUb68o0S$_Z2!5YG~lbTysYvrOk=_%x~+>Lkmug^L+doEzn;Q zBIk*E^BDQ2ak7IuNUgg@n;tC@@8hf5dAa4WaU*jYcMMNy7-M8fxJx5}_KLgv+m_a&7=XT3W5nl{5L4NEMR7V^+P-9?f0m zbq3~mw_Y8Xr7g>ouVDKRn)Au};e}|zqF`2-H)i<>i--BbA_Sn;2}^;6!6X}bZ1b~9 z4U-9rgBdy6C~J2aW$h;}O{)lb7V;$?9_EJ^kII2XK%!&(0{OQFR(Mh!Nz$g}bz<&7 zJ~}|Xu=aD<5UdU6>GDVXwtmG_EX;Ql3xzpg46qqLaTg5}|5gY|HzSw;8$4ORYIwXz z9Hs&XaEMxA{Dft}`eE(>#=#0;eJ54!Pqg*dcVI65=SWJ)GiP8`u+hum2Vu3aAj4-f z<$VW;6wIACHe@!K^Pg(k_1dhwILw)+M&2!cC&Mz%VHvP=*yx1Fho!;X2_Xqz?DEgO z275eeB_Gnr1y#rfa4+wWAOA1+a02^*qZ6bH)(3MZh*XGuuwK}Hb?|!nQi*!b1$hj5 zncjE=|Cj9l-q<9Hgr&hoCs7FR8r9f|i1~FL`%mN#oW=@y~%$;U7+;} z`x22n6ADX&DRZfDR5HzDGOYG}b#*fOU;$>|(<(Pv3z}7dIpeg;Ph!E11cn;ufTx{S zUnFbO=1LXagFWj!?gowL#a)oKo6pewH0q@2#ov81(bS8V-hf^#>0MD#ug|g|TKlcq zbpy*+a)3FNF1W>jm#O|67!+$ml30aOLV{SfeLGPGou;X~ZqzO|)++?_(!M{PN4(UU ziKvFU&NvX#_2g9b=F|2ms`Ez8=4ttc3gJEV?Ty+3-$=~P59{>`6fRZu@!kLxyIr$} zq=GHIBWg`N(a(ZeVIw<7%n7Ur2ZCw@qrXWftZEb!4|-v>pVS*0Js$7RpBVHlG5mDo zTZ@}`Sm|d*b-YeF(y5?}A+|nrfFpv@1Oe#4<%U$XwOoYYL+=A2*N`hV-);U;#UPvgO98QUzHa?T~Gug&lo>Rn=n`*EOHc!fmMuh zCxs{xRu0=B?oi5P&AIS&csG2s(w7R)hY!Gijae2eGnTJj`#9$+)p4_C@j8RlHs2u~ zn-yL*4^NLIEU05E@=~xA%y*bA4v)$^GGW!Q(eDsm0B=7JFN2?fOM@d}buYlKfd?;e zm{a74M3HKTMZ;1t6Hevzrnbn~EbNU+ld?elaEmrKFB0>99Gx5acz7+`c+seZz^U*7 zxBF2;4cYMUg)VkJcIQ=UHjLQt-#}$XQkY zGX^x|%aj0P1#Uk(QN_0A%bcaO%>BqJkadV`(^<9S0c}Zugd$QR)KhQH?Jnk_elHaE z3}_*=Qk?rkM<#b2h|)i(H`gG)@x{dRtwA2bP0UC2<}B|^xXBD--L|pHR}v~;M0|hC zBZ%BHGp?n)WylBrZK@NN>yVFJzSM!J?c;j=zee8zkugaVnXcKap%zb%tWWCo|A@%n zEvnqhYLeJ?7HnoCw|#~lCk^T8&s}Zo4^Gy4RX)X{xDMwYAAj6b20Dz(DJ`dPlN!#V z+M(p!>EVeagE?xJ!f6$~6t64+qjp<{Va-OCuu$D~JKf!K%pHb#?i|T{9oVbMXzo#g z1lSpvW6HxE^J83mb25$I2%)to+Z~$4C(0YGdFsP@i6I>qCTlEbs?3}`IueKyr#-BH z(GBs zGAQd`xi4X-x-dhV?Gt+e`E-wiLjw=`F|HP|XYYk7HAAy`6(QJPNK5oz?S`BBu?!SC z%w9Tgky2yf90cpH$5ynnTv$MfAYBD+YQMGxuw!kM9bJlMh`aW~2h^hmQaBr!DiW+#aI%5 z?-I^0|iCysl?@r(F2m@{>fRJ7jyo*s8k zd05T9TbnvJ6iWda{}Ke!NV@N&AonoT=gB4W>a9QK&YdOSI|F;zk&&Q?n|#>76!QSI zO3cv#hS{qNb4~znBMIUxt0) z1fOpxXI)+|mheu;@MyjifgxD>-^VuNl6(PEJUza!;1v&(+7y1y?c;M(H&cSvf z>ZE44Lqw}SP_yr+P4pmM-j6!;e+Zm}``F0*i`9-h^CqgJcjry?k3(LL{C1I_>`{m1 zp?paUpfPUrm!65L=!x7&s{A~(w45`a2WXwQTQEJF+R!QNT1{ zhd5o?qYh*;4C{@ERCGj6SZKTIsHLE;#hC1~C>kulDmp^X=2u|J{{h>VGZ@FBSx`U-tzx@lc)fib5etLGw5v6F z+IpW%%x6xJb7ePofO;yAwU%YDD5{H()v`AFZ>RG#^KhA7cy}iqp=NFBB~mYVv$!jm zI=~0B{DcL=0!Oh>SR^dq?+!JS;r9!|0m*=rzpJbBWt0SS?cY_dm}@ZmzN?PpYd3lZ z&Zb3pSNUn$jnUbd6Hbn_Kk}kNSo|oK2uqXLct5Ofmn8f8l&kp-wA04cAnNyXiB66Y z`SR9q67HC!=3fvQOJf}JR4SqplFtx5GDcL5$a37(2=6#G(KxL(e4pm+6*tGILe=h! zy;tNFVL3y3tRZ+(AC6uIE)8E5a?88_zq|u^=>H>^cCsIN^`~ZiB>wMqX<5=l`_A0bcRmM9bOIk1Lk6sul8#*B7)~p z8G1L2=}|?(LPs&lQE{+X?*_BADa4YEgVDL743-63fMfZ|2dst_ox|E-mFKW-SR2ee zTqn==!#dA#X9?vCdpBG;tTIb87YH1};UdhEJTeC<9z?V$EImtcVq(DDq;Vmhv zGrL^fh4f!)whK}up~!5XPEcQH+Qq6CXsrTzpK|DlqR|~aIZ}ZAmGvP?sf9pV{_QaP zL`P4mwGXk3JRDf)hRT3br$`zmA*l9$*xX~7cTydANW09x6d3cdLl2TNz5S#*`H(ip z+71lwMZuPEuHwB^4D;0D0z@{V)%&$W-|mZOx;se5RR{S6TBuL;0+bxj20g>vVDQ~H zOe%XUq(!@+K~@~CQ>ouF;*kKU@onG`J8jOqEG!*<4f>^o(agYPF^rOVi;#8BYB0ym zs>Gk!4d%F6he)tQ9L}BL)sJO=ScBSnICq^_G+RpA-IjbT0gH@`vsy*!%uBXGY9m$M zugH=En0q1{^jo;fU~dSOtEfJ*({lT^v{g|9@>^GY9wy8fP0t*kE2&b6bb{Y@vOLHEao`6nKjfRh)`KVNHfcUesXj zjoJcDv}h@+NRqe>k#$Ogd2iO&$6c>};f37Q{*g;bQAB?izv_=G|HoK%k`2rZXfS8$ z596N2=&>rl6tCJ5`A%&xRg7JT&ZA;@N04c!HR$`LbZ)`T$aqE7cN|upiQGG=L9do~ zPH_jP^)YRRwHncWM8*=xuiv5}>e1$^mnLwc{-R=Sp4Apfxt!ggUnyVd1>{EWGQU`} z`D7ugwjol#{x))bE7mfUV|HBL zpuc8bi_)aD5Z``Sb!>zFCT>;cIxR}|zsjI`)TOj$>l*Yo%u4dLG6o`^dL_rphhq^v zoa-C(-KHOhOW2;(4(p8@cKjL4GjJt^dZY4tLfd#{8s^kZKW>0>;Ms7g(&T65uLxEQ zn`aPnr%gGm3f7^nk!wJlPiVF+1F*=%hT*=KG<4q4Gzzc{^6aQFZyP)fF8)jUxF?>X z;VFp?>TEI@FPKPlFQm!7fKPkf>#mkB=SlKUCZcT8?S9G5n-Cd!uH#7>6U${J>DC54 z$lQ}N?-0q62rK4wBNI6Dy-HzSyh;w=diz{YPg!g zw_pE1Ue;5Z z&9@vuY(azmA`2W%<=rWG09Etq#zJwMSk+$LRdP?PzhbE47MNUo_|h+SHm1% zsuzE!UA@vfmY$ARqrMR?rAr2{BrR?5B)E~*!sGmmvkar*gW`TVWj}vLTjdqK4mAk}5zlJ(&aTGdF{yD( z;g06@?eOSHjppK@B!KsNN)|wWR=Xx76-zNsxj%|Lkp-)QJ!)>ad$?Si?o$P`_%_NF z@N!-H@p8)X5Uc>!X|kEmp(w_0AQ2`vs(5QqmI$fA&q=;7wF4w3aCOFqwY(ztkU z;;Zq3(~)ia<`=rORw8fgC+dS0(D@S`A8;a9+|UH-erwP|DMt~ ze2nM5@vID@-i$&Pj;sX7r|;goL?yn+q(mCT1J|Rbzl`z89(dFg^8ZlIYGZQv(y%MZ zy12X=e<)&+a*>W?n^<@`Ty$r+vE(}&V={bbin{tG?Y_iL%#i^jIbA+NFDzjc6TgOF z=`iVUxwl9qV(cQqQohX0_E%SCTT-blwn~(O$rLYJB(ou6-RC^@NToK%w{|lXEg_5X zp<-rT1M43u>5TM&y+h3Gaeed_LJ`t9rUFWUCBsH%nN(O3Z1kRtELc8lwuF{n41Wc% z!Vs1FvUa&=8|LuYs_tcNn$H=`9djG?&E^b_<%kyS8JR#UI8QA&LNl9zIemVk-e{J4 z?TEH8qz2Z#sBw61G!p;YVO_8Wb@%N|0=;)co8skh73$X#%Dt@Ybv#1{F$Pir*|9+T zX-q<%A@8UZZ|y0&s)d{q8?7NSBJJYoW_4FSn@v58CSazKOI}#NV}L!8ZPY zloOjVc@l;!BXUf&;*$ld8q2&Kuz|=%vu|Ftlc_x4YtYA5k^<`OhqyIx!7EyjPaQ4mOYR^N8Ry?HqEODR()Bc+2`)UGUT?{uB9t>xvS_IeeQHfc#OKKT3g^%j5#sJ z^;|cW61OGvI%-b0B?C*?YMLjb_lzagw5&EpaUHy`11BvQA{anB^xmV==6Q2t{0_%`qw^2f(t%NSSrpysszwt!dO3 z81BuAJs6gJ4vU0kox|c_MKJf|Z8Ux)!%Fd7R*ozy#?pa<(fPds);Ef!@lY+yd#x15 zn_odYezQi~qiAYni-QOCk5GHJavy+XR|LEktoCD~>13{56}TUF4j+T2+Qn7g(%j#+Fh zPp84HiRwn&L>FR?P8=KEGFZ|mmc%pJM7M|N9`oW0-&X0;{kmR7%l5qi6%Xel4Y8@> z%}hp+fb@p2A3eOrWeTm#*$UJoy=J0FOTbR8Dh4&Pwv#+6}h2BWj z@smc@^Rn}CzFJ?a%?iqdWo;X&fFxG?Vf{C#2WwerQ;9kH#>O$rCLORSn70IlpM#l z+N(6v@vy!d8`aSyZE9W`mdNd6LzN8+KZg~C04b=y+04E%H? zz2u=RnDwT{F^fwJV8O5;!)r7BOJQL!ckFUN=>p{ee&TT~EDbg~&5~fLFn5~SaF=d+sE%*bLV^mh)Zjooke_7rGFbJ^ z>T!oQ*ILMj7)of=Pf3Hk`+3)lO`n57_Z~#O*EX6Hh})l6GaIzaeA0H%<=)w-&o>94 zK0C|8m$&NkJ$`!ZSUvhc8L7U+@2**7I z3weS7{#$O0U7)r&>E~p_67Fr(|7hL_>ph5~H~U1+L?0&t$Ni1^v*y58iZv(DFENUs zTXzz^{6_s(=9qg(vo_N+9@d?&)-Rw}S0yHW3uk96P} z*LY%mGg?y>qQD$#IuUJ0WQ>vex6sLqNh6ErQ|db@n5kHz?2Y<=34^Of>~pUgkz~$C z9)0l5+)s}juV=jHmG?!uy`cCv>(WG{?YD>H)H z5O~aK(r2l=uXaC?FM>)0U13dfl~^N+$g$U%NgRY_EK^^+&K9z$bV``ZoQb*9G<#KI z_Fmp3SNg2DwHHgw@}@Dv7@~l9+)hJ#9v%pTmjW6lcG7fXB%bbrn&1J5| zTyRk3zJ;4X%(VyA7jH6!ADe;J`gD{2k^1sYZJtVbi>_Y*tnx*}?FCNEJuf!t*G)E4 zq6Y{59R#A}L32dOvh@iy={OTU9{5@Vl+i59swbFWdVo{kU+d6(tdWSi+@ghJM5%}d z-J&UO(Wysref*0MCGSBul#Uh)6ZL;`5>GoYV=sdoQe~YeQL9cd85Y^Wu|eL1v?|vX zs256%K>0@lJKXRw6I#=O{Xiq6c_pJmT7t-$=MwF5i#{U~{%wdN5wSL&6(S{S!rN^1 z^}Unoo-&_It7X|#qBaRk1bY9P;V&5wUs<9a5m*c?SB?7hlYt9M)N!D-3+O0s)OQP< z&g+bUfJN_U{#M_+&`kf>sNW!4MpwB-KNV3dqTZ%PeH-nvWrs^ND`+a0gpAxynT4aQ ze-g5pINDdDj=n>!S^@O?^<&ryn7n)D*VXQsuSo!TVN0&r=Y%I(x98kF<_Yr*&!Sd;&-5n=a}V;-{uINk?~nDT6P5}qGyVBO z{0X?1eDc0oStep0FwI_tn8QvtnY#QTA>)K+?8jQqg$Vp}%#jA>ydRB^s#c?zWDgQnpg zs-$%$tQvM3W@9Lh3u>inTLAa{&c<=qx+a1E zyM*=VJrA)Sy{8B5V*n=||8i2A=G@8w29`ryARqRD7VVRnNA~}>Q@_VdFjlAdsRJKq zbCMkhLVKOVn$Rn#(IldXbi1oniV>ns1q_ z{`oKMQtKH+`+J-8A$h?nM41!wqgBUMdEVA2JKE4EP5PG-_9=)A)&8-6(^QrsO85ei zv`-%u5NtWmxu0lrH5gDgjt@&8nY)#q6Q{Oh}gGPgAib7TG$23D# zhz5t6^d}gAx4eMJINQ$eP4bhqSBv%!-cRl5>C`WiN53vm+j==tDjjI^cA7`JN+{&S zWYyM-f3=7TCppd4q2D1gPLA;VMB8W$`vv*N&#B)a(K?ODSeg+O$SKS3X}Mn3Y~)2# zocfS7u!oTw$3ys)QcHDxA}uEJm_Vog0=)xE6LKShvpyw)F~3B#z^V5cx&|qRhzZY6 zd8!CeSEN&akrvM~6OnoHdj%2f7x@|FX^BqVPuk&4lGv}xDcg~(OQIZlTcaN$uG^gY z5lJ9<-biud4Bi-TlwRw{nnkUt+~a92#AQyZQ%{k`e!9F#&bxl^GcCf}gQ#GaQ=cy3 z+k(h_o`#=_?IRFT1!&{g`d+(_i~V7vB(RM_vAbEPbCry#{{BR)k=hQ>|kiX%Mm2oIq;ukz0_bVExH=OzcX?AqCoF%yas$AqH$gAEa1BrYJ z{*OdGcqx^g^(@B=_u;7hpHBVfQW!qMld`WkO?TZ{ZRUoIU!(uN=Nz8GAO$?~VV&?2ts%9WSP$=wlON^^xEdWO38OKrjI0?ef|n)Qu5;2urR z##jdLoYAbdzNFo)tatCR%nB~z>jaH;DKU?N#|Ej3|D!G6mVvnhH)LSrXQUvk_#9RY zD}sfOa#sPX9OW(*SuLz0sM(mgnq@mkkZ@p3u+e_VJ-4FIM8ms-)Y7lCOXp-^?wQ#< zW;R=@{X+O4yvJN5{nA&QCDjQl3U1annP-jle5Hl%2>1={*X-uuu`bc0!eEs!@y9(4 zDLfW_2EIx7sDl)f;lZ}%VO>ljnE{KoHLJXBIg7kX#WDxW?e-O(U0Ay3tG5QUjX|N0 z(M*Rm>*QbKm%_i%u=XWt>3_8wgGw;RENvd%86rMc!s3^zXZ}mQTt?ngmQb=`>gDov^#M z0PKf$LXAWC>g}rje_XvXAh64+Z<6=zva5)%wK@J0iGhDOjd4iJZoAq6v?c+QD0R=y zz~y#4HLJ#Qs>bTCDY_NNtG;sTVN&OPWWo-SrGBL9x3;&H9$ZM6xg!t!Oq@?anR5Qn9l6M_R9B?;2zo z>(r5N(c=a%_iSj^uQkU9r+mi{TsT{qmHoysdy% zEo(Mc-fo4Cti0_)WL@5DYJIEnOsy~Q5QQEn1=%tQ_r}WGc&NuE&1T{FI9Ccsh$_?B zTat|=6-kT4?_@5YKA#~gzlHWotv!g^qMA)z?l2-_Ijyyn0>7f!)a4$7x|h?g_RmJt z5#4Mqn$62K7tK}!vo32ki{SmZH+JEifmTC}BDgbG%^qjJ*_!ke=@Qdy9+c{DhGm}O zaLhop^~pUGt>ws*)->z?lDHk;YlM{r5$jPtt>Ij)#sZsNzN|j>K45wqDn;J>znm{5lum4oQyhY0-_;A?PO7>h#00eMcYM`a2SPcW3%2a zqQ?-K%i|{4BdleJyyKhAWwzIe=vK7duX!pc^ch;)&CSEDtu$@Xu$0Yesl~q0Yd>bk zW?J5(d#>>5z|#3ZGZ#$E*{E&s34WH(aiE#oi@)5>TB*uyXaTwdfpIo_Tn z2r_=J9`&|wy!;I2{Fj^c=Otuue);3Z|IXNfAWf#n3)CH$@Xk%seU>o zk(Wdgut5a9m@Qt}YS#t!E3Es0PLeHO>b4X2k1Uf8RNq}-pJVMu-ldw^N`m}ZAula&)??%Y zZ@OR2{x!)N<7*G(ZYFXC>D(?MoqfMLyBJBfuRXv&6-gVCZ{?XO_p3v`_C?kbVC;+3 zolG-Zym*nJ_s%*_<2vYTXJJ0_(vvhr^3ci$)WXU3`BvNSiC9;&F50eT_5;R4p(=ke z?z0f>|E5__5%>Ok)KPI?0j&C#{2mR|yY@h{Py&S=p&oP>&u@EKLo3;xtQC5U2Y`7L_3q|#?(VI$P77c#8SfvD@^ z7X1?W3MzZz|t%t!dFyd7u15Q|yssSZsa-%PZupkx`HMqYUIBYQdS2 zyA6>ss*&1~&)t5=lfzrg274xt8Vz5rjS-u9I2{Rl2BL(B z7IVbr1q<*>0{i;u=*)R1UKoqyK#nfKc-m4;}+tHq1L1~L_F>e@Le6QLu z-9Fzx8EC!C1!ucpAuwlEi+;Q0k16-6R$N*gz#2aAZIYiR@H&PV=Be3%lmcrt`Dty7 zxivsU#+Zh2A5vzZJ=C-O&*bL~>d8QRP@WTWBagoj zt#eoaEEVQnLLlzKU>Pu4KI12S$rxDnwJqcIyH$go!>QtD*n@qtfDRn)Hd#K*n)xSM zqV+BM12UBI(LJuZ*BA=um_e1~M4ok}OWt&k(r4JOSe8_SN84J4uNs$7q{G@_!Db+g zA>JHV7wjfeYd91{)7J*;yRn6h4~Ak9@K+S^lomb5oO>@UV+`l;D@@9#B8a-Bg^s4t zZ0616*-BX3&KCWj=1}b$Fwa3)%FomnGwm!}c$IElx>^%#U*l7VIq)uWojKdzS4jmK zaw5mevkO5+rg|$Fl_&HyKGn}#%o89}u|#2!BPR|0T7uxcwMBo1C|P!AszV{(E<}#srwiZ(hy2Sm|L$i4*8&Oeui++tfHNpMVJ3Qqxbd1X9=M+U{tfdRK*@Hdf z>*&U3t2H+JMr(2{9qrvM`ZNj4C3m^94~sjvvB^f$U4=Y4v&CHDarVv;d4!6bW48pw z)l)ulTgK?5$*`DQb@d$k#%1N0d-7X`*XKx1tATaH?!+v=aQ@n1(OQe1Y-a7QIrf0X zL$Gw%7vjjRn2k?#C9`?;vCY9Zw^RAcT1e?6CIHg+nVH}MX}}5qrTbLwT(V0iW{(0> zI7wb>ZQyZ$F&YsomWNx^;g9yN2+6^c{&36i*oAmi1ltdLLyapzi9R~d9um< zx%6FpX z)zf0`!rWJ?-U_uZ@Ub+}{eG%NPchcG1YRrmd9c3No4x_QMItSKx1MaG4>$z8Mc_OMkcA~!!A|n+<1XdI zKiyPLY=yLdg-C)`i>{0N*KTw9|52EIhP4Ax7ou(vDYt0hB18erWP>NrF(exI+-B~y zFI;5zURvHtC30w_)EK1#mN1G*U%wred`LAevS04w+eV_7wdghGCcecNQ+FoA3ZGF| zUu z7IOyYp)|E4oP^E+4iWW97D?zdqpqE*o(iWs-GQj20yRLY#!C<-X6y-3--O#|S_27_ zNxZmHcCosOu#wMAbgv43pmNJPgEx#MYEYgB|g@7G)OKZ~lK=YG3m zsXcUl=$oD%C+_<3+G+V>vB=VnQxaTXEW$p+vj7&{p{|awGZcoox}!y(ZLWuU%9GeS zBG4^@-=bOUYSI1G&2l^!0#2uow1;@5BWS;~Kpp>M?tHIeERlD)HsUz2WVtPUSZdss zz~g)nENAcLgfPspyIqfDV%hJulwxUfTiUSrW)`T!(fP(BiI|P;KDG|NxZ9F|rOR!} z#u9vYf!cZ*k6=#4Y&_C|rQB^9!qSW7xMrX474|l&+s}=SKFfSku_Se)9*i7pbVV7= z3G*c_nxt8-y(uy)vDal|i`ziwFchb{W$rpEK#CH55|vG4Fb_7n`CkS~QH z3Fh$;CCzN>Qlsp)rA4rE*ywkZ!!mnYbX*%h(F2-qxzBrnA^ zTE6^J`*LeE?u#bPj2SwJC);7&|QxExpsOjjQya5Rq3_c>F% zs$r?0nhkzXKbB(N>3+z@Lza__;C!aOjkeG748z?1nVNN({XWlf;h(D~FSD=l>cyNP zZ@V)0%+jFIs3Zlu#umJ>TAmX{PGao$&; z|4CkW`UZ9Ua{9boK*xgxdac0MZg3~wDwODye^AGMfd*=R_PxKe`>Xs_cFW9S1gU37 zQcWuV3RoXuIK0ZfB(EEb_t!1@4SZ+$$v{*;Ea@9pSAe-2*=H@>`=6-g@LR><)qb(# zuW{`hg-7KSn3rD3wzeBg9u3bqhsW}0BE0k47TLF`w#3*kn^_E~`fj8LBsxz8toA!Y z4ZGM=%tPi!2@NJ_iS_3xUmeQTrw5knj?aO@jW2v&VatyA~M=V-v zx2~>&oq?^EAfYrH_m5|@KF<}x4&;`Jt@_bXo}Ph~dA90zm}TRewe~5igWsp7g{6=7 zXZ%jXBZ+AY@-VMf{gBHasn`->)!wc8&&>+;wpjbr)rGJ@*ugRW6b}1Si9Fk<)wp9& zoJdi3z_KQdjaMJ60`~vK%l9;m3Gz=|;Sqmqu=2^H{qdb7g(3moHJLv>mRCE(W(@)G1%xVGHOVUxjecH8=d4@rcq0^>4A+!hzHmv;|RtO7Q(`wEv zyWEw*(qL=lL4NXVHLM7Bi$P4*2J3^dkk$B!yKY$Tn$|7)To(-F|9+rlZL7I|gjjJ0 z6aSjWo%)_30LZQu_t5)C$-%S;o$gY(4aE%!E8yH84 zgu(mPsac!s(>&uaN3K_wZ?az;l!e)`ek8FZ4GLhv8&ocCgUT@{ZWxJ+$ZKE;8`Kfx zKAo5|k-uwp6F=BwpW*D`MP-fl)N-p(G@=qzlEvJX?z_9@+n-cpJ}7=o#md$AY?%hsC$56Y=)d zp|+1HQz;`^J`o3lBy4|tt6t0(vusOnt+}du#_p$*ucV4kAhIzxwCXx-qh)1+dgw~~ zYM)L#vf|N}QJ1iNcBOrWPe3o(U}r0f@IQEE4|fx_Gs6(InX#2r1X;JL>o?n%dzE1B zMhiZ$8D*diODW23{bu_TuOTdfsIjN7w$C@t>gXr<-Zzmnq6@vY+11xb++q*)+5ZWx z`mKCL^Y-}n{>qBQw&nSO%JCs979CscmRmbz>t{_QV@SsAgSWwrbs5-& zd3;JHyuH=9Qbeqhu>xTEuonclep&n<4iCJeRo_XPc*Lt&pYOG)%?b9+UgZd^8Lh_l zUgI=?e({yqp`6W`ELBwSXMB&0Ry|2Z4q2^vl|8~M1Lp14_e-!OxGhdBXql!u*N>$W zi>PzPAWc{wPDSaznsiLY;wwtOSPHOIV#yOr4VDyidqdKTWk?i#B-U<1S9mAsAm8?* zjjn0?%eUHRS;Ki*;qF%bIT5{p$k1;e*~+L|4x+xhTg}y&yAT-@Ufo;i|8*dWzK7V! zQxS-aBZVRoMLYX~f`5POm_^`4%W^HA6$rw!)zgXgn|yrFQu^;M(Ep({t9_}ucVq z%gB3M^*Iz0%R_FL#_cn7MqFnm^`Bv~dkB|(gz|5q)^EV2F$8|~wf3b83j6u!gyR~? z2xcjbckf+F22~E9q$E z@Fwkl905~%y*wh`#G|IxmG)~f%)EQ>EMXX#qPIJ(Pajg-Z*WSYWioWy?K%DBDp+D+lCRqjjX zJ;7g-Zf9HdHd7FDgFVTo3|9VStNt(9&$LiEZXm9{gOr?aTJI_D$AkJdM1) zKz~&}?1mVltMq?#y$xJc)%O37HV%xTjtYW`I4UX`8Wt5PDi#?TDi#%)8Wt59DixI# zD$bBmkx`M`TV&LwF5fO06%~~l-N*(Qp25p4GAb%6GAnLTk>~eb`wXDp|M$=9WqtN% z?X%B*SbOcYpU%XnvNbGCjJj-c_^ywRZj(Lpwc8&82yIo#uaT|^D=%Wk0 zEzK#%QEJPz%!rGB$BP}|R?j9nuhGtt-yd|NTXU6hEru$FKy*4CY(0St0%5EK=s+<6 zJH|~%YbW5Lhw6a;pEyZ$N*#zLkV8Mz&twwFxWg+cA&`*i4RjC)ztbDA_hU@%(g7~m ztfx}rV6t)H*UK+!@&hjGo#DQ@aE14IQ$9c-H_Q9{aRRB^yn(R4XpimQKr(^!o!&qm zfq17kP)Q)nr2It%v{RG@T?!oC#Je{bO%q88B zYi@jh6Q#yn?_4y-!JE=hkalV1x1!XwkoI(W{q4EN6S9EyKolF>%d)1bkFLkIRUpb{ zSz?74u&WU@j^yiWqi=9d8q<%+volwJ;NNmilsf+g=S*ArFkhdodAS_E*VpK_-QYaa zS^*bfsQouMFAVb;?#l+b+q^1HCS}BJC-cXRkBnp-Gt15Ac()Foxtec!N2g*$BYb>) z`u*L;Q(CE!>!ZoFAKW)FCWFWI$aH{ow~lrj`9kbq7Rt~jqgX5aad;WMPIfwPbXt6qV9HEpitkne zJp{JgDBXlWq}{EazR@{tiibe1-930{Qx@Jj;fmpM%+HV-r0E-OAq5_qdXw|w)tLm! z#tvm!GA15Z9()CSix8&0ZDRb^gLMhAta0vBc2^F-<-*M{*MUE|$vNe0zY)GZ=>gu- z-Zk2OmDd4qz?Z{Mm$dnlxpov>V}P1-vvbalRB+h@xBkeU$fatx!g+#*o{=ifgX@Mn zM93B?nwtE8s5tuVe^ny^gOpwZLSG_zmY|e z02^8m<CTDsa|pyP@TwyxwdA3_aOo#;QE;VjxeMK#Yvv=-<5lO# zZ8r7QCYH+@(-~B{kQN~wx*Q&)?^1=F?(5SBPl@zjIRA}=>s_d3ZF0`Ka4py`#yxmK zUEZDnXMw|=(tpC`!1=%lmOshSUby~+YWF7Af*QdAF@onUlsJC==oj(n`}$9~5V&wS zuWIXwdN0isMfF^Z-15sA>a5KiV2-3P?T|kT`N0|L+0D*rs`2kxmN8Ou2y0DI<*kD5`A{ipo!UTayWR7O9n6*gh==+ZVgllKY)k>bqOM7H)8) zEFo%kmi|PdEfFFh)@@8sJ73-zs&3A32HNuA`rtk_5B&cf&gzCMT&fObI9E;y8H2i9 z=+-}4%j@;YgGjj6)o$ZPa~*rtZO$npv*7x!a2u8&b*yt|n0o3qXXsQnWa+JLV~0G+ z=_vUTXKArcNg&;=&-d!ze`zH1;V8&jgqBBVF&Yl%GyDGJV=Wc2M%e3M^WWVbVULH+ z9$?Rc)xV)3AHE%vgD7l~w-XvOr4!D#S3$NebsPT@g|D4S+6TD`6LtrqTrl}ayvS|r zlacmy5%H@bDj|qtsrh#}{p{(;3omw?TZ=B_Y*2-&!*{UbUydkgncMhAQe7pYJGdp2 zer`5Db$0Hyk#;{jgWx4@W3G(vlaUV{yiRxW1LcmWZGQH(MDi?m>vcNIcr%d+YJaA4 zp1llFa00KE^#2{^P2YYe-P$siZhf;`Z@pWd4pU3+q?B=xnQ3lwL4OPJ?U!wfRAqOH z7m6tM7B{{SNnM6Wul0X+r*q1+{sCwvqI(c|$G41u`GoZB2;_Ym-NuE3@_Pov+8QJv z52t}cMb4i*lL8lYt6PnE^1g{XvI(RT;ebfZ0rd5O6r>LoA@929)c#ry*ZIFVH(cR% z23ARjKY6wTt^#hBW=!rlT)w2&fm#W6^?z4=EqzSnUTy40K-tpd??2~>M#u^@s=`U8H_d1tZGr^7*OuOP1{>4-6N(Wr{Kio{u zUY*ZO^3QvjvWHK^2E6JvK2g)|b6z;?T=NHrcHGC=!U9B%uVY2{_|?2d0+#Yq6{HK! z0XH!COSSgFMU<;!_i?_)kM|Yb=QdVJ_rD=jkDq*;_>~<%wGrW`hBfL#@;e%!`LavZV*QaAe4P;aA;=0nPHld$c3-A1o$ko68-yf>k1Y&1TMh{+h8!N?PeGzZfnowf9kw5)+Y~QViG1DG8b5>fj#G>8cdl7j0`6Z{JvfsQD}DgZ?~-cc4zSm7 zGd&ynZKV$6X_r)MD?h8Bz+NK%UshfeEhLP=Y`Ge@%emm3wcy;f)uvB?Ogd)K;(w%Q#7<4E@lJ8mJ4fsGYMQ@tu^$lI06~hSD!Lq zB*W#wooo-%;r8;-D|cf)9sfg5%97eRj3t!&pKk?vHL~sjIj|yyZgLy-vNHD#B39;R zs`~NKT9)-(9RKBFzbhPe3r0r4}Y&)!Wv@Vjg#L?=3?H^#r zd)YDJv*|Oj-nd@|nRY1^G7eJPrPqVRE?qpP0y5>oYI90-Ymhl5(nq%XkQ5CI zm60+vYm!=-hhG&FPGz8rv5b$F(-CP?QJ6=u@)1>BR&CrPrs7AcSi4-u5sm3W)OAI* zd7$&N335qcuHA74>l0U3o6E=V5`VyakKzXwoQr%tXZrdiuRE!mSc4-l4w-87Zs)SF zRB+K+ zJQX7<-&t+mkGd9-u4~I{<3hWX~5g)!ggwZgKxQUP%wN9}*i zc_BR%e-ELy!Vv^Y1_H?hdI+3#+qPL&a3sr^+6b2s$Rr?J3L|CFr5hsRes%P5w!tj( z>BhUNPdWG;4A+yZMnA#9x>Rsyel_=%`Ss`kb=wop@wNiE?8mE(0`muIez=D=c>M|P zXmuiJ-BUg2--YqAUbx0R>bobL)8~bpjg>@B6X`#(`jK#5|Ld7JxXwLlK>;-%NoK?E zuQsli>ix~`-H6C9pbm2oSstu5?v?5HyNI;DZoPx;p^VG3EGn{vuZ#zrLw9=ql;#~B zD>gF)L2q%jFY6jwl4B z)kdkgP;&4C_M~F>ke?{B-CIy?9@;-=3?@i^=ImzQ>_8R!eU_g+M~eESSBXwTKBPqL zi1Migt@!Va9O1koP=NZCuWNyw|- zt2Ta<1x%3-`3>dB&+9sm?%Pm3_}i<}pknv?;Va-D(?d;`9%{sU2{@lgQj;3_H|O$6 zso?zP>Or4hlyWOvUbEW#H|K;M1>nS%YW))rKEp2>oiaR|iLL4Lmm#yXRvR-3%b&cn z3eIs7C%ouZ2YfXAJR$fK?l@f1NnAXfB^n(~%{BH)o|leRm;7_PL(Q(-ZeDA%B_a;H zDc88bJleM9Dd&_63gLop&NZhu%)2i0F0INdDv*UB8<>H}D?D%|6p5qgZMfdwlg5tV z3oyl`E8Rp~0C_e7t{YCToq4%fxK^?}Fsw?vB)HE16)#QFBi_LEig4m(!L`FZ4mRBk zzQuxrL;Xdwn6 z6YTR{wXxHjjXccdKU*1G5}eCiW8*^L#P~jW=GW@MvxE-bX&NcXE7X)BBvx_D(X2z;fpZ+zgHWd znXmHJ8Yk+4>}iPXe^eWMrr&(wP^#R}<2xnKv3PTUNLfa;`P)}_leAv;82`NUjPVJv z*d(8tleJF0`m{4p@3Cz~SURjmZF$}~*H%g({(81@OXZrTRMc8W8kIy?V z^^ISO=YL(UTJm?i@Js?J1a6f;5rKk%fQNv4AkaskpTLhqvxYHfW=vF*ik;KL62QTe zY6cCDC|nqCvrr7JaA%D;N#X2yi2NzWEmA{YT259TH=(i@N#+Rt6Fv3&+%$cW$mDYSD%rQ`J9SVBWSDk;7hN%&`!Ew!A<-;G!pIeJ{{N zJ(Aw|8skR2$c5ZoKYP*Xn2{An6$jT0ev4S#`6P z-ue%x%{PZ<9P3P1<@6{XD1*p>5MN_~q}mP96H+sH;zCS)KU~_B8gn{?jdKl`EgN%j zD|i_b-zjQziF49f(cmoNoox3K;Bp6^<(WACrN9+TsWE@Zu7rSx2q&*69e{JgoxG6M z2-o$$xGp%K(3-*7s1%?NuAg`(XTtvy=5E9rSWb!{o&zo*w8oq%B}!mwjlMR%L!Qio zh@U$21eC7yV)(}CHK+W7Km}Ys+`x${nJs$c`585O#vx{0U(+6*tPQ8+vPa~zYX(if zG+q_e+#6bBGe5xatj~x&FFE7vQo)_%#8+&|-995!o;@qZo(fy+s4;(TYYA-8OHRI^ z0Gm%v4odT%1*^RdzklMS##~BInpLB2ot-tsH-$hPfk$WK6leUCn^8G18MC}iOkTtT z*b5gDp?3e1a|PXC&spl|Ke@vbu$iK zqnU7#7t|OQ^XH46D8=opgzJUVvs>ag_}2=TxKP!UIzuP-fjbrsRgN^5|78qw3%$)1 zBY~J2bLnL(fsjQ*4K7ugNOp6>YK&*a9@hTF$s_q>k^Ur8*~|3PV&tjwYmAA~k43I4 zs{bA=TH+5Jj&ST}nYfQgk>}Q!AGUgyNJAq>@_x=bly0dK!PCHGV4@9OH+yG7K&RE;(L}E8~=B9JmPipX&2&(z&wCImVg^mvO1uh}>ET zPSZG4m6frpR0WZ8X^k<0Up8_D2mETDyp{SsjwEimG;-4=uHfj&!w{Jt%@p-^tYS$! zvBv!1HQtFnw|gm34l_YO9_y8VEEx$>iLY{vCljudXD(2yC-SaY!}ZIg2t`PGFR$UC z=BrI?7rgSS(`I$UWnG~Ty^6y1f+N>UULNbp4h*__Fe~z~BN1osx*FrKy3H?V^2l(g zcqq?xYVm6vSIh)grg)1Fu1HajzeZJ6fy+{Aj5PIroijM#8Y!Q7@bNg3-VGQP{Wf*t zHRovGkkxo)*Vm})4!9;G!E;&FkEevs_MV->Ck8_l*Dt-$>e1U1#X>q(G$JsIDrf zF7m)zZ>%xSP(NMf;^s@abE?$?mv*!I7jkPKIQ?dIteix{ub>Y1s?ayU$tGU;2A2b} zz!iJd?QgI+R}M~mx`xx#?|)C-fA9v|UEOebPphMEFvIdo!ni%HPP{>eBf-5-t66W7 zMiSUwsFn-f3O-P%Hor;ci@@cq?7iE?9+=dW8=|yO9eR_*y3L43d1#LRmGt4l8si4y zh=tTDbdk%l`?TTLN_{z?BZ(9BjGFb9yb#>@j9M;uD>z}F+6%0B z2XF6y&wahd5UV$sm5V9oc+-WWaR}eMP?3VAo}UdcE#lnrJIrNUhrZE>%F&rH{SCz|kqW;O2sX{3T1TWa|6 z-Lb7W#djVcegT}LT|IQbImx#i9NW&Y=${j)Di2WWtuT@8eA-$q{hl{`#S=EmwV3#i zYm5g?Wg2^sgT_&Cou8;V2eB+`!O=%+_%7{Z!+B=YLEe%FSMrr|BDa=3xhOe65_DyzhK!|A*r9SLyad*C{F&eZf52zffsTd@=-8F_)+flXR5H*zsm;Rkv ze~9$+!I|HyU5A_ttc~ED@73W$9Nh3nb>5$o@<2@I?D$UxHqsf?*b7~!-AfU*X+>`7|G(v8@KcaS^ai3@#8!F?xc)R?zif%;dvJ(^Je`WH5)9~^8*j%I0D#0KWyCxzD% zjyA({rK<7>#~VYAxwfHKn5rW3Qm=fZdgeJ-&}@%>+&gLy^ug6b(R)m}T6HyflVG6w?11o8<<(Xl% zMh58))*8)zIq&~~nsY!qZop0tt5rcC@^l=5P67)*bWXFS6X@pYE$X&mbgFG1I>-1H z!iA5}5vHiJ4>=K61rrJLE|2*35Qrx5r33;}>DmNNKTKjV1d#B9-P+%{?67m}>~y#i zxCxTI0gooZY*gtFd8S;s4s&?>fH&~^VdiVc2^3gs`AF|`KVrP?H=YuV916C&#*dOY zSE84itZsW3U0e5&bFyzPT$XRG+VK(jE0YMmwZ_Y8n42~8_daq4`gXzD{c6=GA5k@a zH!-FXFeDH`pq;=BH|0zs5NoSdm${j6XA`I*aJQR%<6;7lBWsO+sM}WIem!oQ8Yx;M zl5+oA<5q3c)u(Rf_*;B#W<-QLr8>M0o@kcCE7wa`1JxC8R+sL?UimcQ4pwKiZ$;d}(zgMlO;V^6lT$H_5-CRSZ z77$1wu;WLrB$pFNw5xqh&d@U@^$x=E_FD6h$3Q>2#(cYEvfq@eUuwu?*ewh?ME=$s zF^>*KOF*)BY_0L5S?|}>I#0J{!ljI>Wk}nwhF0!Cnx^6-2v zTwajc?!k8}8N`!EnDCI$-Gel~;;lNm=EF^_AB{hWPdkavJ&7+d@xD6Jbc0K|ms`h} z-3!hSmX;DR$STwFF!kCVgYNwGmtprVckTVESd?cZI1FO=K+^oG?et zYX+x*`9F1g8=Nn>yO;wAy+R_$;b5 z{?cyOgxiN463M>fHk^vZwd%7ssQg$0aZ7613DU~2qJ;`ggG-N9>s#n5`QYN%TBFNU zpl3v0A#y&~*&+%7&f|((3m#PP?I_VwHM*7j#(^EnMDcV377jFEI(*3`Vp?Uor|xMb zy#lz%c=b>#^>RSu@#;0wuy%tx;?>bs-s5+N#wS{(j=%|g8nKP|Dd4!}I*u({<67e_ z_5N?pNfX2%tdUlcLYEL@`*Psiii)EhruJp>& z)%>emGqkI7T#~t=cJPv?c6Cxs{gk?}-$k7v_fE02_a5oxZdV&*e(?LHIj4`3xGC@j zHw}*1+1{Y;^Fg*EZ;w7NcP@Z%X;{*FP)Rr znE$Z75_LB=D_yEd`xjFmag7)y)w&iwc~h-1*&KB?{fCtE;Nmye8XL@c-826YU4+Zq zqW1rX>hA{k-=U8FhpM!1#faTk%eKw5r(D5-mvIKy+h`6_k42i}Qs;e4uS^AVSZdA3 z&gpaVzGk_ymgmq=2@HN9!j#S*7&z> zya`o1y?huJPKv&DYPX zU7tATjI0EQ5PO27|LQ2xb!0x3jPE)`(2qaYMz{rLJ4S`Ng71tIu-wjp-N>CM*O)Z7Qy3@vT+h%73bN1b2fYO4ZS$3>)@s z#Mxh~w!G+?I5Ltz-2Pf)vqgpOa0N|RZ6UD_JazRAhjphyob#ILs*Mtz%7(~%tyX>) zOWfQ?K116};gVmkHG0jS@$P4MS6y(9a`o|N&grxKw^K)N)S6SP;d~lOE2|7crk+M1 zcfX;gccQ1s;Dk5T%1)e{EO7gq7@?C3e6v|t;R=y=Bj2m%+3Mv^=a@@9aLI4~-_J;a zdXT66|Hvgx{~h!b z4NmLA&VKKjXv-py+$CidGoc>&M!FT;)^EjW((C>QC4qK0|L;`&H#lSVU0BCol(m~W zjs{2cs(IZ^X4Zm}duxp(spEk;foWM~>Z^@mHgO7inZ*Cba*Ooq5(xkQs(sy*rWIWI z-&z*m^;mh&WiHmo9>C50T}CMx@B_SG^N=Sakhqb|G%etwZ^>pdoaYa<@>^2N0mu4# z_!;x27M>h#es@EjER`pbyx?8^*XQj?K;}xQsC# zb>?@@nP;RB2(Ww1Zz>NI6_dD`a%yXZJPP@lI&Wu(m?OL>S}Bs~v7W)7iys(2H+rA+ zAWt6aQ6KOc-#!BV<2>re@3>JH{2*0LV9fWN$%r9PGESZSJ?)VRc8pgWzo$J4!F$K6 z#|2k`eFD`xU|SD3d4k8tRX@GNbolr0QLNyH=%~SJbPw`aaCxxDJL37K5G)S%sOuPu zeX|J^5E%10N12Yl;IdeYU~+=ho*vf7s=z4|J-D$ur=me^JuIdAJd7&|r+Z6~uY+LK zM2}kXMRuTenPN?1K%PRd9J%>60>$2d%II-gMih{eIrREgBFsVfV`uh6-%bLV0|B2r zb~Ogla1cl`C4suHT4Q12CaUv}Q7>uW^oeTyF>FdP*fB{xCA>r7n3U-93H9s=O<$?=C)b<~-KPBM25VemuwnlL46c2aX zq%#NHZz_SPM?aGIsU8$v=3r{naa1>aH?!}lD)cz2n*`2xcw|a3DJS6EBicUbaW)G{ z@-&Y=U^>{xmg{{Ey)Pt2q=+~L)6|~h&N;q~;MSQQ^~zCfk9Z^f5b@z2KGU*c7t_nr ze_|>b_9#pCaF422|3h}p{mB``&w~(eELQmyaFmmH7#-lkJ~cT$zqXMVmx}GM_b$! zh~9-BW1@~4U@@agedHnOj-f)$`gr;m>Z2UavB)D+Xlb}%W^Ae2P9y<~JuDpVY{zGA zr(wK{DltgykE3==l(m;n7AArVVkO|D`5NzzG+t zegCBe8^PrltHZ+gf{$OUPWZYe*#Zi9%Q6qkKzbRk#azY{I~9whDt_SowiJTp13|76 z4$EPQ4Xzk&y}1RuYG@0#6iNGX&%l!CyT4MYt#FB#d5j=)y7JYpREXtCOmu?BSgDQs zxQk3Zm)A!iNlQ4%=J+PSB*QE@hW99)CM+> zh1YzH*B!vVhmt^T^iHSxP{wGu{uOFoAB~U<4q2&I_VG=mJaG6b>zeydD=PY|^g<`6;R{Mb?}bC;8OpbAZx!4 zs-OR)VIrR5Ve)>ixtPDG-#NyAEnNHU9^+ch8Y@RThZsvAz$>1w>MFan*sJjPOU`~BP# z7|tX(p9j_Q6Bye}kw55Rf}`ER`;ps<;mRKN82>f7H-u}2%YMXTI87(%ZnNR^NQ0ys z$rGk&9%aZ#^9+;Cd=+ZYK@!1Hk9k;JknP3bS7n-b(#j3e-n}vo$&y;&|A?lUQZb!n zNXwsBFOrV69em(FBv^ib!AZ zFqeHf3uzfrb0(-)6Y`OUz34F>H{H=qSGZ31Jpi}&MUR^LvuoN&F}>XoF(pqPbuF-l zKTGjSROm3*Bx^i4p+qen=Hi3G;JlaAX2B(bUsn4Bw}U+|t9psw2ln5ujtz5#+CqxZ zq5U4?b+a>_F`W0t!DYOH|Iy~0q6e0Ah~zR4Uy0TJ^EM(`_rjIFp>_`^aW~j=Kph(H znm+P4xcHFAi1k(5H@YV5In8CaI-a8rKU5P=bIr3QfgOiE%+vG&(~YO${^r1SR(p(} z^fu^(N*Riz1*C1uGZJr-f6B;w{*Y-*LjQ%P6rM82dDj8n+UP1{m9!JJcB>@ zG2njQM7kmmc^-X1uFp|iP-RNOQR9l!!EtEGE&fcWfSX}r*;KC-g z-QqgiS_w{VR&QEdv#dSf;1>0T#Wmd)@OP|!i${L+lHoO~9oZL~5}76?8nDmJ8ck*Ds3%tCOD%Z<5=P;+`EisVEpL@(xegg(K z+1t3trF~O~Q$QSl-FL_Sagx)5Wb2p8=}T>vgNwfO@NrLl)Zs&4*BEOTTt=_@*q0XZ zdlA2~R}c5raBzRG3iYFYlEASi)N()999s_9$EVK7RL`a7Oqg)R!1}H3<;aq(b;eC* ziy!o(8??g}TI*DUpKF@0&p&7s0>4QhoPfV?otkQM@x3Yn34V2Il>~ALl#Z-ZJ8Z6* zwsHa~r`Pc@Az1+(cb8c-DRd{2D*rn5Z;9q#g4WpUn3d>#+9@OH5ixLmJ=9b;dL1csSy8(hU0_Uw$yI z&Ui$ZajcVeAS+rMknp3f7oJW{XM*ih>dd{j@%$>N%imfE*N)8>c@@|vv<}ypLvBIJ zbA!un>xJu_QOA;}w%{ZD+MNL3H%ndUPtLQz;j`=byk*m7 z+{Xv~DP=KS%53$N$Xme~=hWf2$$>icm85I=C$95?I`0x$z;9+Lq*5c01TU=PsEW?T zqEV!l43{3GR*u4C?gfV|R=Y=u#R4b9szalw%U*EGQuQ6!<|w6~Ev@4_hvIbvo^*u6 z!5b2hTH@5Q(P&&II3-SP9PK*KS|c!Ed&;j0iaUI_^z+MHa??z)IB3@>gU#6yy zp|>T08<(l&U|SZrds&^4A!7)wImR_^WGP(uC3VI=U%jZS4vwMF9gurh*6}T^=ek+k zk~Jg0{giB#Ix)s|o-GdCxk}OzMN?PWUE{4=;X+oc&2|^(y}-$<>x>ON=fi>80edJ* z&2o-F+NF{>#l%^t=Few%>K(AbriL6xT7FF(haR;}GVCs!HTV_!#~L+yEICL5N3N?g z{$=``KhAZH(6=tK;M>=!+s9)0%fM0VrL|=RFSu3~gv@bPcL}7a*VoC?Ag2}19d9ld zImnrR8Mf@kI{j6GL2G}Bo(;YttuLF?k;Jjwq-KrdJ*nW7o9g&#vb3SPbDV3ktq86v zt&SP?`|RXL|88oHr0zjda!Z}@7I-jqbFNt;u_SNDi4(W6j>}+rrNkKL8f6Q4l{SUz zF=w}P$D?iWa9Ovi#pBU~OmIee9dm1~jn|002rhM#+9PrgICyiN_$+cb?NgEW!Lf;P zY`iOUdB|(%#+EwsRP2E5WbsAX%t4-Fko#rS8CBrHd~=9R#yF`}iIYN{@(guV00Vsv zIP$hS4sU)g)6<7vq6}qlnRnC~)6{LjIJ{yC8liHaevpmXKy@_0HPYG#mvEOl7T}uY z8}>TJ=I%Q6N&yoTc_1Ewuih;VWYoz3M{TV$=D-i;=|;1zrIqrL$8A-+0$ua0Rp8G1 z)jNR#4A_y70l64lR`W(C60i`Xz^Z%5_K3#^krXUh!D#+d2!9+fsySm#F6_&=Xt1*_Wsq!TsR&OVrO3um|C9(6jg$ zVvs9zP6D_+zTWtPXMKj340RnDujx7t=nUc%U#e~*zO?|{d8v9xa0NIaLG2Hsgzeyv z6{=ovpYSWxF-bqUR5tksvL?_p9W(z`r-Ok*VChAX;7{B7^@KR7PWi1nm&y5>xgYnm7HiyIBp%~lh`B0x8e$EXc z7fEojE~zM;Lb92H7>R5oIrrCNz;w^q9pV~oD}~E_px!vDb{@qP9h4M0;G&SPrWiQuria?q>P!8CbmRKva{QxD|udJb%jVN%& zEA_as5;H&>Tywx!i^Q+2o}F3!u1%qoBM+|cHMo<@ah29&ZAeSK@;=pMpk{CLjI|xk zQ?BYm>HIzi>F00O^ZP4$qPF7Ho*jv_{9wJYTn}>r3y1uvwMg0z)f;Qfh2LAIl5Q@X z|NCnDRM$M;1K{NM>y_c_3e;w<10wDHdfC+wegAeU%49i2<-M=|m`Xnj2e18?3Uz=J zz~LXL3mx>iOmKdc+6bOoDEx=@#vdma)*P3PNs&eAD~V${T+b-ia3wfGNoB6}K+ zSdoym^~N>kX3fjfD3%+p!=nyOBm2j}U3Kc~Y4nAVO7yV49-l%xA9JR=CRh{UEDdVe zbmDFW7dEKP)9Fto;Fu=0Z#s3^0d_ant1*wdX6h|^|M#hfmU>2(4eu~tnLLB}N(@|D zi#mG--cKgj-l{gvpf2}<6I#{o8FbhRaC)oSKZDG4fs2mR4;&i)?+lbQ;9t~Jd%bb3 zc{(~Qi~`2NIgZxj7i;@-MHuhOgv&UpHin_Dd%+#Z^#W>;wSu7MsM;UqT5RhF@BOTv zg;a3?)To)RAZyeIyrNUhnMoN_!S*h-W+vIp1^a)kw$CJ+CE)b0)xMcbZX3b9-_#q? z!_??+sr1g7EgY8aRe5p3}tjVsy;@Dk<7OWa(gXgYkin zWGYD2oy(aGu}HB9st^>Kw(23%R>>pjNTNdy(Omxifhvt7$zJqJok4}9fCJ8I zVBPGe?YMK_4=w0OH_At9Ik(}|bMtf~Ij$h}QYKF$KV;^<)Xcru*$yNn=SjWHKj56K zj-SC`W^pqIKEHtl3$1eF=a9cBxa{Z#*1h#ubnzVOFBPtAL4&cvtnGzEwVjWoHKxH> zWiDYnD=AdM^)8Y;=pBlNIk;mzaJh>cj1NuK_-l@gLDeXHYy*ela1(=6%qK2C-L{GF z1xp)@5H+kB1@n~}Fny&gB)Q8P2An!6a3R!QsOQbd={}csDqL16F5J zwe}h+=h6mN*<@ez+&gr-=4>9xRwO@>;@}{qeU;(gN-PI-3UQK_tE(c!;{@j~Z!p%W z&;N8yRC^*^qpjs|l?m#l2;SNX&RC&pBIy4H623g*G7hl7FF0;;XxQHuM=v)Tz zByi;#wS2B?nY9R-JPiR%IGzgw+8iyFuS$8T+5lUm=Q`P*6IRl!x>BdQ}K&sgtKa&bXAko4W7 zn$L2bZ40QUQ`{@6&~$cAKtQ?K3ZkMhNZOHnW{zB!Mv`VKT>gCxtd{E0?6yd{?q0aH zS!#DArKtjEZ)=dbIPV;H(utNHB;DH@)Mf9vCYl=u4H%Q{4Hz>$LQk2Gszk%3?@;sR zOYy*252*F?QI&jf)dLL}=;t1A1*(1XnJZMl^*^BY&zDLBXXG|;fCeasR?k=`3-m(Vx!c$b136hxQ@rw z=5t)!#{w5W(V*UY*EPjfMZl*(vZm|qV)HdpfF2~_1r2J;dqlH0lS87J4)}SmnY~}4 z#UKe+4aUXbQ@hPJot9Xv6yhW)-uRkcXg^J&+MxRT?$AQb97V{~+;+$vYJ@T4#_~^n0;{nOamEOtBaM{j3 ztljQBw>fIYhbxdUNByJc=4p^-%8EH zHw;%R{YYTSd9E|9$&ejIYUO$4GFR{mYP;YHu>b37|9L1t2Y7FJ1HUDyS1^7!4+qY_ zjo$O78h$>`NEA5#Z8hh7nlu$$`A&oPzRCpAX027y^!dmN-fiGp`lh0s&x%SVTq|6_ zFtz<=2JOc4naK1&x(|p(=@L{}9^6??0)ck6E3$GMdWB zgpU2Lf&DSPy5o#?U2g4yOZ;Aa98KN={>@{(4IHD@YY#Ioz=S8jmHgVkkMZe4UDpVg z4cGcxgB($n6`RN4tQBx;e^>i2aILiYx6@L?8jZKr&hx0pkOi)2TPj@9=tlXNi#YOk zE^y7TmcXS4t9=V-^;U4%q(-cOSnhzYwRY)t_4yCVIl0k0oSwUAs3VG2L?X3JY1BWW z$>|Vtb!hnGS}PpL6Ok88QOg$6PMP52#4Rm#mQuL1X^qAgYUl6VVmq)9|EB{k z{Jchf9YF5~e!tK)#TxuE+Iqek9fN&K1P3gVvgku!8)9$-a^Q|HZj@u4578~3jA7lT z5-#k*Mmd)JDrq;yNYlZ^Co~#O>b3`n_g9Q-jDN@{*v?gr##AmCnOk9V7fDZt?tZC} zU-s6AeAX?Jo(`A(PqldwDp&#zDOLLfw}U(OtNKL@6_yT~u}u8|wnc)YUTx%*i#T6_ z?+sO+C|rt2-%wXAmO%xa^hP6-(@v@J5QHzG+r&}eKo`{t)h z=r;Xu6$jMM@?^wO-gvOlc-6Fii(*}4=B$Ntzt?!`BqMW3S+bCKBi}Pd?e0VO9^oNd zDP(ek6iw^(!C2a>6Ry0e(YV)q`46!a$L}-xbhA1UONB;*6Z+J=r4%O>oc)Jdvy@B~ zfbD@zC#{_M>Ul&Zd@FpC&TOznifIlPQq-ToG~g?gab1umJYZoi14mw^2pYM;bu z2ZuORyS}BX z(m0M7PJM_IH4;4!BKWZ;BinQ#pSf5@2Dr4xo0zo8D4=RDW>umKt`aUnvM7T3rNYhH1@Za_3g#RTjhb`tSxcyE1mJ(|UL2BR$KWiF- zyf@VPOK6O};H(O@`x5pE+QEefRQ)Bai}`;|u|H^HJkjpMlz4PJ8gA`}YF@nPHMsIa zwI*IBjo_XS)%JMOs{luQr1lH$28UFuqw%bhhkb)PU(;kfrK@b*Zs~SXy$SGp;Xg9R zgLRiuy;*S49;uQQewdR-ge!%MuWOP8EV=jc_N7dWI^dG)RP&{bla_A!S)KX=Y>NPw z5cd~Tc`sOwaY%;Cs#hzQldoKGLWA1AoVaD+oQ5WTLR61O?=Q#p_P|9qH5q?b!}c(2 z{<$1aH~3q2z?;J;43qIMJ4zY^Tr(qvr6 z%8+TFn=WI>>w^yYQ5}=$;os35KdI0Jv4r4+UbQ%ZzL5z|{I$ubGd1dvsZr90i;(pG zCOWA-la~`{9Jf5#rw%2!=FRE{C;!o8e$Rbiadmi{)VW@b3i+O%^oJU^f`*I-_xzz2 z3(f?0{;4*u5Zep(`Kt-*uMOWDD`UBaux zf?cOyslN{%Nxa_S&1&{a*L>eJ0{*8ptLs*>4Ol=RV?;BPG}$nl5H~Q+=w&2%%G%6Y zjXp}(w9+-+w->I`w^@C^l6}Xp9|(+UR+Cq`W?B;oSVGjYRkDf$_772;S5d+)S(33J5oy7>YT4zeM;5sK+~$Gl`NNkplPiWRI!`@y zIWxpAaO4He?C6<}-i@x)_i(P+H{d7KYC*Fcvh<#wjDm?>pyphGZm$KWEoeUZIEFPB zu494Peg$tW2bV2u_I?UutoKtG4)WHCq&G$#xdMw|`I&)bvHC-vhy+J1ZpPlqj7}{| zLRpjHdKRmdNlZGj!M#hGmGK5=hQ%b5h;WJ8mqh)wgR^2)eG=Mf`Gu-ls{Rl@3e0qR z&XwR)aO4WL=1Q`k2aaFS%=J`#XyWNBsg!cK{FUk@;@Y~vjmgd4V^?Fn$F8Ks?Y%f$ z*DC8(w0JZ);aWBCDpF4a$E;K9uc9ag;KY<>eg#mrt^!ib9vRQml}L&=G_yI32ZXzJ zl`F#L|6i)|`ettCnkMvWF`;lhscPQUXkxO7R|?(=?nqUeujY*<;L;oUi5|+_3bx;@ zYObbI`@r(G4tQJGuatRXGeew=K;!f@SxS)r?}q26l;!;AwaJW3nQ$q$Hgix_tVQq* zr>ayD(pIFy^^QaIakCFNNXvtyBBS}#iy`K+hg`qc=KDBt!ftD3xj;5A14j?JE|P-d zH+n^;ns5ycbR5`mXEUo^W20Tc41Rh8J{>k0mY*n+dmr~)!yKysF7~cwh6+*Ku@9TI zEa_DuDZg92Dd}~A8}DvrFDLLI-N)g?pH_TY0K|e$NSYEYg`xF62R@b z&FmKHJ%DxB%CH5uH?Nt+205^*9=VqGE`jTRMD4qlH#UNkcB{kJl2$Lc>{0dIwXXAK zMgBn@<~JLel+b7Sz%(S0OdQr9cuYi|ldqPorBmmFqaJJKw8@kmuHeabsT=b!mGqoS zkv`sRB+1-O&cy|9^Cg1mOG<>}L`Xf@%>6nR&w}=>!*g)_$=g3@W{X#kC3Dx2>v%Z6 z483?Ab(0S6Mt-3=IoY<3(W@BF=OgvhI*tHzf|IJ%k#&^NuOIEMRVUUl(TN3j)irZC zL;Fap*3%Qy;L_{W`t@jhu8Fs=N6X8>kqzo4cv~yDsG*q&u{?KntF(no*81U-8=L>n zGmN77A%D@^kPkN}UT0p1=EuP$HZ>c~W)HmbI`knE&WA<$>&R~*IJ{Loew}N9Z-+ep zakD-Kug{$ILnME!enx1EIYGDjq}j+bSGp5YT>idka1p~>)O9H`h7rgf-oirm=N&BL z6{WC#S`JqUSFCr@0v?w=9n*|=B8juL813e~=CF8#ro`W1I_KZQEpBruM*60q)-K!Do40gF%tjtp$UX}Rrcw6AT0Ym9F%Tv=d?E|XlFw+8t5 z`4k1Jk=Ik{vEbf7bsl(RIyibli}9_G+IKlFPuBG=$DC5gZb;5F`S@Jo)ytd6DhDTI zF!CVx3u@u#n#|>ej_dJ*`XD=k)v@bo;^1KziHZF75^swGmrYU&Zot50f*V8B#vA0^ z71%MQMQ(($R2DGJ?Chee2atq^w(wihdg!mafiBhq*ADlW*}?yUJ0oPcpHIo`7IT^D z7H>A?td%#LG059RZg$lbsVI0F97Ff|R59w{{CO>$kkDQEg;d^C1(z|uW#E!-Q>wUZ za6R+YF?lBJH0tr}7Wu-23SlS~HGhc~j0&;z%3i73y&NIk|k#Ny}Uj&0GSqKdo;b&7+jjBVkzzf9J~Hfx>H zMmPgW&Qf*LO$=~F;MR*<*x(d3m>?URCmptIL|VM8h4WiEGe`Cl*m?=C9j@k`PhLme zOvxigP@9*wc(*asA~@?>xUC85@|)>0Ibffa>fW0vOBuLoWsC8W&e6E1%^Zo_)`28` z6^h5rW%a|&OkeC)Kc9jtTKIU4{?N^|G`Kjp{536{6#4uP>f*{Y>7H=ex2uh5uCx71 z!MTsL7@NioEJYn8ime0M{cH;#%-XPx$9_noLH+1J{blMznk#f<1UUHB7UNkTnHEn_ zF}Gmm)I6&mtFWbUCb5aAj`wj>PQ+XIHE5 zHu7$Vje4wB6K%7{(zvWlg>8Cp= zWTo(*wkUPn73v3${IrFm==x&|$=F(%ZrPU#t4fn<$KykLuKq%8@q2+pYu&)5t~kLzIHM ze`>+ITzZUU>vy)W@zV|$^Rueo!Wa|GmoU?Rp;PD0V71wpXpmpgMBE zDgRaXf_;m@j$d2UXL1a+ECVZ0WitHYkhY3#8O($G;5@&oV;K}Ne2kw@!fz_{HgGa{ zZJ%0s8(GT%JO0#mK|UVUWiF+O#VSM6`&SExJy>xKR`1{D3i9oSi#WZNFMV=B%+K!U zQ%Jy&KsbR80#k2y&Gbzo;26`YF1y{ukIIRJz!oI7Vgj9GTQS~ZAcNjFYgbG|Ba(vg zt;U0#jxi_1pWluxu#82CrnhpEPp+$vz1FN?83!VfM4hQ--Qk+!p91bZtJPS2_JD(9 z52Phb2S?g2p9o>sw{mJjdmYc-A=_JU2d-E9Nx<3-E=*NN@4!QGj3e&0RyLI6aCbnR zxsxcVBp_*bwsN~#`1s7%)U?f9j=MjTiZ6ld*{$|vif)0|KB?*j`;Di~ zx$iAF8hqd>HSbPv8rZ*3t-sSX?~Fol#WSt{=XYU5uMT+SOH6;J_D&Wdy5T(g)KN(* zIDmAYQ={)fWun2J=hVEr@YmLYlb>&8V^I5Zx7>wCodb6qZk;}j6A*v;p!Sv_3ESVw zx{;jI2?!mMbRda-yA@AFcAC`>q+|69r1suZC+c09F66%IHm(i_{LF8dQ3={vLE`?Y)#%qlp{m=8W6}e+ z_vcpQO36w5KxxhIB1umDgU~YM|Kr_Iy$3Cef=l_Ol_6F7%{cGz30!d`$o7 zk^4})61a}ZYTtbnsui3vy^U25T|rHzVUfo3nS=s_waHgq0!POXc0d$_wHZ4tYH=Bh z$n&#YQ+-n)yTZxie6E&AE95~WpP`&t48G;y{4>-`S+Y_9_L{e)7m+$A?FNY}G{ zGD9}f6*`*%3x_=T7$n_u+n9suL+vZJQGhhK()nurHWpS3!F?CD$tI2*Smc{E<86&_ z#TU2nB>yvn+wv$?T2;*gv3f)e1B!TT$wsFNstkSr%%>FCY zk&PrOrA^5)&nQuiB8ae*HryuZmI2GmZYdHElH}Aj@jqpM?NfP4A6(vMb&Qu-!=_Lz znQGDwswD{=c~=|ju-bpUaR+Uh0~dQw8(Y9)E+@PuZ6b9gX_q1Ey0?v`Y;BO=+u<5@ zSqEJ1eQm}%DMV7TvyHD5n6;lRwGUVBYGcb$xUv5*?SbegR0y zg|MmA^aE||{A&Au?oJ%{wQ%u|w;4;kr|))>S{__-K^vzbpL^5$1&0GdsW!frB|59> zc2d9HaO_$i-AQ%>9GK^)+c@PU83@97`+$5p*yl2@Zpc{?bVIzA801@#e-W$} zyD8qv9M=R}CS+tMwnmRtfe#JMw2P1wf8A!JTJ(|WfGdYe;6c>=ooJ@^dp^w(t%u9& zQ^#^d>u2Ed{h=n^kBb`*=EDPv@5hPD73_ON?YQ4H*LHwFvi}jI&Fr6b_e;~l`Hxpe z@0U?JjAo8kqj!;D9JuT~34p)?L zgv%^a{XyR9muBlk60rJ+aUOPG@6reAg*($iiSe_~M03_D>jUIC5!|)zh+J!%X5C7_ zvtDg}fZUaUdp4+j51`+T;Hc}5NS~2aw-{4JL<#zlB;TNZet%5X;d8+WH>vHpR8}c??@j9YTr6rU*oXDYT)dQCaO2JDJL20Mv&nzj5mtxHRVO~n zW{rm{OjC;=M4k!Gxa9~}IbW@3Jtg}=SAeY;t_bd=FGAQF;fgmNk$qlHv;-6mb)9}B z(VLF2)TMX8M?J(F!oz8eO)B&ua-0Ni+^m*AM2@$DJzLb~hwuu^z^%6*kyB*faPEv< zGk^aMxc1B=hGn3()ejG`&*(UVmuJf>gNW%Vh#JDO{Z;S@-p$mD(FR^Szh?CwrVoWl>zxqlZezF@bp+X(X zV|~+pCXV*OBgQe)Z1Ytru2I7sIHcx1!YGywF8SaHD{Ie%`v|M0dJt%Mgtgk^aJ_IT=0GxRw`;aFI)a|#QS)|-uMAG8Q|ou5Il17F|3}xk zfJIfcZFnQYVdOL_=!k%5sA#BYR8(YIR8*u?R%Uoe(agx``>d$&kZcbXDiyj&$*`!X z$gC`>$gHf$D5*>a7+?@9Dl#i7GHd_$yZ0d2_5E{Q=(C=8?R8%7Iklyp zZ4SmBFCYGCRj2e#uY!vyF=4As^{KHI|rNZSjs&gME zyxHL5Mm6tY;!y%FZ&Z&wOk8WhwkGu<*ltV2e)Ay?C7IVJhd+|PEZz=Mmgb0q6d3 zh&f-b6B(d#67&7km5;J8T_A!V53%vBNAS1^+z8hFbckyQ&6ND^QL0DOL<;+{L!1|% zuwJ@)Mfs=O7r|Bic8DXMS6$C+#yZoemKRCJg42IjJBmnbH+bFehm5zq&K2dK5s+{O z&i!#n{dEm1eKRmbF|^%8ime34b@9d%xEx&5rM7J%_YQ&!|5pEOBJ~23=wkk3VtWcW z-Z;elY%K3as%e`kjX7|ga0g9ItM1>7hx_1G+UnF>oAW2wPhhC_t7GG@nf<|v>`y(D zBGI>wW!RUWlKUN1zB3>ft{|jN-L6>X+=*dVXq|diF>~v|aHLZD-+j|2p*- z2LE^rDFakYF{&sXoI5~WSWIM=f_IvHq&Xv9jF}reHx$cSJ-B3$`mC791)ohdaH>9! zF&&=qu*8n&W)^NuFc$c^x|Xr14h@vWkuB)Sw7T@?tYf<98>d4 zTYC)ouIM_^LXBjg&lYklKA8c=7!|XH9Gd|SK0{62Lh;hUJI}78`5HDtAM8EECiQJ6 z0{y%?cIM0NBN3@q11u`ajimB|I-{S}Hme`EE&sO5l z3C_8)j=2@HxARF)P-D-fh+SRBF%+|MjW40dWC(Xn9sLKh37b_SKEc&oqn4C#j;#b7 zKfTUqlbFo)hLp|N=6I45RMOkoi=9sFWQ(1XJJ95EL+gZ<2YKqX>JaYv+sY!AobT*S8V04)CHIOt-8<73Wh>Z>VFn(rhb+Ji$S$ z6u9;q>eTokJ=%XRhSZtrx+idG9XNZYTK)t&qXJwxvyPWVO%?RU6Kq0jhwHdeH6srQ zo#U2h5-=oZ*2%$9IRKoz1FJSg)zUsRjoD60icieWWaV|LTKWfQ# zQlbp(xKnM}PHOD~YbGAz2Ryb^H3)5+j5ABs@$J;UByi3KHReg2oB^)csHQ$i{;mY? zDy(C1$tQIW?^Vc4wdXh_Tv`h=&b z^)ui)iq+Joq^5(lEp>9KjErVBq5AwQ;i|T(9moTYfHR(`Q!jn9a+tZE@3@eT%X7-V z3^gF}TW?>e!ZGowwg*FZ-1JA?t@$TikkWi@+07!SL(cX z4p~=U*;47lyrPajji2%0lvh>EGotFi>ATGD{BwohrIz4^e=#^=m)an9s=>i;sXcynj$=X_kdLkc=eE=tm-5}oir?o#Gc=-4+{nw3b5cPjE`BUW z-vwSuzPGA>%1MfNFjM+5&(VR%1g|?>NBeCi{VmVQBqUsHTOGF$2p7JiBc)8A$*qe_f}3x}I5_Se%#vapGGw z^?4$i2QF@}V>H;>ec;!>94`0=we5KVS`E(rLG5{7@(CQp)cNx?qY0Nu2SW{ifx0*Y z98CWLY}dh)|E!ZO+H~XxFOl}q>Pky1wi9XR@jBxZYv;G~Rq6IxOKuV{58|S~>x_#e z*5+AKp7eP^qH{STz`xb;7o?1da_pm|w@s@*~4kHFN9Rt-Bur(H=xj8?~YNJ4-+Myr^Y;M2hF z(Q1<5Mc|szYPR5Y;HuGTx!`he#b~wdCCYCNc+nV_kw$2H%I`&HF_ZG!fxH-bly{Z& zCCaaJDh<@>Ds(4BAq{-ubfyZ$KQOOZ&E82_E&)5ox)|!14blrc^CRrFaQonjEv{uJ zoqTN?b#0vbeJ8~y0la9O8vZih&jlYG@1k^@kLKL|G6$Xt-~tm}#(b+{Z+)5J(=ND- z2`*;Vq?kpF>Y1Z)i)#~I#(?gQ(#s4HgEJT+p5@}it2rEueud~H!?DqP+$)rwEb!d3 zUCbPcn(h~EITuSp6e7t=R%>1%!FPdclhyNrUEqXs)MtWEfXmNOzr2FJO}L8I_goh< zmhYBQ3eR~JJ(>wue7?H+RU*3%TycR~E_fHXaI$*-RTA-_h4;TosXhT-H(C7xw#Qvf zS8=k7BR(>P*zZ+q?o)JIG@{>|JY4o=ylsSbNC(GX?qUaxIRu~e8sF!_aiQ0e z*F+nGYp-<~mzwVPJJTA)n%Wpi=k@Bt*N9ISIPV5^RB-Hc(sQO7_&Tnqg7?mpT#;4P z@V?g9(wWXdvMbX?2V6Rr>i*Yh`O4u+=BjP4Q~X@ug85c)?*iw|S4Uqb6`j|Tr&%g= zH#phC<96fgTyPwRx&*HSM=|@do7QF@xb$WhF9Mo-puXBoS?PrH-0otcLS~UCd}+;( zNe49cI#j}4uK#({rNntM@_l!yvnugr5%|PiYIY_0Tm;U!TRkXz6?l<}V^nP=Q&~qK z=H4w?Cq*x5l~we_JID3ZgQTzINVAri*}&4CbGG#4EvZCGn?W&K<&quXvVA-3O(`O9 z!TBoYO{Re6g5&aCMx)gr*O9&1 zQ8!RUHo8t-OtMZ%%N1Cf6z0j;Nh)-)OF&`~zRyZ2kz^xje^|mG61D6tifJ)i$)jq+ zTliEBc5hO9-lEWTfRi`7Sa37r^G+4<37<(Z*sMOQqB19gGZoJo@!cYD-eYRE@CD%F z$1FE?f;%5O)eVxpS z&T8nlS;;2}pNAyy4O3^yl-L7ey9`cyvzto_?I|oZNVo0$}6CP`GKLieCS(!uRjF0M{8mnvrM$sc-20bKcBYQMPIJ-S(Bg-{A; z1@cPd|LY=;6y!C?tM{t+_T-;CCU_RHdEaG}^uCPC95475NxhH7ej;}C57fwaNbc!i z&j;#q!MWh557iQ|y#$>5p^FPqn6MwDD&8R;)o>l3sXgyd*E+#_52)|uJLhZ~!UHOF zFCFbvu;X)=dDmQU76$Eem$7GXcmCYDmmOe5(1qXAGdRdo2fHkTY~_<4+y_|jKLWS2 z!)#gYym+gXcDSrtVtIK?UaE_OE9~HvAv{k5pXgAN-lfv#fRFsB7QRbBO2CyzUFK6* z=KBy;5amZ*)`OMZx5!^&<(!lQw<%X&;E>%m7Y}|in@8rI2VG?4zx1pUk?cF>GWwiG zl+4|XBSLVar2S}tdyyU-qF#TG2JV=6 z6jjd#mCtX_AGG;>*%f&c3M8hU-E`)?6XV~fN~OVZh3lmEX(g6|kHyw=je_(s2bp*L zi{ldRQe<(b)f=OH&9i_JVbZ|$^ayK3JW_r5J}KG>E*z=8e;>u2IFF(-s-Asn(m*)x zk|NZjv*sc#JH4KZ9?Xh%+dlNcIym>~YWY6ubtO0|MePvW4n8qSHSZ&!;q&ofNAJe! z*hPd-RwPTim5U{*OvkWObWOc+?r!-Ba$&0F6v%2?jw>T3+z~4Z`^Da1-0~}{1p3MxVRPdyj8Zn zp88q)ksQT?OMbAP=Eq!J`1>PtVbnr`09R~v>Q1kwQn;8QX)f<(4tlJnIA9LJi zGGyoGdhcNdb=Ai*w1&&vs+N3A)XKm)kJo!o#SCgUv(jwUYLKLDt2ZW`+>+DuG5()` zp7CNmdjza0&;86YCoLjOFW1YlL}r=$pCe(CEXjl~f2E#@VKe-EHR6R}mN%tuGO%fWej z)V5EE%t7$vy%JgTHJGnI!9Uw#RN2SsxX2U1`cL&-nlJlW24~B0shNUDsOC>)`%yNvV^jl&#Lb)iCVVFI zEO5ceYU*cb*p*qzvrg+T_oew%BLl25x&ZrwgpklO{Fh7?PxO8yKjX8=5|| zYO7>V)NM3{7c@{@UzVBNu?NWBbhz>h8{}FLX^d|;z(8^-oFlctdem^`0Y+*?FdWK$ z@BjnkYH(hv+H(N4eGIH$(jeRCNSE+?t&XmQB5DbKr8RIP+OWf`qNJe}U62M_a9M-( zs&aS3JJl+K^3@_!u4dOt=MpS;N!8*{71()211n}`!TPk8^lyi&n5vp<=@JLtPF_q^ z|I{*dnE;+Kt-(mgy^}i0n!rmHpJJgiu%k~?mwZmcyA+(0(I5|&&_bxypEIy2hl{$J zCyMa97TkGt121j9%R3>Xj$u54aZ_*S`xKgSkSIEGP?py;SVz|7F{sNDVWwZx@c;HU zGOREDXKzCiB?tLZzIRy}`k|Gf;#Uch_!}B}H`QaS;GA$om6JE)s8Ri7`3auQg(v4I7Db5Z=_FDWQla5;+_xI)*QPx1yQ-UL2h@EJtUvdT-Tt!d^dlX zy$r*#bq&TCv&4j(4;@<@uWFHWKF}aTtk2k8^QHN{ndB#sR6f|yy^lTQ5G6Q%DFf1n zRLmigJOg~>p$57d);jSac0{d%%UrLPA0n_7;Ntb_N$mO`1edN?`wyW@YQ^ww< zs#}+TjIGDE{i-uYI%U!0B+crn2n+u9Yofy97!67Y*v{Vcg~< zm8}M%>&ph3Wpl*U)IhMi;Hth-M;jPUB-}$aIn-dhZtd*nLYiRz8F0Z3YHB0NvJRZU z)k%UY!3E!_9gR%!cY(9p)zQZMahE6DOIc`du->)o)4eB<)s=Y!%)2I(fgRg-4aPO6 z^qQuct-q-hkf(mH7P|9ChL(Vp(CYTc^iT>CSHDJeMoa$KfE*0d`!}j9cCQ+&R<=k} z1ykE!J=nrMO&)OSfJPQMWR1Yt>lmfvy<<5IWq6}pxWK73=N;ljPw!wd(xrnMnPHXw znN!9sme8!uIr42}BX27+FBPGdd-6l=C2(m&8aa(;b`oCnpfzjYT+xl}org2`kp%XY z=yk!T$Eu?qYC_x!ru>FAvJ1mn$!O)f>2Q;Wo6(mJ$AVT?Hwxg&;~M3aaoIHaIQIOj z;MR>$ue8!&wu4LPR+uCs71x{g>u?_cat5B!sHX3gA?ydPl&03fURhH7o zI|7pX{rs%lmrTn7_k+A*G-ZHF%K_1euR=;3CE*-JCO5s4XyYz)hyVVPbs`w zg?>#!rGk%Lqt5-BTkNvIfzum}!M*F5HGJDb)g$l)2YRUZ2-7k{J+x1rJagYCK2JuS1KdGQCe z_IM+UAEp*t|1EX560TsI+VL&f-vQ1oZDg{+48q^vl5);9DDsL$HjZxFtgR_Hy|_d7J*biqt;e1{z!99ymK{f^353y%A^ zk^PM~ea&3m**)nbsi>`^B7f4z#R=wV|BoBEfFeX!7@L#L9$oeb#7v zEel@O#sA+E=>q8D!;Nz2NhZr4|DH%!!(|>(d%hv1apG}e{7^SpVa+nWQy4Gt9wFW4zo(p1BR}(6zhy^Ryeoy<7vfgxD0hZeW2nLK&QV%{E)3}oxB5v8@#`s<{oHEk zPx-_AGccqMP~&{`;UN;oJj}V_ZtmkUJLl_vBHhd3sXrG{~)XXUiQ~7$)P`9cF4KNh^*OapoA3A|&Q~bN6`1dmVz@V(r+7oTNV_UBT?&xtAKOGxY=1~&iB6cGb;WdT+UFn z|7VJ%R*2t2-SQ5GEPRLjLej;<)$sn%FUY5ZbB4L4uPzrZoV@E>l6@tz)YII?D`v7M zc`rDW7*!x~#ks9#lDiYmyK7s%tU(?)!i{DzGptE`?Sji2sgC|474Kmx)hHG7tAqgT z8RO=9SW^ov_?1}caE0S=-{is*tz{vp4LgzSn&75QFegH^p5{6hsRwD;d0=(FIq0!_WKQcagWk*x>&w7>;0K<{?p+mr>V<- zBibv$ahJK-TET%wc8~nVOyf>C{U$dTdzw9!`zb=bdK(_32j{t2C>-XmM>`lt6ZGIB zI(AuZUGGP7e6UU2fxRX*9dHUtO6W zP~gAbZ-Z^4udjL1%hFVmb*0!Z%XQP@nOV21C+nnQ)FMs2SAB@f{$1dd`_$1t7@j0< zCKP#YMx15=zo3f(J_9Z-_MTYOoU>}7<@eo*b{;(JYz&Kuku!j(!B z5g|p%N>r(OiAb{^RU`kzrEG9{k(<34X3MecPYP`@9H+N7{7HvlAK1aAbANJAM<+OE zD{8NM%o|>eqTK3cYn%C!;K!fl2dP8`!~UrV3ZGQx9;aiS3$A)fEjcdj3OJ=)Z9C2o z>!5u6yxSULN&N|Y3?=rw`Ujys30(S;o4pcdurB_Kf|Lb!Y^R#}7mgN#(_dB_{zCax zg0o*%JO09hcCdSwYW|C$hi}1+-Ky^i8SsL+U~Ak7HjT^$?|Z{-+#S8tj3O6*)-n&64O}IF(x*t z%m2pfTyT1`S^^Fz0hc$^6uiZyi{jN@2wRKWSZ-=OXSftZE2E@%978(2)y+u;Q;+@q zH<{xsp(}e>h5kb}XMnT5b{kG>8|>VF@`D5N;AVVJn?fdykHwJ^h_vtB#+2UOygr9d zdL+tWFLsvx;I>}v@rbKGxEV5<^YCMO3Xg=z@i-wq=9YnlY%UpUNcS4<#4$C_Am_5c zj$hpD79h-Qoi*g*D!6L6q`GT*2>Zk3k`{Z6o#maI+O!cGYfj=-~lLP&t3n zPR^svo{nMfpKf})GQ8;bkEPWlax0OP{k7>Xohd?K?TJlWfYrc~zc=mCCj|sQLF?S7 zN%{KdBLb2!oWL+%3|SZw*-(6=k3Kj+_ZsfQU@x-_oR2eycdz;AbP?cU1Dm?%o6HBo zI^i5}y&iM6$Cgrd>`l^YOVcy9j~*0|2A64XQWx~mhuL#5#Pw~W>mqaa&bXfaN|F(! zNK^YYv0C9X(EAY8UZ^anzN%?E{U#5_=@>a6M_M>1pITxZ+fkPTnkW%g?)d8D4qQ#R z8t1DsHv)D?soB0d`$&Z6T|Zxaxc^RYc(h<^SEvhfX0-auS7(s&Bt<=@i6u_yG4;=; zTMW!5WW9|9tPn1tiFVZ-Hoj@o*??!K#c;)X$*pk6jiE5l5VaWpTo!G>V8|GGH)iOUFbp@AwhT7mq z{HnnPNotSaE--V+M+L{1!C#)b&-Y1!7D$)=;2^Rk>3A0RbPcNrHz@BC5a=Shz zKnK?@Z!&K`^)JVeutIIK<3TNW=?e9soxJT7ODme#^=tNA{QByH>@mg}t?quXVMJ6#No#;*ln^dt!Eft1NCdmPFm*>zRDd}(}o7K1=bU-G!YqPpKNFN@cgKJfjnlqc8W?>L- z`;}P?hXJI4o!BDTC&8$JFD`B}SaI!{L1%hO-inh_(Sf`i`Ab%B^zR^I8udIQ&TUQ1 zi%E@CV}l848eCPWniNd7Ewb?JV6v?k9QCx?Abce__-VBxn9#bxuBX)(*s%w{z}Wto zrtT}hqC;>m87}i#H7-PA2A=V(x;jK3IdL60@7X4!%2xucY2o~pz;*q##<%VtPm&57Wo3NCv0W^#oE%RXW_ZKi60<2cO*KZjHG_o{jtq?T#%x%5^3j!>aPB%?+UOpwV6eScLVjPh)d|g^;%N%AU&9>4vEd+m$7+qGgk+j z!~W<2`he)ka7W;z%*an%pAP4No3CCjVo|VZfF3kUqE0MvJ0*d zj+eCA7aV_Qk8L-SE+pM)Ad37LobB3X?p-xUSjIrTpMUIYOdDLQ1`eXhO9NM2t0sXb z2NDqh+yZhivZ|Jcr?zqJ64Ll2^5A^Q@ z@4Z2Nk6+PIuQT0&-J#f#pX5U#Tqj(Ul@C*Ru_-bOu5@Oz;b+>ix?{O;Jj=DjN%L3= z-gTo?OrKk5^maJ)zLC{%>6ty@kdBrc&N)kcA>Y_`lOLzAWuM1YgY^Lcu^7)keXW`{ zSdR!u!Ejx-VFrfVy$1VI4267UH@~tMV%#^YS&UNCD==1KWKl_OTz`A89_H_c%blyf z7_47zPpG79+|taYPUZ>1^CR`JzL{|A7B?F_c^As&1+^qn@9Vz~vSPVf9;u%jPz_EQ zyH>pusSg`>3`5p~&Bia@l#mp+y@A4hsM%mInE8|3al%zS#EU_c!c>tLSU3lKq=2O( zN?ZvzcYQOr@tNxkFAkyUt%l3oV97hd$2K%`3AWUT_{V$7SnQj0kv29P3#5O;Ps&&l zTy0^qT)D(5i@G>U4~xu#%XzHX*lyagX4G=wg14w8Q8ex);PNeMPZXi77Jj>l?Va-7 zv(56(&2nZeF19uUNxWj;qUn9HneK-v+1?{bN0Pj&*?SGJniZ`FMd!k${Mc-Cdi|I3 zRR9-%w3!tRb47%!wgU(FHd@n9QbzV7FCx2lN9)7<+c9+fteT_g%{Z$F;IAq)2Al>y z@vE97I2RoCn_42c9GvpI+7?5E4uT7RSNmhAU?;%szpL4?Q~>AO*#AR?4y6!I2A7RF z*-~U;4Afe9>&R?~ZW~HtQUK@lZSlTy5hOvZfQt8RQBMukhpjk>VKPog9%$Elm)(I< zGL9h&_iM4lOW_=F z&&s5FgjzLB?|)hu+{&O9OL_NlP>SSUWO`7G+C5AkF{=YZ`RKK$ZhXO#1V3;ubuzfc z=nIx#EPqjOm2lSx(d{tL+x0Q8zOVu`89SZWNi~mZta!9%{=Cf_2>d)=JJLr|F^51#oGk5~D`*C*PF9O^4eg7u=i{ zBvdWbzA^|GlIBSufC78hR;&G5|9+1*TRz0lu_R2{nDW-p=zyTGdINP zLH4C^b5mMq1Z9)wdF#3-Cnal2k#(NmVq|y~nsB?|5-(`6CO^d+{;J_J*%K8~ez^g3|D)h)&-zLlSpmlC)%xI3)I_=ORAh<~+wf3?~( z0=3);PMqGtH4xH>^jjxmajUSz?xTCe(Swnw&Qx&Djq2Qy`tUK?;OZN_q3_N%^KD1p zDH2Nwc_DT>Gu4`rgm5odpQTBk9hG6aKh=9 z{gvRQUR(~Y^5R;sjng^eW*0c!i(@~c2wFHG4eU->t1cKVlTH}o#;!GDtp@>@jn)H( z6u})^+F~7{B{EW?%i)4)SfAjdfLaVXhL1*b=(H0i(MOt(OO_*k>VekYsZbXy*zrRI>*%K_3}V2`=&Vs8p5P4VP^))?8C9TwIjL(0Q<7 z=Y7%YhjGkaIzE*`<6(2~_J82UjAx=P1FmSShY2}zy<>>gwo4_*L*kt1VLC_ptPwl9 zRe+Tu6(UVl&y1&MdJycm%wtG}m7k<#J6sLiR5fJ~JB&|^CnAo|s8*M$zKK+yWN^t9 z9uEJRr(Lg1r1!T74rMSek(@397f$1;BJf_YexrIbkPiclwDE}nFoD8m6;gPLja_jaLNqR^?7F_Mfwd(dG zD+l}Q7_@w~Jc&Y40Zv}+;aYHW==Bbkhr8hN@W|{4bO#boq%rG6#>T~?NhDPGmvr0< zdFzM*GZ~y!q|QAPel9p^vzmD(1$7~L@0r)9@EGmyg>ft~w3My!0(o#Ho|4XYW& z60f}XB-&26aK7oD$PmBx!8zc%qaan%1s4pL-rH?+XOS$Io9(Ci>1^FEQaeN-vB+)~ z^C#&O4mWu}Z!t+0gLmy$8^GhT!GWK6PMNh>31@@r4y1fv1gCvsezRgPcslZK_a(pw z;pW1PG<~%4vmGw)U)%|};(u|DI>PfWE)K5tUtBU=$0wfICua<$9H&EeVR4>#+@0cm zbD?HF>rv-s(!)*uOog5+Ne|W!sB_QNW3I{q?>yi!ypznP`?+uxy*P2d z0InR4dB~m^qH5&!2vIi`$g7dF{Lk=N-O!#NG*-TI!@CZ8jM4Jl4c-oWuY+zUkmtIn z(b7oq&gOY~aCCeYY5(NqTt&3CKdC^ zyq7B#F`A7Sa;KZHNvERQ%}euDSlg;VH7$j;jVx?vO=hSf3&E*P9&43?4w2=7rb=hF|L4xan1w^`tO$^S)g>Fpi{CgxuHJ9#H<68;ZY4_DI5 zo>|P{%HiHM$A#uTf^4X?pjPgikepjlK*{Q*3rdjY2Dci#sc!znvz>5D;d*3ACp6%gEFT2QA?(Fd#8DY`8n5#~s6t1!`L zJ4uPnAFna)#jfqA#l9y?Jj47Oyg@HSHW_>C_OCI9k?6LT#cIVAeYN8tWZV~Pj69~; zZTBtqwYEWyzEBTy1WNc@*BD6z!uC+NXcnT2QV@kb-DHeo;BPz7%gZ^v_U>Fh@npJ5 zI_L0BWqXq`l=QF-%~mg6s1FM(g)INB$w-zk4art57wRJ%2O)R;++>UtGAO${OczO* zi1^Mwn~W9`+VB1%Ez4rn%h6;Yj66f>}AzeT(R0!uSkv5{88_Q|d4rHl0F zuoB3W{>{b?A>X-GHD08T2&;v3Mc|x}FWjoaQXy>~su7DjO#Emzb*p;l5-z7o#*m1h z8bOl??!Hyso~n;^WJ6{T@{-pRqRBhC0Q}uleNb3AqLt2O;|{TZ_O0q$Tz9x3Yal-q zGOXMFgp2h-4reR+WpK0cn~2C`%ciXpB08{G?Y@L4t%R(EoFU{pi`6?9 z6Z&0{M<8zzQY}`e&x34-jE!mLdV9#*7pqZxK0uPnc^H)t>y7IbizTknVX2Ve!@QoI z?)5AOG7WN(I5A|gy8aSU=Zc&3S)klXp z+ED3`W`Li#MNPXDo2iiDqneE}vAOmZl`l5)AZy~ise1b@>a9!lVU7yOywjVFZ^h>P zTfFJxlScZqBU+ijBAtjXxJ8XhBdMc~P*@-Et@*p!$tTCRFFS#J-tu%^pg*XTKijXaf)U?a==pHUJ(0h7pWDO;d%~a8Y?E3NjPp@q+Ys=v?+tk zV#(wjA+NyZ<@&Gz;-w2g4(lc|)nq%}v^`Lby<86q3;u?&$PDi%LA^eijG&k$5!0<7 zZWgyjBrk>B$C}413Cbro_ox+AQ+Mx3{42p;#RAqBm|eBKbF)=jhxR&sE0$`pl+03q zKSL7RRxFuMf!=Th_15++nXJ5``%V!hBRW>vY`j5_)OKUHXuW)ujVSN=X5)~E&hv_5 zcqOS@Rg15sSc+%Le!KWO;56JqP6J^7(yyZtiKHbtQS9f+(uK*+>%6jSJO%S5mODA@dr&Nioe# zieX_TLb{uchs2XJ7OF;UI%*&jnwyPwA;YYIg{#=9y3NsvsI0{+G8UL&4i8VpV$}Ee z`CYR?*+FR`F@tl*RP@;lL>V2;Mo2KCAG$>^il_)tTvxMkOuV`XuS~7^-Bdm7>}o_i z|7}dce8Pqv~&Y-x3G()ynCim zm}Vf#VFLUu3Fs$Q>>|{Qh{6gG1rKDhUYt6xP<=9u)Tn~Yqn2JK`P^+k#(bmE>L*Bh zcMMB22DKPP^5sheB7+ok#d{ zw8eNpD%TuDW)e0b3adgCd}WJqrzFOlh3c;icJ#JGmO@@jH`sOs_UBIF_RdLH>7jk2 zex&cSw#8W5SA|}v4^huOlOJR%j0^-^>0qh3R~M>{R}m&1a#wMSG0h67w=~nRrGZ7>ja{T(>&(ND*9BNH=Wib z{uuci=P^np6JNbq-8>x)l?hoj!eiVlB%U4+vJkSXoBXer+y|L7(qj~h%`0wJzSoj< zU67vBnGupOj3NvfzfuOKUaNiN;)p2Ye2?*|q{)}v#Wngmi6f%m$sS{jh~8SL&byA}O8A8##1uy2 zlDtn~Uy^s|ttX3%srlw&sr@34v0Fm6w%gIy#L*%|;a7N!#p38w3)Po68nzFz{7NQi zgnVeB^1WUkmedJp>$BE)S(4-SUi#s(d~-Xwj18T?25JMRd5li^aK7bH_yB3{CnHI| z%478js8B1er$S{z)<8x};6)or;A6Ua0xw^dVyXQaQbhc{V4-T^%di^As_7o{!XDe% z3sukzoa=%-cD={=LVO>Ob7s-Gat6^%=)}IyS|dnAD6Lz?{tU>(Opo!Jc<&Pf3o7o}Ro&ip337yM*6} z@x|^IEZX_v_5$)XMF*aEub+_1G1Q}cGG2RtNUhTI3w%9L0RP~rOQEc0~KsDZ| z4?b_7xDP2iCv0mM^zV?F-cSOlcdsDAHR!hEqJ8zd+3q zG6S->#ACcK88VUK?S?aXe`UFSZaRvl0dv!+`&YN0z>>2q% zW_Ni8;@DR`hU^`%4e0j$#o1_`0z@5%c8e&$D~egxlde(`_hKn`y5|A(FtJCKYwB;16Cu{+eg`kqH^~qsMqcYUEGzXr#~09~dq- zMp!)@QKdBo3bRw~zeyh+R*C(&zj%!G;_?gg)L-IqJLFzSbHKTFo;Bc%`iJHKa=X}^ zGf&N#hs|_IH*;G~A*an#8|SgTvO=?KGU0WV@~K`mP`y7-?>E5>7ue}B4iGK*p+&S~ zaQlAq7%zFbSU8)HT|3sbhJzK`KVKCtQ#BIec-a5^p+?Tvho6=K&hPS^T(On%6Hknc z-)X+Zh5K$&x6UWIr$gR_#WG3BTW|7~*%#;QVH5V@My5}zIlko+Yl;VMFWeCyHi49$P6F}}A}ds@C)X%CXH%!T)0X-<|t!d`$O zm1#I@tCV^=i;X)~a8vwS4S#jNi%m>FlQ(=OpSB|z!o1pab1TDH7f3;~0ut_P*Jd)= zH33^E=ZR!Hww0@9V3V2&`bhs?I6d}4F zVF8MrQe-+ZZ}ylIthuDf?|*oVb;xaV=7_c$<|v1pN=nNO;zTR(QJ~*{*rD7@fgvTfm-+r1cgLZ3tz7)X`1O?*c zf!W?c!KVvR^5uw%wN~S}eDl_9^}|BSsS9!*8CfIb*4b*v&5(h_?;prjLavytuDBVs zkpwxN95ct%b7rf%vFXTy+|4BDAo1=}yfb^6Z{Lh}#fY|(XWPV)QL|N}II<6N0F$8~ z2pKS21uW8|0}}l0+Mj_;{w-oJ!gLIml0Qdf&C#iDTSURo;r#lZr<(M?MU|YYgOOg!lnpEpk3?05Vlw! zYfr^6e?Y5jIhDIl*NzTuwI0msSp~3d@NRw*r5lf(p^>e|Va6h6Uo2u)&k-UC?hGt^9;dFm zRUbZSCHR>Ut!8H(krvBe5!}R)t;TagbbDgmlCxb>QrxJ-&hC-wb=>gxfPF`)X0Sc5 zuU#vTm&lpxyhF3~5PK5baX5|~aiB?EnXM1<&xUIluNG(Pj)yxQdH<|$kV{5U(m`kJ>~+$O}8Om33r6sRd2)panw_sWH>Ec!^sso!p6mt}CUU2|X5YMd@{8e(1-5Z%?SSeiBRlK99Pvjj%iayuUV|RrXLLI-Z$1l+z;c%Pa zdapai!xh1aU-A=sDR5=~vX=q(6dcn;=1=Tp!4<?R#FYH*unzQ*>*ZLei=&Vw>UD%ZdO@5+!cfsw1Gn1nG5*V+n z7TLa;>a!eupuGdU7ke{hr$9uwEFQ~Br&W%fVN@2je%-;Lq!e)UjjbG=wjNTwg8*m4 zjsLG&e20`l@MTNYwmVq3ss&$}t3DH~IqX_cp8Dqw_An%Xi&wOA*Hpi(JYF!~d0!Ck zI_iV{vk@fctHu8zO$)&5^3{WaE5NRN_56Q00n!CdS*?Em4=bl}{p?!KYBl`7`b>Wv ztUaJs2|fZ&EKrBRV^aIuwaFV>&2gW0VW?w$p9@2s1-_}Fv1@FXhI)L{NsUF=ac@)$ zm+F`MSAw$()$2=j?$ZHR6sq5s60w8537ZD!k2(&Jgm0dsgGGv z19m;!)0jzt^1vO0V+>;cgzJQ>g`?p;$pwbnwHmnIg)s`Q8ZN7sy+pXUM_Ny=;K@o} zDqP|ttt-sFiS_;;H3LB=f*DwrK1*+YDg4U+!56@n{|~+lzV?6cRq$OWd55eg;}3ov zL=gL^`rVhjR7B{%M2k zTJV$Vl6&-VXXSy{J=tnZ#(ga_)G2W@&u%eIdrz&pkFf0p?|V<}xsR{~N0LD` zs&5{61~{)yU6)67SqH8dqSoZ;i|t+Df|$d+qH$F&TZclHv2`eZ2yPEmBbVV%E;wbp zx=V01*nNTea2ea7I>0j~tM8ZT7uu7e>{`Z@!^SlA<1&4yn!a3*9g+v<8q{|3u!ZP| zLO9#kYt+W&Y|N{~P&G)szFgWSa8{&h1_uO2lZaRfSV1$Lh#?ijcntnCFr=TRu3MqU zEY1V3JFV@M0$m8VG_LLBnH^E2WpFug(cqJ1kuUGrV=U^^gKS@% zYF&o_A}?Uw|<%p(h9utWvmexb8+P7H$_@ zY(kqnp*c8B(%A)(H?GY%2YfPKqUbS-BVEW#kaxGFiEy=~Z&!Gmah`ObgHpZ2o6)Nn zbK8j8Qc4fY4$KQ(Ji`0FqWXc3GV?>+rjC60`@!F5+b(yyK zCuv_m{Kt!>trw}5RrKO(A>&VPGx|#)R&@I`I-i3xsl)8r+QVzCgEMn6lzI*JJPdCf zUc-J+^KeWt#;CDv+-)K!@0=g^to%x{S0fFc(8hr9=X^HRwdAu4* z-gTbZpwpIBft}~8H+5>R2R!|J)hvAQX%v@9s_$w#Wl7+@liJvfZ*B5iO@}@U&NHP= z?ph7u7~MT^{)KS6E>au#Cb|+_k=j!YB@8ui<#7G2t5y3*4_t~!Cw%F}ZAOy1i3h_% zgbI(t%N|M!a1xXUifij-k5Eo(zD;GUVL$3z1hz}n>@~FU1>nj{+qj77;q!Icc#-df z>%3I$K<@7bcU`6$*3d@`9)aB}+RRlq+&BBqv2bPSDrPNxj#Tj8^fn{yBn!#P*=@Y)YHbaA05=QaQs<}* z58!5%xH-3tD;_2s&W}}159k5*4!AD3(bhc*eh(7+*mzQ#_Y)taK&65m3nVoo&2w^# zFefZ%J2^_1)Xam6U!d+4`=#>j0=4Zya;gSAgLf5$KL(zDvpV`9`ZjhndMR7QJR~^; zcHGt`x6;YTVAeyZ)m*p}4Qu3fw!wOM9Hx~^+Km55Kd8HE07b*^M0OCH@;A7ajGugd!fQXtQs>SwROZGt=DP?4BMGAe8K^#i}7yvNxN>0xfpreNLrCDjCmOQo5tbcrxRa{ z(n|G?L^8%iWJhv%-B3R(G8??;t~PV(QmjhZSP2(?w_3hlA33}N?8KdF5%x+q(U#T7 zYI4*e+VaPB>A+?pZx=S4J8o_bBwKpb*~z^fxJowr{SW!8=(&vz6fKw82^U{ zI{$A!D|_rp&LzP)?mv}F zgFEprE)%YORh#jggomHxa1NZFua<1o2aYHJ$LnpzJ0k4!?8*Lsgr^)?4YCU?E&q3q zmKUiDse4@;FCfT06KOkoRuIIGz;S#_sXL_`0xw~YLzc6??c_#QaV;6H04~sU&3XH& zel0?p{cszHn`L}9B%^0?RK6`nTA|vEB9@!YnFZ&xQ?Hw^#zNGyZCn>TrJdWGJcYW$ zzYDJB4fT5=4M*&Fy1IK*%)@NS%K(?v(*{1w5Iq}1+MYIJyLGH`^~1CVC2-a6s4WjO zI;|4<-Zt**qB9Y#8Xo2pc?VoB++CJx%#xPMQjN|;ynL_C_{?l?5}xRZqohh2((Dg= zl!z3`Ot?jGS6RyR&PVhCgA3t`J|d-%%P*F{GPptxe(OV{tAUx-J^cM^58YDkyy>j7#54D+Vz9(x> zpTEoqio6(k$061Hs2(}45}Z=kX5`7o{3MDsaOH4=OvZB41GlTr`bL{bi|3-j^vdO% zaJWRc-rvN-rMTo9>lM2+%mprWNfA0V8@#CAl&O_Py3M};qOx8+Sfmf1vJ1SkfnJt) z&|Uql%V^ya3-Qo}oxH|2OVwbO1n7V(hPzx1TdPN;i7K#;>r1SiXVA+;xDrD#Xdfd`t0|) z>t`3qJ8dfhiUQu zR_uQm*h=^G*}gg89YM}RAzEwDh_6Dmhk`@E#le)vmxBF)uLaLT?Su@*-o6mWQ=>z* z&X5sXLw)vyh8+m?afSB%E7a$3=ul0Q)j~}(44=Tzp!2oC7w%r{v-%4kzX5*dXihcd ziM0dNT;J8fs-#YjWU_8`torA+)gkJD@9N>|-FAJ%*%$jHpXYafAHUoCDqn4-kNpu} zt<1;I?V~;Clc#A<_i+^X`n=m`bVncUr#>NTe0{e2_IuOU=M^>Dwt9jZ=+Z~2^;`55 zb=e1udf%|E&QkYm)w9%deyhJ3616b+bDQ>`)$}I*@O3bho^Sn)S^%?ZNul8%7;s5w*2Yd&^{Os%3LayJd}7{<)V}kH zc`8y{+b2ekBwnMx9<2T8+plEB5N(Go^3%cEr?$8Hlnl|H2?#&x)Q$y2e(KaJ0{viq z4h-#ZYR}uxePFQmNZ-i62Wrm*g}*mQ`!Oi;sX^Mi!G7>f!O2BVtvV#KZlHENBpkdg zH1wT;+Lxh`r31C9un_Fr@0hr2i1w-@Q`0u~3w~iRcNa%iIOl19^z(x+@9*5_)av^O zt#WEd`p2{l(!LrHa&VA#ctAVpw||h|Gftn?&c26+_$X%xc$d>}kJIN*r(gbHpMRX! zYuZhDaL`*fG^oY zuwK)5yYN5C0piDk9S28h&jpiC`+^6Tk0c*|VOV$|EM)aa?VGST?5%jv@ly~fG-LuU zS42(tW`y=!bjYhCwBKR|?iisxG*tE@sbOQ0ZaXdHc%1g-X(1oRXBHl~$4>TX*?Lw64&!D}ow=sfPXE2+{WVocBeD zwqkdm0UJYz@K(P5+BV?R5N)+z&|4we>weC%5UtRE5cqR{=kZ|esJ|chPk+C|!P=gH zKx`fe@Ov*6uHA2Su(r8x*3-e-@xFNeLC|=7-xTck zM6k~*!G2Z2KC42a{2mDLQ6YZYLVPMh{9X<5*%NZ{L!mzTp_19^g^<A3Hiyc{pH*LP1d_?k!)gk+b^ssP+RXG@c~<@{QWis zY2W!rJZaZn3h*nW!44orUJZ0We;Vjl7wGf6-EWuO=OuM_=<1{?tNgWBd>lvowD)`l zeC4NoN}fbi@)}l&B0fNew%z=^!Iss zcSq>z&unVuL#u;Ue0r94Ur_Ct+IFA*Z=a!+^odw|hW2}(khZh6a^H}~v$gkqBmPd* zT)x+G^^(o;)mhpHwjkG;+8?(5ZD(kI`1Rj-hW3De3MuB_|EDCa&VMRpETI37N!sp! z>6-RXVE-SIw8sN8H0{SgNA+3S{q~^hGqnnPe|3iTOkbz5Vxsm+P`^DBwU2^AADgJv z2ZeS{(0&LCJupH0J1F$|3EC6EeOFJ=b_Y9mBx(nO$(k)8{l81nDnig}e~0w{Hc9&= zG_>F>?Yq#3x1hs9A3RIj9~SXOlD6Ivy6!CP1xNqgNm{!jwBt-|O}~hTlC+Qeg?@jg z_HDn22MF(qaL4OsYO9<<`DbcRIV0*PYP$zV{E?*nH8}j|L~VOy=+}wbtC6AgiP{H| zp`Rvd-$w@jaJJSG9r1Re)*1aATM8aOEo5t=_VQ^F$H!}5ofh)jcrW*{Vg>IT5?VR)wLK+E#1k!|!WCO-@5MX-mm}+`A#Wd54 zap=7l)4}&QCMV~--~Ha_zR$gX*t7Q9YuDMc_o`dhBc(~n^g2>{l0>7Uq{B(LqFh>> zL>r@|K}mEt%5X7}E=8GlBvM6GTt4{B>_lb0M<&YI^=M|IGV99|<;QhtU8-|M19mjE zd({`>FJ0=5_M4gSL%%xE=k&Y!_@eyZ2Kpuxbs6tn!2D@ypFuFvTSRYhpHo|Z=dW_Z z{2SFii@&n3?|uLI9p0M$yK{0|DuTANnUlQf{$XX_Uj90h zz3+I&N?3u(Wg^|@tn)-HmGGG&E!S;U0X0|%*&@)s%Gpfl$L<94uS$h^znLeVlI z^}Z*FMu_$^ID|~jtRPxr>I-_=dd5jx}20Y#@;l_%2 zX1T&l>&)_Ci-9hh>&~>Y2^QRo`;7M`Ydu#Ra=d7}MEBRVcBxkDuDt|nA0 zDexu9THFK-hg8vw&YP_J(rJ?{J#0o7Wot<~Z8l4zn$cOa)iWiX)?1|fW_a_~3F)-X zD&--=YW1hnSi5+zDQ$8Z@|xnqw;yUmgF?a|HK8jZ=KMzFb4icW$nTQQrr{uzcBj!! zm$CRQniy(b-G~;4K8Jhn+-UonG};)g_*`GACeBO4FAP|Qq|wM2(U(TEW29A{hO{n5 z8qtsr#z;3C(4`n@X9Ic`BTa8W!>SuzrqZ`EqSNixMIjv#sb_IJlb&5jK>45&`4Oxk$&P_VhGz7~Voo+-TLlOq!4|4Nb01 zj98LMWrPb(T!-!V0Vrqu^K`UEUn2KiI9&)U(R z)M{7TQDth_l6JJTLD-A7pXdX>=>OqBYHKC1cKuTFK8^(Y{vvsaEW0 zD#{!6OwQMp z4eSc>d|_Adepj}pt9Y|3EA9$$`Cn&5!3XSZ@+q~mfIN{FsRyvZBJI}&f_HkU>wnfr z5A-HrvIKaL?Gg;5McQak5c>=`$o)n`F1WfG##n@98^whpJvGM97THX(*56Gi8%`Y@ zOGCjV4pm3rm1u>=bwZ+2%?qr)y}EI1wM6H6Fw1mW60b|_s$~Cn;mU%(>^s^I%KfU4 z6*3)B%hP1KsGgy8nF}2+(@f6e*Wgz+_3qmyx+ln|O}M-0vf|E(rMG4jF`U11mH9U43OiXO>Z+ z($BEKmCaCKrG?GaS9L_A48IJt(kx^9EGtcsx%n2FZ02TJXup|TVWklk8Tbd5@Ud1l z#VRhgvSO=oNB}!zO|P14|JJ46x}NUc4cMb{S^7Z}l`>1d2|saTy$>ycS{x$L5zeqf z1{dI(EYoP6bixD|u}$dhS#Y@~oY>_U#Xf^!l0@Uh=(93y7Nb$y6;ZxpqNk#KP@+N7 zUzeGJ_lW(_a1ya_EOjeSV^xO7I-2HD?ZzUx#u;|%XsRw6**kP=(ZHL7ER%*KcevWH zMJAudz1#!|$y#Kht6IZx6HV6fD`h&Q<5$ZFv~3Uke8GTjoiP|jn<(GNV-;*Q@>m_W zq-fMI(sW;`8664fY{*g53}(9TZ#3WA$&(vEiGthI7(Z zE3M~@WdU?U8#LKULv+FgD^2BthFNJdufUV^#v_}p( zkwTAUg?^w}SvOlPA(b`gjzz(|ukHfPA08g$pG)b`Q|S?5r&b$boE6xxFqW>m07r(tPjoX>0gt<-2{mY7SjbvW4O$$gbh{{Q1FC%(?p{`dW=4qq8dMm8 z4{~Z`Ewu0|Qi8CsH9BNe4Z0bvkXJ-62KkJb5Qq=^Vie5r809>k8XIyinGVG&n6g+L zCd=az0gj88jwRE%1mluA^eDkNs}AKR8ll!L@FW^5YSWTLBjJ z%aY}BDL5VFi;x48#T&`&RkAR&2AfnvSXhG{uKnAFy6iw5XlHROKz;D$yZ8YPzc_@M zRAPWbsl3oY+3?QWt<(PN%(sg1K&RZb_#h#=$Biwn^fSMs!f} zg6N1zKQ*07%!a}ybj&;ttL&90z**dw=GcVIjp&r!Ii)c@w+~jKx#dBkuMuqy8ijI) zoO&>pr=7Wg&j&j~!)}#0UeMP+4_jb~CzK{X+=OvCij>>3Xb! z&W(4TZcOvyvltSi5}n5y(~3kT<58kGxe+Z&lJ_^J4M~ck>S}V>8Ss%clxEhZgzZnG zizy0RTGP0-DV?eL0r-VlVS9nDrJxVgHWoFd)3rTVL(A%f?Mb6Q>&$1!*jP_om`0E4 z$y?K?q<%D}{89aAypSoW(U`UEsZeqsOO-IO*HR@+(~DFIV;|N)LZ_xTke{W~>IU*H zyom<#(R4c3K;D*46%AxB-bX|EY-5_*P%dmt%Noiv8`JiN(j!kJI?_{*I*u9=HRQ)?sFmTd+W)%^`d-~OZ>Xnb z7kqQ$8Q8Z%CTwGD9s9drC8T59|5JWA3xVDk$2}`$78a}7Z1tOF{9%<| zt>u5G^UIZ9d(Hn-`~OY3|E4}$r%Gl>ox)BrI>D;F`9ckev!PC;!%#uK;j)qv<^38O z%L0luxF@5_Gz}e43(!)ZR5t~Frbbw+VT(1wW<}ZDu;});z98nmumjTmTQ|IIinP6a zyHtLCZ{NL)f>c&}AQQLgXat*&*R@tH&ePFzjW|_D6`Zg@N0r=Hcyrp?02k}T4LY_? z7x?#Cqo@|D6o4S=RNpV;=DPi}UB1?S{Y2ORKl~0#`>7{zsaVNgUxCM}4zY|W&@)ogPiM8w#RcZO@U zaHtbprEK|`G&SC~yfZCLupRG2=MtOm>qPUC6w1v>O7795Jg{hI>xj=gQ)NA|urocY zZ{GC|m8J4UUFc$J7;eyN;6Rmg8#qwS%7*4CZ_~~+{(`3q9Y~vnH}Igb`DrH_m=4y6 z_Ox`Mm4mI7QBG`Q#wa(mQ3klDjUo}1wC#zShqS+|LLGS>t08f|qeAqcV+|ZoodVJ4 zwVg(y&XO+TnJ%oNi62{4%e?V%B-KjtOVXgLuy%dGYt33S40JW_)$TA@xY z3b3zCphp3w(O@@hiUIACT^y4{GXssQYSPj`a3q5r_MM3|)!|%_NG}}rdkHi$$ayq@ z9t7z@@*)W5!bzvSAc5{Uof8shQ1B6qtIQQR285w@Pi_Lu4R!hwXn2_WaXhUG6E-B! z)Ntd8WLg-moQKOJ+>hevWQ2m>6lp9@rjkfV|C^)S58~-^RDlZk0WniSIkmd`VmuXB zS15C14ZGthH_nVcjE`#uX)R8|0B_)T1Cr@^oO~#WhQ>=95wWt4PiTE3wIH1p1!U2+T`sHV9t_e6*|{mD?r&er)f!v>_D- z;`J_bgwIE{PRsOqaFTUTtw)E0h5Y)oF~mK&9^DL4;Kwf8*n0HR?Xg$Z$8<}p>(k=! z+35Y@NO4tNx)xjc8zOf_p?gTAv_{txq!&EraUOiA49n zdh{w$!CXjkzphJzk`;J#4IAiprHFUx(C(VHVRfmz<|}mcbnWP^4JfZpbWQ^*s^gJM z>eHS&5(=EEBcWqg>pTI`pY;rP>ai#F47v5$wEBj^`fPHlFux(2n~F&p+(1~>fX!(j zu5ZA0G!SMsWak@*$h^}a2Sxw-rYl(>oyV-5S6i~Qu&;?%I69(Hv`~1Ju`H6X(f%WB ztAA*bx0-)JxwnaC5@Tchcgnp%pRUz7|Ag?aDFF2y&OXyjUM-f~GwpRvo*CSq2+Ree zJkLyHL>}-qky|1|7pO<>e95^-rbiMt4#K`kp*(Af2BlYS4GBvYPRi`OEc(Cv(;KGr zw6fq4V|Uq4w*1zZD@r_?L<_4qHYd{EYW%2p+8@J@NF-l%zAS;Z zROja;(2MH)t$5lI8+bF0=Ev!;)uOd=Ml`%DPCgq?r{ZLMfY;*+(ZdNzoA^_8+4Z`{ z)Ad+I-LL<8N&czly%CbKXU%2C#frhQfcZZQ@-^^OvaxKD%C^8wTUE*)uv;wy9?6+z zh0-)m5wcHdwTIkPsB_M6LrZ8Z45Q(^F)xe?_<_*BO_9W67cG=dVnGiy+fjC3K+r1} zq%`SOD4ns%>qF_5?Jnvm3v{jsrBM#!F*i+dD9pnyr`+_2n@%{vh+cQf$3y7_{<|nI z_%f6}cw?1VJj%I!i%D~|bDhT2s_c)FFuGtEYlf)p+hD9w+t(#grN#*MHIIX7K35x^l|(zW;;kfF z%17g;%dhex3Ki;IHxg;QU@u9ee9<~O0pn>aX$O6=xM$y8=PiTG4xG(Mg-hkXu>Rk+b}BZXFm zThN1@;Z2|!jWCQ%qO}njKn$$LVF4|!#_dg}(ijVt%Y~SCp&P4iSeuM(9F7n)Jx=L+ zVVv|jiMGVaC9r12$(WBzadJ)qJ&u!0<7rU5v?87g;>E@B>`6SgpfQ`DaEJ|Q%BDls zO!Bb7$>Nz5HZBqnA}Q&ZTq8Vb?u8p@EdKOysG&Dy|Btp!HnN-e{z#%yNoN(Rh= zy4>SNthAm&pH-im-H7GXFU1-;-oOIPwFX_#+lGdzjaYHRd#G$xBjdeBY;WT^O6&L2 zKZ1jJ6VU?rLX)I7Yu=we*@p3isxppN(?B(!uV&-a z!aOyb_J()&zZK-G=0D=}{p2Y)$=G2=f8w+HQ>{H5N0-!!uJWEX0dS6F9Tra$B=_ie zD2}9CakMWu{8&6~2+`h%qeCHvf;eawN|c=?Tm)uC_=WVaJCFg1yWbv4)Wzi8?c{Mu6x_JZo^v867+P5N`F^P zMJhg9Lz`8QyPo5VQqvT*e!rULs=?!LQj5bim@55sHG81uU#i(CO~4Wjo1;;rBLDL1 zVBMpOy}t(jH^xc-!5iN9{d@ZP@S~=aeJ${-wBBz16Z?Dt{)fxF0iFn4bOr{;CS8rB zC3fL(B(1SOG(v?P84XNnB;;*nlTT#T6U`?y@b9r~)6nWFM~cfTak_>csE*+M7i)w= z8g@|gx8C%BSjO}JTWEq}sTaK*9c0QHzYpuWhF+_bSDOn}2ggQo|64;tI9kKR3JtWs zlThZoN<6BeJE|cl^Hd{vIhOsOWhz-zyf8~mP3ky_IvR2F$Ui>8&Cty7L`A!pbW{Z= z0p%RWW@)NpfY{PR+RUhfqN2jc^h{Gf;-5@8|NNia_S!sN6?@GBEek^_S8dr0w;;9g zybCXlza0$EB|Y$?G~h6Iq@NHAPu6!J;hAHW0atn))?n$9vj7)}7rA;C0~ zHv%)6{}tXddh_yNI;_{9386CmHVkL7F>p{Ym@ms+m}8C38&2}96fFmxv{Q7JIBAK= z4ZG`6ldvurYIdVHgi2*>Wh>3@g-$wcR?wHs%HBBG>Yn4IrPcx!P?zoQp-##RRI-l+ zYOe-iV>P3LGlL{xRs_kboKzenk96X6ma?66BS?H1#3qJpX0tOgO7jfL z;V?intj(l3qP(vwtrn$2UEvF7oQ@8OR=|~#3HTQ#c~d6k$zNcJ$@;ly(d?Ytg-%$U zle$oWRUFd=9EyHX7ustT@%vu4tP&AA`Hc?=}?5Mn6x6KgRSXqgt4$Sjg2$|o)u*QetxuJUpqP&Eg$JXXQL(0$qsZo z+PJbEJ9-o+0iGNmeyANC zju+Rrp_2(sH+RPG=2|yo(99%7iCmU!JJFU-)#x}MjiorIw4;$VjicL9QB7%fM=Gu< z!W?%6u%{#C)T;KZEiI~LKHnBDn&R2Ew7s@@Nn5%F*0>E#tS=R1kS{f4P6nMwmB(g4 z@;2sY(EbMcL7j0L87nYP4J5#G8b;l2PbCfQ$J$eQ8n-orE~F{4QC4H|S{pjt*aFO^ zrs8}U0GnCR(J9TP4exnqMRQBRJG8I4wEAs2+dMk|Z5rGnI`3^-(IR^A+f?2nI`?hL zeJgrzH(K^q^qy{1_Ez++ZscnjUEGb9wv681jZU?U-r9`@w2IE|Msr)qH#*baR`R&^ zRMrZ7)a_RC^ER}iwXnH0yVO>^+Mc~`E9AFhMeT&0?bsPlJK<(KHl@9=xIJ6nUMOkL zPPP|7^t8Q@o58X(w&1|p)>*vNnPqnohIL_=y9kfEuWG0)HDXhq3TQh~yOm-?$ zEb7XxWR3s}_(lw<+D2o-#4Bp*8Ms-E&COPzrqfJ<4P^r?32GXw7RIX8Y_$Kkhu(T- z3&8{tFX4dgBfwtv`yc*(72d|40b2H)>lTpx2OL*iPkx2>8@I*-HPc|s>^4mh@V7L^ zxlm_3)f@t9kJf(h7doJG-u@La`?l%7z)epY{tG?gC0uurYml$?qjd(ctRH0Xup|B8 zwkTEfg}G@T#(C7j4f%uqv_xL|g=X4p%YLGzc8`d}2D`Es?{t_p{!GUlx0RNi+=icM zS}=F?H<}&19^C1dfj?7DX-J45!)Fx>ftPIR#3^rq__ zfLPbjaQzo5?sy8_n3AbbZ+u(yeM=|bw%`4RhQBM`>qU>>l_q~fv)*&P{+hPF2ctHzoO0mh#cbiihTc+F7%|yADHud((6wSvn}6H>1W6A5@vT#y!#~`?ha;V z-WN)3r@!DYeMuvF$fpJ{D(N8)#p78IaWyn8U+OD=rGhWz^8U2=OSz;EZT?c;`#l}{ zQo^*Ael1SVVr+FU=doVw&tBryU)hQ83)q0(Y@DZe!uelWN$)Cxv%NzG^kMt^wA=9` zJNct{<3~2RuR^@MZ&mjGszm-z+}>=)p8b@=luGaa!lQx>zlMSC%x|Wbzq7G_n2!I! zF8yID^sr)&DQ5s+~SULWMX*wL)dX`XUabFrKk>-aM~4dcausE2m|zn&Ly!R>AyynGcWk;-aPI#IfK z$3nAAWva6l8f_7fm~4qsOm-p+ouw9WsFf~QVBBP{E!d6z+8V03lMvWV@e)<0(HdIm zQI%kvuhr5p4Gq<_e8av_Z=&99q14>K@{o%iMPb?>U_Cuh%SSYXYaBOeXq-l#r=f)! z!x{}+trY!tBLz?_z`f~>nlErgIGt9HP!)vJO>N+WaLU$A!B;=cFhx}y=AliZ!pxV* zpDzrfCFUuB_XW7-gwgeYq^vOV*%f$(Jq|a1pAU5U-89^hbkI#JgB1M0VAm)&Z48c^ z=n+9>P_G(v?tgLpZH-3wzw`wp)1SW<$cm7l*Tj;qapTdz&o9i}tWY^Wk9X z?kWj}j>~#F7`&Xk4J??-yd)U>wS7)7E!DXF!OH8t03M1njtHgGoZ^vjR_lHo1Y@g$ zpU6kRMQtaqz(qpz^&q+`h^TFe!BrZB>nrSqLEsX^u|aURt_vZ+n*KV7UfH>hH-afU zPW7Oh=nsi%j^C#14oiwHf9o4;ayarYB(!)5)*KfrlIUw3Vb5VpvZJlI*+v-tM z5D*V#SuqBi7hoYnF9gg=dy7>mR2F1eo=mrcCIX)u5;{B?VxV%(NsY;B`rg(d=omIIIR+S3@j?88=0A zFHK=(&H#u6bvMtjhf9b~;wO|&%5V|E+F%`^e|@2t-+;4! z&=B(5307~08qP|cr>PP0GS4ey*xMPlt7skzg!y5RDjM)&MXs(Y6A`D+;;7aERFD8olnf18 z97=_u;^|N}$<5Dqvkh)>W*B?n7Uzetm0{vVz+qx(IGY_V9uH^B!{h#1mQ}+mdaJ;v zQWv!g_VLWji+n?}wWu1kk%A!4HgWj^{li0iziZ!Q`7JJAQo?_b@lHOCBI;Ka`A=Ao zd7QG2;mrSy%jb^Qi~R3oQxw{7=2YK@AIs=hlzWq|YApz@hu8N@C%g^)id#;9>_FNZGb9zwi@C*J=5{8O_asIM76K<+(MbM1;y2Dje$P^uO|b4 z3+`w}urkMu{5})?X&i~ycGu({A+rgxFk5D;WaBNFZIx>iRmeiFnN|LOT+q5P2sWs( zn5ACUk>`#|EYVS++KI#fIO*yrU-Jo+>KcBP4*q0@vG7&XrodfJtK^;5;^SDO6L#p> zHJ$KW#~$buB~8_kF<)>~>-4Y6m*L7Cm0_HohG|j&muh6(*>_H}6HU+2^0*Q2sLt>w z4_{Lqx;mOya%b|WJxec+(6g0#VW*yL(*L{m|BK512XzL7HCx*H|$GV7l%E6 zWISKggBHY}WuTdwD9`OdyArDsN0O6}SWzQOjhvOW=HpG5*W+gNqzmoI`?oAS?k zP+rrH*fyK;t9sCdri!}qaPx1_@|w47C%VJcGvHZwh;MmXpz<=hpoWZ53>mXKUr}nE z-c4z5Tekqfx2oX$w|@XU<{bsU>Kz4t`5h+;9C_DP+?_VO_rZ$pwCDYZk=?1)hJP$o zJ^P$Cd}zD+IZgS$Cjonf}$SLzF@Tnx~P{Ls=#bksLz=v72;60Dd)}Xz>bwm7!3=ut-)hnoI^oo-f@I`~#q%GahUHL=Y))OoaR^Ox8=bJ{n$ zfQK(bT(iW(XiJ7|0+Ne6)Z2~6kd6+e!CO62mLT=~%lOJgj1Kh_6_TM}*)}1y;VXxd z8uxY30;EQM<1WRc@S7gkFTM>L0r_o4~VYTlN3*=vQ<&`po|C3BTQQy$c&@;+WE!(g$1`U-6BYcl z=`2Q}FF{&q`UIZJWL?5jnb-SxDsOW6Y{qBdWqa1mW`i)kZF3l34t&WRb^>c*`U2)q zn%jz}Qtf#>mFBV*GKbRK>V>R%$r5nliHua~v!Ic0)0O5T@DH1R3QwyR4uG#)mU5`=Xok|G zryX`Etrc``gWLZmcCp;6omde;;|${Y2%2e7)YHXA)5IvK6!|q# zw9v#4jG{ly0x-iYrd^RrK<3p*8fSF@bJ1!Z7D@91%yS~>Mu0gtf?foSKn*SP>;g!) z*iGf(a8Pgn9us(yq1^DG?}0iI%$Gz#CgOcjloxUbtL2&dtdhMxD&$NQI}xQA&^_dT zTpRE8nu?uMk)n#(r?JiI4`wYd^ksiK8z~}jC(3rYAAW2jV0b^+LLI=|te&GC-5(zP z@_>HyB>8~~iR(4@!vwH8o!{3V9`^XShBe_|_M>r4?w}oSvw)j@X+<*;6vmo+qrFC=Iz|-HWh7OH*KLWW2-=CpDBKISutMfi`0e=0_ zVg}kLpV*4}(#cP~Y__K_9r;|L9@IU0c3*nd-8Q-}ZTuqQ!H=}Mhwa>tbmHqklo?T%Y==VDgV4s9@8NE)+f3KjLwJcEA4x zbo*bYI*#>ODmF<)#aKYas->QQ2i@qA9rhf0?BtJiqt{MM;DKPi67ddUe~jx!lOp7s zUFmYfREC5%N{P1^6~}MFVvSRJGb5gV)fLC&NWkk8LP2vPQ7Pa}9;HEITrI^fV_j|A z)~+FSfVN@80( zrAwFFxlm$6dlzV*x3}HtM9(_9PwPjn+D7^uC~L#AS-R)?fRzPP9xKhf5>%W9yG zC8g~Anf%FTRY**(DI@WqW-#FVS}Va=ZEeb5=|_7Mrel0FJ_m}oW{VkeSGOo(Xd=6n z(!{P-R>139EAWZd>k*ph8P(YV)bK90<3EwN+oY-@Z!49bdwYTg<%j=6Y4-F#a+I6_ zABZ5!{{Ys;(;wKN4ZZsT)HUZnw9WX59)Ae%+DEoQKhdj?6fXkrC${T-X~HK8Jnd5( z)QhV=Rp1Su*%tPt0iQ<j-X{dj4EZqx4HW-qMU^-E zMUCmthWGcUH1Gv`#)RTdTk&cL9nm^pg}{$XJQPA>_?CdT>BZwAbVCqtg}_B{CK{P& zG#zu%Es?+HqRA4!$pr;~d8CV`%eNRZ9$L)VE+|v1fCmQHkuf1)pVBuQk6T+N+W9Rm z+Uw{AnqxuYK14JI?NRR1T^O9jfLj-$;2yeyhJ~`BMHd?Qda$HT0_CarkphiYDULwX zwER+m=4r2?)Cis*E+D*FQ7$YOf{qAmum9&XUrYZd>Aq{)H2$B|qMN^C6JL)jau*)~z|2RB1 z-v8d=9Ah-%Lf=%ONC2}KgFuYE!^2Ngygm6TI$Fs1Jb&X>zHqkE|8Xl{jO7o+WpR9t zmZod@C0e#m8|t6h%2&^SvXyUw=b}v8bo_EiK0JQ`F(EvMD3Jv`w?L*#Jog&^_1sVk zytVjTnKlXhD-)bDxnjlbhd*aR*gAgz;<}M9HsN=_xeYQsGd9J3BJvAN@B@MZAz$M6 z$+TPIX2`T(QX->%kWiaCH}87e37i!KRm-1C~k?RZIS%bILeFCkN2+t zfo^sz&5z-y6unG)CgPhr@ZG2h+V3oSKzZWbWg<sYW|{{cB!!&`!)P5f9K$w zdj9^}-0-4J@A-zS-nhdy{=k=3C4VNs@i06eW4P%8hL==GV=8W>6cmZ8XgPn!J-+m!Uq0}C!*n0>*^ItSFS`Aq?>AM@n?s`R01gVX zYTGrmOe3Gw&{j>?qU=w6D_G<>103xP&NW6j(Hfp3D9j)}H7ZwpJT~IJ{9PeUOp($YrVJ*UyT5>bXA$!l#N{@n+rZw7>$v|@-TsyunyqkugRgywCi!~-G01=DpegdP2dwCquYDesKkK+Rp-zE~$&I+V zkVu$_aPMs_>F;yDQpx+4gYU~_CSo}}_&d*Ea@?EZ$5s&G&p+Xf^-pQ)bNf3xe4P}Z z?E0>Pa28yRU&ZKq?gm6R#$RHrg2`*JqExAV;iR{YKX{op&L4ZqTfJ?8i`ny49Ohz_ z>NCiTDsh3DWvl<)4P{SBWYy{$wyUYR5=xz=manU6wAzZ`jX7#C;S45Tv*;+-@qGW@YF4Oh)bfJ==c@GC{vsRmRjN^=@u4_ShVUT-!dcc3+De>#;} zr2(mQDInps2acEa27?;X89Pq!y@3&+c@h|l8pa11=Qp9%P6ObrRk0+ygU#ze9%7zX zUx^{ksgK)215VYW+b;7&c!h=r4yZ>1!i`Is(9m!g%gVz;Zq}t|;giw-agpxQx`-`t zt*lG0qa;+kAzE6UN+q7?>&RXaqx3nadho!;bR$-J(vY6U#XM+8)8oy+>`O40rcrLP z`Vqo1k`LmE)W0L+Lw@~kE_yN>)a9X_El#=EDE zJPxC-Bfn@&x9dnF8qupd(%DqXuj{cEq|%JK^2Pd8R97mg%Z}6)FV|(W8;ZLdvgHl= z?M>OChT^S;?0G|RY8uN;GXS$OO}dlLj--j#(%7>!aY!R}rjfIvG277CjogckMZm8c zizCw6gmiINIx9^N0exkGg+HdbTDs+sa)7@Q!=W2$^~lmXr2*mbVC#S4eR70fm1cN(LTUVR65 z9V6h;YIp{oQFF+BthUeop5|(dD}Se4!8++X+G)sTr+QPi7c_jZr}$niDN;?x}bduKS+0Rd=oU77=aSq~5TA zn{UCc9orsVnI6xr>P2_sT}!{AO9|qwuW4fwwHOB{$9El!q-zlB$ZbHKN>rj~H> z8yZkM;P^LmzqZHv^cy-;*FNuCI$kgI*|)Sf&9UbjDo!)(fK50JhsAfZ-(HE4HVa)o%Lz6P(iC@y#4z8>(sHB5^b$2T4D9!3ln>wyQ zrT05YC%e<6&h~?!(W%b%d!N(Y&Nb(KPFb0f|8w$owIlaNSJ$b}=zcfp=w~$Z9VaL^ zzJo259=t1+dBy%v@Kb5@r?l=fZpUZr$rs|V9?aWgGiG9YPvKrqw!f$N^anQT8*%+rKxy{(+VLpupunfHR-f+jyim8{CJx(TCmbW1R9M z^Y;~}{>*Oo<@W!~9`sf2`8f8oabiDqwx4s{@9bGW`TB2caDT-TJNI{P$?t5@?|t5U z;Qmfmyz#iyqZ}uHxo!nJ=o8)jnj1rBV}!@m*_`U)%j&GGx_GZTyH*{Z=>HPGD+~6iSg~ph6Ry{wQ)>I7x^TGj ztgTDqIoHq>Fa%M6JzlLeB!xz4SE7SEbxxpe=v)I*C|BVWX^`>#d3A6thO;;Yk+jmC z8noKPfo8pFG6r$dY&>2UE^~?__!yg|5SGor5cr|r3seH$y-q_(4ayD?=hdL$A;Lby ztb{1;BLmzDCf_{>rN(+99LrEeM630Yqr&O2l_BQHG>%yqqVAv3bxaxo*VYt7- z^3+Y0M$;8H4HHdA-88@yGBJ!E${K!zI6nph5^OviMt=rFnMIpIMI1c)L&XDOG{!BC3Zt2B#}+ru4ii^~ zvE322r`1?_L|P`{ry3g@t)Le~cfzQiM;j(qW4>xNV3DY1dQk1Jb3s}C zl?Z4Ae~CZiO~v(N11rT(Qt)@NR4NM^v3i6Lvej3(b?82eosA#BQf1uJ!6ns?{6U&N zz`W3WtAx>efF8k%Q+YO-$8qxaOd@K^NDv!RjgULv+QIzOHsG4W-72<4HIW(5IA{(t ztas2dW`uQ(;wtK$yh3OStu@8G$q zf%qAEd9a;^=`ELSxI4lE{3!t|W`V&_kl;dm$P|mlBYiw+*O)Rys4ySBFxt(3J2^a0`iA@AN z)Gh*^V-LJ;XT^5$gq`^UO%npysz4Lu{Of_HSAlGT!?f7J&N{?L4tC1{CCy*6>|b}@ z8{+waeNB(yGW|GEs)(gWPRpiPdK%LBc`O}r8BSNHywE_v8$%7tW9e0>1USz<8mntc zxZx5c*$5{vt0Kg#SXvw@poX#-^Y&O;UR{AN#tL_1*{fLbN*o&!r)XXN;{_XbGyMvc z2Uh|i$_A^(#e%3mQU7Y=RWz0ThCo)8VTp>(QT^RxtUBCcgjx!jakb)_qlD^Im2Jat z;SWCCH5%s(BOTUYdtJ-LUNAr<@FVP+TI*Bg<})}b<6AU>VvimzZ4;au4Ia8|Fg!4@ zdxq}+(Ypd%s&STeUd>UVDs~!&?^qA`rrH31yJ4I>MvEA7Lm|hOaKrv%P|9avh7SN* zQ5$z?XpdS^pD7(w@DN<^w}eep(|C3KzwjQJUgMN`t<#_7v9sbFsVsudbrvBt*_)zf~V+r*yamsTraIIO7KRsc_K2?bTXG z=;)l*TA`(fT7@4`>VJ=QOEnZ4sYU!MqlbDa;8j+jhWh#{XBus!joQ!l8sS=v8S(J} zV~zBupg)03K{=|{7!AcnmLu6lm{@^?Hf~6UAtqKKh5tvVs?!|2jX4_QVjiOAbIki9 zoji)iEqU^D9hK_j3+R>ZYjnn+bqtrF4m{=!2n{2c+~X9d{uU+-wvXv4t}tXTS*`{Q z=0JbeR2)(TYy!0WNRXr0=yK}*GI z7`O{G(m5?m6@j2WrkRAgl=-M+LNy67%C!{}c2!yi>yBF&RNPcGR3dPMP*$hiA%ADa zo%=+YLEq7@+#tpVFv*VyWrv6=f{Ys&MJxVZ{yW2b!9^9re5aXTx7^!P`q*$!MFUiN z{Mz4ORbT&Cc3(&T$dSGQb#duT3N;{VAWwq61(%s<;9OyOp{83Z0hq_COek^H!H`Wy zXo8k$*!(x)3;)>aP4HVsAvmOIA73Z4;_3l|9{yyQWHvLKe|aBYM*J2vO=H{vHO*#I zF`6A}ex8P1R+|t80aXs*sTu?^RDF;|+03_&`Cm@(>izkJ-hVqq)%I^z`(lg!`IC=h zesi(6z5me9(9=!Wj>|{O`}yjJ4rLrXx5hILb2o$W5GNKO6rPT<@K=z~{Rb!eGOIL0 z{=pZ0uA-d&zR{|t1x;C65om@4je7#y$Xdsm6Pits~Y8l(5!R0%%F%<}281-L* z7yJ#jds}#Vvd<5(w+n1Em6MhQ&`HiRKLCNe4qy)Q!2<$t#Z>T7D~%Sbms@CzS(qL` zrDkql04{A);Jp@knvGTlh&uz=u>jLp8@m*c4x6h@ylG=QZGo3sjT zOLH(zsUb-5h%X?OWpsq9*d{ooSv1z$rS@A zWa4X_Zu?o-BRp>7G6v-c5ER^Tb=VC&9;+1{!4{oOAvmUkvib;bQ!=jc%H&QH;s|)IKO)2Am7e}IG3E%q%-gXscLp+= z5*KqFkK(u{I5pynt48o5G3GFOHX{iGIGvQJG`1)?W|pu~ki(^*8D{-%bKA7v$lt=YwjZ5r5vJgCTEg zp7rm`GhuH1hx0USwEv7yGM@d@GX7T@{Q-C8SK9pnx9C^e{{c?gH6O}*exkAuZ7cfH zn2&6mf1>#x!55JBe=5%WiB5m2T()rYbH$RJ-JL^+Ms}CC;e?&jU7mo)`tI^`ES=Ks z@;N*%cb6A>@T};*2sKRaDVF!8B|Vi>X8+gX&c1Z%YYuH)|2hv$z@uKGw=ZRVpNHI` zy~W!<(#+l*`drjo-i_71ueZDykMq6dWnfPp_m=0toSWB29)!oVK5`y>;a2sL?_yDx z^pVHF3*t;4`5D&P|6=bwprg35b>VZmySgj%sjiMvD`yD_L=a$%4F+Ruu#I!Z*v4az zXN++er_C1S0?65#u=b-Z%5Vb?^V)T0d)v z+I3QeK6S$0-~RTcz}MPn7zwh!(Qf1d8dt0EAkA=Xu$Q0L{x*swWNO<$L8)k>AFMT*@}$xIUMD$ItHvsmSlEK&Nabj-@*Nke`q2MMZv# zGkQ@r3CF#?=o5e735)V6o6?)oKD}w!4&3ds-c;mwy{R|gG&tVtO}p{)9(_>}$2mCe z!*Ml^y!=TVt8sjQ&bh`<*n9Y`j5h{>{btRe;KyLXGqY;U&t^=#eCAco znVD_@C)=o*j|K0kmO4eJVCUF z%MhbWzu-{goRRy9@on2+d(QaNupM6@beDq`@;wIl*_1(ohb!L)nS>)|zSW0AbT5lI z+(HZLR>ruqhc_p`Eli-~y?)eaGaAd}YkP7ZG1||~O?&eJP5bw5y+!PIZeVOWXchbN zO$nPXLQNNr^z$~>ACbmwtsbr}o_E9cxX`3-Fu~;W2=CR=G;A34fwX+NNd*GF#w3IN z=fSYGxea3Y4mLp@K*?(&#+`TJ<1mPdp@|6`blr99Ec$!P2P1efXqhjiy>1j>m=;xn6@ScR1?}zhi;aZ(wnZi*p$Bgf(8-;TCc$B@G@>{1Nv7aLX_E7FOvb=zq}U7ULa zd;ib=s*Q}~6luX|I3>GA!$(#4Y-$AQC(8FC$rN$71*F8+S@_0C5G!c#sokP#2O~fM ze8hx|N{<4s*-PG-!4agd58E|p_83~qj$}h3>F7wgIFeqCN*Wzahep?3_TleS_%Z#U zH`Q)+!DE=bxWkpExe9dHjGIdatICQPa5)s4aB@=%yAnhCJK<|VdWUOB*&ogp#*j1N z`2s3LE3=?Ei*XN(C3j<3pIEv+HsX9NJzn>W#>f&7F=%8JZfliU8)_k2&41)CdX$x7 z1HT?GeLZzMW)4Q2F^-iI4ZkeqHqBx z#}fr!;!I*KsvSvTyjpf<#9CZ)S!V21OuhRvZ{t>XHd1+7RpYREeJDBAxByReqiGC3 zDQy;AgDG@>v$<%ZZ;SYuNITy`Ekxa0ffiilR?xD25zcnD9EuX>9{1kF+`YP$?=&ua zpp}x1!_?LZvvG$TTF=3Iz1{9Osd z7j1=}Q`X~f=eguu6e)WC0iJK{%ZF%gKdjQ$y&BESoP3qv-mo{~gQ!>ihVS@LEa%>I zm*TMQ&4}6|l$?H34dQHL7iR@t!qF~lEDjewbZ)~v-1`v!k-i_fH{b$eKRSb^@;(`6 z;b)_}L6g|~vj{Fv{A?9E(#h|8m^R=lr~fiPfPy3cU0Q`3$p69H3x^>A?+rY_nSihZ zhnj#;g+q^QDy+pZFPr9}Mt{uPH*iNqI9B6$q$l;Sz}sJ*OWn6|nQWa#hj4yNr(K5O zxGRso00RVO=e;Ps2*(9DuEQ}f55m4)RNIDUx!em(`U5G+#jJf6?{_ec+i)z+r_Rf` zwXOM7+lu=>o=?>(909-8Dsb$D*?a|N`NMr^m#dhq7Y?9o?Evhr2T=R80!nulP<92! z;6Q3$KakSUK+39c+%u2{*oeU>Fc@T^Lnu8k1ZccQ76(Ph*kwIa)x@}NpB+i1v zY5cEth4C~ZojY~BYKL#>`*ZLM3^0jfP1N{VC&$NIzRxAa$StPI21IVAry-9qM@^7O zpwcYwkjN5?9b^6)%cs~Y2~mKjZx?v-L18#b)>xy4A~26_Jw?)SQLBN}wXcl=Ki6QoI*N?4MdU}piOPKpcB|+_wV-y z$PK%;#7}evRmxtb-Hrf(sdsh++3ScZh#*&;>F52V&;_gtndo81{ba7^9KNi=aJJY_ zriLeDMH?PI!B5VGYttjh?Ql$2z5N~}O!kj7UyP#LqJZO~hogSE>*t<=K}>f++tW9U zmJ@SR1K8cd55~S8k*Fm7?vwA8;xy3>^uC#%ChBBja7rQeZF z$;#-D$bn?_IxK=24J*5n6&W*d*>m+Zy(`JhY&iB!GC#8f&vT)%@AwB~YI9%B8{|Us zrzgEf<~`!O{4UI+zJqU&i7kBn-XrBLGArI7JpzwvL*5|g9#xLLPHsJ_-hG{1Z@Gjn zd5;`<+;`%2vaXdkuM0WUI${)H((T;0-z0aQZn*hPa{L+A`%QA?ndKO3XTHSty-t?A z#LcC9UQwsQkoby<7t#M!74Klys}i2C{8i=1$E502Xh{0MrcL;mjCrj!EYz?0I;{g_ z^mT2(J7n1F;qzZ7i(Y37yVA^C3fzh1BI^ZK8>;BOu<@ZbJ~zq#OVt{3=Uy;7i% z(qfY1Dj!Lc&)Ufyg5KS~I&0`W*tmfV$ zIT-J9Y*7eEvPfp9GfA1OV;`_Sk{!z=wUJ>*GRdqM&!$YEjw5G*`zq-qzN^bA-pWjp zoBBFN=2T^FLqsK*Vd#Wlkc zHz$%KVK5C8hJOU{K{(36PZnoF!oo-bc1WG%#Ag%3cSFEUy0nuU%CdG|Hf2!nUI0DhKIh%b_PU;3&tT$?1~kfP>tUhMD?mn6)DIIY^B? zpC(*&koyd}y1Q!R3I{P9OHp>4CLhx10j*ixjT&RBa`yyAnJB;n*ht~e8%ieo&Ed0? zNxl$25y7IY38#|CeK~o55=;lNE0f52JG+)bvQ=-dBr?b$=cXcBj|;VKYn?FuJC%JY zWSf&avsSt_6g=V)mL-$bUim71XD=^)&RdR4&GIX~lSqkQ=f?0A5uWTM5T$wNrjQv? z+{K_O+EtiHjz(i&M~dSas+Gp`>mH2vqM--zc9a~HAoWdw<!w%S%K&tt@gvhaTNCQHPX6bzeTr|_nQumdS%pOxRE6>Y@o65&Gqqei&0LQPd6h@& z;e{bhJLw_EJ^g6t(>u8@(m-J`rBi4n^&@=5RoaghYk)l4muLV4W2QN5@ek2J)+i%+ zn$-`_`}I~XZ@2PK|EA4<7Q>auQwpVYxL==@5+GlZ{ihwIK;h{%ivOeo0pGZs!~DnL ziom$sTlF7xkm&)n6Haca8oUVlI8?}m4(+A`ph*pZLsmFc#Jt$8$wQoA6Mgi@_s4Z{ z{pf4Qv1O*I{3XDu+=#>x5@!bx>91N2UJz|{zyzaSGUN>=d6$XqFu{;A#w;Vu($t4+ zgZ%vkP`MRtPB2~7R|c8MV=C6axik#h?LsOeVKa@H0qZm$`_7x>nt=lYpBsaA=8iSG zjtTm_-sa8{6M5zct~bS`9X7#H^D$x+jDdsx_hS%_bIO>Y!`L<^*u==T=0$}pV}n*H z96WfWH#oypGDLVEhABZUP)Uw;SSVp?@VMECF9zQ?eOiwR{jNCp9!U0}ddrm<0`Co! z<`|o21}7=@12poWiQJ@c<^OzE@OPo3vx1}T8Fl^;qwD-3^q>79pc*VN35JQ@HN{Od z(@}N5z(4pyd{F^^h=gUqjaJ@Fl6A6)R8bLr(TAwe+eD7zhr$|EDEhTqMJT>3*w$nW zdn2#&FP$su8ndnmjslzdx?oc1(7Sob)cCg_@*<7?>w|x8`!6_d@ScKybzuJAxNjKN z4Z$ysMH}#*zuSO!wrfLh@xOBOh-Dvq>o>RE+PKv=sD{4S7*y!P+x+L==fk`Fhuf@s z5vI+-Q`G7dvxvWik$GEBG)8RKr-!Px1;eQEsh|%wx^D@_hE{F~en^c|+k;O(@v}G5 z1xioRPT~cTyq*Sr84)Uq{#1kpqnrx206wDPIgy;AQn3ieBw?Wh3P16T(QQXC{^?U9 z*(Hde7C$7sfeF?sPUpcr1&|w^vtn&M(I$--5#dwVB?2QOR@<1DdE0LxvDq@bjGT1S3|V7$1&SGB&^$3I)p98-E#ey(RzoT z6nb`d@KfsEm`?V3#Jqa&!4>s%vcM}$H(p$;x3rC~M;7~q9Y(=gy(vYMn!Um3DNZYAc19c>QAtWCKBH`CA zP6?qg$ATYG&YRLFE97)1j>?aqP*4z1@>=k*9IBbL$H2*k)``#}t85w1r!YA7iR`!t0kRDQ$8{zObazO53cU}!Ax9z^O;iQV$(dvHg z7C^>0qmPD@Q%+ufj*F-F47h!Za5eWQScZGN5c9%$?xJuPN^T10S6tyQHeHOQ!6?VN zC^{tSr=1*n^0)L$0(~I<22&leNX>{6ijc_hkV`D#yD;rqBT6`OOB-S*D{RUDJ9x>l z`a?7m_kKHOrEf5B50I5o_(j!`k!Rawl%H%@=5uTf+hV7O>@9{fx{x_)87*hOyg2ky z!_2Fwv!ofhZuTK~Jy|C}cp+wRtBuMMP1aeo>S*GyyMtbI4~U4ssZ$ zxcdj1!ZjeWM;0lztNojj#cFp10=?>Z339fRq4Fpv8<9n(IYFI4PP)@k`L?Io`YclJ z6K`aZ!#;IYQ}V#4UdkdX!-Q41l`vngW}u_TotA}vjN)hfQokl-TEv@>vm;?X28*O` zP!o7dG)6Ol^U(}ftc^~_6+wrMEAEPM@oF(#sWmP^oYaIYOHel=@=$^rY(mZ@BywH- zr>J`%Ni1tZ_9SbN%TnM%-57mSoAL;`l%^ePPDa&hJP+YS>oK&}H(i4~D*bO5p)(R7 z4yw;ke0TkC}wnHQq(wg+#~dC6ZLKr+B<6=#mS+j>eZ%nQZs3O z=)~#Z1{&C2AD+_ALG>}7v>sU>*x9g(24ucff>+^LYZJ(YHqP*3Na52m$Zm^}(!qMMO2CmWIEL^dWv=I`bnU zkUSv*FQF(>>s60jj$}x<8_7^-dbC!N2}%^6o*TnAxnpCs^J!#fobL#hpYht=2BaJV zMjFY7=XV;>J0vpP;o!sz_?aH0M1qifVrn=tZlp3a(JRf5lv`3)yOyslrcyl-Rm?m!yJCc0qCMM z(}D26EszqlMPOlf!6r>`kYl26k^}Lxd`LMjsY?*HO@0$)D&TU6rJf?dbF@a`-{=E7 zLwaAvx0FE#&mH36Dcc?DaR;CR#?X%IqdkgpV^JV~Zjoz?E*V2YI%A4;s!>+d)3&(5|A8rj%!qv6_NvM>GLpYKJ=2 z9!<#vC#I=NXE+KTcLwoUopDQJ8k{;|IE0RPANW`d6_Ap$9*6Y%Q~ zO4JG)k)w$W36~IFpb@!~$WUo@k~X6`2`2O0p~-wF^7ix;?LGooq-y7zkuj+Z*IJ&+ zRy8C&(s=ryw0_tLjm?ZHZb&v~h9hNULmmrvcEg2qQgeEvv6|n6?r)-;Z$f8eDWzHT zP?mBji;it7%{01S3|1H$-U0zz>-Y7QM&?sNPw4)Y;A_;F{1?4d=!;9iDG%rOHb2kr zS15YJi{c`Qya}}kkQzrNgsTw`*G=#U6O9Refy5_xIF4e1e^58U&olO13r78Mj)XAL z;!+V2$pl?N02&xA4qBxfSP)pB#6HHxmkkGO(g~5AwF$5U^b^HJB9!{#A(0kJ;%%93 zl*GLfEFo>7%#$723f(MA)iT{LdyqL)(T*r|lOl$?Tn}2PI^4?_0XIE-5pddVd{GVW z@&g`FY>3+cTC|IZ#5Tt+_Vkhqc5$tnOlQKPFfyBcfR3+<#cn{@h5bH+u>#T1WQUmJ z#Rwp6@Q^DGD9&eT;%pB%euWw@ndlUUco6Ijs=6GP*xN&vxTGE^=Mv_7Nrl&O*-L{y z`%WKSs8rm=Q^nr(NkN))8)WBcc$@pv z#EYP*uP1KHAY1AQV;Yg2^#BDTyVIo&U_wt9D?-YxV7d7hYl1m7`YWOId%-)l|HNCp zsmYie9gGQmCg^`Q86H`GE8*v!G4cO;KlC)<4Sk2V!%u>8+si;Rv!}TX2fe<*8t@@k5FpWy?1suB|lA7?+|4`M%+OSqo{Px$hPMt7@0}>9OE4e_z0qR1RY5~_bIGLX99zWZmU!AvW*y2yl6w< zb#awZkgBKBYsTVKJwDG*_4P3K)pRmGQ5;d9 zoKKXNq$8THFwc1L_vl2s(#fb)ajWse@AW7)9dQlniIdX7L?Tp%9@N*nS^tx*fPt+* zRA_US-u^fHffqubHpBKL0Gu-LJX_7DtYnB4W7=7(e8)xYd>hr)*|j-dvW!I_WfudS z5dk#i)gCg|5#Sl4G=_|68bcF{GLwzp; zgzd9KyypAWjhg6iXyeg7LXGr=Mv-cLlF2nX&xDgXf-nxsN^3mi<>EHe^ot zLe{ruC4k~;{SXe2{ z8ewkajt}R#3&Po)BvKnLtW6?${${w$GC#vL2SvC~CXq=IP^#ocdLgcle4;b}TBs;q zw=n7n0E46CqY30hR1p?-xp6)uua4tQR>koqr(ujqK*)*=GN9h7dSvHnp3AOFm@VD6;S zc`sQC!xs-SKFS9Olr<-e1kK(`FCucO@WMJGblQz{t6JoR1ztn$DVukV7bv!eb53&B6q&V(9R)e)GQAf;^O5y-Ec7!>f>&NllyLY zB07$TVZQe;B$Rp7{T`tEF}c%R9~;~ zm~pI)1gU8Blt=`km99mQ?RH*%7mGy7epR{>0ZWXGlxYt261KcrEYinmT&=gwS#L^9 zL|gEjG-KPmE}Wf3JOw{P*^v=ko)QrYd0hm<6?R8xvvH3R5k@4P7s*yd(iPCp=`s!6 z@nVgnZuBCIgF4eo$|M)OGH%PRs~)mXVFx`}@iNGR8QTMY3I-1dGLx~(Sc)))T0K-& z=pj2*Ugo08%iKl^ocA30_?!ydtQP{_x*1$67P@))8}1#n#!EMN;bXwNNJd>J;ZT;} zC3t%P#A15(JUFM#$|#W>GbjVI3zB9EO|CJp(u7}Oq(U4{4>8!`7?LK+422fSVy+4$u`o_0HS$i3 zOdFUB=|@@o7zbUbI)0g~-=pM`)WgagY>+R)@6?w&d-N{rhwlXi9|o5C_t_ei*Q3$E~f z2jq$faln@eT3e$?abkaFU2Ub6ae zY;w-FnnfWSPprlT_qO4+9<=!@s2SSws!N`PX)&*zw&zoFs2xN7^X(XJHQ3%)`Y9MV z8=%PaXVL>@pORr67@FDILED3O)WM5p)^waqkw5-9ZQUQqpy%14KajHL?NxsuRnN2O ze};?3n4Im*Tf5QuPZ(m~ zl`nimuDyE?w&}|sv3=nE{HRvUkNKAF{@ii?bGqUSb=w!T_6wH(E#38{Vl~Xzm zvSsH}KohyrWwf8;RtCE1T(`2yO)t9HT{m6f5!ZX@HqY;uce=c; z0LKl}DI2+D?PpwWtAF3MEA7&}$tD^2iWG1LO*gr~D74k=nJfcTeFlTnCNToJr$w#| z=^<+q6<8v*9WsK1v)u|^D$8qSx?5&r6na8#2kVBywkmX)!ZX(^9Drt&J*j^Y*kPiZ zOd;w*;F|UHZBXL@s2onR&EYfQ|11ch2#iQ}WR?hA$5vxYgFIT~U(8fVn-xZ`N#ZIW zSs)8le#~r6$!^u<7Up4Zd{`Eef zl-m+o{+%4n_|%d2dQ72}oZ;-?YXk|zOWOtEhLv0s#9>x4*D9782OW7k0(o_d`;9KX zxUY8d#XZ)HD@1M?uaty~F0w_!Al*+E$GONtnJ?uBDdIJ#^c8WB3yME+H!9f$c$2T- zi~F%mT;?SEm;f)t!<<)skt*&2U%4ujyT~|)aMDex9Ktdeh~_W^S7@p1!k~`_;)V(MLR5fBwGFj>ouOuU6s4&qu`huQi8>n#1uxb(7q z^w(R!7YwZ6HyW9T^!GzAzot7)0j$%9Q-bXz{<_q7;556>v=)Aw0*2=b>jk9OBadi;Jff`r6Yw3u}DqKlNNWY_AtaIzB zIq8V!@;+9`>2g6j9g!|iN~g2ZvD(X z3f-wVhTG{0<(Kw=ztC?8u+aFMPVd;UV1i9!j5!+OQZxEZu_Y3iHUQTO7y{uu_fnM| zQMuHKKmbT8Jn+{@3?;WqSbShvZx6uP9n(B|5WM#m{K&(EXo&L#+{#32WF-t?qEP50 zQ{&QFAnM4XaC7^%eZyn*dROh26dnk3ZMJ?l|Z%hi9mh4%gJF8eO2h z4|$T)am+~V(1RU#;#+(V%pvaGWO^bB92 z13~X74<0hW?B|wzZBhoFRcu=H!c=Q^eGqYv7 zS(2(HxDInik?AH3+LyZODMsFLI9|m(&_DS8#fkA{>7c4Yvg#R!YX z=v5})V<5g@g(@Aa@)7o@&$RHNRrx5o#8DbH$VqA`+CFVcfS7Lv1_jnSWhAIVt;z-m z!XA7I6uHg2UnNDNaN9w4irO5N)QW7oO8U#GNM9%qLZ!8Kxl$#E?F`LawX+M1+_wX9 zTEZBLtYMDUD8G+s2Dn97GtBGicWW>*Ih1k-J>gKQ9WKC2W^M4=eIjg`N#{`I6rs_w_kr^4?alv61p};u=mV9EvvtL~S&W1@S_E3FK++&im!+L5nJ;thmpmpFtB8{#Z&jh&)cQL} zsj5_}^fcn?sI-SeUh1F&9c-0@j&XP)&v)?G^y9CLQ9jo~?*e3~Tx$i@5%;qXncNkv z$N-BURE1{+@yO$3xV0Ze_9PpJ{}zkxS&xx?*>$TWDOSYwEy;eR9&&T-+(HNl;ewXr zjQSN)f||YP5i-E#oB0TU0m?qGEPCB#kCPGJKr!yTB1|lM3>)nl$g3lw3m+keBLD{? z!RXIsw*sxt2pvLscR(417H=AzpA zdTi<=WLLeZknd-R4<04+>WjM{C7bFiQyv9n7S@Eb8;IGsN&_*l2PXIi%2e3wGO$#tas_fsLC$L(rIwZ$_#cv(e2+P7^k?8R_5TMcBTYXlL7!yII=l zHe_H^ZABY0p{X{q4O!S!yV{zpZJM^WHOTd}v8_qZX4>smWMi{ehCE5vHn(nTPA@fQ zkjn#)D2E@RXC9eOaklDFW$UB#;-hauKGV{Ay(PWd@=3_IA6LNcIJgy;@3mqhTGQUG zWys@OvsJC>>^d2A07Kf)oF`sF`sg;Sqz#?khL>60h8=A~tJ?6ZoomC6x23sl<%4bM z_O>EYE(F@L{!h|dZF#o-4YoLJmrE`A6!HVBa?1&$ zruD3oWQ#G2fJl-UVj)#aVE`2MkulU}D+v(CD@9n>u6D??fJAc0lbvKgDE~1`I+SVH ztY``v7^lf#LRq9a_qt$o)NFx8thUgxcs!trEGW6%{+2guA8lN(}C_U`3;9& z{$79GWc2Q!H#AtXKFN6R-}NgYSa)OTFBb?8cV7Sh+Tg#z*i*MQ2z{RY-@PoT>Y;~B z0ZdFebO#mqBF-cLB8RA)`8Lc7;w3A&WPT0Z%pz{Lk`0#EF(V4%Vk=bm!d7t933Kp4 z47F*OY;==NJY%CfZQ?P$RC8c?wp0{$i?m!6w~DkzbW}>TSQ08E1S}L+Nc5znmB@6g zY@aX6bcW0a0b^i|?)wjxCv|(%4+1b110}GGg&}vZ3sDp4Dc8^6 zIp-UT`QVt*u9uz~co?(Fz%PPO)DYx$cyaa^Dq+l*Mi?uXgL$E3NY}r0n)-sC?F^ zLB12?(~x~OOt}$8`-dxq;dEkn%TwX>&_mj~kdlubLi#Se)?F5Lj+LCSypOp@P@zTH zDnQvf#wt&@()ox#Vx=psH{t%DuX~N|z4eN;ak%0*v+IC>feiE5K#N)~kg=Aj=oD)O zwo9Og1y(Hpm-9{^ef)>@VQ#=AXxFe{SYiJXB6AulNC4k~CP9@at8})?%2m3oZXw9m zOneu}U8wQ|EHngHr40t8?y%}wor_@AwQ@hL5}RCMqnmB)xQ*@)z1LTNjfQi$HFt1X zO562Sp`{Zps-QNjF&R7iAxz<|etO4_+#w8l>c44I?EpGz6R@i`Y2yI!Gs}<*EW%}A z?}e3S@QCjaBam@PT#bjFV}InVod%g>C8I+a=IB5uufM*O2D(5ixCVE*Mu^xKg|CC} z>yB7Iy~bk@us%_sam2=_mo(mDL<2E$1x<}~zLXP1R=D$p!BJ$IU%nMdN+Qy3Mv|iu z9EE>8s_VvRTK=<(KXhbR;2;<~{-k-JSG1@(K61>$31d-3oJ&$A8#Ia=h9> z9~lhWgpZ8(vZX#U*UQktN-smLO&5V*v@k`{IZI(UWKTfs6x3*of+%N_gQ zwS#;ETU-Yw{x7$yKPm&$hv|vZV6zBU;X?C}&>O?_LK+CwDgH3`Lh(1T&ZTWU-K5#Cw)gcV^kOvOof{WyGt9}g>e=A&^k8B&txW&1lABOAoP3&Bq zRX@AVs^8BDjnv!Fpiw(gZy9?&os`CkhwGDru^ipoFHS74Pm1Hj-WlXvoOsH3xlj-D z98D+V62*LAe-b5_^lv5#yF%g*!nc?&NfOK)v%d8IYmk@?{y)-tOd9#ypCXi z=}xXS3daR5G!~0sR%;Rgd2^NqTiV?$hKK6YOq_)H&CO(7W^HqRfrXE`io26LkKHvb zL)hmhct+(D49%6dap9sP+jM5#l*K*&X7jrvffqW>$M`_4j1t(=z0RPYZKF z_TAAt6qdn`hUrjua^kr-tlFNJOS_Z)&!fXuKF^!X?!<0?OD=cf&vCyKPnq?Ch6V<` zC>Y(zh!>k8W!KBRvCFTpg6?F=t10|7y(%O9=Bv)*pOLE9ytf0Nk^DCp9<1<%KmYn$j+xOjFx9|q1{0@u~^L{7a>_(RT4la|qAL%@K&Bx(Kx{=WD0|_`O z_{6)p8=3S8Uy1MgLn2bPeaidV%x=8>72VjG&&bwpvCF=vx4W_Y&p5R;ogVGJjN)YR7wp&=$G{Fm#qI+bl9IJ@r+|%t2JNK!f(`B-_RA` ze20{yfp697Z)xzmg*?CFd$#X;y6yX43`iKY3e2>Zxr{pZApC_%z0v?-Ykk`qVp}rW zGAE0um zq)!GLnL#IHu#F9ANe0`ULDyuw1$kwCb$fk!y}p#wfKF>5g&NS^4WyF|XunLUFq2Nq ztbd^qJ)X%PWYQ~{EE5%aHDqfV(xDCOK|a~gF|QFF*GNX8rH$CNMzpd~EaW?lSV3dj z%h*0yfAOKsB8nZclX@I@JkZpeE@xz=S%7Banz;q!iGonUxb}Q3PysxY1vw&f8MtT%W}p$AmKAxroy=A^j%=Y_SYfAI?7}`ft+6{oj80{8Z;kF`ju9&D zt;)UQ5Ykh1K;==cw&mbtN3KRUJ2Fw`Zk@)1gr-f=1ydvLM}|Gzx9t%byTBy%wZPrz z4?qZ+)tg-9+zrejW?8q;ffjj!g%(W@kk|CsbUpXyn*ZcQd0#ii%+Pb2{dCQLp(Zgd&(a?=h8635 zYzS?;()9UQBW8+xn{|EUs ztQ7QV0L+6XI8oF7KN3KM!a{l$t(%N`QU%5O|*_W^WW%q1})JWJNb~1YYE39dX_~B31p4& zWQpEVs(Wg#GBkRW=qvEIvXN5Z1>ja-YY-7l z7AC1mtF+z*WxR05Ms8ZgLK}Es#4i8D~cQR z)-iBEEkYX(VO|{U%1w|nLt|^B;dq}8x!4&2!mvFqHY}19d)VYCQtIIpV%I#bq0uDQ z%eeyv``MBxQsj5ei6T{gHY17*t)m$mA0hWfbh8My0xd+aoEUO4;vlZMI_kXnWIU~i za~zIGP-o>`oa~P0yLMvStkT;Bj=}hP%qo?_hlAT>r`zne0K~F!o%=dTn(QE3rOwda z%IXP~ERo;h{1Fng#f1tsj<*!GN(Co;B?@m-!n&}j~~*g;o0 z*e(YK)?a+r7#zUP(3mX1T`r)m@n$%TD)6x#Yf|>WOV-o|yzz@qg}W#w{%rV~1FFcs2TYq$7cgyFUBI+j!7z5} z$?nNka?~vD152=3*lC5TQoL?d?bH+O?{D28%4)`T<#;}E& z4d@bM-(EdBWnmiG7blKM=RS!&)46leU@TYS#W`u9VGz$C>{^1LP$Fng0f0PXJB2$xv0;3k;Pbkc^_I910MWJvCl*pr%ei1q}l%kTA8|U%-Js4Arn$*wmv%c0yK?Ph7%6tMQa4@eW|cmAz%8G5({hir%|mNEY7hts zuR6y|w|k|dUV7OZhBA|U%0eG4^@)eW=p~2 z_rl}>;WY1|76^EV$YTIHEx`jWp&lr9`eTK`{o5t*OHbn;(OHvn0~4ZIDKyiGW^sX; z7XR$!&ZlV3k@CM&AMmy9^v?nroh;JLT~2!NsaI` zfao{Q1$FL8{m*c__m4ivQO3oS`kr5Evj1YVKdpD=ljGM$gLAs$$I0=@bNWj$_<%2Q zG|@&u+F&Jn1p&l~wSqXxNJ<-ZZj0K?F3WPogx}=|d~#ZpDosnnPFkFp7W%_w{Y61qW)eX`RBn39SXr%~{`m_J zaStI=7NB~B8~CTu&+-@|i*P!ZBLF%u#72;GpjWJ`F*Nm(q%n4wI;W%LQlnw5o@IPg ztG|(1hVwG2!c#HNB&{`rDnY_XU(Gw_0<*HpO!u0VQ)YVEEIu&Po1rtcdVk7&(|)#K z>;=h2{vCa^k-1a<2~MWo*WXA6&FV;C$Rwz~`Tei6 zN?UAXr&ZWvBe$&LcpFT$;szuCzJB-rM1(=eO7j3NfQG5sBSKL$(<}5do-^ff4O3rJ zUQ4>eSZhLms7faz zCH)%9ljs40GE|^r_!mZ(2+CTnMOH2dv_^=nV;TKv>Ids6rLf3t77^e{)CMKMhm@_4 zBh#c5=;b7ya9Em*UKR+l^vQ8#Ez^3(k=+cui_lClaHQVUbxV+$zwDQ#|F~-ZzZB*e0?hH1|9G65;LiJb zoI2so;|Rz9WSpA+ACFUM5&y*qHQJNc{I|>b(EYHy4yIPE5K5JMUyq@$mD}Ls17+H5 z69Z4!Y13l59bpkHFjWiyD#Av_2^=7>*5-$$3(-sYE;$g9_eIwN^q&HAw_6IiW9GCq zV})<;0BKyeh#zKiO;t#asO(V407@ zOYC%zU4hEd&KB6|bi263crh|BE0ArWz(8~D#u=J*v=gZoB1=1xQY-7+W(QY10GBWMEHiVjd~xKJ`EPc`*i+} zeNiY{;{WUNXUY7C?=VtFPNa}GMOESXM#TkucRKWo3!f$DQ(U8;CPPxa zkhj!R*K{OT>Z!=y0{p|XWM}>1bmp^UR%Y~(_GENJ@5%P$UPF0*d$PQdFQ+~54u69B zu(5op9T<4I$>?SiWzAEhM-~Xk$gpPW?x)G3X1ukL&E?W|WM}h$hTEI+2wT^I%xj@m zK0{WvfZHCq+uFCLJz4hzD{F^<;%xs@Ogw8RdZ0XtyI%(ka1G|w3_z}o&Jov{26-Y8THOHbW8_zZU=g?gPPru-su2^%<+!ujgGYcvl06{ z(Us3Oz4a^|`kZ4h+Ig-$+8OmcyZAg^_k08zyYW05)QR@Y%Z6WiH|PVJNd`9vq^ z;_3ZPPISB%*acL2;e~52SUBR;(4+JI?a@M$7k)EE7WInA2{tZDK%sNFOpsQJz-B2k z;7u*4w*iB7*&?gAn}W>64SAkjfk*!UW{13+DGNdL z#{t|Ws=cBq>^QB5Hw78Me%!=zmohesRJ&EQQ{;IO#VdVw$Vb8>QRZ5NS_uw|DCK$+ zvNKxUm4#RzN`4@VjEiG=O|Y_Z&TmX6Bq(5}s7ZJmDgBb1-~p&iS})+FHr<|$z`UaY)?y0tVLQIGlQx5VnKm?LA3*G?y-o5G!FY48NnSiX zYs6;dHEHxSD^f?=_U8%qL7|_zAF=+Cv_dPeM1rCjOwW8|&dJJ4ZItmCzs~DcDD-}N zvGde=9Z@Sx;$Rc)^P`&2=-VzY-nf;O_eWzxyS&s;Nz=SeH1KeI2W2uDZGHeN?}Kl-Vg6ek5sJDe{Y_Za}bw!k;o*BF9V>s6RzaD+hW{1n?#( zrOcFo@sUdZZs;lkCn3{W496`_T! z5*ert4j40<=d})8LY+&dmsGp?3pr|Ag$Bk;ek4^YYBkE(lUKcm2(*9O{vJ8(NS^W@SnHz? zcLCRILq;@< zu6-Mm0|Ky-8ILnudGX^6cah!7x-5Xot*p4Q$*uSRbh?cXH-E1USkuUucIw5qF~GA? z?~syq89=zUE1}38-M$w}PU^tZ=XE#=dGPb$JKiUAo)2I3K3V-j^rUym;TPbRPR_g> zeifH_b{I{*(*6`O_fk5E^Of(u#qX@UV_N3Zw3mHtY;X>p` zcG*Ht|GEwEv!K`&>IYM7z9}Bvx305scu#u@&z!+~@ z{p+Advq2O1k5BQ>KSU~4JD5o%e}6luCO^REGeY(k*vUK@+OqzNA2Zq%#7+L!o{D?( zLWO4Wmsvq!_cBbyg1^FwxfCW${xwCBt$4t#CjVF~bc@6Ba(Y<(qphUSnt;z21)|AG zhFOdGp~Jh+_?yge_$O;*qyqyU|Mw5S+du!r(PU6KCc*HiJ27Nd_-x$h!6^T&7;+AAOFJd7fsIL^_`Ram)ZYA+?BvtHNO8dGc`>$Q#bAVZ7+%`*;;RVD5g!jZfR4| z)uOCt5Hkpua6@z>#4j3&LUApnQcQ#-Ohi#>q->S`&+|UtcjnGB=ki9X`AjgM2<^UKt_r>wKI%#}QeXDlh3 z^laITSIQ=xEL-8;)TGc9Czo=eUtC&&;X2`neuCPb9f7(QhUub)~^QsnSzrXip;b zrZV(AF75s@^fPh%_TRiRX?OEa1J>J7CTUHX3Lln9qLXxVy}3+LucplK$L;xJUTc!7 zZRO`$73iCqpCs`S*w^WqK$)bSaea<6Z@!jyJKbx$HE9pD(j9f22k7%^JUg6sjD9pE zEtR$p?@vp6ypFRdjo+;bSFE&+zPDEKX}VsYkxIYT`d&tWzF%Ca{r9zv6fS{Hyu8zTK+J-&0jq=zlQUK zTRoK`d8ay4TdPyWtgDf>xkl2#8mYh4NV=(J+TAsiR@cm+A5Gm^Gvfg7IHo;YE9u2r z8L!t(+C;Ck*Gl@N)@-`oPQRm8JLy2}I&^(kos30wk`C0lfUaMz8$ZwYPwM?E6>}Or zndA1hHsy>B?dV}unN(U%M#^N+;jpD;=yRI%%prqLguPvEFfEqN#Q02FMl3Dq=l?IH8E%X|4VmFX146Uk+tO{U*9sJ}HOX=O?y?)$4B zdG}V@>FnAWpPFlYV@pFCWr|Nl;Ez5EqbB`w{QD>vw zV){$JbEb17wae`@o$VP_BQxotR^5;3ZSTrew}hNszKrD|=X)Q&ckpG^YGiY`DnH)H zt=^5Vt6w*7mUFNM{UVd|L!A~khnz+A+RO_%o9gkD^Kip-x?bSVBFkGEHKJG^YL))< z40?@mo?DJ?*fXo%I@5WxQ_aOQogJO{S69C3QkAYB?V9oC4ClM9>2$p?@Iu+w$>@TI zk~hqBHePZmanWH_w@-J%!!wB4IjTv{OlSF}J__lPvGbEZA*0J0{W0D7{<04!m1`zs zJU`pnJfYIjS+dcS`%fAj9O%Q><4PN}qXrn7iT zb=qQ^(umT%drGC(u5yk{sZGkerq-r+6{kK_hOWP!o^fP)(xK`6lxF=^8TZUc`tGXK z6IUhOGb4S;jHG90q`x*J>FJs2Yi1_>HYDPW~xt8Rk!veRMq2tw*26Vl{Y}Gic-&&DVqpZ6! zb>(PhafP+1PmHEd7^i+Yl79QK%E^(=ks29~jCP){Q}Yp8(bmg&Z6tl$Fyq+~&J+HO z--kPqvonqkcb+^iFnv5@#{!uvD+v8oe|E8zUjY>aQ2E==C4~$BRj$)e+N2T8vO!|$O(Mj`0r{|7I+BUlK-D8qIx-^5XBN=1T z=LeHs9K%L$j9LB7OF8#dtRdS3sq|C1xA7tHohcdnQ=Gl4?|d^SseB}`EvHKPGidQZ zF6)fQRnh4+-0-H$jBGKmS>(_Mf_d)?MqyU~z5hM4FAHo*&mpw`BGr`mP%DBvrh3~(9l94SxM9JCDh@RG9#<@E` z65=J(^AN|ijZl7HLf;fx*Id@RLC~GPD;M0q<`0$pi(#r2DET9?#LIC zfO3$JL8dzp<3=n0S15TsGGzS+ON39a=*(6gLKCDYZ4ud`ETkuM#zn@im{K!2aBbwL z@zZNOST47``)~!LhlCA$LYJJ?`3Z9v?ph|N6kw5N}f~MUJ<7xt@!r%Fj`%&1V2$Q>AYZVrIx4eSpb1RM^$6nGi% za^Q5})j*q&8zA2Tyd4+@K0wIvI}d}10-pjt2b_zNSq1r3;2Xg8!1sY419t(RM9#m3 zydQY@yGbT~=XVg!UMZq-z~|tx66BX4uLD*CzaDZO$Xg*dhnxxQ2)qC|e6K4JUIt=1 z@H*hFz>g8&eUKjoJ_TF}Tnl^~_yzoJhWrumE8spC7e|06fZxJnd34%pz&gN2z?Q%> zfM)@_0DA)a1BU^}0w+we2EAl78i>~OIIRllpA%L5xwLF%zQ5v!!2V~&b9fCSqxa|3 zJu`o%%n=2%1heM|<^%oU{j-G+T%8^nxj(1=X@BQ23myw+i3j)ZJb1)pvc2X}S?$CH zX!x|pgwIAq!Ovw#4;ZcE6wRI|%nBlMvd;WE$w}!nj8~6jm(4AooFdoZS7(%W5yqRmHLQ1Z4MfjLTd+Jk6a3#UOqIAw>s#yx)lEi@w6rCw9x_m zo+1Hhz7q41D^BLrE7Ojmj-047zg~wVshSuwdBl2 zX;MHG8ZR&0%oOqFd?n>cJ9oy}^2|eYy-%BLcuJ9R=R>w@kc%LfgMNt8kAN((IL>&H zT~pxf=PDR*1+bfq?q?HnBlM}FcIK%7?}kieY+S!TWL&Z9yDTM#jXe#1c7^#E0+3Z1 zZ3n0bPN)Rw1ik=!yMkQ{*(UfcrQgaj556|SPhen+@C)T(A7pF)6J%QjxP92sbgCre^;c=vr@!%1?y9?F41AC3Md06ekV~5Y9m3mMEsDgy-sj5 zWv|-+=QuvYS$|;iu_YjlB5|A0McP`GSO+Edg)GZW$MLHY%Dg}kBUJ#GD|rrNyAth- z9OXuDm7Bfn?`EW>ofKbr+&SF2&airPgFOIV@9Yr!O!Kf0RiKzMBH2DM)7=0uz z0*TRW41R;+HPX1$3q~mrPQI}%Ok`XloaZO(tpj${uI132*hM@p_L-74+ z+UEA5`98#%YCd1WX$JfHWZ#>w6+e)8vZtFjrD!LLQhZB9tnDvLU>{;W^5)m`t4A7S z%}8~_$f%zF#?Tyb4y8}?L&57=?k{IQ3TM{2QvP=8nIp36*tujLD#Z5U{u`v5r*SEf zy9oi56VdtYJPFVaBPSr+fi<;*==T+P@CDUF4>{ISs31J~hVFO(EzrY28*clC_`4rDj#l8ZJotAIX&}7hPAV zB)cTd&z~6i=4!YjG=Cd-S1St1n3_r{4}4?g z6;M?QsHunj6k7Cq*$<}ikVl}Q15H9*CrAQ2K6i9Fp-#|r;=KY&N|if9ZJ(TgZ%aJ) znUeM{Z8RnD&@BOf6#`1ohE)Xg1}2qUlTs1W0}v{PhJ8E~{M+hYIp&XndhxFk&(Bh- zMgF4E`I<>X8WfZc)>=B49pzonjVL|rqL8_2*}VgqGs14K(pMZS!CL**kZpJo$VK6a z>^iL_Jf{!_Z6MblhjcVLddSxP8OW_*{~=^MUnE~9;aT|{BUANT4?!55fdKAN z9{P+gnV^>;p9cFH6GVS1Y91Z4iILp#26BPZ6@3JiTD5E zI)3ucj+@ituYEMAC8x=vAa;}%3tRkVfe?QkZy$fn17gx5^K8Woj)|^qS(z|V4(1y1 zn$4vR60e`riR^y0Czzybqzu{KUUQn=Yh}5-M#f%e0Gj6GG(t6| zYt1htzB%!F9kZ84E`CVp#dC+({O4rPd?ZkBel0g|Y#q9$`Th!C2{=xvDQ}$U^I`XQ zQ=X2^PSu*9O1YuY!*%?tkN^{X@v<`?i=RaL81&XI^xCZ^1RoY3c@5>ER2IA(9EX7+ zO1X-_p)c6?en-!$@$Fx#f>@pSY5-!Y!x+gp^}uV-L;*#QOF&@+ zkcGOAt(1t*RS`$4NJNSBZax~5HOzZm8dDzlY@|2~7_>P6+O`l-vgQ8zbWRU9LCg9)9L6QG|YoIdyU!_owRR67UfZK%W z|GLrqr)}b2s45dbeXC7cX;hU&$+{{%4pgVxm-DH;=o{Ov#QoI`?}#arO)IM)`KO01 zAS$Er&4g?Z&pfAgZ*GO2wib*FL*^R-b`L?e`&Cauwi{$CtX}d@C+$FFh<|6Jl0UW{ zpx>?ZKSH)oCXPe48LBu{a%{tY2^n(J5MV7B*c>&1Y&XnKg=}-s9x@Gl#&v*fOP~j2 zo1g)Z<;I%M8A}3O$0lGL3~U0{t0KQb>E}YW33MUb1l|tWCLjXYCLnA1BuY4yza6WW z!9ecJs7h1-n^XWn6F{;5oyz|~$Tma2LAF&;b{bU)*S}3+NCjAx4anI>)DW_@Zv)xZ zaVN+&hy5Yj5*iHIR?(Z6Of++iInTxs^`O7{vRh{!Sd_0PyLEnkND{15{GgyiC+=OP z`=ed#g`SqNTrVaA6%k_ay4>>Jk4wo!F?DHUe;0|Q;3@CG3M^`f{>J{Et9>FZ8JWg_Hhf9tPiziL+{dZ*0_TRFa@$+a$wzCYDx2k ztcRUWS4jdTJ9xlBVI?|I_0;9YzNeEX7tV{obwC&-)W={KqlY!-G^DBR_{Ty;kF<=Y zGaeXabEAlV*wqx9V&SHec3q$OovA(}{oUMp$uB-c zgz|YRUAsxcq)FLLp5{byVQZd^IX{{|jX0hSG|&0>;(7NgE*#fvG!kbv*}Wp>T8;Bi zWY4x<5pa2W@qX|nn=FlS{ABZV8PUyS>naT~i1U+b`&5~qZo9a>*KKAAg-o~U&c-=1 zEZMo^pqib(guX1?=W1g|Qgcj;Y2RRVvPfULPCt7JH28$l3qs?>;+t?YB-OPJz! z>chY;P>#nNkp6^9;3aoT0&EV0N}dAQmeAEopADJY6vyw}4#FnjLCAImwFI&a@Go;j z;r(5PwiPG9#P93`VT-K#yOTIwMQhZFC?VU*|I-8pNsyx#|?Eje|O%ZW4OB{ zQZI1|-jjs+&hpkfTm6yGm*h4`gPj~PY!ccc-X6hx#RD$8&*+bLn{e9R)oaQBt!*%S z8(nzNPqCHW0}h9YObt|M1RV2*@R z#Q*A^K%&(}c$=R>^C%R_c%B}I}xLY5z`jep1KLX$Di_}vyu zC%5-)Y1?IGn)i|5o{{3O1^n?2D9`$u=M%JEyvv6gycFjW&k4{0oktuuWzBaYjvvx$ zzFPu&zJF}SW4Ugq6=_AriAvPKV&_PSg{7hWvj+l7od=M3BUfhR)`?7BF~yhS^lls( zNhi>z^pprlrerwI+>fQG1HD8J0yBY@&w@Pu8L6`A!0Ujw0`CVt^33G8_g4H*iLchc zw!m|Moq*kdJ%Q%~F9Hq$jsOOM6M@r!Gk|jiUB?A64|oUg9^eDOMZm{_%Ye@VR{>uI zz6sm_+yeX%_^CmfQ@#LU-&^?_@;=}p;4xs*(^6vPfIgt9H2SYPz%1Yp;7H(D;AEgp$aKiFf!6_V2IdfQ{LXw3_W>6I7X$5kD^EdQ z4txQ)8W;n<3w$4F-&^?<@)y88PvhwDeh`O&zXR=iE6y@0@|SRW+rGDw3f{iAk`CFv zw^9poV_+L#C*Xz4TnTsxh_S#cfOCK!BjS0G?*T3ZE(Jahd=ZV0z<&7f!70XGDy8A2SoZYX$kWo-v@jM_$2Tj zz!!k80&S;#1M+&{W}sbr=5DL|W$+K?Lp zn*mz`+Y*}5<7^O}fZc)T1AQo&EXbDtM*_zJrvR@4&IQ`F$4!uPfOo%+(c=LSi-1o6 z?b_p6$a?LO2i~qdUWRPf9&bV30{j&C9q{Pu7(GscsQ88yX-!}wpj~^M4*5J_ci=_9 zVZbp!yY`p}c{=bq;5-)>Vc3-yyjbivB8?MZXKhF7Gu#-*=0u!F-;Os2=reg1|eYKbdh6M3kK&1`pS#H-^a=AaMnDDkazQG zbHUl(K=cI`8;NXo+e!b{c=ku}KYzajFt@)91pd(?FO3Kut0DZ?#sh7;OcHt1!=nH7 zGr_7s!L>sK2O*ry-eTVZa>WUvpMM|m+!1SBCd9A6`C~-RgggN_0GNY>UJUsFF#mpu z=u^L7J`%hT2`dA;Ip7~0HBknFUPzfu`IDqzcQ!n9ttSB6W$O;Mfy3K-z#!oZ@k@NUaiC{lG-iHL-4-7sc9#SU@UW^29fc--@htRLPQo>7t zoCZ7y`({X3c$6yveuNY@hawGF2G|&w1U!m>egb|DtPcGtz}mpdz!R{m1i7J$i&{Vj zSPobZ_y;`x3fv6bii9tQyla-J~mbY zIv)|7yj0|J2;g+s4F_f*!q;Fw1$YGh#>3xov3H%7sN>&&Pa>eR;UNaT7Wg#a`@j`Q z;5x{S;O}81yTPnZSpUppTGIJMdg3vg_Oi#V(9k?Iuc!J7VM; z0Nf6I4Oky`Wr025e--da*v*0*1s(wZp7BTRe20ArQ?^AfC>ox}_8WQ8?*SBv+%!GrPX@ z$A?&_fVa8b0AA;IGljzi{}$|r(Y0~3;^s;bzPjUB#o1Imtgz20wZ~6L$IqNJKbRuw zMtt3=l3@FaBtLIz>A_!09EbB1T8hnxL-BJ4!Ml!CTu&q`=BNll6fr-T{1XA$jL`4R zIp-4p5$tW1&L{3v5B?qE+Rg#)83YNDg#Y@ z@PDfhHP2m<>`r|{^j89R;p=GELcSe%H*f*40Co!@KMH&t*ajQhX91rE|2%M&pzG{G zK>L6%!(c7&ZQy2L8=NiL3i$)z$G}g4yMcRvKL8H_519C<6&wZe2k-@c8EnsJqNCTF+6*h&U7Zj}^p9X9PJQvsz*d5p(I2bq_I1V@- zcsX!7a4w&9TmW1Id>r@=a=Z-k3&7RDwZON5n}9>!#TWBF0Pz{{ zYv8xQ1Hhkw$AQV{&S^jgy)^@J8OWztJIK``*L~BKI!=ee4@EOzYhYX8*+3uc&x70% z*acV(`fiZ>0Q&<60c*K191h}A;CSFvU_IDf33)cq5B^5Tw*c<|HibS6`2pZ)RQ1$H zKwNia$=P)$HB3Hk_bGTg`O#Oj&HHPa)5U%a*|6(Re>{C?o%aEO+iII+da@$WGLj6)%yH~;VLe^* ztbNW^!rO(|0*a`i@R1iOz!ZO-7V`Q+0UxBV-GYmbzfIAuZVNJ_mD^C+uT3+O*jh<&;R?ZQg_chcp$eB~n}5#mKew|9_aVWt z*C+!NzY_!zvN>S|xzKZh`Bnx7fidVat^RqjcU~3Dvph=7w+=k)@(4M8hcEse48mU! z5jc4gP7Hh&GE{(s_`utY5}Np(IKm|4|4w!##s6u9S+)cZl`|O#K+aYRCWgcc|{whI1%{+C1!0Ske_m%*cI+yW3;t3}~kBbX1&dPU?g z&?X>X$(hJ$0UDnla=vYRhsv3fiIqgxQN;hTOzrRgH38iI|34F8`rrR!0;K;t7@7X> z5&i%FK4R%rj{EMd3RUk}&{couL) z3We8&()uLse7Zu>9oQ3SOX5PvmjH*C4e^2Pb+l!IBwY!d4ZI$BBd{JvNv;tv3uVx>c1m;5DlVmf9R)k^#5>N#O_kxcAJAgOM zj}rbG`0Xfx9?)+EKL}V0ybJjX;B32P(hmj=fn{Nk0=WXPE$~t3p8<{rPD4V@f_y#X z``~W@@B!e$GOo1hhe137Tm*a!XnsnYavX)c1h^FVB=BkAv%uxR=YY=xR{--|T&x7X z2z&{+8u$uuEpQ$14PXrT7H~cAUEoIGX5bc}yA>A~Un}?7c5CE8x}B^`IbH@nNZ0J% zra!*9)BIZE_-0M>uYfOqorIr7GP|bqM>`FXb4KRYPH{eNT=ezn4pU@$837MLwkFqs z{y0IJ?+E_2>n2A2F_Lae!D1~!W{|2$QTNfNK z-)MgZK1kQ>a_Fxa{dKre?CloFm&8#xZmEE#kfJgDC5kwLfb9KnU$W(#YMvA9#q%Bu zXIbp2N85nzBSrHfMKRB>M|<&Y6Yy;k@Ovq2E};?$Al-vAJ!U_}6i~ zw9PbKFby6ohSs$#ej^sP&)h7Lj)W~2GuON3`B^{j7;8R>63JWBWp9yiY-#ctfHQ~> zg0~YD|H3o(n7Wi|0$R_R|LIdpq&SDJIX7pH%WYFPFjsPssgMn>8<%@}@;$9&|23O_ z-JrwWtxPxpWSv_CJDc^w*CntRvYq#s*waH_{)vJ55&vbmKG%0rtbRhGZB1N5C0?IG z)BFMAxp8ZL5M`blx8^^mJn~b0%_q?oY;B&4w1R(~lbe9%E0PB;UCj?5dma`vA0)mJ z@xiwwgSvQv<~#^>y-bvVEgv0F6$*$)=R^U8lcZe9#5iv7?5LoOE5NsNgKv5yB$Z6^ zl2EJW*DPLW75DVPfyVrR#U&8TC!mHvh&@9e3?V<}3qj^3=fRSkP<=rkl92|CW(uFJ zu-iOooI%*Nr}E|s)%+0Rc~VL=3}2`{3`R@R{6ymU>4N4P6YnRUx?QB>L~8V58x=EJ#gH=WO572LABgX4H&2Q|e;a-y#ms}c=2s9` zf&LDomSTs2=Ae4-KV!rr$^d5~Wh!YFCzjP=wrG?+4$HuG!sOUXDNQ20u_}=NP4s z8-?aK5Jz5$_#W27%TVYDE~2vJF;w$UrpofAea^&4lgnwXoSE8|%;?!vj-=eCure9; z_TVq1fEy7XJ}Z;GmHKRMb~y0O*32-KG z?0}Gcg6IzCmO@pHATKl$t$L~Ix%95il|1tF%PyZvn zE1WbMRCo%ux5p$JcW|IYWXIGRgF=x3w_QbZ7k_PJgjC%Ll}i5nR#Wn>Vs}oXP`Q{j ze+Kbh_U94LxxMfXDFxf*pXD&<&-I-l2{@Moc6Nx34HG`=R6*U9^MB&`gbop^gkMJq z=R=21J8yrPql9-x8ccU=#bQctGtz%p(y|yywkwTH3bZl(-G=gf7`OuXF0jcUDerE; zF~GjpOqOxfOi{zzi@r;LL{@Fv z-9t@`BhWnk)UrA0>6<2nD3ftSZ=KKp%aKyT3cPHS#O~4-|-m8jE5YPRj zMEHE~l2VRVQCdNAJ>tFUt|@irQ#|ZPCP>g(#B;|j5i!Z_iFdse<4D01u=a3If(XVY zh+t=egnmLi;^q7oSHTd=F#Cz3oP6GQ=iT6s-<-~hM+>iJ^ z6rZb8I+7G#t#@~V2%0Bo6Za*s-%LCw2;H@{If}(O=K+BmMQExFBzn}(I^0@@O|$&` zNe1df#@VYRkgOL4?MSbe2v^dm){7$5{3POeqSgHK#Pi7KZpUadfCP3!>5q%8(%lnT zbh6Cr*sTo~szA5qC5;Z@CI4}D-#It?y#98z|4V$`nqL)Zc^?GY2&BD#e#~37x zE1g}8?kXD+J`LVZbsdP~rG)0Mrv!6@6rKiI>)~lAf*sBwwcL9!+S%NBjBj`7hvlKy z33r}2&#b$%`KKe^lQ4ckqU>sQI4ztyQW%=Qgt(IZnPZ|KRii^FWe+C=eDe>sE zxWO%=$V5VHyGgY2(+T3A^y7#%KZy7)9{e!k1I2tb$L4riv4_YLvuPo3mnJ>#DcQid z%TSkcG|wxn(>-{e^r`Ag*>h`WUaw_WP|>&J7n6tGMd$IZrW>Wu;d6^`3SF$#68+X# zqGqS}A`8$bAPr&;<_`8W~QN+_UX;XABk)4Ya#XL8C9vXGTT<~5AdXF-2wuk*z;<=(q zghTN=Z+j_--%SyCC1?lPQ_C$G5Xqkrf0_rslX$NLao;sZL3G{QPhN*nXJaT(cH`)e z7ka)sq!IDLf}I@^GanYd@JYew(}Ma_*mI~Vxla_{i)RjlOfdA05~Y;;_v6@1xfuL% zN@;Dn)@^?P1?0tZ1YUg4QamSv+*bNy53}x@7yt#Q-y*vJXb3K`^S zgdNu=JIY_A<8{=T0ZjPMZlB2=8sV;WIixh{HI06&dROn1|j<4-Hxx>mv%DDjZaGO zc3j>_EC-O;LxS-etKxAvYpD41=ZU@mm-7>K@79sg$KyKO(GY74{a7X0Dazj zA{RpMhkZ68Ohbf4Z3gj0x47ald#j+WL)%Ji9Y#M8ecl$qP`+RR{N=qb@|Wl>mzw<1 z76wJkQ$=;~lfidHXYA}gRkC|MNgntfw6o%$BA(|2&GUfA1CQo^Ag-qezXEg7qX^#} zNk;6RrID&Cp;cJ^3F7Mz|2sU`VP+GJV5bs4^l8b!1iEH-8Qm%~uXEglMo8w>c;Y&C zcGNle7Ae*Io8VPI&PNn+pmYRIMT(f$c{-deG>)9k?O!YS`2c&+5nN9t;T1s+@m)Ra zxkSAp=0(!!HbLC~#?T+Tj}k<@IYB^kD91$sk$)Rb%LF;Th9c%Fh|e&`@7zU-wo1YL z>ID2Z;KTi-)yJqUu(RVx)=25jv5!RW+b5V0Ia(-k@Q02jekb#1A^dv;qrV7-eh|!q z2R|Y#P!UEEq3;j)OTl;oIa(bFDl3>-3;JW||AB@=IH`hp@KA7p1Q@GOGKaoVVwcra zu;6sTAY@-Jk)3ve(LREC%>)ZB5{$Vp@WVqiL6Q1Lzypdf?-NOwEz-;{MRqw zY1%1r)0cCqM}oa(q&0CakC zwv>+yga(K{O7+JMErHs{7+dSqjNP%4+)+3xO={zgKN&Je&CIw(PnDDxL#7ejxF?nT z56HYQXSWJ6#y96R$Q5k>Z$1@@)V({m`8d?i-;Ec}5Xqk31Yg{2!rut*p!o99tdM;t zvncocV?e>q(jd7hcMnQY6rvMns*pn~B;)|}I%elmTdo}Pj|iEcM$-W5_Q4<(4;sRaBgYQ^n5B6yK_-V@Q` zKS})AMLbQ$HU~>d!8NNrJd%LFnfR_A_O}qv?V(gSUK!xUzQ{9UN{n-ibAj9wvFKr>u+h0bP^j(;cJJw>{nK2dg< z#uXlU_3cA>3ap6-e~9e4y=eQ&WY4!Jnm?1eu-T;3inB=Jb>DI^@x0d6_LmS}*Mpx# z3G%X^K|J@Uy)sm|X2Y6fhgaFWS*s%$MLaj)L}wPY{SQ<<^*zF2p0_?r+EdfnPYSOb zFwa}{7d$hO{&$>_=6}cG(T|6F&GU(1FP^vdd6w1oONnpEO;;=ONWl$8^XJeg=vAca zXm02GnUXnkoK3{@(R^ykH|C*Dtg+I|CZybJJ@sxp3j#8sgAVa@=_nO5{5 zW3RgBQ!D3?PFGEjVm|VAg!;I%GH#XaV(r^ux#W8Ug}{zCYB_rkfWO?ZbgG1!Ja=fSsYmE)MMF|>OVSA%NiEMSLFbb_4+>e$WwfIDHO^vS zobWnfO~@ZD?%h%;u0#qRi?s(%F^>+KZ<(N^S|#B5T_e6vk#MMy+oHXY6dj8c#r!Jb zt9bAQ#B+t~1U*d&^{S#?RDz{8FSwKO*?S(A;x`dUbm-@lJo)<6*-HgP_D>S;70y|- zU|{p{>FhyTc;$8uYz z1nu65X~GN9;LMGVy;CaaOuH)f=E%HXU|X#D5!a%Y&W-e9^a0yDWTRl$l(ZhPsI^-#^0%zF`JZ?}$0>9)k8yfx)S4ymUd|R{k>t=JXCNyRcwd)Df zBv)aqHG(zchn__(*WB>iYU5Xnk_+QHh5LeRC=Ojwa%^2g71xq7s`=N6Z|uRpO+0sK zZND`E{}J)rA}`b)J|+b((7br|px@}(NF3jE5+51#BtKxrcApO5e#EHx2NLiPg10X# zlE28$6hVr=TzqZVk~I}zOO~JEr=eu!M@UorFT~fB^<^rjBEmCI{`X0X*Ei`eQx8qP~ZRG~&4-XnTIz)x?A6lm1@( za^f2m+ZW#jt{?@sO&!6?k{*gm;$`9+c-XH_z`sI#H?l8z=Rl>ul1Of8B^CeFElA|G zWmxgsd7^yxV^X2fw*~p`i5;~r%eds2%4%4aa%FRwkW~4NOjBQbgXSx!7 z-vnScCCy-N1sRL1WMZ{~8H?-|qnS8M2zC}V=_y*>@mTbM-LTXHlf55`CCGi@O;S3U z?|7F^FkAFdbb4L7c{hsQ|E~DYcEs7zfAF5%w7TK--hmfpqv%ax#V~;t@ILFYxY?{p z=r{sLBK#_n%y{b*|8bh~UZi&)iJf{fr$I8wGT~9-xL60D_ma#fcEanSyL`xY zhTbKzYu+AUga7t-LS|-7`Sz#lhJ1vX6p=fhr*53ty9=HR?V3{F?Je`9{wfGB z!+A-sdA_q`zVAwj#@>_6p_@N01I_0U&*M0KU4;HG{{DLfTHtax?(P>vA^$thbW(6Y zn!gVoG|xY}%`0oopCq1F=$hvjxOq4Q?>aW($0!0`25ArcZGNu=wIh4(mfF4}@w^MC z`7XqBNtER2=7JU6UP~&dC=&$4TNS)A(DoM)@0EcKlmV}j*u*^F+~|NlP2l0H1U#P* z_R8_s3G5HY@f?3q75tJQpkL!W#M?WRxqWMyXc>)e_j( zNWk-UmsdEAlh5<6n`Y#}tIlqqy%F${J6BR?&Z~j3+}z^XyE-`|LWLEi|uG-?^feZt;FdYv#z%HaOSqn-iO<*5Lv|WiJhVVc)4qLop`KC!&vjz_k_GNVAs;@ zE16<4_Y(yyyo}Uk)3(uhvhZBBY9eKz_#^2D>6&jAvoT7wWF=q(`0$>&}41;W7 z6np>&qO$&wfCCnh(1N+QnFMDdL)j`rp&s4h^{%50vJXqZ!9$1uXmb?%S@?Wl7W9Rd zhyNHb3z!Fe)S}_3kAzVqzBjs>lsQ0~(!6UVM_KzMVvi$U#$Q7HL1YBz2bv6VAB~}| z@`0H!D6}%r4-7#cg&YPJg3m;W_)s+tkj|o+gl1nW8R9RTvtl3jQc!6Aus1kq4dthrKMVdJO7|gmzYl?OL`3VBABD|kUgPq^}PtX1kdJyw~^Sg|0**1~D zm{0tx!=Fv*c?cAjbv8i;#|uWs2?j1Iei)PHX7&~9Y(x{a(IAw3$a#p43!I&uKl6Hv z|G-+o=sLkn$eHl3m*iOUyQS9Z|JG3xN|27=pIh_0#b;ms$wmJ^z2?_TX}csZ!BhN> zU1RIlBOa$UHg`nLH=;E;cSOB_975c=9{e!kxZ1QmFF4(QZN>T53(iog)V*yR7VWw5 zP+8H(wIMMH*n{I{8A|QbtUOC>3G>p`o)r(LNjO2u06QB`!NtOd%L#fcBqU_BpKI@e zd9~a4qJRl0h#c5Qo;Kv7v)^XHdU4#r?PR-6ZGypSIG8R8&?jgMY}QNy=&@|kBDisX z@sWih@|)xoXRYu;#987|Z{jDyfBg`+`^CsEI&~Pa*=&ciBsJO4+hVZDPkQaK@I+5{ zqZkKwzbPBPsfc4QI->GDjjmqyFHr)x?dkbsE;ZQdyqwVyKTK6upFCWK z9FC@Ic30A0Gx~cI{H08i;{_aVRA_!aacu8@RZ40kU9;;)%OY;vCSHoa=G!c%I{ThLXZ7pyv_<#BD$a#NqH) z8#%}E5isAFa_p6$ual{Na?w!{MN+oV;rviqfIT?+leZ62$XsT0F4^d9*>jzo;=$(< z$3>>??;_4mfB9Rx(*MOrVbB_@Inmvs$Sy|oao#qu8!9~FRQO3c&i-{1BNeyOu1r>X zAX0Zf4GVjE7OhRoCuhVxI!`7G88G-k8piCon`Hc}Xc9!9AXf5O8z&i!?KpQsZ=Yj5 zs^sO6`RUFt8)eUdVix#4crX$pN|oB@ND|i{Nu!R ziL|XXr)tE^v37f%r`3TK0;DbNrv-7`%IU-(tDVW_e`+(-nt5>5JU@D9D10O(pBl3> zAYi~cL#pP#A-;VP?-mzXTPhmPnf9=bJa|R0F#&%mg~RP$hqH-zZfhlY zir?8xindxo@&@8L2YM5E8*z<1_?5(Y*{>s>t44=&GjZmNfm*Si6n+m68;Ikg)3elj zR1#drH=+&Mfv*8^86G^}FR+g6IscAhJ#?aL9w|%Qt&nW*Vy<{o8mav6IQ%YMS<;s% zQj+;y39l;Pr@hP!FQ=4Rb!5;-)Lra5DSCUM3Z;aXb6!06QO#E* zdyab-a%5L14-rS*qLh7OviI|*kPgW1))616yPfylmt*|qJ#Y8%00mY$xN=l#JoV_% zY7x(!PxF4_TYB(4h;Qe?_aUD9lB)yaFBSSdJY=TI?V`4CK|HV6ym%h_HQ$8nc_jzl zb%G?Y>rM)O zaHwtbysmS=-x~9(0{)f-ALh{ZM~GJ$F!4M5Rk+3;9+D|RUX7>>@w~X!5u_3C)ob_% z8TeL7+uz0;(O!x>$iq1v9v&jzt7?`I&-)iTg1Zv%uUg)vhXL$@=6_nXlfo;5=uAUljDUD1r(Y3 z8I6zO&G=Y8@13}7x^cEjZOpGGj`OEC>|Z2~L(_a7alPnICpS^{Fq}KC4yZphF`jTV z&tJf3?!mv3fPXas&*z%r!f}hf?0i2IItO-s(Fy8D1r~bj` zf=VkKXCLuAu9fsa4`qn&`guoYLq#A{D9E6lF&! zU8K7ySA5s5`M-T8>iI~N+Mf={)|oE4^%N;LM$Ny^oCm)>0e_TuzqRM~Kbrp7{gT#j_DPwyjX_h#V`RyQDymZd{_J za^!JTz}V?-VXNd{?G^ZKK(|yswezsfQ6}Zc=aJBRsQ>bvuC@;o&y$Jf7nHE4TD3WN zfE2tg(H<6-@K9{Oh1+3sDRf!@ZnZLW{RmYzb z&l|zo{>ub>>EkI9zhlp*=z#cB#$I(?XdEM}od4p6?)a_}@g9?8$BD(n6P% zrPWtZdGNwYd*hFk2RwLw!@Cjj`knXMR31DaYaZ{y(?_oH_WTGkVrOSv-{5XW4x0aq zA8*BG3!gVt@VB>Qa<+Zx4T`WM`O*9*o_v4W)gcA1*t7@!^aeMff9-7-r29l%pXP3qi+bb&ER!61+-`0!X|Ly#{5PZ(cRun0RjPn!h(m zx=jp)#C?%nntN)VW4{@}?p?8*-K_d;l@~ zjW$ki12eBdJ|@Qh?YDsRr@c2&qIf4Jg%puHf5>gqF}zq(qF?ag3gw$8&2v9z-u_H* ziZ88u(E}@bYaGE9+$q88@-2K(_|SZBefBC_$$WHoNnH6iM)`-~lUE1qZ29}6@x~Zp z3lc;VTnuAV8NW$d^p@`o3Z2Je&Ce%}JFMpKCC=}`FCeZy{Uy3biNp@j*Qg7Szt!)^ z;5v$Yu;XPh@BCIoOseglD-hQ+@DBRmSAiOKrmmI9QH`k#*FP__U ziwzSaZy%(1f;3#1=Z*Y8E$uxiBb@Bcvp;MYzgLqef46Or6pW$J*>$Erz6aBM*982< z#Pgkywx@~2todA>m3v6R6QK5RH*rlo`02#)NUQCG#PR*T=0_42cN6Irt!Vz+%jnW8 z8cGSDLa-~Eysc7A%aT}8+&S!7tb;rGQZJIRPez1^M`XFbPX>Rfy1OYxY6h%$ju zBqZ-E(Z_%R=nE_l|9QXwFbaLx;+Ne_{7!a(1Q-}7m|%B1dbIO6JJEdXPvT z`dY%xLp)(%bf+td@Hc`{i!g{mEo+tF|bB7*u!|K2W?UU2`5I~uO@Pi<*XBJE1*e|JZN+MmwB7Zh1z`t#zw z?$PQ{$?>G1?f3?HLV8@d}L;BP2Kl9EsPa-;?znc!VJ%__%e;gm_aWuDCN~WDI%~E>>=1%$A zfR)S}S9$T%_cc9=&-5f)!JO)R4`$|YD6P-URP5(Bq^yP`va6!FaojAY4jD492i|)tD$o-n;vuOAA46?~j?`|v$VQ*{E_lEG@~FhF=$@n_e43N&vmlu6Z3R9Il57Rm zG@dPCW>=IjZCDoR4pznjPMZ6CHxuEp0kXvAl_@*!h&&Q%K7~3mk6xOuN*$SRX*55$ zoSfqsI#9w7k`23isFj{hSXX<;u*&6=_w>@-M`#l0ROxvqEy(Yzf4eG??XY{$-)f8mY)dYvZEh97g6W(Bjw za5XgCA(E}Yn(lKX{ul}_mx??-9oaXEJ`Z}EK8r=|#B_+|ag8PojmC+zeSS*Kl)RZX zLwrA(-7PZmBz?KeXS2z@rUT{+{nKoFso7McM0xRzgpZt7Z+`tq;mKT|8=l|OJ^BuLON#PjnX z9sVE+9~t2M7Z+(VDZCopo&*u>CEic|bp&%LXI@pAU5c0ZooS@-%JF34y{hIwf)d(K zyq7&c>u=|g(0MK?c=f7tFq%5Cmxrkd_&;cf_sa1W;=K~IAXRu*M|?jNntzn|rXC4h zLcCYET$+I2NEwJrkXzLK+S9Do)*}M@9dwAX(hdqBZ(5q?|C*YUG z@uvOT5pX^!GCU%q#>GB{v028q=^d$9{`UGQ z{?mIGKV9n>Z6l?Uf3Jk0*O#$Rg%5v%IA)4H{eETS*!G+@DFq)O60od5B_?Upbg+#EBklBXDWUp`1bKU*Z(Fc&W%?Pw;6mV#lHtWp!hA| zdn$e__`ZsNpLo;$`YFW+@Q|hWeDH%6zYY8_#eWEXl;S@EA3Q!u+P|#=eqzFIyz=m| z(9UGVe*%7*;&*@#DgHC?vx<0$-`NSp+#-b#pM$?%@w>ohE1sXQu$!m&FNJn;75^3D zcjhZa0X&2izZ?AhivJq?LdAarev#t$fOn%x@huedRBQhZ{8DAV7yL5C?*qSF@!x~b z6W%TAe*E-{-74i_ztGMa#UB8_R`CbH#}xk~_;*V1RQ`vc*jPd#yQQq!qWGU+pRf4C z;I}LOXYe}|e}s9i|6NLP6dno`{|op%ivJD#KE?kI{($2D0AEPFX@9o+9fRVC@^Bpd zZ;C$w{t0}$=_*#lD%Wr(K ztEUvH@ZeW`Iq*#tp9a3A;>&|?t@sMyGhLg0?7*$f?@QVfPf{L#Re!SvqfuF4S z+Tf=to*!74OQ*e%&gO4h{1^A_kZwY>x;#+}VuJ}`vWc+dRl;Sja zSf%*Z;MXXg&k?X&tN7Ew#}t1C_;*gI^?y4kHYyKig5RR}_Tcjse-`-dia#6t4$EWw zan6BamsLoHob$jJD82*uJ&NxLexKqyfj^*lw=)!lO3?-U5yf`}|C{0i;EyZ58+hl> zl5;_K;a#VUQuKg_G{yG>pRV{`;C+hk4ZfP<`+%=ycq)I$PG4NqQywk=?^iq@v}e~; z@%_NJRD6H%t>byF|BIl=j8_nUG5Ge1x5paJRr~JNGeiZo0iXRPrS`m-_cPSL1 zB88N|81S80E zN^v<9ixfWvd{psM!7o+(H1Nw5e+BsE!WXaquY@8`d6*7 z2A`?;d%?F?{C(ihRr~_*oxt0wb?%2EpgcSPzNg|J1m9Qj5%B#KzYu&@N#1oHf?{w< zh4V1@VTykQ{3yjQ0v}ZTqu|FY{xQQ-`;O8rT8TfDf3=k23wWrf_%Ffx75^3Zriw2B-%|0r!MAoMOZ$iHe2t4t<>4Ff?G?WV z{JDz%7JMhge+NEL#7q3nUMPANDNOG(U$x<@zKZ`I_Wcz91Nbb(?*~6v@dxbuJ4`7K z!ow)V{|G*)_(R~wEB+_&yHqbb4Bp+N6hA|8K=DVwPgW5e1wT#kzkm-Z{#Wp`gfCwI z{|3ce<>7bm*DL-H@Y#w#27aF6kAu%G!BhR8fMR|Lg=0QL#&sW7{7Kl~uXqP16c#Ez z3H&0(^I1dF|DsC4KTyVQsp9!NXY7_Ko`1NE-EzgJg3nWYIq<89H|-x8a`=O3?A9m` z<-xC2d8RPJJ;uPoy%>1 z7Ua>7c8hL{@*VLddGsUSqT90ko%l*jA(yZ!kG^|DbX$|}ga?}>J_jOsDBq1|$n)?_ zJbC^Jb|z$LAo}5S(JfoPE1o0I$8+Vo<9X>G3-%!7r-z^bFOWw+{4Tl`%HN9@$@j#I z<%Mw%kAI0m^yRwItyI1jUMAlgFP9hL74m)X%E+Vjmpo?gCsb+R19-K(7_X6k5U-W* zi`S)ieEoljP@fW_SK#A#gFN~nmeH+IUWPZxKZ!TXKecJ%{1g6FOZw{lGy|>ja=cBx zAKotCAMcPKfQSG7pZ>%Zgf0z4Uqleyy5$GqJ@SL`UU?>71YtxW`r7g6HY%^i$K*%iD>_~9)UevixaLr}xO zl>Bq}wEXkz^#~6LRwq9dua}>WH^`%} zfQfF6^6%nJ3E#N>ZzD8o;7q(l9>ZJZ=i+VhPP{$M!{h%$LPuH%g7ffBc^BR#{|VkL z|0&)h?~XiLf4vIlGtei$0AJC+n*9v#*Zq6&0r}7IweWG`>z_3hL|>;B-3B#qAwDGU z#VfVweu>w}e}!i(B^GJ;yovpH5n)(Q(1&Mj48;F{(HD+Iw-Md{5`0wtYkW-Jzggn> zgB(4Vm3UE14|Ek_QUindl>BPEM2~X~KCSy-i_hTe z*Z)I=Sq)r=&&jXH=jG8CB}TUe`7pki?y=xT!cuw&Zo;5D7iu@M5LXUH6 z+{5F)s)5@WSd)+9^;*@p<4y8A@G3oV^!1=LMz=C~>}f)|!ZUb<{8_wGK8IJy|A1G^pG$Zw zs8M*Hfm-=IUMGJ6ua`&PZZokHTLW=#?+y16p){!-wRr<9&LZH}L+= zbp5|V=+G{B6CcqN`~x4*6RhHc@_*t(>(2gri!i(%qNNjl!wP&v{%?F#{vUiy{$G4t z9>g*dPc)&h2|g*`6rYku-@+rhP0ORNgpF=9^3Czstil$AIr*0OygUSzI-RVAoTe9Po4qaO(@hr9$qAmzN$I870Y+Q zr}Po;ikEEMKe7Mv38fn${=mE8GkT!i@ot?b_Q1>ZI0bmQ{5=_o=MO3r-pfFxd{4Ye zUWixA--p-8_rm9V{l7P%Rs%(NoqQj>UjBZ(LH+@}5nsRlFD5i;;DdOxd|!M)hvq|g zi|!x&So`R%S-VN`N!~1`N!iP9{(q1k z_^|w7d_-P}kIE0h$K;1*B+frU#%t?;ArL)pa2NyQ8mPi2kxjg4}3}fd3;%3i?7I!!B^$6FA&xg zzK92#r@mc=e=mq<$iIYV%D;?f$-k2DSdgty&p?j+SUgw$)#MXs4IP(!9C-tt7k`}a z^-pfU;|ckCf)nrp`PcA5c_UsVKM^lZ_2~GYL?}rOp?@7Ol{evK@^9ef@^9i5@^6J6 zUw@Sf%?wn@PsXd|r{Fd6Q}J4P3tqRmuK!OX)N9~$yg~kLyiwkYH_6Yyo7bKF_Z>pZ zdPwZQ@8YfUHoQ%KCf+Xp9^N63C9nTG71|l-lAncl%g@7m@m2W% zz9zo{54K2u@3<1r*zm;uyNZyx5#sxA5YLidjc3cR!E@x-;<@sn}q7`K@>bzJC3G8=+DIqj;74cD!1C2VNt; z6R%D8STIJYOAoM&TYOMHjStBm zPhS5IEBuau5&0ANsC)(=lRt@%%b&t0w$Sze?+KF{n8l~$Pvg__XYd*Mv-oV%6X(D1 zJ%BKm4Ds_{5c~n3mp_Lu$e+g-<@5NGJoW-%S>Z)|Mg9`LDqq0Y8dUX8% zMkr1Vp}&ro$lt(A#suL9}HB=SMe(OKk;h$TX>E9UwG}7y8d4y z)M?<~c)k2Tc!T`Ec%wZ2)91riwrSnjf17Nc*l*42A@Tc7@fP`Jc&j`EZALJ?&F_0(U2hW#B-!V73705q;7s@|~ z7sV9zB^1j)gqO%m@KX7Q@iO^G@N#_p`v0SZ3JsLvmGY0_Rq~JH)$&i^HR&D;$_TaT zA^0R-C;t>)FaI>&ATP%o<@?1wJpN4z`!moiKLBr$SKzJk1MxQbL3n%Q(fUhnzk>-K z8mPoO<%i&1@5FzL|%iB%0G*b$v=mWXX^U@Si*z`zKT!EkHe?r4fwSDczh=5oPUB72(!tM zc*TOR;dAmvd|rMcz92sdUzEq9@8=%fmK2)sW%)Po75O*uRr$B@HFor z|91&RsUh?>yjXrFULyY&@CNzic;mXqKmPE`YY0v2A@PJ);LY+Y@fP`2c&mI6 zZ`?TmvuS74nzxO8Ej_C4U*OPWM>wM?y_{2>yiE$`|oE`73z6 z{LgrU{MERJ$G=fwiGe2hYk0H#FL;amuXw9`8E=a`T7Svy_cubj242TIzWcS27}h~EGHf%nQ+@jm%K@qYPR_<;Q1*@^Q{FsSe!28QJS#fRnnJ4Aon z@auu(m*S)H%kZ(Sb^U)iVO#?P_=Nlld{TZTJ|({jpH4dGpJ0$MlMIOuD7YG*m0yF; z$*;xd-gNqz&qEFZ>KoW&ye4OXUcEI zv*fqo*>MkF|K#=?CFE$}c05;p2c9Rt6VI2A;RUH49sj!sg{dL*yYV9VJ$SKv950dI ziy%>FA~PmLJ+)! zkINVE3Hi(Tr2LQgl>ASTN9%7|VUd9u`78LW{LlED{8fBjzJxD?9$){-GvI54MGgD~ zUy}b7UzRW9EAqeLs~eu!f3FkPHbQ*=y@3Z=>F)(Ac!vB#Q7yJrq512i%=jx8!wWdgO|uV@IifU`1kPJ%8 zkY9*b%6suD`7iNm`LFPr$fNa_+*bf=4f0>(jVT^q|NVrfln|}Y zOYvs;Wq6DHa=cYOfVau7$V;4mf_8-~8R(E-g?Gva@hwAuMSi*d_5m z%koX|75S$4s(dqiE!CsrpFs$=O@Ei$9M6z%foICM#Ixj?c(#12(BtbbM`3FQa^>6L zdGahgU%o9~Am0uz+*a5BZzB|GAR8~1za1};Z;zMCcfiZmo&A?XC|?hWW4*RTOy?kf9LB0zfYgE{k&?L{to8`OVE%M#*R{0)y zTf#T4{|gB18h8)hA%8F4Dc=+Ck{9CLX&xT`_Yr#1LJ;hQ_saLi`{YGq&7 zqxCna@Bs#f3r#C#Y z|2{^T*$DCd_i=ny{t0|eUWU)hKZ!5MKebEZ`GZA;PcyJ2FUObV`{66{{qa@#0r*-> zp@I->m;PRGAf6#V2+x!sjAzL!@oaqk@Ba@W`^*G1vk~sea zEeZ_`w91di+vF$U?eeeT9r8xJb30xCpGfG^z)5(w{Ofp+yb14>e*^DJI_IC@n}q&k zh@bz0;9K~Bycr*qpNtR5Pr--fu~P{n3N84k{4{(_emXub|294$Z%ugk{XZ4XU|>rA z9ei5;U3^B~hR@2+#OLB3zW&MW_dUYA29lR(3-WgEzbHQoUy`4VFQ81H=Ed;qVJUxCMJ6|N-I$*;oe<%4*G{A#>WehuD~@Qv&LYYEL77{XiR*Ws=5 z>+v@E4S0K+hsS@I(2*8`;6}VteiPm$zZvhAkKjG>TOyCvU$4Tg4D`uw!~5l<_<;O& zd{BM|J`{R<{U^_WcM^s*FouuF@4`prcjII7d+_lMPwc;O!o)_1@4tKTN%?*Fl>B~t zT0Vi#$R9{v|IaG?hJiWxgZR9B5?_!%gfGe;#+PCWj}VsSQ}~MfQG8YY7``U|Egobi z-Xqqp|ECEVgyfod9M6>h4$qQ5foIES@SJpy1y2%k(?jqSo+tl3o-d!p3*=AZh4N?O z91#gu9IeGoxr0^;O&GIF@MgAJzD*p@KCjTqmo~`Tu zWkQDr{)TtTU&p)TZ{XeX6}%_uiSu6&yh-RyhWPm}2>y=u$^U`(%UAIM`9JYNdF(C1 zkix(4Vfh+9BL6o&D*q2YCjW23W5KvWuxsK&pO9~YPs%sNr{tUA)A9^_ChoC~?YB8$ zRs&n$bMh_md3h$jAm0jKO!Zj!!EQ}hN)4fJgD=ao@D=&C_^Nz6d`*YmwgM1&nG2t86|L-R>Y2X8Rv%DB@k$(_xmG6tUrFkp}K166w3*pZN!aL+2#yjO7 z!Mo%i#k=LDk;lUS^(cIdfnNE?@jm${@P2t2J|O=jJ{Wo|TIb0NkWUeYH1KJBSYD2g z$oIoX<@@7f8y<_EKRAFez7gVHfltT}#3$vI_>}w*d|H0!uCe&@1TzYUF)%By!sq0N zr+X}DAmpTn;CMV&egd8+{~DezZ^R4aC&oP%1ceGGF;FD`I$kVq!b{}ez)R)d z#LFU&h3hZ5{k}yg*FZB~AwLt`H%5o`A_f>`A_jtd3VCY z@BgK6J_FUVKjeOMG7bD|{iwxH{K@S7oXA-9Dz^E>+zYG z!m)%|`B(8d`EmHXya8X3ACE8M>(~D$5SBFXHGEm#h_A>`#8>4f;cMw03%*VWa?;;y zn(z$yH}FjPH}Nd_xA1IvbKJw@pQCUx1G(~3@I3jcc)q*^FOZ*x7e*efzfBTrEjXP} zq=9ea#qw6XM1BTdD*p~%mg4dC|6M|PN{IeUZFq(JOuSP5J-kXD!>i@z?3OtH1T_jB z4AjbREl9jnb@JQrdif~cAio`N%+dA#9fT$g+=(~K$M6>UU3jbfZoDn&oPUCQ2<^$h zni$7B}%O~*x`9leh1%nC?GcY871Rs`9 z;Un@#@lp9>_*mS-*MAdU>faK^H871&$REci<-fzHnTXR{j(| zC;vS@FQ3I1Ys&*3ZbKj5qK=kT>0UH?B%2zE?=FPO(O?xO8fzejJOhuFDQr$Cmv4br$hX8R<(YVud@H;<;TzZgTN7$Dunk@-&%*2E+v4@|?eK;) z50C%b2#skW{IOhkll<*?vwVBJMZN>xD$j{LT7PW{J2KENe+S+ne<$84&&9js@4~x7 zkFWpa8E_{;j|Se2_sa9|KKaggzkC;bV8avpZ&$+LMu;!}e0)g08$K-G9UqbJfse`y ziW1Ksj48Z_fpPhJ@d^2!_@ulLpOU{1pN=W)MVOKAjnB%9@HzQD_`Ll6_yWFu{r>^N zq6UibCHV*OW%<7Niu^@^3NMH}T7Svy_h~|*2Fmdw`F?n@e1E(|egIyY;_>xgK`2WJ z(f&ISFP9&LSI7^>E9I4VmHd#R#Q7(vRydS_8u?*(t-K1alOK-P%a6bt-l6ON&k!0l zP>naqkHnkhN8v59$v~m}hj@|vJiJ)`BfLc3g~v)2eoQEn{{%0W{}iu~cjJ}v z^YN;LZ(RRhK&aNh&+r;~4_+((IbJ9K1zw-#;qkwa(2y3wPs_&}<-f$6C z^1jHU_1CI!F#~P#OYnC2ukj9fKi(<76z>W>zW$SEz{?2T8n_(qkq_X#@+a8}X@_ z!cBx}`OWx@d<37B--6G{Z^h^F_3QuJ2n!k*#TVtb<4f{8@MZa(_)5Bm|NWn^njV6? z@HP3}c#xa^UT_bdAs@#x<@d%tTz^>#_c4$yzaP($PvE)o2k<=kZ}9xcqxGNMeh(4~ zG%$%5${)gud9(#e%rSKx&Eq@8`k-v)f%9rpy`D+P}1^o(tVPHW1IzA|W z10RwH`y^h%VfiNbNZiBMKe_!jC5&odGki>*fse~K$0y`l;FGBy9sex}Q>h{JOnh3t z6+R>18lRPKgU`vcLXWS%d4+8mSdedrFUsGBFUhmZn zmHu9^Bc36D2cEg^?7w#sverXl@#Ny!@^|4m@}2Np`MdEvd0vc=udp*-Am0Tql<$fc z$@B4I`EGcuL}7PAseBK-OkRMO%in`n$oIr66TWf%zYn2G1LapH4n?(mKfFf1KVB<8 z0Iy5)@c35{>eE8_w_kXJ{2;thelXr7uf&_>heRH&zZQi<8R*a_nH+|9$*b^IJzHQp&d67P~9g?G!3#(OqAvHxlay&EBZ&_9d!$v=np%Ri3~ z$ZPRI`7u`}oqTZC*bq)ui*>wMtm{dW5J1prSuS-gfGj#j<3j@@KyOY@HKgR+@s^a zQ~I07Sqx;z&&D(5=ipiL4m?}_eLN@f==qb|?_5Hz27Z9&$vg3U`48~|`FVI@ipSUg zj|fF6A=-akc(MG)c!~Tcc&YrSc$vKW%Eb96C|5Y2feQHrc%}Sjc$K^dua^HDuh~i0 z|GyyAYT!b=PTq^x%YTVC$bW@5CY|$7a1o&?8KT!eyzPEf;-Ag(J@6KJ0p2Qq58ft^ zy_e9guqWOjFT^|L@58&~d*R*ky%Qb_dK8Kn=#}q-_sQRn_sc(k56Jh$2jd>T{>kn4 zA;ORbO7LO%hw%~lNAOYkNAa;#kB)ySVLUa2{xN(){&9R#{t0|aUWQN0KN)&_{mm$R zih)`Ar|~&?IX*An4_}b)k1y_|>;D4?OB$%am*ofIEAoTzRr$gA+Pbs7B7*1 z4lk8|9xqGy#`S+KpOJZ;_vXx5~eUw}l>G|H(68BcWXbC*mFQlkiUY*YPfS z6W+byiT(EtLeEABUzZ^GCf+Om7Tza61MioA2Op4s_o~G62ZIW23=GN7#E0eI!$;)F z?L8`Q=l)|cg|i6b^0V;?`8oKcyaS(-e;=R5*RTK2CCq5x2l%Y~NBEq)3!j(&7+*;D z@caK07Sls;KE5Qs0AH5>3}2D=;H&aq#63LzYYG=K5agx57xdy8@?YYa@?YUu@{927 z$fNa_+W&yt@KBV;R_ zis#5%@Lc(6c%J-pJYW88JXWC4N+^_{ffvcYgBQ!ciqa*DAU0A@N#+b zm8+1qbN@>DS$I{NhsXbHLUmdQzb!9bBk#a#<=@Ba^4swu`5kz%{7$??K6Y*5{1cQa+{Hkd{BFEleh*$D-~3O{XRk%(7;FWL3t@YB>xybEdMw@lIpSWIPXUoO%0*%kB`X@z{lkk_=NmGd{TZ; z=&@*>O(`7Az_h#)pOGJe&&m(Q=j4as^SkK!zlyM+fy41d`4RY%{4@Bnyc%Cw_gM7t z2f>kq)%B3@qwqEP(Ri?H`g=hQo+1A%o+Gv(QlhtD5mDZHJ5Z29(hj(i6^SDu6C z$#=x_Lyw++{Q~42gaQq`6EBqK;zjaz;l=Wu@RALWKY#FULg_|`zqEOHnS5uwT)qol zA>S3Rl;>w~{adB58w1ty-SHav9(b+10I!q32d|GQyqC}*-xF_?7vlNa-|xelbpO5Z zW_l`~!GLy2pZILT7piK8Sb8_r<&AAHsX&C3vs=!*LJS zU!TH980eRO6d#b6;)C*!;Y0F|pyt{`2=A^17-NA{FC^Y{8RY2{L}bEipTd~ zIbkv-MEh?)d`iAQJ}o~0pOIJKv+@HoHc9ONIfa85n3o@nFUTwLMfoB4lKfD7`Sn=( zIs7ofiUz9iRr%rgn*0bncq9GsKZ9o^J@N5}KZJ&mnGEssU-(04@GSXFc((jzJV!o) z=gMQZ5b_jm#q;I2;RW(hyik5SUL?OG;o<&QxRZer`50a*zY8yu-;I~c@4+kL9=`s` z?Ke)S)WE%XmHa-uT7ExXBcH%)Q$0HV4-o27L+HQ3>*WvP4f096QT`C#B!4*c`1)&B zc!Yr#`4rwNe-v+%KZdu5`Gcnk zed{5y|DM77<@ce}yoofj{H(@>lT%`4YY;e+^$s^YHlpg|M6!g5aQ2d|d5;WhFz@!FWe_Xu_J7+x=L#~b8l;f?aM@g{uz`u`k4vj#fw7Wwz_R{6Pj zoBRiOd%DMhPC`d|2!4on%Fn~Q23T%Z+}zQ z|K}4rG;jglDgPPXCGWwz=y z&&#jI7v$IAi)kJn|7!_LX(2o%_^SK{d`&(ad3^o-J^fz{Ze$=seiNQ4 zAIG!g_u|>|`|zC5@NW5w zaSzvDkHSj~^vW0TKKaXdzx4a}w|KCiQ(ZC2kE58Mwli!NZ%WuOM(mXu=qlCq@5Cpg5OY%GLW%-@> zihK-TmERS4wEorcw+xe5(+m$eEU6w7s(&Si{+2tCGsh}RQ_l#*S}>7k1YH5u zPb(bFz>K^GpOt?WpOb$MpO=3gUs%=ke=T8A1IOS?@-N`a@-N~m@;ZDq>521S_)RDX zYsnBl|ApU#0uTO~{$B7EJVRcOXUdPov*fX_60#MJ!*k>fc&_|-JWqZCo-hAe!ec>! zLL&o(@)PkQ`AK-O{Ofp$ya_Ljd-(b%x8FAiWg7SiT$@3-XqVzd*z$seex~ve)*O$!hk|1J}BP` zAChm456ic~N90*}Y*b-e!kBzJd|duEd_ta$Ps-npPbGZg`hR=Ev<7y-XXH8ftb9j& zPW}#jKF!19|4za}S_pz%d{O=`d`Z3&zAS$?z9P?yJX(LN3Oh5fCf@}Q-b#Nj*cH!^ z=i{03-SDi?1JBk272rAY_u#qm_u_dQp4fkT67n}feE${V1@iad zh4Q`dBKh8UvApONu767u_Ff*$hd+kbXD43S-0(ipE#&yE{!0ljj$ZlbM3a5$LGiF_#R>6$9v1)qAUJuOAlNSa zb@ODxzl3iR9>eYbhkq#a=oRw)-}8U?J428CuLs)t|9FB={T~mMvDyEA;w}FV-+IG~ z;~!J43h2q-A_&vhi7v92EQx*S35VULHaJ)l) z1l}qC4BjQL#=GT5Mjox&9)+VA=#?Lhm!^FLK~RJD>HeR^`{kd*2SSgYfBj#OK2I3b zKrKEbKL#I`e*qtnAB&G}c>MW;uM);KLR^l+$K?(9g#37XQl8lL;nzXS`S^oiS|PDS z!k^qMKanSxm7j#q$-j=5>yS0!^Dzy4gRmg~CcY^D7QQ4;TtEiFvixN3zk;t{9-Km0 z)xfEEr9Ofdd`Zxb@pL(qz6$p8P&MUw%JcAfLbs^u+lu{P949_GIAqf8d`T^5^hQ`SW;}d>-$X$6g@x zD7=XG%3s3!C_O~>-dcP4SZIxbaqgf$KP2M_+8zFq%|XULNem?@90_oG|Z zx+k7L*p!gH9ujxl49}7Cbx}dCoKHy)^5k3aIQjA|lkdL;3YiQP%D2Lc@c2jn@)q41(n5G$fH%tDg*VA}!kgvq##`ihkw@#VRbgia+T^?7?ebmm4tYM_ zDc=q63O&C5`F*fEzv$M$9(a$u0PmGY|C$`#`sDA$`!_tX|MnydY=rm&6yk&O_u)hG zz3^fA-uQ^Ti2pnpj4JHIz?l5~__+K7_=J34d{Q3$1w(Y3iYb&3rsW^TXXGEjXXPKo z=j0#5=ketEC-^vFK?9$_7v*L6lKhkSvOM}5kLb3N?y=z0gw^yAl;dmi{qW#F>F)*m z;~DY;@Jx9{+{5*krEnkv+46(%9Qna`uDlY@lOKxbM;@(zejgmhFA6kJg%`>X$BX1g z;KlOK;3X*@-+$GF(v%P#t0VC;`B8Ye{Aj#FUV~T4Kg)lf45}19$3V6G^LUNC7O#~b zgV)KwfY<*=*Z*H6G-#j>ZeER;N$XB@d47v*Q-OY-mG%kt!#PGc?K9x(YgAngpSXy7b-Remf*sBH zHzlwChZSySU_?HGkIHYs$KQq{qjffLHQIuEPoWA(<|{<+{5)Zukc$2 z7Ua|TqWp1uN&Y*0S^fmR5_z=#`F$|MFIF}1B)%qpDmfS={&Q9Gx4-8X8S+^?GsWZk z?`c9-N{F`KGkCWASv*HRhv&-wfal4dOJ4uyD?HCYfqWh>l)r!%$zQ~aSL_KhU2UsFuHq*T|RfTKQ{uojmpz zLcPLY@do+d@KJ5E*YR=r8~CJrCE>B~;nP3jHyLP>{~d3Z{{wH4ui~xpf8uR%4_|+N zAH2mc+BNVmyhFZ*cgp{bcgg>Qcc*%E{QpbnNe$8d}#8@^|5}HHDoB!6xb3 z^4)lbJP*&5?~G^3cfqq0zH$A(Dc)$Emd;nj+{y&T`sDUbcNPajzEI$Gt zk$(msP4`$(O&CiL!IAj5{3v`vel$KQufeC}pNo5V{HGN@&%liQ7<^X#MSMJXAE0p4#HS4M z+n||W)ad>vj|#FC*)V+nR=Y7@JZc&5RXkM zTusQ<16_kp>;Bi`Gx8yPR(>5mm++12|LY0!8n^*pkPqXF@*D9b`Av9k+6NXM|CW+y;UNa<^*|5f`MUojc!7KhFO)xu7sV7FBNWSjiSnw>NGCc%yc$NGQc(wdFyhi>! zUMruEdwBfo6kcGUUj8E9Ab$yOlrP{-@|W@E$fNa_+Ogq^9K2um z-x1H&{ojG-$ln=yeEsDr_LuaWPCkFI}Y ziSxkTc&+YVgvaU>_94{E-;X!QKY%yNi}5D;2l3{FZ(RTHOBmNj@FBcK4^)Cr>i!?b zTXp}B;B9Fh9{-OL+S5W1l;R!okKvv2kKn&*4k>`rrS5p0KQeT6{%*48AJ=0=_2yA|7m>_HG#q>IfO>A@~xWDgQE_CI1SZ zEw9IOAx>>2Z$7^W`Vt1(8SVFL?p-HA0~V8u23eiFmR6 zB)mlab-XmieeGLjEnhQr?VL$xqHmoPUC9g;N-)k)Mj!%3JU{ z`Du8){B*owbN&7Qw+W3JXvLf4XW-59@8B)+@8YdV=lm125!#X=e*O!BGx2u$_wWvR z@_M3E-p>8I^egQrr z{~10S_we;kZoeMFmWmGF)0|Jw=G8n^?mk>82e%E$0J`CWK@nuo{#ZbCy^ z2!ea?M)^42B)=DLmfwfB$nTFlT7Rty6AZM;AHdt?zrj1?58|EjNxUoc`1((t0UsiC zYv5tLNB#)jE1$yqlU( z`4XNde+|!%JX-(B?e`Z#fd>AH7s{9MBKhC&V)^TMNs7n!-y4L|lo0K|6}(LTCSET8 zJ6<9G2VN;(O84`~hWZ-S`&GB~m7I=qzOT1Ga%OrFuY=w8rx5j(q+u*(O zEWA&?ZNkH^|5ezIfdTp3@IiStJ|urTJ}loJABlVT`X{&F4unw+kB)yXVJbC*{w{o4z7swpe>XlW&%@{BJBJ=$fAb2vFt8xs6a;I1t zsxgfGg!jxWiZ@a1%m1>Yd7q=(>}_^SL{_?o;K z54K8wFE|;`ke?Fw@c3scoXS9!yamsepN8kiPselR-^TMIkJevu`?V7CHE;%AApZ_t zDE}^AByYouQ#`)@&m@$jglPYL4=7y#%7+sk3;GmpWT0Pu6Fwln86T97 z;6w6T@Zq?JuYYp;-AWkIz-{=bd=wv(-;R&V@4zQgJv#n(5++ka=wtYl{4RW2em6cN zzXzX{kB1&#e{%}=GB7W{4_}bqk1xt6@Fn>J`0`e|{(q3LqJc;8Rrxf&CVw0cwoZR9 z_#K|H?(DxO2$}04vHxc9Ecugow)`nPNB(;}S3VmfXUMio*%j7TMC4U*Omj4m2k^c#=P4n>hFB0m~ zLijBi@Ot^5@do*;c%ys?Z<46Ni~O&6t9%)6lm88Gm%om8gdSi2$ur;^ zgiZ~t;9c@J@oxFw@gDg<@ZJqi?7vk)-$sbc&`G4>s`G2z$&mRmc z^zV?^|0D8C@lp9@_?Y~1d|W<&Ps9|iAWX`y#HZv};nVU#d`5mXK8vqk|6fCx)4;X( zynF~>kY9%{%CE!7#oezY$-R--NHpZ^nad(%%b4;vOFV424@5$duoT zXUT8Fv*n|Bj{J5!H}YuxCAZ%lggg!0iRa75@B;Z=c%l4myeP%v>;E1?aY~5R|2SSE zzZWl+--nmU@5jsK6FVf%KS71U0}NEke}h-aAH=KWlX#8%A-r}QUH?B!sMEkBc)ffI zZ;(HVH_9Kwo087?C-^O)IT;e?pI{nqkw1>N%72Hq$)CX6<*^w;hr*M1r~E0rOa6Pj zTRw~T$e&JlEa+8uhJil$vv|LJ4j+*J0Uwke2CEAWWo&&|k(U<$uJdu*-!&kW4TU&ZI;OZbBPHGEP2 z7kp_OUH|`;u&jY)d`12@d{zEBz9xSI53-W)1?;~SLdJSX?7uhhO!+FFCI2U$Eq@Eo zk^d`3$W>Uw^W^`=^X2V%i9hhFJ~{O)JorN5BTjy*_t|(XQ{fy!w!8!H(dUMLAMcl+ zi|6Wbet_pEJeK?>S3xJCPy;{23-l46m%P8c8!wWdj~AzTc>FIQl%$0q_!(X*@4?ID zKgY}EzrZWx7e*efzeeO$ z;W+obrzDKT1#dun6#QxNG4N-^$HAWyp8$V8;=%Y&LU=(AQ{WeG?v~hT@b8GvfZrfK z3m*L~%^<6}kcaVS({Hu#k8B2?c{r>tz5u?4_zLh8ac@1l%DH^k6i-RwQT(qZVM&q@ zx~wgp3cf@<4ScD1I`}%`8Q|+~;np876GEySvcT69&j$a3cnF96?AybwHHya;?F@nYjzf4nbBC^5kenYXccDR_o>8TcmR<=~r&SAhGQNvMR7 zDP9G>xp+1B7UDJFTZ-3$XF2YBbr80aLp}J`;tk;2h&O^~i#LI9E8ZOOp#N?9Z6~1x z4%>^jg6D|0f$t#R4!)y!M=}q`eV36lxI z&A&qNDe(Qor@{9Zp8@}}_$+vlFJTVC0pj!E2Z}F%A0)m4yja}(Li}2Au(+QB;SdQ+ zzz-Eq1uqd#13yeW9sF?d496G0|35-PCLBt|v%rrO&jvqAJO}(}@!S{>#=lHLUQ7u7 z`i*!#__5*z;Kzv(%_fqzxJ9Q;J_ihxJ)Z!6%} zBvitoQoIWMB=KtSuZ!1!e?z=>k-Pax0q2hUr!aU2d@@y06%RD zSH9N>;dD7PfuAAX3|=GN0{%_$R`4^$+k6OTNoWVJ74HB)Tf7te9PuvjbH%&G6Z`)< z2|aK)PrMiWeDOZ;3&i`u>%|A+-1jb&Fc=rSi^PY(za>5l-XJ~#ezEu{__re-jQ<#f zOXM&P-Y7l+{vGj2@Jq#~z-`H&4tW@VHvO99Faw9n#Am@T7oP*aLVO;)S$rXhNAZ89 zgcV6bIG?T(_m;&k9#@N}fVYS*0l!8(75rM;|EEE?P7dkdt>PKr*NbO@-yog^exrEy zGVK4`B;>&1Ch=VGo5l0MZxPQ2Zx=5xuJy;eRYIW&(RvpAaT@U=tblhKNBS=DV({;a zmw@|sODKhKk9Zk)w|F`Dz2X(%_lZ}6-|x8ZRYB;HLpAsV;x*t8ir0caBwh#JD_$S* zp#N?9JuIOC4v&a8fu^iw}W&Te_uu z7<{#e2k|!oVRbo-g0CSy2A(254!)-N1o&FwlOgwm%GvZ=Tf!6^mWWS-FBP8wUq^fv zd|mOmB<_culPY08NeKCR;tSwk5MKelOx#;Pel187PXS+lOSk@bOCW3@hg9$l#nZsk z#nZtz63+n75YJp6`~OWOWWixm@oezT#B;zi#dEbu$_1*`1ayu;5p*u;5&#{fbZyd@cu6dJISF6JXgFLd}r|* z@Lj}f!FLs}i@3iy{qiK#!(lh^2Jqd*8^QMwZvxL3Z%*cZFwT2QXh{|V{w48N@V&&_ zzzf9N!S@#L0N*Fzei&z+5cZWr7kHs~H~4dI`_kw>}yl;K%|BEE_!{Gq&0q_IG z2f+^#9|A8HA5L&TY`^CnEMX)eI9?(?3VxXQ82I7h|C1#wfx{`{so_fOhLJN4a zcq{mo;%(qpiMNAaE#4uX*#Eak=!C;H;$7g^ig$xwC*A|zD&8CCzIVNZzPRAsAl?st zqxb-LoA@C3P2xk~H%Hv}ykQ8p$YBJ$U3?V$R`D_L?~0FuFBhK(xgW%zO}`EalW_Q+ z_!RhU;?v-_i_d^}iq9r-KaBr7B+MlVA-_|69{eux1@JEM72w|&_cn}Q3+}f4e+qW`^3}1?-$S55c~fg37K$sKs*clLGf(xhs1Nhd&P5&`(Zx^pNA#n znc%SZhmxB*F?t2vwo|i)} z4ivv6UWxQ0;#J_Uh*yKZDqa(De{uS~CZQG%qvCboW8(GTZ;3a6zb)RF%>7`TCnPi_ z3ju#myczsw;w|9si?@PLiMN6OBH(@)XYCMvDTfa5Ux{~u&xm({|5m&k{CDC#8`k(? z`H2|BHA(_=5NV_}|3`6Wou=_db;{ln@;Mhxjn~3h@!}?Y44$C3sx7 zqj(wkPQHW+2)m1qqJX2s$H1$^$H7k)p8!8a+@FMSs)Q-G6wAt$0d|2jhRXge5V-^Ue`Z1wU6j4ZKb~9sE4; z4DjW~J+29w7=YU@%o(ujh@w|YC@Jqyt zz#GMj7kN~^_Zp1^AQMu6(Z&!c*B%`Cb+HfOs|d z)8aMY&xqH8KPz76Ll~4$5B{8Z1Nigejo>ecH-Qg{H;X6s|1U~tfx{2QTftuvZv!6| zZwG%_yd%zi??)0kH22^>BW zPX+&_cpCV$csls6#50U*{qcS+A=3oK#Bap2z-Pp>!G9~B1McOz40FN#)g$;z9r0^h=dc35WH> ztH8e?UJbrXyaqf?yf&GK8;HJyg z;{D*;h!22giw}ZtD?S9ioiAY+!uH}L;5p)>;5&$qf$u0j4!)DPKLH_E!X)_4;#1(e zh);v>Dn0|ACqC==;{Ja(33G7RU3?yV5Ag-?eDM|Fdy0EsOuiNbK);QNVZf$uM#4gO{E9PlFX+<-^%Z!6#d67t}1 zpm;v`LE;7A#o~qF2a6XiayS1Dkx;xSMDy=Z@e=S7@lx=^#LK`B7cU1tBG;AgRX`|} zLnZi;;#J^BiC2RkEnWj&CSL19I7UJp__5;k;KzwKfR~Fmf*&v5B%av+e?>ww98M5# z0k05m1^=pe8~BOh?Q!mVUz5-g7raXGPVke&yTHFL-VOc@@gDH1hzH}}3*lrr^nsru z-Vc7N_yBmd_#pUc;zJ=1BhDGc+C%|MfZ*biAiXhx5hhp$H@e=Tx#7n_%7B2(8MZ7%XLI2zI zYnM;~hg-!f!M`hB1-@Lo8oWciCYguh|2+w{$wI(y6R!imUA!K=Q@jEE4)I3tI|ClY zUlWA8CEf!5eeqWCyT#kU?-6g`82kTj2_0~_SG*JaKJhN_`^CG#d&GMZT=VY% z3B3uy&A$i5`@kO(?+5P{9{_(?d=UH*TX2RTJSvA_@ILVo@W;eQ!G9n=2L8CXKMtW^ z!UXse;*;P{icf(*B|Z&4AU@;x;{N|>3A1o`MtlzZS@C)BLGcCf=fqdUcrgCYOYkz{ zYyJ!3Dd0omOTb?gPX+&>cpCUiArIp(9m232GQeLJ&jkOGcoz7N#k0Xj#B%~3#lNk9 zuSm#+!>i(X;IE12gO7?AfWIzYxX9i7dqYCeq7co$H^qy=$HYs(-x4nc|A}}R_}jMs zFNZKLhYIkYidTZaBVGmmu6Q;0gm{e);XMhp;6D?u1Akw<9(+=~0sI5;M)Ab{|K}2# z;P9b%Gx(Hv3-~X@TfsjPZ;SKb?|(^Xj|<+%;vL|#;+^2X7w-c9gLpUipCcZ`Uk`-8 z$e|b9+u2FMZypq))XHGUrT%hd~NYj@Fn77Nj#c= zOC^ja31M5;5uX5GS9}sYReTD3J@IMqFYN5rA8!W2GC9nGr-{#juP;6izJd4x_=e&u zGO+(om*8y@zZPsHo&x?w@g?9Ji>HETh^HBM>tFDN(e4UP7v<~uMqD6|EhQ|_=)0u;9v74^h2l=9{@i|d=UKW;zQuy z5FZAw68A?SoGf7!{1ovq@KeRd!K=k5z)usObbN9Df4YPzIGiCq4PGNY1O83%S@1K( z=VCk<|Fa~_$AsY9FYyKNv&C0{H;H?j#;+ZhiKl>H9`Z2$mO!{d4yoYH;%VSlil>8L zC7uC(wRmR0qxiQKuth=^9Ig@12ESH32mCtmT<})$yhZNj-}Msm7lmm4-5_27exrCH zc$;_;_)X%);5Xmr%J)hj+#-ik@OJSs@LR>p!M`hB0lr+k(udF?p$hza;?>}{iPwPN zE?x`1_5E&Ps}oP`|F@A)4~J~=2Jmgg8^N~|Zvx+5ygAN&FGoU4T<~@fZw22`ybXLO z@pkZB@ec5vBOZ)@Cxl((&;`D$csKa&;yvJdi1&i$i}!^*j6a)xdrIhs!4944M(TVK{uS{8 z@Ds!f!7Idzz`rV99Pyz4ZTg)kp#%*5v3JRJXTNT^H}0$wFv z1%9%4HTWswHQ=X;*Me6EJc_?M2&c)R9{hCi2Jkb)8^LSDo4~&*-n<$1|7S{Qfx}ti zt>CrdZQy5%w}YP}-jU#%f9Fc*ObBlNT`b-O{%!GY@Jqydz#GMT!N226=!0;nct5zM z9{_Js`a$r^#D~By7x#xDTp?iuyjgq{{7UgL@TWrvf3t+Sm=L`GUwj_CU3>xjyW%Urmy3Iu@oPax$iw(cf$%*!ECIhw zJQe(Q@ig#G@pSMz#4`dO#lNk9cS^{F!(HN8;9cU`;NKU|0l!;3cagjKcaMa;MIoAh z-QxM+_lg&Q-zQ!Oe!qATc+dT=e6JY719B({2}o&@Lut9@Q1}KdfrE%cs=-&;tk+Wi8q1|h&RQ#?>#M{IWBn5h_`@0 zE8Yq|DBcGCoOnC<^AQimzXQSxa_9sf67K?kQM?=chvGfpFNybtJd8h^e!~*_;PA3| zKlqQt2f%+UJ_tS{K9t0x_s3&AfHFN%23|2F-63B_<|5-$P2OuQ8Qa`7_oE5yr_c{u*f5-O5~ zfL|$I34WD$75LTS)!;4SHQ?6-Jc_?s2-nJ?4*WXtdhk~92Jq{}8^Lc7Z`vID{~IMV z!=X*Q1^g!QR`8p}+rV!TZ%=T|zjg^73Bk?3Tg5xUzboDazFfQ;yhFSP{CgI3y%27b zLm&9<;{D*A;sfA!h!29_DeezJxJ$w?c$fGH`1i#}!S5Cy1HVUn-0{Wzf477QINU2f z34WjW6!`t()8IYgGcg{F{{s?cV}j>BC_V@NkoY`!ulNG^!{RHz9|?IBe_O_*#GB9sD;B0;&tFVir0hhB;Ej?E8ZCAzPGc4 zrnunkBHj$Xt9T1|o_H(xZsKj=yGPs)-hKgL4>@#z=ZklO?WF3VE=!VgjqNoEj|ZcCO!{-jQ9fhvEnO? z`(Zx^pW`HWTgI;i<>D#e$BQok|B84j_zB`^;C_XKbO>J+&j3GBJQLhYabZ0Ar@E*? z7SyYWkAbi5xbKZaSVImI;3?vh;A@Icfv+V#4ZgPcOvL@g>9<6}EF6}K&w;NaJ`cXG z_yTyU_=;p6l<%!4!TWQ(>%JhK0=`Uq33!@#D){>1Y2X_KJS^W!hp?d>GQiWtGr>0! z&jSCVcsBUP;yHh=i^@-|fEf~U;joE#9{8r>`QV$07l3Dq7bdtX-`iY5Q9_7<*xN$9 z7<^0d67VeXQt++B%fPqxC6q(hM!W(%Tf7o{Tk$IJ?Zm6Ww-@(oAmm8M!u;MrycX$q z6t4r{NxU9BSG>XT#r^-z5*p#Ki+B_GuHwz$dEzbLyNS2PcrgCEOK6J;p0|g1J9xf$ z2l$@io#0;*?*iW|n>10{?t3eo&KNPHZ;SbPHfVDU-tL&T@R4^7ejcN#*89A>}| z6Q2b?Tzn4v2=RIFQt<^J!jTeIfFC99{Uv^lI9fafyia@y_+#R!;)(tL4Vp@cFxyd+)@J}h1V{<3%__>aV^l6W-#ek`FnNeJiP zhLcY?QtJdD3C2sg>08~kSR9`JVYUhrGR`@olr_Xj+R ze_H`NBn-ged*XxOw}}sd-!483-YGt^$ld(AL&E5y5Y4|k#mB(!5+4We5}yG7zW5~g zJ!@+JI|ZRz4%6WGiqC-GCq4^)zxW(@kNCU~;QEG z@x=cB5ecbqcvL(MyiYtG{4wzi@W;h7W|6dQ`201i<-zeS)-X`7zev^1J z_|4)if5ra)7749zXcun-zg4^){JY{E;LF83jl1+$lZ)ewX+lc$fGP`1i$!!S8n5_eLPxBZpD&Zt*ejd&S4W?-QQ@ zzh8VZ;z9r0^y`r@1&0U3r@7j(-x4na|A}}x z_}k(Y;N#+cC4`?!r~-dSyc+yn@fz?6@mlcr#OoYi-2eYfLOmSb7jFQc6mJCoK)eb3 z=i<#V9*qBo5?W${=S_*Xg8xFi4g4eVcJN<{cYsfaJdD3i2)~j;7x=HmyTN}W-UB`( z-V6R)@xFjZ@oy{O?6DPorSx@`lNeIWvVG8^>@oDgK@fq;r#b?34B0lFsI6=ZZc!l@^_*cbO zfS)Mt{Vjei_?mc%cw+xwDPaj5P7+TA|GIb@_&3DU!K=hG;@tO6mXH}2yi>%pz)uy= z2Co*+0Y6PV7rZXwLHy-GI8P4w;OC1MfL|b92wpE<1b(4-amd5?x9NA0gc3M>OS}}k zLA(t7V)1hDZ;MwX@o4^CBB3%#2d|$j3{BH3!@O#AD!MnvfjJx&E^X`?Xq4kO@? ziI0N+Kzt1Raq)5Re({Nj2mNo;?+FQ$aClOD3j8VYY48E@8StmYXOnq2{?AC5OBMqD ztoS_mp!fp#bK)z&pBMKQ;@5%~0v^R*3WOm!ECGK}JQe(h;%VS7iKl}Pi)Sow|1TjE z4nGpl0{^jiHu#8m4)`nLxe2cM_o{@vgy81iYvTFfqv8eNuZtIgzad@({-!UX7{Zu% z3HV#$rQkmiF9UyDyc~R7+^>M}Qwf#e?}%4{zbjr1J|SKM{+@WP<4OB}33YIIU%VcC zQoI5D1Mx=ipNluecrgASN@$J=o>z2^t_9!+h_`|tDBcEska#ws{u96G@d z5$^&&RJgCj2QNQI``-x&$ID?7{43&9;3tSrgI9>pfPYnd)`xJSggNl9 ziO+*qiZ6hlB)$Ut>*C(u-L)XG|Nn-B6bTj+RpLv)PZm!FKSewZ{8aJuIQPA32^n$0 zJ54+j{B-dw@H51-!E3~Gz`q&sApUY8oGFJq@Uz78!E40}z|R&h1V2Z-DCA-MTUVbe zp%@Nz;w9kciI;+(FJ1s`G_W!TAMd?uNP zN$n~KkYZzetuo+-WnzPb1c@GS!F;_uV=jlq_3NCD3hUjn|Bcq;hT;@LP$ zzKwX=r`Z2zOGrmX+lps^ZzrA!zP)%Bc#e2>f@}WmAR#9qxcRrEcrN%(;(6e?;`!h^ zix+_J;!7xmu&a0xc%FDM_-^7Q;Jb^Lg6|>jmqEyvP!7JQcm?>E#4Ew~60ZU;5U+N8 zasR)!gc>;PBVG%>uXr7Jp?E#`e&P)=9*qC~5*lNI=Y3hc3A{+W8T2<+`1VnHKi<{-fv~a) z4I~`m>7Nex$}0M5z_V6Tp-%%ItEko=?-L4vbNE>+XmQNJVQ=+S@Qqf%(^kR#%s|*N z$dp~3vkHf8R>AiP_^vC-=YUo4T^;xTqu?*C!l5PL+pVO4YgWOpT?Ick#?SIRn`U(} zVUeG=3VwJnWG;x}F>yq|H(p6a4`04jXUmprOywT3eAkR`ZM3}Tu9l^%zgg^zte;-J zboDh3c1GW3txH#bLrP@4E~Dt<>z1zGaPabFW#@0SyzccNgAe3W)ZMbwf8mgz+~CE5 zkDd|zn-=}A=bgLedC~t{NA|_C(`8=|eYxzlr5EV>9Zc`&GoRw}%Mt%nwx27Ir+-&S z_?Q0ml=15Nx9&vuYu6OnY5KQ8d;{6P`LXNb6I6j0f|`W00(|+S`&_rx8fkx5msDL6 zbji-HOGQY^qXAf_|Xn~E7k~pNErP8 ztthM=gz#RWgJraQwcodFzsA@(S{qaK*9-HFuTEBS&T1=wuB!{Q})szT{wZr zZx2|wxgmcdVBx@!`v(W%vB64&e+DB{v=VMtP-b*-Ff!q#p1e2UD=YY6$Nm3s=nI5p zE6M2YfUm5@cZugMbE|0R8-9vp2XgeOkZr4Wbz}z{hQ9_q5Pnj(cNwHQ(`!FGjKbi% z1lbwV!%Bpo9NFhP(;L{)`F}z7i0t8@C&SN7fz#J?rZ*Sb;fCcdftmeWg79h=efD?u z`Kn;sF>$>|_7zG#bbMT&kbS!RmwYp>X9adxzIUM#j5Nj*Ov%1T`pi4-LTEkG*5=aN z#3;MT*8Jv%f37 z{|;wc3=GS*0@Lqwx(!kH@9~Az)=IFUGAxj7)3V_%mq9pV!cVL0GG}_JcRPI}+1av> zlHL1z_tk(+?{>f2IqaYWllMB?f^1gyr3&N87oBb`oRuA3IKxl(>v8{n+50%zYkt$| z*28|Az*Q{?ZKd(Z%deHXE@vuvyR?ckt5R zauu)wa%5YNwaH#r4eFG=iE20;8-gJIs(}J{4W0SJ2$L3}4ru2=wIsf(D`WM`?3s?_~ z$hLXfkEQq>={5^yWZS|svb!t5f_7Z?RjNSBUQS=;*1zDD9Rjwn6w0;=l*+aOatmAr zHVcYmn}3aLOW!Qp8aOEX3$Fh?Z&bjdIDcQ{=Qzgr z*wpi)qO5{bUvRdK>47c-&!$@0Or*}1NQR)I>{y{f=qbEpUXZ(Vwt3N(A3(`=cm zfCb$PQ3k;?g{YdI#|_4?bgMA+H$puee;ae|VBS{-s<8I~GW?Bfs{l7Jv(l{x=>OM1 zkK)g|m>GCix-PT=xC2^Kx(xyK^`&p19@<8>#nfI`p6}w{f~Z6ZY+8Lywk;%evTeiB zBHM=SI@wm?HrUi}z0!9rvX@#ZSoIL_P9`FJLw1Ch=|QHbg&{ zZC(C5*%m|Gg#JysHINnXKUD$?vK6we%lFr;Fr5`x|0>smmY$o~?Vw{5>wEhm0e7)w z(yhzBDchFTE8)*w?rqTHo7^D&9#sNsA-nJ~=@zvw%eJU~SGJAuA7opPtZ}vLA)AJ& zvZJLvjQtEKkZ$XM%`)Gjb^r-D zjh=-bpGHA5-#sI0tKA#&Uh#SPTMzui*}iAfCT2g32U9wDB`cclqZHC!TYIW zZAkf*&99U5596QT+5A}vtP5Fz1?d*WEa0E;XZ~wk=VC4l`e^=bBw#JtLAG@vzciXg zj8M;oKl9&JdeA^O|M{iUp2&dT=@dw}3iCUoQt7r%@H?ZEq_3m$Jr=M8D;>WRIzt)Q zww(I;(ya&MUpfWfHCz6!{hr6K)hkr zQ zR?5)EFk7~b`L42UsVvud%r5^y!Sf`deXgLRK9nS5?Bp+?{_&e z;Jx4V(0T9IA>F3w;=P|M-@8`{Y~J$TuV1>|C-C0ydFeJwhLHY1e1r0;^yuOg&cF8s zEa;fQv~-)Mu?Gl&|8Jz*G^4&ky3La0=L^yNU+o4LwN`=T1Ysx5VXcp~Akee1)batv zI?CS~ko15eD&JdA39JQ43BvNdt)*KR=b*rB6tKN)+k&-^pAqF_OZ}q1E8p8w4%P$v z%eFBuk!@X={0Jq?`AF!;%C?v~S+)gXLRb0TImqAwWN*vjl%s0;^#fD)3k7Rsrg3+~|VX zVu(YOA>CqV)5s3`-@0r&C9tK~C0NvVlWytvlWjeCxNIwcdZ~14;7~ZL!tu8*PWeOp z;V?HG$0`GxMtlr+JPP1rxLnMd@G)Ezf3{7|LjpdA`x*-1W4M#0+brQ@xYMQEtO$C* z#lQ8yg-WnRG+Tp{H@mtq-CFi7((MZiw(vd_ko2H2%Hav=HiRR)>HB{Rsuz{OY_{-C z>9$U=g%j{!e9-7x@N4N711WyuNu*0Kiv)ZS`Df`CbkzSX-G-D8D%09rkJ=FVOt6s> zSdj5aW`=b8288R*rP zLHymQ>$8n{5w6z-vTe+&l!5iosj@97FOqHP>7SLnu!ZqoqXMi&jVOS+FWp*b&vI?y zx=gyo2pgD}RDo#z@nP`|r~q4dlXR;v^}D6pS1tpx?JJ$JMcvK+Un9Z47868Kk$#hl zTFW3;whaLb*j2h+zS%>2OJCEif1byO&HJMOR`7sC1}aP8scZ7g- zX+D->K8~+cdJ8)0MM+~ER+0IiD}UdD=sZ;5a@n>}@Ol5$(rqc_mg^Sjc6sNnxy)72 zPh9<2!2PHI4;1uDx22eR6?%XTd?NA>`rm?xH5^a@HY7T+kT|gLigX)-N!ixoIoa03 z)c+jn;rLqu7BG(t?$A16QT>T@vl+zR&2H9M57B>Vphxj%D-|;+KoIiC#WEFO3lH@| z`143c>L2ujHM{+0f=yL`1tGsq-xB&-`sUM?@@(nWKz^IPn&n&`K zEl(L+>=dD;#jy84MN3S#`CotprIsL^yuoKb=w;9kgkBE)Q0NuXeY+(pMS@CXa18V+ z=wE?e4gG7-tDNm8re6&lPL+doaV_-Iq1QqGCiHsfwUM4Q{~M6tTx8G){e0+6&@Y7E z480-L!|}HkwjjYJ$e!UB=>T+2(?K5zI8TS5bD9oA z=QJIG&S~n8A_3>=gBU|j(}$sRn)X4*H1)m5k%05`N$8xWPebQ49fTgAra}C_fCQYU zKZMR{`Z9D*(-G*gX&TPI*N}ko^bP2orf)&#G#!VYJWZqd|1J`6p8gCvr|AdM?Tq)& zW!q)@m$Gf2@CVt!-Z3iQ`&7VoLMvq3Js=OEt#OOn+1O>f4tu?&(rxRuscg%Cah-7G zdzngLHD(32LI&H&wifLo+itnWFst^JZhOO{WpAYNy=nn#F)MJEbZf!cvTbj7k!;)f zG|RTHT5f^eq#pC_tCzc#z*izJ4QMUQFVTj&zx*z>oDcs>q$-%~y zWBhCB8-$62&w^~*ey`i^V#0zjRkr2Fui;$~2K{e$FdM*u8DvPe^x3kl$999gzijJ) zQrR|yU15)e<8MQ76cUulwlVw$^pj;39cKN=pUDtn`M&CsRI%Hc5yP-44?t|VV+j{UR*)}9)?b`oaf|rrtZDjC~ zZ0mtv%C;c;t!x`(&XSLzXZr#cM88LZKgzZq_=jw30b98A*7(ZE3NDjw4fe+{##<sC@6$MG({}_z|gWn?{_5 zKZPDFB$1z0_+99Xq4%VJLFIeD5wL0TiEPV|3(;!dbp_hxmzU#pq+5@$hti}kbM5!M za4il$wiJKXoz0e(LFDCmT&j1GZbQT#-c@?y`#)Z-c2fcy0#2hnptA?L<>H1$8y??V z)sAOR6tEZUyPuqh2w=bU#PzE-wj*xBlhSZNj1ZgW! zIR4hfr^&%ua29Op=SjB;*2}hQ!{xGV<)j|&n48(efhW(5W z@GR^>*;YYbj-QuqUHl^KVcFKhufX;vuxDd}N$77V1G~IVL7#>_rSx{^GcQ|@>3weo z4ojE2rPXG^GT78J`P3ZtEb^a=t+YY>%^^WHGT2BJu%OF?Jr95C3(#|u^AF=cKN$pe zEJ0weKnB~W0yd^Ou&H|!?%HBs_3o_n;ldQnzd}?XIYE%d`w|M+7j_B?puPlpk^F-m zb@TtwMG%(fB_)XLqomtXdIIcJ6hJ)+apaL1xO1Eiv9&GBB@Gn$)YhV?0zZqTF3Y&U0GPpNdE)a)azpYLHxC&#htLJ*T*vOyr)&54bk(ksW&A1hwDHzm4kf=)trN~3dTPazA0_k>NurKT`<)3)|zrPaL7#72(-i->ffWzf)H=`A@?HiUJ=kF(e zeshKrSPYy4n|d!QaG}y$11^D0y)W_)`ro$2m%-s`*wp(`fpHAsBy8#fp?^63Rsq}^ zd4nk6dR54RYzX@Iq}#E(Vd#%ax21X{(4+XX3cREQwhoNRwlRy}It72~L%Ln>XJG$H zwiUSMZEjlI5$#QH)AirLu1a7n7)1*XmToIu^LWsONj*4?E}w->{pj0p{^xiVU_ms7 z3d|$Jap)_ce@*#WP@e)j#s6bn)B^hwiWz)U4jV+Nf|pgGOD9l)N!XX5KsLaig2Ux< zur6+eP5rylZQFgnY`gwHBiokh7vi?>O``%x;LSiERt7diufnE2Cf$Pcy_g-ue=3IH zqgVpZn?(iK!a3+1<9X<@{KNUTfCLP}70}aB;g3~eTkHP-+j}oQOOo|y{{Kl1Hb$So zPC*9Lmq7oA{B4L93VZLnN7M>gwUxv>w|ZHuZz0+cZ4N*?#nL(6CN`A3z5ED6k0rL(=VX{yNf!dK7<_pcol= zPq>A{9!ix!e@_|O_5JsAKpE_E*b1UV3{^m{gk1%DS*Nc5 z7L^&DZlyAN8SJfO+fthiyBY;huYun0t4l0sYLQ@j6=2hB7ueMIgkA`{4*B~`aF86V z2TEbrBZK3mTM$>twpm5JA@1*cXCi|OVN-96XW%s{y=_36ptAwZF+GUCmKgB7o0OsT zz-_3|y|OI^dSJIAKk9ACdKmu?DFeIO^}(k8gmjxV-1oO97ZBwC3i5j+$&Th96TGbi z7IcNUcyyou>OYge1vT$0el0!dVK@J|um7VG*h0cF{?}p#LHj&!?K|AGwV+!c_GYpz zNViMqu6!>K3HFt36*yeB1=-hRTZ~l8whEjB{aes4lbyK!UoBu=-ia=zeuL6m%-jOI z3;wKNxAesQKQrh+hIhd3K>^fzq2DckYv2RUUVQ$~1bxVWUD&4#ZA^K0+zwuC+P zo*#@s(hxAgGz#FiSlcQ?n}$2Wo`FC0U41!N5Ej6mMF!O8pmU*`hkk(aw;}eA0APXz zWN^IF+qA2aZ9#el>~mmW7`J_I1qwg{&-+E z;$I*H-ycA~6$N&}j_Kk2OG5_tz~MpI)YIY5i&+Nr$CO`qd5-4)a{|@_$qB;Qk%YGTnS(0V8YkzR}ivmh_#{Yy{)d$1IGZJ1v;{#M}mC?HCZq?aLsY79|1^hA(p%qyTb1m#8XXBD^<6$lfA zAJZ$5K^5#~rMD=*4t6!t2fB-YbErj&*uolQa5D<{9&GBh@UObdkGHT(8CZ+&gI$LV zsMkYpfZeP7?9OPw7qG8j8sRViy$O0fdaMyP^_P{O^|(I@@V0EbyL|_CGb%v61$r|o z)Ec*aj|tvWfi?sm!fr(YzmjfC_nd6o;ZSdj`3LdG0@`B@E~QeI2E@eSR9g#_LYYFD$HV2(+3X-=8v-7^{;Z?fyP<;o zFKOPv2>f&);2gHVCPVN*W_Ivb$h8TpB~ zU-D!#e?(HD4DC+oWZ2Ztlx{;eG%*i*%rinM-27vgz3Za zw;KA$fNwyd{#j?ko0Xr%%yqECSrEmaRe-bLW;xi(cZY1ddOaZ9{HgyBexl*x-@0t& zztQ;r^CudOG65)sX|MoW^{!6Ig zo3d>~GAY|CNd4!~d4M4$e!cgg`yN+1M(Jm*bW^CnN3v~5KZZRYvxE5ir+{rZ*7$zX z^*QMBrLrwU7O;VI>!El7p2r`h#_|v2KV2EvJlza7e>6({f3s9a^Y62M^ZD67qj5cE zJ-}blY^?^_I$Mue{TWHASpo@^xnSGZ3v2Cf7VL4>R-vY@;(0TrVe(>nF#i0tOHnLA5PYhDRj3R)f2`7^ z2H4Q>$0+GX)I@EMzI6}duNW<;=yp9|`*;4BWK6mZ0e>sfi6JOQ{xeDbQT+3_9#v?e zeyfrAsfKs2tDseYKgGz9Zav1IViYavZvOGdB>Bl*6!cI&PC%7IuSN@NRDc~`;V&t= z6Fub0_jm`>qYUnHQR?x*L)U|@2ka)3_ki`NAb%+_C;z1?-{UVG>M#V2Xko!at^ymV zf;{Knn`og+z#l4%$lqV84ERfhv|d+$bs>LfP$u1Cpcxgck#0T2-vKna2KtHJFVFc8 zpa7oZ??(&UQGqe}Z>t9K82-Y;VFQEyx9fg4GRS$v^?>b!c#OVHx}A>UDfhHToxjZj z9%3&D^>F;{FB|JR_dg?{quf%co}p{rLWmXS@e7#yrD4rvfYpc|?236RrXlgyZPJiYK)H zw=U!f=PvXBzX_RA0d}3|BiKIp^9ksXj|9`G;n0(=1=d2I4V{o~LB_+U-c#`*!1mko zdz8SAT=M{N#X!6QJU~2*A-Y^=KdsB_F)`;Xh8}Gr`;ni^d7b$@)M476Pb=f>x zkPW>~{?_H0c%nKa-C`v9Y-w1&H-;eO;nDQcny4jqP9Pr@$cN6epX2D^q+@$g`QD6b zXkRSwY-h<}ybF1@vkE$oW;Q@yJeujs_u7=ex|~NZC(!~Py&OXUJbF2W9`DkMPYGWtq^65hF7wfJe5fP(hzZuG$f$ zJP}og40xg`8x`b%rL-Z}qY6^rkdJum+?^6@6 zp%*)^6U0T6bh|z0ah$$b0iMUxG(*XH82>yjlZ!6oX_&F(3<7_ic3D6V@i0rli;F`L z%|D)2DN_QwFW^CfnWPNDiBgIn>_-gc-sK9kS>ydMVY~UCjtugYz@|wh^eOZ}kqWT% zbE~P2BqDc~z+4917rBkF1vxEeJcntA&4=bi1$MNs0;SiT8i-C`HX82`2b;p4YrWWcA`c^|q8?5qO$kh){a>BYL7^4ZbA?{xjQm5lHADn$kOj5zOO zSHSnxg?t8F;9U?^?I=}{&wzVYzrfA^-IM{J;pU~g4D6QFDx+(#F43@7$?1stp~e5lKHx!oyw zhdJGXkYijR-M+-)<2%1g32YC@F9_zO|4(CtPrqsocNMfAjy?Sf(zT_`|6n{>Jr3A#KyihfkFlB0hW?IWk^BzJje9{;?Pd1$>QP8D>H9G>_&V=Q-cwS9YQ+;6@d|_xO#R9@qIAzM>19 zZr6gOA#n4bL%`SQ4Jm`$)dPHuUKLu%x9FuG87#HI$5z_KA#&w=9HXLl-PO$;IvQLH zveW~73x3CCPPgv?`4;>-RItwSSNUGU@T;zN`o7Zn7W{&1 zoPMNqdkcQz_di|NxddNP0=@;m>;|WABb{%-ues6bUzN_c;OE`!^b@7GxgJ=2|A%+0 zOK_$V@Gbad9q|JA7W`UFGrk4C2}9uCf*R7xOLn72kJy zzWn*eCKm2?dXDtyZ%KsXZx@?_?sx(GV-jiiIlWLB@Q+EZ38&k&f-7koVxajJ-(61a>Ncee?1=^6{oL}TE5J4+e7|>(-kN`?D##a< z=RD>7t;gT=^%i@(Q7BUayBza+-X+~C$ahDV47dX98x_99zDxSO+OYV1i~Y#!E(049 zz8AO=LDm&}ou=pIyy5)6s{;5A_=%64et=s^eUI;Mo|;txbs^t^Kl2BtA0eIZhA#S( z)3=n)H>4N*+3BZ66$s*wuSl=@r%TYL493vHsnzNu3q9&R=(4pmCjc;Hzy5YH2=0y5L(bI?CmnJdCd2J zr|skPAE^MoT)L>p=}V;Z-Ox?agH3BxzQ@;V4=BO&%7CxW?m5I2Fe|+eL$u^5r`s%A z{CgkHp+frm_V@Sr`s}>ZTn5)^40%eX5B)!%l5y=14w>nYiLG2+D!?u_*?2CP zj~xw9$#h|*Th%F<#QVSh`!SjS`%^Lvmqb12d-mJU|9VU&7`9^cz>LcvaeVjx?UYQ= z|Ju@dJSNkJF61eh)}(0^hSldiCDR`IhvRP*;4zsYRDh>s#-M-RQ!-v-y_rqWAe9k z;6G2v_`w(i9~%N5lgYZ=wZN{|pZAnZH!9eSATIGyz$%W(B)+8LDVbgr@Oe+k_;sp) z-B|FLOxqRlG3-VQdZb$y^OVejbQ?mPlJUKMw1CHCa++NQY>UQIGF{TG0X!u$bfxpR zbtZmFCWybht6T!BA&<#aNw>RKo{||v19(biO8$1jB6dn9jQ_T)<2B?lnJLV3o{||x z1wPLy88`o0Tm|eq8Xl7=k#0Toc~8kS$ba)_t_3$dIwKNnVqN*4c}xaRzo!v|RoA!% z*h0orGIQv`#Zxk_e6K+n*wbS-1o2}s|NB!iW!JhP+FmiBQ!s$u56ldajK%aDrfo$j_ z(t}NB^l_(Tg8sLQPab+8`It=TF^Xx%Q!+*H&qoEu;UArn3CG`RG%g3b7$qH(iE8Y% zx*o9fJS9_x3jD_@8R+3DnJE4&!&M)Xapf1F0{;i6WL*5)v|Hsd8CU*io|2hF7k~CC z8NC0a4qar&WNc-uQxDim_<2vsbjja>xX|x(UqILplw)7dw^w<1F!^~;$&9E1HcR~f z*)f^LS(1hd@RUpwdhjz&$@t#?=$K3}{?=vx+fy=au?hz9$73>6=%VnLO!%=KP9A#T z^PZBKP7cyA{{IKZWTFb#?RiRa27&Tz5eSlnV=W`uTRO0MLit! zzwOC*OePau$Wt=)SgLtSW(q^dQ!+DQ4~64zYcY?>%prsS>r*n`(|Z4x^#G5_csIBx z-#|h5c~8l-qlb7(rtOA$7ylL{JSLNkA^49|GC>Q%k2Qd&WERu_ySnm}Ox}%td>-?d zOcn~@DVbbUkf&twq4ShXiGQQ3fUWJTI3|;5;s5n189xiRMk_xiv)IKvCDWw_SP=7+ zOdfg=r(|;fZ{tx+@ z0pDXKd}+Y<54cZ!_WYZ!gg+GU{Z_*73;4k+;g#`<`kuAmoInVFXEgN7 zmG`dqE62TQ_ivr-M~-kkVGld@Un}^%a*)r;_*VbHgL%FCqQmmXe|7cFe%`09gcCy7)8E%(4v*4UpL@8t|Wcf^||4K%Z$7|j&AN&)@S6VN zc$q40U9)AeYk)n7Ixm>R;f{!1^FnZg8GbqS>?L9PV8N>h{s}(|!GAjh|2bii|1)6u zE8!_YPlT0Y`jBtC68@)v?LrwC^Z8-W6XBOk++w;wjOz3 zwz@3%;~eo|{*`R&q2Iv%E$rm;6Jhz@$4Kyd*ngC575tZMYvDTY#bYQ{w)M#Ru)iof zSeT>oy=(ysnjK|ZfqTog0uPpL2QE&O9b_1NYGhlFoGIJ#Upyb-%J=G&z=G-$*>(b? zRkjVu^|Gyi8{vN&{2zjUuk1BdzW11bW%#&k+k_86|Do)))#6v6zXtnt*l)^C95w%` zK)A?+pAX^i3)wa#bF!_=|0Uach)@6jE!}!F?PpQU_|Y~ZEHKl7#HM-+*jcc*hMg_j zDzL3=tKc3;zh`6z{cl}9q9xlNDYf-o6V}Tsi~cv*^UmBl_rRY0zu>Wg5xiKfX41_D z3${_{=OpXl_}2^ASdPKr66rSo$Dub%w^-o~*#y$xl$2i-f0IaXn-W+oOhKQ9Jp;R2 z{?@Z6jhqqnp&dP$T}0+TTBo+-j`J45X?hA8~%?fy$u2GZ@ed@udDUP8xpV~ z_%Skg9rgk$umbkmN^dLR1Z?kp*CQ#e{lWG|39JGsmLO_>5PYzwkvIghUqFW0~ut({S39yLva!^W~Lso)fDk$(Z~LfA#H??w5=&`V&~s`$iC?LqNw<)=!s@T~l_CB0 z$ge}TUFFMT>4TB#Rs!pwidX{AM4G?b5TA?8HmKTi~#>64=G@OR}v?_eF-S@Na{Cu+m%1w43hQ z?|DB(3py-;qP$cYSdex?KNfly^a@nq8?qCJ0%`^|5! zRVNt_y9vPf(%AskHH>?Jpp?% zw8QbY3QQqEn<`}YBln_3(@6iIbc>Nb*iXu~9(yjZqxds0tOPbq-jHoe^RLh&GpN9O zC}0-)9PIh`&v5ad=mBM5=chiAZQJ_=WUvDE+8?@g#2VmD23?%g!|O}824#MzhXS_J zrNCh)IoST?_vnEyqrfHbPlcTZJKYC3Oc`3#W3<|UXrP<_uOUGzGI&e6?cx54Y50M3yBPfr>G_(ZwnYA}eD4!E*wyaevMs9D zoN^0`70{l@An3#3(*e5^b{Fgol;1`w-|I$#9@zgxi+iE>!S07W0DDk&;!bS{35H>B zuNvFZx|?hZ(h;N|h5aR^w;mmH`r`G!Pzh{|$B{t^GB^qqm_YhT*i*2lBRlASyZ9WZ z3~gG?z=3D(XQ9u*o`<~vdqrr6<8Ku>Q5CQa$eOPN34)K^N_rnS+tQze3a3D?hJKc8 z8^R?)`Y8S^!}U<$g~%Wk1*E}Fhn)dC6L#a24h2|HWFf)js(|S&vTd5)gbHTEKL>U$ z?2akl&1>5b;l+@uFBJJ}YcRdENe8umQW*FgW5 zbnB6&zi>nD$_wIunSgCNdE|Xtw6GQh6zD79Lo{RqPh1oIzZ3PH@%PqP{mDCjzV=6R zD#v~}p88Qxk#PQ5*Q_6c;1eE82tPTp?F-KxW!q+TZ`n2*4wP-*O_#~G0&8X4`f_ey zNAutA+b=_cX4zK3Yh3=D+w{C&w)NOE zvaJVRlx;)sx@_Il`rbzZ*5bb-18+J$D>jyGE#5@7RdDW|_=>uPbgSTQvRw}a@t=?yiUo1PSKsf&z1*}Dv%C=c>wQLKzTcO`6+hXD&*%kwj z$hM$+T6TDmist{T0@j6Z%C-VOfc}we8`D2P|BGy^z$dW(E<0#&RKB+#qVgX~U|raP zplpS`+OJ%USOeOiuLXTw*=wnMZ+!ua>ULzXS;E0h?5$+m5bY@2TD-Gt>+%BGwl6r$ z(!2Ht?^T0CnJyMKEt_7uCMx+9SAOEL;R*7y?}%z;Z>Drt%eIQP$+qD2?-#JE-*d99 z<|EMi`rP!gU>$kORm^6KJV}pr*GSu^#GUXNdLTIAvuDZjv;KYc7XDr(Q2|8-?%z87>+S7q z9^LwUr`=+Q-J)`iR;$88a9aic-+$(v(<5|@gkJ`&3r8vSUB~Tc*nI)Z3;tt;>@3;u zN?rORl+O`CK4C|Zmx`Ai95paIIzJZrERo~fajxY<$GU&h{=eqlJ5GvXYaefR6_zAT zMq-oPl^_kGfU%LZfJhqw12jQcL3BhBP_NS}!UBtqfjeB|m@$v(g1Hp~<}u^NFe)nM zFNZx;W>Fo5EQ)~LSE>uBrmZ&l|wyo(u-aQE!?tGwkGSH`>xnW*L&Hg;* z)6&caq}`J%)BC@%yh}2;7R2Jsa9F^C^TZ@I9E$Pk$;~)_ZawmfA5ld9Jn+dsDOw>J z$`|npQo{Eu!e1`@o<;bL!mCH!f_)asf6FqPiMo1aCdo+%s70XRJMh3UTHLZW2&&MF z^XfYn)U$GR6-ipSVxCd_m?H8^gzMN){z(j{x3sV6pUkq8f*a5$6_Tl5WdJL2FZjea zK>K@O^dKWR!YW0|%P4dh zMFG(>QAJD;ZDOb}{a&)V^M2pSJ9+(cOWknmDkgiv$DLiP6Vm(dUemuc`i|qz{d{@j z9vqY>4M86tPn8qrga$w+ipE zl2x6glFaR6vHqxzJc1orpR?oB{Ac1_wa6FayMs^n?JPs8@C?j9?mhID`F5?Q{wRo{00$3a7$1&i4^L#pHVmKe1SW zrjntq?&B@kM>y4&aekO^ssX~MZ|EvP<$j8wDtraZ5VOQ!3;4-WNWIDc@zUJc@SFd|5$ zQ{x%`U!yeMm;Y7YtDrK$@;`OH@=!e5o3Um8+h{+VU8DAss(!0X6shr7J45k}SwWmv z3!K_d#rYn>tC_4AuXZJIzFz~Wzj2X_a#5tlU%bGwdMa2hyxPXZjb;@zX+-VYNr@4aHQ~R9LM|E%v|$7L<6RZ zjCu`_^-qoQIIq5ctgrAlmY%7^tec6+b$oLh@ebl$#4Pb%;{C+G6CWf#LVTR~B=KqD zCgKakmw`zuN8&Z&8^pJWn~DD*zDsBYoq;#%T5;s)X-;%4GD;&x)$`BJaC==C2=ybiny3q4NO{VNvtDoAZ{XVCT=5cCzhQJ0}OUx zx`H^ASV^2rtRgPfD4kPHVl8nUaYGC@F}<0%jkujyb`A|9b|6*|hY~A^lNGALSw&(o zv6{G+xGshpnBGL(Ox#A?PAvNiyNFmp97?Pd)RV(x5>>>-#A@PN;yU7n7;a*EGjSVn zJF)Cs8WO{bbE|ay)=*|t5+@U@OqwlP%yczzEpZ)j191~^GjSVnJFzTDgOYqxK^#h~ zBu*w)5f>AyiED}Lh#QETh?|Mqh}(%}p)r7ODu_dgmBh)!D&k^dHE}I*9dQG36LB+f z8*w|aj59>C1K(_30gJW~w-d`&f;ZTK=?dadVkL1hv5L5uSWR3@TnFrIZ6L9UxS6xdhOn~0l<+lbqVWf!nBh!w=4z@$}4VluIcxR_W?TuWR> z+z`V}Om8M`BW@>_Eu|sE3XL*EhLWfxP9|2xa52->#I?kA#0@dr#Pnw3HsW?-*@a?| z8loN48+la_hY~A^lVe!L^kQN)aV>EjaRYG^aWional4?NBFdK0HN*mJD%`R5F)TKEw~_!rpZ_?H(bgOx#XULDS=E2ouP;UaTl zqPVVVw3bgo#xx<+>lyhgSAQ{nvQ+E}KXnY`kCyl9)n5#4D;Vbw624^-enf5gbnks@ z`tF^mMkhqef$I83y~+TVzY@H&f^sXz?F_NYz{RVdB-IR&^1IPob2H`_nic1VNb{6W z;{0xu_gIfjn~ZIxiRIduzshl9kK16&)1Sgc$5U3_=sH0Dgip^LqK*fv$KiM|I9qN{ znS}Z_izHo;XLWH`*mt0cJgbW6n)=Qb?UxVLMilh1x4dnD$d*>=m&5$jE09aNui}mM z8ZbwUUPsz_1DM(jbhiMlcgg305iw6JY-9eHz#Osg71D{XH70j_A5P&{7NBMS$uR3T z$lFARSf62ZfK}Hp84&eYw&SacTq-s$`%i}DDWCWq2Ih!G4U_tJyy{w3zhR~?ZT4-V z6n$*(0(5r;hQt&xZ_*8suPRnUK)#c3C!vy=MibJnwoddOk!6Nc1_7Ct^{a3&(g_b3 z4F`IofVt7Y{OAekOP))dm@IU~F<{?sFj9JVM*7fhz*{~C?{z@>kMEFfV1^$nKD-a| zOVlZoyv(S)u^sr*HQ>$vufNcGQ{TdbQY7aGy(iwNtu!b>3&1hJo%Z+N_ z5qqHGGMP?lzmC`)JnGfG?CAW7d)Lm8yHYH50zK2%-lLAHy?=qBO^hJrzxo~gj)n=Z zTC$}L^p|iZX`$p*{U@`6X7&IRKq||+g)MHrW=I`R9Q0$n0yz80f5mu~+UIgqyqoc~ z)-KQ1h$nRWTb$NcZWru)?M;L1={venXO4_l?4iu9-=bpEGNb8>U+WrVDmiwir!mS* zuuNpdE$vPXjS_`jzlWk$F<0;U&$<jK!;RaK1lXNfm*?$y>uQGb&M6* zKv#E)Rd6+9MeS^Azs_RAy4dQ(c;{;i<&#~z?w)EV`%FJWKytfR=XR&#XpicL^=GlqIkFVitZ_10?TIo19>y0@s4ana5K zjg+uy!RqetP9*YrLzaiOney=tJl0%#OuO?oBxkm#>QIz!A7?TdW1YRymng{#fm zEzp#Adjo&vz+Y%1-Gi&xtL*$W9l=jv`K>*WZbiJMJ<>-~{vj?dHPpKbu9F9&V+F5g z!2_E?LE;Da{xupBuW&S{z(%ZKB$mS+dYiLCW7uNKf3F)LQS&7y`va(;+n11<+mzbgzbQ%Y({tFr2??8Jl_`*?CM1wM8X~1$|wwm%Qn63fliS}xwJ)(1wPA9Fz#mGnz zv&0-Re=-gG7;+(7?sZ_fA;5^9$r2NXqMTC&Oxy>#0=qb$R%wa9AVfy?JPHuK^N~&v z-3yq0j7T|aDbgM>Bt}HP9rLHdkbnl~ElI2R4@?l2(b6m{_z38K0Cb)JW|>ZXNI3>) zm_hkyGq0!L23l_ba}NWZH-QC7>+$CqBBX^(hnJy(LJDYIi+qO_gwH^c{Tk4H9T+j# z+)G&gQlNb^8`zQh)^hx3s5n|ofxkn*-N1s6u!4n17dV_;Gg|%vK0E;!Jp(=NQ$U-; zH1fzF2y`Az(qfKFe|IRzy#+o`Mc#Vwfdkwz+)ja_@C=}p0H#dWIY?)_0dt%=ohQgA zpTwJp76tTxPY*<#NGw(yJVHf}vfM*d)EBWWR%$DF=R#n98IgT$--C3Dqs3ym+(Xb?_y+lfgS4UY!DzPFJ_(ll zrq9PAZF3^BIMW5PYN=Pii6qO4oV8TUVVCHE^4VU%1TiA!+bVg9f4>bCwg&pf11+eq z6)<8tr3=cV4@8d`m^6b;TPgqY>_VF!iyFfqPmLlq{!$NvC_Dyq9|z`$!3Lyl_H~BQ zZ%qIn(NlRY!@2q3GYrbGC(;31?lJ9)$MpD5aO2<{ftGrMfMJA!dAc+;8ENYQlh^fI`4>>2R)#Lpj|J4Bqf(-jsn@Vt{4tU?{D@i= z*Onh!i&wHn{;-FV#VyV9qS0JVU$78cu{J}1za9Xb!1N6V*5%Fa*ptfC>m6=74?)tJ z&LfrKXumw>&h7UI3Oq6a3e6SETe0+5Dlk`6Eh`{zt{#TR@*BtUk3J0YOivEmSygg^vpV;B~=oY)OIF z5i=$lgV3~H(uc1)twTwp$=d9fznh)zK4qr6zC56|J{(}TLrV zK5MlX_%nDx^=f~WmLa%Rzp<>d*lX`P5WIqd2T;-Blxxq+`-r{RhgWWwFl{Qd20+i% zypp?&KF4KTjRdgNk7h?5X@JpE77HlOIIIi&)`H#I39#K0dC6Vyn$b(Bm8l>^%qAoO)jvuF~`6H$_%rqOmzpK%4jzn}UBGQjH-We{nsco==$?K8$~R)Z z$CDl-e{Jow8uoMdL)Vq)H9)@1ltEhB0PA=^ZZAq3VAWht`CWLD*_Er4a~U45KAQ%H zcOq@hrn3(qZ5o_ef1Fh(XrlKZU@-g;=|T(>A5o5&`xxo$C&W*gPYgag4#V37PvSEO zWQaLp_G=a-2Hzl^_!j6CnEyM_x740ct^y1;MLHs;Iw5U$)|j+Hys=WmEYa&iMO~?Y z=yXFmK@1(FM{_i0*j3{=%*}Ms=vX-RW=;>2^fV%!-IIbpGouOTlP`y&fZ4hE&z_)L zmLP`29MOG_`NR}4A{I>k^N`CZRKe!H0K$KX3SI-cuLDD3o|t_TeD-b1zXObj+DiFn zg3zSHq2@VlFz3qosj7d+%NbQyAk7_|5wOSB&7a2!E|>&9nhJCR;8(m6lAA?-4lqN! zqdC8SAvzfu-YLKw@pLZR-cjgd?--zcD&)+T)xQeqfS4d=uLkd41I!ZL>&Rc<9d9x> z0i)Z1IihpNR5eT%CjZ(C!EicpBJm^M8!|3BkS;l366CjWmw41MNO$EXxMUd8cTxVe zqmgbxm;b_ad&niN?cnBQITDolSO z@dOo)V+$_b7oK^>EXQ=|RN~WIuJ2{b#xcE^={aoa<>Y&lf0+2qnRBf4tf@8qIz=~4 z)BT(w2Dc(TdC{Eo?drqE(dU5{@ZScSPa!w;z5tPXJHWFz;bqm0n~-!ilgxIiY24&X zNM>r2hkQly9pJBrV?D!^D|a+yp;K#RoL@%%`lYkeuTPU_ft+V%uzvYvo8&Vyq>OwT zV-Xx!p;MN1ugrLz<^3z<%?+)6Ww@z}#<+ZEk>6AJc)Bol11gW^Yyq|}kQ%PE5nURx?#6I%=mZpm|(}R3l zqTN5x?`)>cz9at#Jm@!^sdKzWKvu9r{~~FCdOWSJ9tmor$bS@y0xC$AK~dcTJLLgwWs<+bO^t>4oVct`W zF!ibalBa7YOzi0&Qj)~4GitgdU50k<{XnbSZ!vM6}@X=op<6`u@KZ)3rjw2o?or!?cwGOEb!CVz2$jBx6~rFb9TBST}L@G|D*zkH*H z{%=_y2^QmT7JiVL*y5wWJo`9h2|QsGwUY|=l0q+#FJ}cC1XU~J{DZ>vmOpnuAph|} zE`>W{Zi~c(TJF4mAULw@<7MoEBb2nf_Ljfq@;B)Sv@DSK>UD8F20bmjdH_fomi}Ru z`ZWF~RFK=qiinXx4lhS(m6x%w5J5im39zsgn4zIxodpk>@vTz**P|j?pgAcpsnWwRS@}foiWIu^&kwf+(4IzKR6k(VgBAKUuF?Ui`8n`J2TX! zUOH+lPhrwKC!7Q0%yfHzl+m*u`ID)`73?T~cg6S~!l?=CIU7S%O*rahCb1{~0{usU zw&n?rl)q#=LoRvsFYvTK7X>D>z{P?p4&wZ7(%;I^$7Axp3$Fq&&NmdV4;KEEmfFxH z4?1>^(RhJ1BCJ{w=aoUl`0MK7GevnBf>##Jkn2$LQ04ut@;Ll>n`nA#T#ZLN0a!jbtD3Zln>S)y4I|FIN4pT%->r0tlWF@Gp-wWDdO)oudHdL_P+9ZaMhgjDiLjJKMO)u^z=p{ zdfLx}sBib|{%c11_B*P(mYAy=_34@>PovOPHFMI(ohI{1{wWagD_4H0jkRJmns9=) zM$BiY)mLk71N2Sh&i62uYa}z78WPr!Kr@_&k~_B2;i)I;jF`ZV{M0<;8@ZtaS892? z1?D4{Xb}zXFdd&-A1GB{>LJk;&$v@({F%Q_c(usH<<)201_~daz>XEZO%eVC@>-7y z)iLs?UY|+E0Ks?#`Fi*_g)e5%m%^8e9x*6A^K^+a-$X!WhHT~;0r3tuUse>~HB>gj z;@#{b1cK76URTMVnoi>U&BFDPzxYzA#?S=$OU5%w85wcj9KytTb2%K}Cz*@A!8Ejw zRHk12IT@2r>b;c8xJ@$Dv=OhMzHP}hCB?U}W)l$C^AP3h8YJ_=MyYtPRQz;mPI~`^ zG6>f#pQ#M`|Lyol6l;CP1!|ZRrabr2(bdieX= z`BemxbL*KH%<5d}M=1&x|d^ zFNvxW^?H-(Pl%bfP{EDFySgvZPY%4s{Evt)6MrT`o7~G`dK>X4;zo`F)!*vXOa9d8 z_=3E;l2ET@GLBWO#Q3CjKtqhFllSW7$e-G`#CbJ5)i{L zg1=p6LN&L?`QL=!TWvSu8ERuRL<+ZrD75QSGIK2_Rs#MFXacx}m zj$}+IqG+WUq*@TKV3qI_>*3XjPh5U^5k0DYt6DM+DpEl4#T4}rzD*JNp2D{(!tX1* zTJ7Qn4cSHfUo2yo6i|ynyug0K?@z@hc<6YAj)i&r4J)l2ck^zSoKGK#rW@x==C6TW@= ziH~a9rn~wJ8kS^EO@B9RQM_4-i$l#Am0=j2yj4|&sP8R3xEMW#rX~O@DCQ_)m}k;yt$a7CghJ#FA^!s zx`H%kv(cUt>oyvu(QA+CZ9?!E83fAwOTZ`f<%^`P_h~;fcJS(pcxn~c!RPDYrH^z2;(GRxL8Goj z>+&K!R5Dc8*UgX{Q}yt-i)=B2l*5bhchO}TMA#yAiIIoUo2y>C@ID-sfRzS9{w-&@J|V^y6nef z%+YB>Z#yt^6}XEPJ((5$uoR%h9uPMlURn~UnXC;vF-%BO3J83G0v+A zv#u5`_H;9i_Tbq_|Gu9_ z^Eqd)FifYz(LFR;r@_P@+n%V>av!KOh^S(e7D()cwDTw`&I~}>YXXCE=K{0O?5*W< zW0?M)a=ld7$mZ!GU4i8+LB8L(h31_uDkvp?l#-LN;Z=^&ME)2k$o4_YvW;N5?W(m- z9%=e_xkk4OTIzO(eC7>!EVlukh;Bp0!Iz`8yfLuwF+3MtfP7zlR9(#t(c=xFcn%GC z01C6sP*L;^cyC#Smh--VeE2vF%AD6n^O;^SIC~!^HKz|+?w$&{?C3G5XMnnDl5OS% zvvmD(_6k8`Y2xN-S|Dr=SB>7t7@1e8`JkNga~Lb90Ik#a(fQs3sK9@ev9UJ{%56kB zEhPWKFNUf1%W=XOSQb7oT&J^3AsF%;BlsFEOSEaE^ApdE)EK44YD|5J0g^Zi<-JD^ z)_i_6x-Q^NO>1`;7CejCagRu!IGUs2unH|_JX=_c*vft8Y2Hd+f;Yh=Sd<#kLuZ)Jwr9_U!qj6h%>S8& zwn5i8F7m^T@JQ-4c*>0sBZ-?48^Kz&_DHbL9jfv0YBVG{a~0l1lVDk5VYwCv8l%P8 zVcm5)`#216*U-iPfFX7Q2INf_W??{d{5W0i_>&iEv`<0W{+GIRl3z;cs7FE4x^5*6 z7+$F}@>fjP81zv40GVJa;aZQ6qUMGn*a8~{1W%zW@)u%QWnO1~6Bzd0a_CP@XM?if z?T?kA5`XrdaAodRD9D8fp0moq=TAV_1fQW`Vg?lXAD|+4MxgApQd3~)vd)P1=o5G< zTm;t#>){#uK^k~d4~+lZt01gf&=T)63N8SjTZ43F1U%z?#=hqa=^fZz8{i*;{K6BA zmAUBq@W~1ocni89ycG@1el!~6e@CANur&V$T{#R2^Fg2$IGl>Zx8Mn*~?LZeLpb4U<Or}caJ|oq)!c!I4QmGU=R2WXCizoGU19VK3S|5-I-RLO#l3EW zXL7g0P-iKy>qFoZ?V&I_5aq&QvvqlU1iB!{H6zgveEyz2b-q`DAWr5757I>HgOSKs z$i8A97W%`Lu4(CC(9+(D%L!-kkNj!;R>8`+9PEl=%@2`lIbx!_*7xDXzy>l*IBQ-!W@^`6F)l+P|v zhD-c8^Pt##HOy0k(1+@~Y1AwCG&~c~1JN3E)j|J;A-8k>NSpz&J=hkd;)7l?g$!mDTZ6_H8j9|2cC;QI#^dY^aXTbw3-IqgDy-arlEzI7EqXS zJzXH%8kT2vLj`Z87&OLX_h49>ZF%8D$oYFA2(wFuYdv}M*w$!N7^a}#KQp27_B_mFxz31sXEp@f#!zJKkKoMR0|kYjpxC*a(aq!Ee=^u| z`%(VK(b|CE-U^NG7wCI$!vQ*N9j$L9$v%7}f+s(5jLvYLL-06FJ)O=EhathOz!{tm z&f*N{UJ5yvU*r;2Ltk_*8j^Y)u@F3i;7xFT@NXKc+AmikRAm2-ar`~^{f9h)pgZ7S zKzAq%@Eg*gK0vd#GxLI(5q;`u!EL;|jx#N<8R361AVZhre}(}Lm*KDjVj|cdeQiBL z7v77C!yn~4K@GF)$$`r7c*G)P_}G5`uHnuK9AtIO$+q!0Xa{hA@*qq#{840Cwmv=Rg=*$f2Bl z4ax<*d~Ubw#PCU_jz$1h?g)>#|8MB7P$pFt7{yYz-86v(P2^N3l-`FID$* zbA&uc{ger_~wgdzFQU_ij{!b;lRpul|wp0Hm))aM3aSVaxs zVlO{hD|Wf{GA;6X4>EfYqCe9L$8Y{&@RZdFi<G}VVASPyVFsT*N!o95>7a*C8Kmj!2-zTet}1GJU9Hli)ygM-*ueH zawnidbE7f!09==Nm4{0+1FbNc(p{swk@@3Lfqe*K!W`!XZ?VD)(4dfKyTgF!`*T2P0SB27qQ%4i0woJG6bN|v?qfKluPuT zjr5cB%!%Ob1keBSJQQ~Si3w+iqQ}v4`zaK#c=YNm#nx*n&jm6MLeA$&NQTRB@H5J} zKO=_R*C7|$@SvMK11(BDKU!OuScC%RIv^Z{mYJ6N!!f1$U&4SKcS5OV<+{9ML*BbF z&}s7}p1}>cn6<8h{v40@l7%!Tj%`)CAVZWKmAY7SPt7G|%RQC%VN=O2d*hX;gtu2wrXk%us!-|A^Eg27AA z=h`s~X|E;Fc+y>kmS;kAv9r%??a9E#jOeMoqKbdlMTYx#&TNd*Xe1OyytT5HyIF4+ zC@yS)hjPp4;xz`*rRKEkc?PLl3i-rFbWxZ)5aZw94|8{(%cOTHT;^YgXwC9`Khq1# zY@&eiZam~9IsO@}7O#d=T!zE0FerCyh4z4T4GhiR!1I6WHz-aWj25S|blIU}bOCQA z3V7Tfq`3WlvIGV?1ChV3JBL@kQkN@mV#@u{P^VK{VQ8Mqb<{kG3hn#gnv^#Rf;?XL zF2=I!{f1!kPNZCj3Pau(aJwSf3opU&0#7*2$76G^pj_VESveA29VDB;)d?;l$MSmM z>h|trxY#Dv4Y@5$fbA)Wp^B{6$iYt9<7IBbHoz9h&kUq6MC(8(2#5o1iORd z{G|07w@Rs|>M3NocnYW_57VwXSK&BrIb2rnKW06kr>w}#m5sdyF zFvz>HLYIH#nOPb=F1uz*PTkGrooh~1)kw{MvYXuxiqC9=7W#J~o!bUOyj}>J{4L#yC=Wzq8KTHfNNSz#(XU@k=LyWs&dC7X-W){mi!l8HMJ-NE=VTG5GjBIDAj zFvw%qwBoW^=!sw|@Psq^C%VGrtylL`R5*#p^o6rAYEs9-u&@jC7TW6uBrSI|T97&z z7Y^=W-5Po_L0h(Up-oveoAf z5`W=w*o_uGL?35(>onjE$5c~Toae6BzYJZG`vmhtj=S4L4^)ufgs%CJ6Ol6sbA5*6 zKl&6w9sD5;SMfjUA{0p6f++RQK!wgR6}rWRvG7249}deAm`EbtO3AV1qaH@rS?9xp znbBi(`Dnmw=;0$BiBqX(0Ka_OnOms^5}zWfy|JjkDW#$$a1qaN5;Ga3yptJqfqdp~ z_kUf(vzW^~&c&M3bzdU5qx3NZlIuxa@=7wS0aLt>elZUugcr!*p z{!UCN{!kjsJ09NcJ#@Yq@6oP^8S^FI&Ql|GzI-GdF3R#lf!6FmXB2qkYBRV(dgQjR z;ECXMbj?_^>^={J+yfA_)_9=3KRvrIy6_F&Tl&u2@Jw1*7M(6oY#rQP8<6MD$DfaU z_d5);)CP{{uhCV7^;j$1mKZ%|qH(yUWO>6eU_Ghb;8AnDpL8yUW$r{8;L&39yv`|{ zsa}Q4!e+Sp;F>9u=$- zf4MAIr49%h>vBf%$@IuDj^CS_YDK9P;BUAe2Fp)KXgi<{EK^YDbZ z52$b7srb+IhXI+hd6;~irxluOKxZ}zCZ=%s?1G8LeG8bmewvogjzd@4Jn{5)=P=`A z+hgRmPm#z+G zRBr`>Gj#`IDC8=eYY30qKl0f?a~I32feRC@P|;c*I6CEs4SPeND|W9#jAZyckb5EI zb1gCMr}6QcdGrKA7k-{Jp_U@?aDU|2RV{ZoVBsm0yL$C9;t3b5(Owhy{l? zqcfbS{Fdml;0}0ZE0^DB}DQSKN#2yD1W!^*u=D;Dd5rSE^z`YL! zWp@R-%i&_bIfiXZKI=7oAiBVAu)j7S;BCGH4<_9!0>#TWFOPwRnG(#=&KkHnWoE#C z!=>&;o+@BvINn1W1|$;Yns<4m8jM6k3T==do{3@W@Z!|wEgkQ7<#8E*Dc+UJ^uPq; zPUD0TqL1_6LQ%xSW`7vcSuVpFm+6~OF2l{GH6M#oPL^5q3VGK%^C3KzIdzP#-^#v^ z=+6EfQJM8Q;c$@&Z(FDpWO+CpWzj-&i5AR8(0WxcOCeE?H& zR<0G*{9{?)!Xm3X`p~~C(Ddj1(1-r*2&$X7JsGb=PoIS;xh1=KJ3#gu?e*g z8>92h&kXtVVQ7It=k&+8_kYI>`N!$-Y&OXQM{g7qI|l%L-Um!&FdEFd?sr7~#t2>D z3`Wouc)LA8!*W{@Q_fnBs->PbF!deEhrBDE%)g5%H#iq9E%55ro{0%4!FAkSg|5gi z;qY9K7Ptwt)Om!#dvdw1C|C~za-Ttu-yE0Sem4YPbdD}3ez#Ig(0`=(Z?_}?1JI^JRe#~95hDDd2<37_>k%fiND}iSk$K- zgY8CCkT?{UdtG69#6za6xnMa5a!+qUSJ=FY&c4-9S738*=yq+Q)7`m0a8{v9+%;l= zivR4*qfp=nbVW1=Q>M!Yl5=;UBJX&Nil8Ne$%<(35(IB>6z2{8=tdNwOCvs#;l2ua zD>qWtmzSW`9QF>&NOV+suqhXLkF#^VDCQ6bO?;(mk*@@=8W8dIqi?L7u9 zuy!lgX`fFx1ZJ;yhJL)p@=rmZyDeG1JA&Cc8G4e=IRXI!pTDS_<+EVX{WO#ZnW-xvU*N>%^9jjl=xlgmzRs8ZKYyvo%c4MnKRx9nV0rFC z@X;ZNk-{f*c?so5!KLQ0+{CHq8tYWVfUZscnV%oawSxhv-m1$a{tA4iE8zY7#PJ+X zeNlmVqABDOB3g%*x zXF3@^wHon}>Ov)gE5ql5&0&+$&nVYwkRR*l}d?ea|MKJFiq&Tv>(!c{C`9=|^wp11^`)3GwWaSq@%g@Gwvh`0%?ZV8?t zIsLFUcsIfDlm`!ZL3i!B#QrEBwLMs+lUBg1TKnff3xrD{kUg_prybs{{)4yaElx0r zePCd&Ehgz~w&QGONgq}be!+i!43^cy^jV}FUi!owA)=uuQ?eU5cM-~$F8A5hAi zIZeyuR$_5-+hgr8AFKK2t=IQ%srp6rGDa0#eZ}y3gkG2Mhy}5 z`E@j`4Tew1$L7MD!Dq*!MZr_ZKZVinb%$qCJhYO}dTGNWZ@R`{He%;NIdM_-TiF({ zEIbeBGX}yXNZWq{<|0&-PoaW*DGbdVk3P=y!T!PLDR<&5mVX{{x_0@On+p#YT9n~g zjRLRr0{!`Uc_fQZFsx_a2YU_IWk(FaF!jGuUw76u8KPks8*~F!P+h)Ezbj?h9ml~! z%n@JY;%np*Wn*-SLK9%t209Iiy8)@zx~ywS*Va=o`^H$#09P*l(i#Ex1mPu+6h#O&H!y=O-g} zcZ!FyM({ce`^GVHNhk1V9$RAGr0(kBYgfSs%?Ot-qONj3xHBQ9z!B zGCDL4CU1_pO{Pcw9CNdW87n$NkNaKSpm~-J#>ZiL8#VHSW9V(moDP|sRqge!@b&N1 zj5U=KZQ`|)c2=LbZfHxipY&&F5y3-3aT}pF3@H2d8e>ppS+zawEe1>?g`<(O* zwTWY!%}IY*n`ry_?DWHL*X&W!6Dr>RVs`rM7ez(b3&hWDG zCOV14mN?NfCpt2HQOm$~bJFv2HKR(^oBJfEXR6<1-ADQUKz}cw={P0$YaJEh^AWEq z74l})tN)D(5$yDuvSKe-@kJ-;M@1A!Y?W@CsR(xfBo6KmraC0^(!QS%>KIBRhH+1lE*Oz}gSQ6D{Oxl$DCY{vZzh_iPdeg4ig%CY6dr zEkYN`c#!D3o6&IyhzpyO@JE4oT{>FF+Zd!@m9*Ar)grXv_MwpIBZl|Qi-Xl0o#Qg+ z9FwEoh|*D|>P_ipvCu@`-4X(?PF&DBJ#}+UtI`4sHWc=ScSX#ffI2?gTOvi3vnPV+ z*jWPO9q}(m){`;{B;C6-pg_=vzDSYSRUCet>Jc9rZs}UPX8≶huHaV-(x9L>#@- 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 ui_1H_kh5YpoVwF5AlI` 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 ui^FB!|^xU?|`5s1$DeDeuxjm8~8~42%j!` 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