diff --git a/docs/.review/PORTABLE_TOOL_DEFINITION_IMPLEMENTATION_PLAN.md b/docs/.review/PORTABLE_TOOL_DEFINITION_IMPLEMENTATION_PLAN.md index 7cb46216..5cfd4323 100644 --- a/docs/.review/PORTABLE_TOOL_DEFINITION_IMPLEMENTATION_PLAN.md +++ b/docs/.review/PORTABLE_TOOL_DEFINITION_IMPLEMENTATION_PLAN.md @@ -1,44 +1,28 @@ --- artifact: swe-design-review-attestation -schema_version: 3 -scope_key: 4288e5336b3329ba610c14ee8eee2615dff4b85f8037754801b69b02d8cd4547 -scope: {"kind": "path", "primary_target": "docs/PORTABLE_TOOL_DEFINITION_IMPLEMENTATION_PLAN.md", "repository": "/home/omry/dev/reploy", "selector": "docs/PORTABLE_TOOL_DEFINITION_IMPLEMENTATION_PLAN.md"} -review_content_identity_sha256: cce9da70072ca8936edc08ea263754f30c4c4c43748fdeccb3fe322903822693 -target_content_identity_sha256: 02df853e94a6f2b9520b78f6cbf81a6a9b3e28706bc5e1e98c02952923f0e8ec -baseline_content_identity_sha256: dae0bab05a19ca44b34a2678c28ec00b315d23fdffd43035c6228ff18c3a3c14 -target_documents: [{"path": "docs/PORTABLE_TOOL_DEFINITION_IMPLEMENTATION_PLAN.md", "repository": "/home/omry/dev/reploy", "sha256": "6d26d88967d14682b5628f211325c91cda966b55f74859efc0670dfbe44a901e"}] -baseline_documents: [{"path": "docs/PORTABLE_TOOL_DEFINITION_DESIGN.md", "repository": "/home/omry/dev/reploy", "sha256": "bd02f5450b2940b3a960f6419994aa9c4e5e65ccb4dc5365b4d17ac233b49d9d"}] -design_dependency_documents: [{"path": "docs/PORTABLE_TOOL_DEFINITION_DESIGN.md", "repository": "/home/omry/dev/reploy", "sha256": "bd02f5450b2940b3a960f6419994aa9c4e5e65ccb4dc5365b4d17ac233b49d9d"}] -document_repository: "/home/omry/dev/reploy" +schema_version: 4 +scope_key: 6e41ee0e23cce83726e428ffd0200d01bd80afad080ceca7aa37fc184c2ece8b +scope: {"kind": "pr", "primary_target": "docs/PORTABLE_TOOL_DEFINITION_IMPLEMENTATION_PLAN.md", "repository": ".", "selector": "pr-142"} +review_content_identity_sha256: 420c290b22da1f05d04bc48e810d2312d4576a1b235055f6f2d82a8a5c5a05e7 +target_content_identity_sha256: 6d0eaca0c38b8934f1858d76d6430ee4fb81b542792a33358c8a5afabf32f189 +baseline_content_identity_sha256: cba65adf2b76aa499cd56fc0f3ed34649d32c18cf354cbc828795515a820769d +target_documents: [{"path": "docs/PORTABLE_TOOL_DEFINITION_IMPLEMENTATION_PLAN.md", "repository": ".", "sha256": "e7e9aea7520328b451074d634a9c437fdb790eeccfabb520f74b21f04ebcb495"}] +baseline_documents: [{"path": "docs/PORTABLE_TOOL_DEFINITION_DESIGN.md", "repository": ".", "sha256": "bd02f5450b2940b3a960f6419994aa9c4e5e65ccb4dc5365b4d17ac233b49d9d"}] +design_dependency_documents: [{"path": "docs/PORTABLE_TOOL_DEFINITION_DESIGN.md", "repository": ".", "sha256": "bd02f5450b2940b3a960f6419994aa9c4e5e65ccb4dc5365b4d17ac233b49d9d"}] +document_repository: "." document_path: "docs/PORTABLE_TOOL_DEFINITION_IMPLEMENTATION_PLAN.md" -document_revision_provenance: "a50aa980cdc0dc0afa754977c5b81508d0818050" -document_sha256: 6d26d88967d14682b5628f211325c91cda966b55f74859efc0670dfbe44a901e +document_revision_provenance: "243e45c5e3e0a57c20a7f23b0fe13035c4aadf7b" +document_sha256: e7e9aea7520328b451074d634a9c437fdb790eeccfabb520f74b21f04ebcb495 verdict: clean -attested_at: 2026-09-08T04:16:10Z +attested_at: 2026-09-11T07:58:31Z --- - + # SWE design-review attestation Review freshness is determined by the target and baseline document bytes -listed in the version-3 header. Revisions are provenance only. +listed in the version-4 header. Revisions are provenance only. ## Durable review state -## Standing decisions - -### R1-5 — rejected — The provider-input recheck does not state the required abort transition - -- Reason: Reject the finding's provider-input-change model. Blueprint and Reploy constraints plus all direct and indirect constraints introduced by candidate tool definitions are jointly solved from one immutable operation snapshot. Providers satisfy the selected complete constraint set; acquisition executes that selected solution. Conflicting constraints in the same resolution scope make a candidate ineligible, while genuinely isolated scopes may coexist. Remove the undefined provider-input recheck from both design and plan rather than specifying an abort transition for it. -- Actor: owner -- Decided: 2026-08-21T04:00:48Z -- Owner: omry -- Trigger: Owner confirmed immutable joint constraints, including tool-introduced constraints and scope isolation. - -### R1-7 — rejected — The binding-default advertisement invariant has no owning acceptance case - -- Reason: Reject the singular default-binding invariant. Model bindings as independently selectable contributions that may reference shared components of the same tool. When omitted, select bindings matching active application providers; allow an explicit binding list or explicit all; if several bindings remain possible without an active-provider match, require an explicit choice. Support typed intra-tool component dependencies now, while deferring general cross-tool dependencies until a concrete requirement justifies their solver complexity. -- Actor: owner -- Decided: 2026-08-21T04:36:53Z -- Owner: omry -- Trigger: Owner approved the Debian-like binding model and scoped dependencies to intra-tool composition. +No standing rejected or deferred decisions. diff --git a/docs/PORTABLE_TOOL_DEFINITION_IMPLEMENTATION_PLAN.md b/docs/PORTABLE_TOOL_DEFINITION_IMPLEMENTATION_PLAN.md index 09e356dd..dc0cb892 100644 --- a/docs/PORTABLE_TOOL_DEFINITION_IMPLEMENTATION_PLAN.md +++ b/docs/PORTABLE_TOOL_DEFINITION_IMPLEMENTATION_PLAN.md @@ -1,6 +1,6 @@ --- status: Active -updated: 2026-09-08 +updated: 2026-09-10 summary: Reviewable delivery plan for portable-tool authoring, definitions, and the embedded Java, Playwright, and asciinema implementations. implements: docs/PORTABLE_TOOL_DEFINITION_DESIGN.md --- @@ -47,6 +47,28 @@ generic mechanisms. Playwright remains the first catalog definition and the tool-specific acceptance case; production code must not branch on Playwright, Node.js, or `playwright-core` identities. +Plan revision note (2026-09-10): the PTD-23 child contracts were tightened +before construction to make their canonical handoffs, execution order, +pre-acquisition compatibility gate, exact-wheel constraint, bounded wheel +inspection, locked replay, and CLI publication responsibilities explicit. The +clarification does not add another ecosystem binding or change the accepted +portable-tool design. Generic in PTD-23 means generic over selected portable +Python-wheel binding records; it does not mean one provider implementation for +unrelated ecosystem package formats. +Deep design review on the same date additionally aligned artifact cardinality +with schema v1, completed the interpreter-observation seam, made the existing +all-selected-artifacts lock invariant explicit, bounded wheel expansion, +required reuse of the existing Python wheel parser, ordered export publication +after binding materialization, and bound alias collisions and link payloads to +their computed Python-runtime targets. A follow-up review bound wheel +compatibility to existing portable-record and Python-provider behavior. PR +review then completed that boundary: the shared portable-record platform parser +also projects the already validated Linux policy and minimum glibc release, and +the provider-owned pre-acquisition gate compares those facts with the selected +interpreter's pip-derived compatible-tag evidence before any artifact is +acquired. This remains one generic wheel eligibility mechanism rather than +tool-specific logic, a second parser, or a second compatibility engine. + Plan correction note (2026-09-01): PTD-21.4 review exposed duplicate ownership of canonical portable-tool record structures and validation between catalog loading and locked replay. Delivery now inserts one behavior-preserving @@ -1082,6 +1104,33 @@ verified acquisition, content inspection, declared CLI placement, and offline materialization. Playwright 1.61.0 is the first catalog-defined consumer and acceptance case, not a production-code dispatch identity. +The milestone has three explicit immutable handoffs. PTD-23.1 produces a +canonical application-scoped Python-binding projection from the selected +portable-tool plan and the resolved application-provider inputs. The projection +retains the scope, owning Python component, selected-closure identity, exact +binding contract and schema-v1 artifact reference, canonical root requirements, +artifact-declared compatibility, exact-wheel constraint, and the selected CLI +export; it contains no locator, acquired path, or acquisition outcome. Schema +v1 selects exactly one binding artifact per binding and target platform. After +the owning Python resolver has observed its interpreter and pip-derived +candidate-tag compatibility, +PTD-23.1's pure eligibility gate validates that exact artifact. PTD-23.2 joins +the complete +projection to manifest-authorized acquisition results and produces +verified-wheel inputs whose descriptors and observed wheel metadata remain +bound to the exact selected artifact references. PTD-23.3 is the only child +that may offer the compatibility-validated verified bytes to the Python +resolver or publish the CLI. Artifact eligibility does not rewrite +selected-closure identity: every artifact selected by the closure is still +acquired, verified, locked, and installed. + +Production dispatch in all three children is by the canonical record schema, +reviewed resolver primitive, resolution scope, and provider/domain owner. Tool, +distribution, CLI, and bundled-component names are data and are never dispatch +keys. The Playwright catalog and exact-artifact acceptance fixture may name +Playwright, Node.js, and `playwright-core`; neutral synthetic fixtures must +exercise the same production entry points with different names. + Acceptance: production projection, acquisition, verification, and materialization code contains no Playwright-, Node.js-, or `playwright-core`-specific branch; catalog data supplies all tool and bundled @@ -1099,20 +1148,237 @@ The milestone is delivered through these first-class slices: #### PTD-23.1: Project Portable Python Binding Contracts into Provider Inputs -Scope: decode selected portable Python binding contracts and artifacts from the -provider-neutral plan; merge their exact root requirements into the owning -application's Python request; represent mandatory exact-wheel constraints; and -gate the selected interpreter against the contract and artifact compatibility -declarations before any binding-artifact acquisition. The projection is generic -over canonical binding records and does not read or recognize a tool name. - -Acceptance: input ordering cannot change the projected Python request; -identical requirements and artifacts deduplicate while incompatible roots, -wheel identities, interpreter ranges, provider ownership, or application scope -fail deterministically; an unsupported interpreter reaches no -binding-artifact acquisition callback; and tests use both neutral synthetic -bindings and the selected Playwright records without introducing a -Playwright-specific production path. +Scope: add one pure projection boundary that consumes a validated +provider-neutral portable-tool plan plus the canonical resolved application +component requests, and returns a freshly allocated canonical component-request +set plus the canonical Python-binding projection described above. It runs +before provider node planning and portable DAG construction. An +`application:` scope maps only to +`application//python`; any other scope or component owner is rejected. +When the application already has a Python contribution, preserve its explicit +interpreter request and merge the binding requirements into it. When a selected +sole or explicit Python binding is the application's first Python contribution, +create that canonical contribution with the Python provider's established +default `python` interpreter requirement. Do not mutate the blueprint, selected +plan, or caller-owned request values. + +Decode binding contracts and artifacts only through their shared strict record +contracts. Join every artifact to its exact contract reference, require the +contract CLI to equal one selected plan export, and normalize every root and +artifact distribution through the Python provider grammar. Key one group by +owning component and normalized contract package. Under schema v1 it has +exactly one contract, exactly one target-platform artifact, and the contract +roots; a second artifact for the same binding and target would collide with the +canonical binding-artifact ID and is not a candidate-selection mechanism. +Identical requirements and exact record references deduplicate. The same +semantic key with different contracts, artifact references, wheel identity, +package identity, provider owner, scope, or selected CLI fails before provider +planning. + +Before joint candidate selection, add one provider-owned canonical +`supported_python` claim model and use it everywhere these records constrain an +interpreter. Over normalized final interpreter releases, a canonical +major/minor claim denotes the closed-open interval from that minor's `.0` +release through, but excluding, the next minor; a canonical +major/minor/patch claim denotes that exact complete release. Normalize a set by +sorting numerically by major, minor, and optional patch with the series before +its patches, removing duplicate claims, and removing exact patch claims already +subsumed by a minor-series claim. Intersect two normalized sets with one linear +merge: equal minor-series claims retain that series, a series and one of its +patches retain the patch, unequal series or unequal exact +patches do not intersect, and the bounded results are normalized again. +Refactor the existing generic Python interpreter claim path in the joint +solver, including active-provider constraints, to use this operation instead +of literal string intersection. This is the sole interpreter-claim +representation; selection, projection, and the later observed-runtime check +must not grow independent minor/patch matching rules. + +Correct the existing target-composition coverage check in the same slice so +its static guarantee agrees with that claim model before selection can reach +the runtime gate. For an exact-patch claim, an artifact covers the claim when +its canonical `Requires-Python` admits that release. For a minor-series claim, +coverage requires proof that the schema-v1 target artifact's canonical +`Requires-Python` admits the complete closed-open minor interval; admitting +only `.0`, a finite prefix, or an interval with an excluded release does not +cover the series. Put the proof in one provider-owned pure helper over the +canonical claim representation. Refactor the Python provider's existing +normalized-release specifier evaluator into one parsed conjunction with +inclusive or exclusive lower and upper bounds, optional exact or prefix +equality, and exact or prefix exclusions; keep +`InterpreterVersionSatisfies` as a consumer of that same representation. The +coverage helper uses overflow-checked minor successors and proves that the +claimed point or interval is a subset of the parsed constraint, including that +no exclusion cuts the interval; the same representation also answers the +weaker nonempty-overlap question for an individual artifact. Contradictory +constraints and a minor whose successor cannot be represented fail closed. +Record validation continues to use the existing PEP 440 parser for general +`Requires-Python` validity, but a form outside the provider's existing +normalized-release subset fails closed when asked to prove complete +minor-series coverage. The helper must not enumerate patch numbers or +introduce a second specifier parser. Reuse it from both +`validateBindingArtifactAgainstContractV1` and +`validateBindingInterpreterCoverageV1`, preserving the existing rule that an +individual artifact must overlap the contract and strengthening the target +artifact to cover every advertised claim. This is a corrective prerequisite +inside PTD-23.1, not a new artifact-selection mechanism or a Playwright +exception. + +Emit one exact-wheel constraint per owning component and normalized +distribution. A constraint retains the exact artifact reference and its +declared filename, distribution, ecosystem version, tags, size, digest, and +`Requires-Python`; it is not a +version override, index preference, local-source override, filesystem path, or +acquisition result. + +Add a pure pre-acquisition compatibility gate over the projection and the +Python provider's observed canonical interpreter evidence. Invoke it at the +existing Python resolver lifecycle seam immediately after interpreter +resolution and before wheel preparation; do not probe or resolve the +interpreter a second time. Reuse the shared portable-record filename-tag +expansion and target-platform compatibility validation; PTD-23 does not add a +second wheel-tag parser or a second platform-policy table. Refactor that +existing validator behind one shared pure platform-policy projection which +both validates and returns the normalized kind, architecture, and optional +minimum glibc release for each platform tag. The one policy mapping recognizes +`any`, `linux_`, legacy `manylinux1` as glibc 2.5 on x86_64, +`manylinux2010` as glibc 2.12 on x86_64, `manylinux2014` as glibc 2.17 on +x86_64 or aarch64, and versioned `manylinux__` tags only when the +major is 2 and the encoded minor is at least 5 for x86_64 or 17 for aarch64. +A syntactically numeric versioned tag with another major or a lower +architecture floor projects as unsupported and fails closed. Record validation +continues to use the projection for syntax and selected-target architecture; +the Python provider consumes its result for the runtime-dependent decision. +Reuse the Python provider's canonical interpreter-version and PEP 440 checks, +existing inspected-wheel tag representation, and ordinary pip resolver as the +final compatibility authority. + +Add only the missing provider-owned pure runtime wheel-eligibility helper +needed before acquisition. Extend the existing fixed interpreter inspection, +in the same invocation, to emit the normalized Python implementation, +canonical ABI tag, normalized libc implementation and canonical nonnegative +major/minor release pair, and two canonical sorted unique string arrays: +`tested_tags` and its `compatible_tags` subset. Before provider planning, +collect `tested_tags` from the union of the already expanded, contract-matching +artifact tags for the owning Python component. Pass that exact bounded set and +the selected target architecture to the fixed probe as validated data, never +as executable source; the returned `tested_tags` must equal it exactly. + +Use one provider-owned command-prefix helper for both the probe and the +ordinary resolver: each invokes the selected interpreter in isolated mode +(`python -I`), and the resolver runs `python -I -m pip` rather than maintaining +a second import-isolation contract. Isolated mode retains system site +initialization, so both steps import the same installed pip while excluding +current-directory, user-site, and environment-controlled import-path shadows; +the existing provider-controlled work directory and environment remain in +force. The probe imports that pip installation's +`pip._vendor.packaging.tags` implementation, enumerates `sys_tags()` once, and +emits only the tested tags present in that set. This read-only step performs no +network operation, artifact acquisition, or wheel parsing. The +existing record expansion and reference-count ceilings bound its candidate +input and canonical output; a missing pip tag generator, execution failure, +malformed or duplicate evidence, an output tag outside `tested_tags`, or any +other partial result fails inspection before acquisition. Pip's own tag +generator therefore remains the one runtime compatibility engine and observes +its executable/architecture checks, libc detection, free-threaded and other ABI +rules, and optional `_manylinux` policy hooks without copying them into Reploy. +Replace `python-interpreter-facts-v1` and every dependent provider profile and +recipe identity with their next versions in this slice; do not add a dual reader +or compatibility path for the unreleased format. The pure helper consumes the +validated facts and already projected artifact tags; it performs no subprocess, +filesystem, network, import, callback, or acquisition operation. + +Before invoking the probe, the shared projection restricts the initial support +envelope to generic `py` and `py` or +`cp` interpreter tags; ABI `none`, exact CPython ABIs, or `abi3` +whose encoded CPython release is at least 3.2; and the supported `any`, native +Linux, and projected manylinux platform forms above. Platform `any` is eligible +only with ABI `none`, so every ABI-bearing `*-any` tuple fails before +inspection. Any other syntactically valid Python, ABI, or platform form also +fails closed before acquisition until the shared support envelope is extended. +For a candidate with a manylinux platform, that envelope additionally requires +the inspected libc implementation to be `glibc` with observed major exactly 2; +unknown libc, a non-glibc implementation, or any other observed major fails +before membership is considered. This is the initial runtime support boundary, +not a second policy-floor or tag-compatibility calculation. + +For every candidate inside that envelope, runtime compatibility means exact +membership of its canonical three-part tag in the inspected `compatible_tags` +subset. The helper must not reconstruct generic-version ordering, CPython or +`abi3` rules, ABI flags, libc floors, architecture compatibility, or +`_manylinux` decisions from the diagnostic fact fields. Thus an older generic +tag on a newer interpreter, a debug or free-threaded ABI, native Linux, and +every legacy or versioned manylinux policy follow the selected interpreter's +own pip result. The provider does not parse wheel filenames, infer +architectures, invoke policy hooks, or duplicate the shared support mapping. + +At least one entry in each contract's `supported_python` must match the +interpreter: a canonical major/minor entry covers that complete minor series, +while a canonical major/minor/patch entry requires exact equality with the +observed complete release. The complete release must also satisfy the +artifact's canonical `Requires-Python`, and at least one filename-derived +artifact tag must be both contract-advertised and compatible with the observed +interpreter/ABI facts and selected target platform. The gate validates every +constraint as one deterministic set before invoking any acquisition callback; failure acquires +nothing. PTD-23.2 repeats all record-to-wheel facts observable from the bytes, +but does not weaken this earlier eligibility gate. PTD-23.1 owns and unit-tests +the inspection-evidence transition, seam, and gate; PTD-23.3 wires the +acquisition and wheel-preparation callback into it. + +Acceptance: reversing tools, contracts, artifacts, or component requests emits +byte-identical projected requests and sidecar projection; projection creates +the missing Python contribution when required and cannot attach one +application's binding to another; compatible duplicate inputs collapse while +conflicting roots, exact artifacts, interpreter evidence, owners, scopes, or +CLI exports fail with stable diagnostics. Tests cover two neutrally named +synthetic tools in one application, the same records in isolated applications, +an existing explicit Python request, a tool-only application, and the selected +Playwright records. Neutral fixtures cover generic Python tags, including an +older `py` on a newer interpreter, CPython-specific tags, +stable-ABI tags at and above the CPython 3.2 floor, rejection of pre-3.2 +`abi3` tags, acceptance of `abi3` on a GIL-enabled debug CPython ABI, rejection +of `abi3` on a free-threaded CPython ABI, and a nonmatching implementation or +ABI. Contract fixtures cover a matching +major/minor series, an exact matching patch release, and a nonmatching patch +release. Joint-selection fixtures cover two neutral bindings and an active +provider constraint at mixed minor-series and exact-patch granularity, +retaining the exact patch when it belongs to the series and rejecting unequal +series and unequal exact patches. Static coverage fixtures prove that +`supported_python: ["3.14"]` accepts complete-series coverage such as +`Requires-Python: >=3.14,<3.15` but rejects a `.0`-only or finite-prefix range +such as `>=3.14,<3.14.1` and rejects a range with a release exclusion; the +corresponding exact-patch claim remains a point-membership check. Boundary +fixtures cover the largest accepted numeric component without overflow or +unbounded enumeration. Platform fixtures cover generic and CPython +`*-none-any`, rejection of exact-ABI and `abi3` `*-any` tuples, native Linux, +every legacy manylinux alias, and versioned +manylinux floors below, equal to, and above the observed glibc release on each +permitted architecture. Separate fixtures cover versioned floors below the +architecture minimum and unsupported policy or observed glibc majors on both +architectures; unknown and non-glibc runtimes fail every manylinux case. +Those runtime-boundary fixtures include otherwise matching +`compatible_tags` evidence and still prove zero artifact-acquisition callbacks, +so pip membership cannot bypass the bounded libc guard. +Inspection fixtures cover an absent `_manylinux` module, callable decisions of +`True`, `False`, and `None` (including non-monotonic policy results), each +legacy compatibility boolean, and raising hooks as observed through the +selected pip tag generator. A hook-rejected tag remains absent even when the +observed glibc release is high enough; the corresponding test proves that the +artifact acquisition callback was invoked zero times. Separate fixtures reject +missing or malformed pip tag evidence, a tested-tag mismatch, duplicates, and +an accepted tag outside the tested set before acquisition. An adversarial +import-shadow fixture places a fake `pip` package in the resolver work +directory and proves that the probe and resolver both retain the shared +isolated command prefix, import the selected interpreter's installed pip, and +cannot diverge before artifact acquisition. Differential fixtures prove that +every supported Python/ABI/platform decision +agrees with the ordinary pip resolver; unsupported forms fail closed. Each +unsupported minor version, complete `Requires-Python` release, interpreter +implementation, ABI tag, libc implementation or release, and platform tag +case—including every below-minimum or unsupported-major manylinux case—proves +that the binding-artifact acquisition callback was invoked zero times. +Production files contain no literal identity check or dispatch on a tool, +package, CLI, or bundled-component name; comparisons between canonical data +values remain required validation. Non-goals: artifact download, wheel content inspection, installation, browser payloads, or ordinary-build production integration. @@ -1120,40 +1386,174 @@ payloads, or ordinary-build production integration. #### PTD-23.2: Acquire and Verify Exact Portable Python Binding Wheels Scope: extend the common embedded manifest/source projection and verified -acquisition path to selected binding artifacts; inspect each acquired wheel -against its selected record and contract; and bind filename, distribution, -ecosystem version, wheel tags, size, digest, `Requires-Python`, declared bundled -component paths and metadata, and acquisition provenance into the portable-tool -lock. Inspection is generic over binding records; Playwright catalog data names -Node.js and `playwright-core` and supplies their expected paths and versions. - -Acceptance: only the exact manifest-authorized artifact reaches inspection; -size or digest mismatch is rejected before wheel parsing; filename, metadata, -tags, interpreter compatibility, or bundled-component mismatch is rejected -before resolver staging; hostile synthetic wheels cover malformed and -conflicting content; and a focused exact-artifact check proves the Playwright -wheel matches its catalog declarations without executing its bundled programs. +acquisition path to every exact binding-artifact reference in the PTD-23.1 +projection. Resolve each artifact through its selected release manifest and +source record; reuse the PTD-17 and PTD-18 cache, mirror, network, cleanup, and +provenance behavior unchanged; and require the returned +provider-store descriptor to match the record's filename, wheel kind, decimal +size, and digest before opening the wheel. The verified bytes are read only +from that descriptor; no index candidate, caller path, or filename discovered +from a directory may select them. + +Extend or refactor the Python provider's existing descriptor-stable wheel +inspection, metadata, tag, and console-script readers into the one shared +data-driven primitive used by both ordinary and portable wheels; do not add a +parallel parser. Apply fixed non-raiseable limits for archive entries, total +declared uncompressed size, normalized path length, individual inspected +metadata, and aggregate inspected bytes. Reuse the core archive ceilings of +10,000 entries, 1 GiB total declared uncompressed bytes, 4,096 UTF-8 path bytes, +and 255 UTF-8 bytes per path component. Sum ZIP uncompressed sizes with +overflow-safe arithmetic before reading member content. Limit each selected +`METADATA`, `WHEEL`, and `entry_points.txt` member to 1 MiB uncompressed and +their aggregate to 4 MiB; definitions cannot tune any ceiling. Reject +duplicate normalized paths, +absolute or escaping paths, encrypted entries, unsupported entry kinds, +ambiguous or mismatched `.dist-info` roots, duplicate consumed singleton +fields, and malformed consumed fields. Unknown metadata fields and entry-point +sections are streamed and ignored within those bounds. Require filename +distribution and version, expanded filename tags, core `METADATA` Name, +Version, and `Requires-Python`, and the selected console-script name to agree +exactly with the artifact and contract after their defined canonical +normalization. Require `WHEEL` to carry +one supported `Wheel-Version`, one boolean `Root-Is-Purelib`, and at least one +unique canonical `Tag`. Validate each internal `Tag` as one already-expanded +canonical compatibility-tag triple with the same shared single-tag validator +used on filename-expansion output, then sort the unique results as observed +metadata; a dot-compressed internal component is malformed, and PTD-23.2 does +not add a second tag parser. Require the internal-tag set to have a nonempty +exact-tag intersection with the filename-derived tags that PTD-23.1 matched in +the selected interpreter's compatible-tag set; intersection only with a +filename tag rejected by that pre-acquisition gate does not count. Do not +require equality or subset in either direction. Valid compressed and metadata +representations may differ, while an internal claim with no runtime-compatible +filename intersection is a contradictory wheel and fails inspection. Internal +tags never add candidate eligibility or replace the filename-derived +compatibility tags used by the pre-acquisition gate and pip. Retain the +observed internal tags and console-script entry point as verified wheel +metadata for PTD-23.3. Require +every declared bundled-component path to identify a nonempty regular file or +nonempty directory prefix in the wheel and reject conflicting declared paths. +Do not extract the wheel or execute a bundled program during inspection. + +Bundled-component names and versions are reviewed declaration metadata covered +by the binding contract and artifact record digests and by the exact wheel +content digest. PTD-23.2 requires contract/artifact metadata equality and +observed path/type presence; it does not infer a component version from an +executable, a package-specific metadata file, or a component name. Adding such +inference would be a new inspection primitive and design change, not an +identity-specific branch in this slice. + +Return one immutable verified-wheel input per exact selected artifact, sorted +by application scope, normalized distribution, and artifact reference. It +retains the selected scope and closure identity, contract and artifact +references, acquired descriptor, observed wheel metadata, selected console +script, and acquisition outcome. The portable-tool lock remains normalized: +the selected plan records bind all expected metadata, and the existing +acquisition entry binds descriptor and source outcome rather than copying a +second mutable metadata structure into the lock. Preserve the existing +one-acquisition-per-selected-artifact invariant. Locked replay reopens every +exact descriptor and repeats inspection before resolver staging; it performs +no acquisition when the verified store objects are present and fails closed +when any is missing or inconsistent. + +Acceptance: only an exact manifest-authorized descriptor reaches inspection; +size or digest mismatch is rejected before ZIP parsing; any filename, +`.dist-info`, Name, Version, `Requires-Python`, filename-derived compatibility +tag, console-script, bundled-path, record-reference, scope, or closure mismatch +is rejected before resolver staging; malformed internal `WHEEL` tags fail, +while valid unequal sets pass only when an internal tag exactly matches a +filename-derived tag accepted for the selected runtime. Fixtures cover equal +sets, overlapping unequal compressed sets, a filename-compatible wheel whose +internal tags are entirely disjoint, and a mixed compatible/incompatible +compressed filename whose internal tags overlap only the incompatible member. +Hostile synthetic wheels cover every bound and malformed, duplicate, escaping, +encrypted, ambiguous, missing, and conflicting case. +Cache-hit and locked-replay tests prove zero network calls and repeat +inspection. A focused exact-artifact check proves that the Playwright wheel +matches its catalog declarations and contains every declared bundled path +without executing Node.js, Playwright, or another bundled program. A neutral +synthetic wheel passes the same production primitive. Non-goals: Python dependency resolution, wheel installation, browser payload acquisition or extraction, or tool-specific acquisition and inspection code. #### PTD-23.3: Materialize Portable Python Bindings Offline -Scope: stage verified binding wheels as mandatory direct constraints in the -existing Python dependency graph, resolve the remaining contract roots through -the Python provider, materialize the closed wheel set through the existing -network-disabled transaction, and expose the catalog-declared CLI at its exact -selected path. All mechanics are generic over portable Python binding inputs; -Playwright is only the selected catalog fixture. - -Acceptance: an index candidate cannot replace the selected binding wheel or -change its inspected metadata; the exact selected wheel participates in the -application's ordinary Python closure; materialization installs only the closed -wheel set with networking disabled; the declared CLI resolves to the generated -Python console script; and command and network assertions prove that neither an -upstream installer nor browser acquisition is invoked. Focused Playwright -evidence demonstrates these generic guarantees without adding a -Playwright-specific production branch. +Scope: at the Python resolver seam defined by PTD-23.1, run the compatibility +gate once over the already observed interpreter, acquire and verify the exact +PTD-23.2 artifact set, and join each artifact reference to its exact +verified-wheel input and projected component constraint. Stage each descriptor +read-only under a deterministic collision-checked resolver path. Extend the +existing Python resolver input—not the public blueprint syntax—with a mandatory +direct constraint from the normalized distribution to that staged local wheel. +The selected wheel is also a direct root in the component request. The resolver may +use its ordinary controlled network path for the remaining contract roots and +transitive dependencies, but the selected distribution can be satisfied only +by the staged descriptor with the selected digest. A missing, duplicate, or +different descriptor, or a resolver output whose selected distribution has +different bytes or inspected metadata, fails before bundle publication. + +Feed the resulting complete closed wheel set into the existing Python bundle +and network-disabled materialization transaction. Installation consumes only +mounted closed wheels, uses the selected application interpreter, and runs no +tool-supplied installer or hook outside the Python provider's fixed install +recipe. Retain the exact selected binding wheel and its verification identity in +the provider bundle and lock/store reachability; do not reinterpret it as a +local-source wheel or an index-selected artifact. + +Extend the provider-neutral portable operation dependencies without changing +their ownership: every binding artifact acquisition still reaches the common +acquisition barrier, the barrier precedes every binding artifact +materialization, every binding artifact materialization precedes that +contract's matching export, and the export precedes its capability. +Reject a contract CLI that cannot be joined uniquely to its export and +capability operations. This ordering is part of canonical DAG and locked-replay +validation, so an executor cannot publish an alias or capability before the +owning Python transaction succeeds. + +After the Python transaction has generated and validated the contract-selected +console script inside the owning application's Python runtime root, satisfy the +portable export operation with one fixed Reploy-owned alias publication +primitive. Atomically create the catalog-declared absolute CLI path as a +symbolic link to that exact generated console script. Create any missing parent +directories at mode `0755` only after the exact export path has been accepted by +the selected filesystem and export domains. Anchor every destination operation +in the provider-owned staging root, walk parent components without following +links, reject non-directory or pre-existing unowned destinations, and publish a +same-directory temporary link with a no-replace rename. The filesystem mutation +uses staging-root-relative handles, but the link payload is the canonical +final-image absolute path of the generated Python console script and must never +contain the host staging-root prefix. Validate that image path separately and +prove it is within the owning Python runtime root; after publication, resolve +the link in final-image path space and require that exact target. Neither path +may conflict with another selected filesystem or export claim. An otherwise +identical declared alias destination deduplicates only when its computed +Python-runtime target and exact binding identity are also identical; two +application scopes cannot silently make one shared alias point at different +virtual environments. The definition supplies only the canonical export name +and path; it cannot supply link syntax, +commands, or an alternate target. Failure publishes neither a usable alias nor +a successful portable-tool materialization result, and ordinary provider +rollback removes any staged partial state. + +Acceptance: an index containing the same version with different bytes, a newer +version, or a matching name with different metadata cannot replace the selected +wheel; the exact wheel is present once in the application's ordinary Python +closure; and resolver output is invariant to input order. Materialization +installs only the closed wheel set with networking disabled. Missing, duplicate, +or mismatched selected wheels and console scripts, alias destination escape, +filesystem/export collision, interrupted alias publication, and locked-replay +descriptor drift all fail closed with cleanup. The declared CLI resolves to the +generated Python console script in final-image path space, contains no host +staging prefix, and remains application-scoped. A focused DAG test proves the +barrier/materialization/export/capability order, and a +shared-export-domain test rejects two application runtimes competing for one +alias destination. Command and network assertions prove that neither an +upstream installer nor browser acquisition is invoked. Two neutral synthetic +bindings and focused Playwright evidence exercise the same production resolver, +transaction, and alias primitive without literal production checks or dispatch +against Playwright, Node.js, `playwright-core`, package, CLI, or +bundled-component names. Non-goals: Chromium, Headless Shell, or FFmpeg materialization; target APT roots; another ecosystem binding; additional catalog definitions; or the