Skip to content

P0: Complete the authoritative Python type gate with Pyright #257

Description

@tomdps

Parent

Tracking parent #245; epic #243. This is one vertical delivery slice. Do not close or bypass either tracker from this child. Close #245 only after #256 and #257 are merged and its original authority matrix still passes.

Current dev truth (2026-07-17)

Baseline reviewed at origin/dev 40020b0b4ff054551c76ae58b8fe5379fd68aec22 after merged PR #278 / #256.

Goal

Make selected Pyright a production type authority on the same exact after-state/evidence path as mypy. Prove independently resolved mixed mypy/Pyright projects without co-authority, fallback, host-state leakage, or human-output parsing.

Dependency contract

Required behavior

1. Exact authority and invocation

  1. Preserve selectPythonTypeAuthority semantics: exactly one authority per canonical project. Explicit/configured missing tools never fall back. Dual configured authorities remain invalid_config. Mixed requests may run mypy for one project and Pyright for another, retaining every project receipt even when one fails.
  2. Execute only the selected pre-provisioned PythonProjectToolProvenance executable. No package installation, npx, alternate binary discovery, Python-module fallback, or second checker attempt.
  3. Run inside the canonical project directory of the temporary exact after-state workspace, with bounded timeout/process-tree/output handling and isolated writable HOME/XDG/cache/temp state. Redirect Pyright cache/state away from the target repo. Clean every process and temporary resource on success, findings, config failure, malformed output, timeout, signal, spawn error, and thrown parser/normalizer error.
  4. Generate deterministic Pyright argv from project evidence. Strip conflicting caller-supplied project/output/file-selection switches, then require documented --outputjson and the selected --project <pyrightconfig.json|pyproject.toml> path. Do not silently invent source/config precedence.

2. Exact after-state Pyright project semantics

  1. Preserve Pyright precedence: pyrightconfig.json wins over [tool.pyright] when both exist for the same project.
  2. Materialize the selected config plus its recursive repo-relative extends closure and the Python/stub/config closure needed by Pyright. Preserve config-relative include, exclude, ignore, extraPaths, stubPath, venvPath/venv, executionEnvironments, target pythonVersion/pythonPlatform, namespace packages, src/flat/stub layouts, and source roots by letting Pyright evaluate the copied after-state configuration.
  3. Config, source, stub, and authority-switch overlays must be reflected in the same run and afterStateManifestFingerprint; deleted after-state files must not remain materialized.
  4. Refuse missing/malformed extends, cycles, absolute paths, parent traversal, symlink escapes, host-only environment paths, and any required config/source/stub path outside the repo with machine-readable invalid_config evidence. Never copy or execute host config/plugins from outside the repo.
  5. Preserve selected interpreter/dependency resolution from P0 — Resolve Python projects, interpreters, environments, and tools canonically #246 without leaking user site packages, ambient PYTHONPATH, editor settings, or unrelated host config/cache state.

3. Machine JSON protocol and normalization

  1. Parse only complete documented --outputjson stdout. Normalize every generalDiagnostics[] entry: repo-relative path, severity (error|warning|information), message, optional rule, and zero-based start/end to one-based validation locations. Preserve multiline messages without human-text parsing.
  2. Require a valid output version, diagnostics array, and summary. Validate summary error/warning/information counts against normalized diagnostics, validate ranges and paths, and require the output version to match exact probed Pyright version provenance. Any malformed, truncated, partial, duplicate/contradictory, version-mismatched, or summary-mismatched payload fails closed as tool_failure; never retain partial findings as authoritative evidence.
  3. Status mapping:
    • exit 0 + valid zero-error JSON -> passed with positive execution/tool/config/source/summary evidence;
    • exit 1 + valid finding JSON -> findings;
    • documented exit 3 -> invalid_config with exited tool failure evidence, even when valid diagnostic JSON is also present;
    • fatal exit 2, illegal/generated-argv exit 4, any other unexpected exit, malformed protocol, spawn/signal, or parser/normalizer failure -> tool_failure;
    • deadline expiry -> timeout.
  4. Exit/payload contradictions fail closed: exit 0 with error findings, exit 1 without finding diagnostics, exit 0/1 with count/version mismatch, or valid-looking JSON accompanied by unclassified protocol/fatal stderr must not pass.
  5. Reuse PythonValidationCapabilityRun, PythonValidationCapabilityToolProvenance, existing aggregate precedence, scan/measure/status propagation, ASP assessment mapping, JSON schema, validators, fixtures, and packlists. Add shared contracts only if the existing envelope cannot truthfully represent Pyright evidence.

Acceptance criteria

  • Replace the current PYTHON_TYPES_PYRIGHT_UNSUPPORTED branch and focused test with real Pyright execution; remove obsolete human-output/fallback paths rather than retaining aliases.
  • Root and nested pyrightconfig.json and [tool.pyright] projects match real on-disk Pyright for clean and findings cases.
  • Recursive extends, extends cycles/missing/escape, execution environments, include/exclude/ignore, extraPaths/stubPath, namespace packages, src/flat/stub layouts, version/platform, and selected-interpreter behavior have observable regressions.
  • Config/source/stub write/delete overlays and mypy-to-Pyright/Pyright-to-mypy authority-switch overlays match equivalent real on-disk after-state projects.
  • Pyright configured but unavailable while mypy is installed does not fall back. One/no/both configs, explicit override, and two independently resolved projects with different authorities retain exact per-project evidence.
  • Clean/findings, exits 2/3/4, timeout, signal, spawn error, malformed/truncated/partial JSON, invalid ranges/paths, stderr protocol failures, and version/summary mismatches all map exactly and never become a pass.
  • Real pre-provisioned Pyright (pin and report exact version) proves actual argv/config/cwd/source, normalized result examples, process cleanup, and source/config/lock/environment/cache immutability.
  • A clean packed public opcore install runs the same Pyright path without workspace/sibling/private-package fallback and without package installation.
  • Existing mypy authority and conflict/multi-project matrices remain green. opcore check --changed --json, scan/reporting, and ASP assessment preserve all project capability receipts.

Observable outcomes

  • Clean success carries positive termination:"exited", exit code, exact tool version/source/portable executable/argv/config/cwd, manifest fingerprint, selected source/config paths, and zero-count summary evidence. Zero diagnostics alone is never proof.
  • Findings and every unavailable/invalid/timeout/crash/resource/stale/unsupported state are machine-readable and fail/degrade exactly as the existing capability contract specifies.
  • No implicit fallback, partial payload, skipped execution, or stale config/source materialization is reported as passed.
  • Target source, config, lockfiles, environments, and caches remain byte-identical; all temporary resources/processes are gone after every terminal path.

Non-goals

Verification

Run focused behavior first, then the configured repository proof only after the acceptance matrix works:

  • npm run build
  • node --test --test-name-pattern="pyright|type authority|mixed project|type after-state|type cleanup" tests/validation-python.test.mjs
  • node --test tests/contracts.test.mjs tests/schema-contracts.test.mjs tests/validation-policy.test.mjs tests/opcore-facade.test.mjs tests/opcore-metrics.test.mjs tests/asp-provider.test.mjs tests/installed-bins.test.mjs
  • exact real Pyright fixture/repository comparisons for clean, findings, config exit 3, and malformed/fatal paths
  • clean packed-install Pyright execution with before/after repo hashes and temp/process cleanup evidence
  • npm run lint
  • bash ./scripts/ci/run-local-ci-equivalent.sh through configured opcore-ci

Attach exact real-tool/artifact versions, generated argv, selected config/cwd/source closure, normalized result examples, before/after cleanliness evidence, focused outputs, packed-install proof, and accepted opcore-ci digest.

Execution rule

Implement only after this JIT review against current dev. Keep exactly one implementation run active. PR base is dev; require green checks, clean scoped diff, merged PR, closed child, parent #245 checklist update/closure, focused real-tool reproductions, and accepted opcore-ci proof before starting #211. Do not claim Python readiness/parity before #251.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions