Skip to content

Complete M6.1 package-mode generation contract - #9

Open
alwunder wants to merge 59 commits into
mainfrom
milestone-6-1-generation-contract
Open

alwunder wants to merge 59 commits into
mainfrom
milestone-6-1-generation-contract

Conversation

@alwunder

@alwunder alwunder commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Summary - Completes PDB's installed-project/package deployment mode. - Adds explicit first-party --application-wheel handling. - Keeps source mode for source-compatible projects. - Makes generation staging use role-aware M6 inventory and provenance rules. - Prevents ignored, untracked, cache, and deployment-support files from leaking into standalone kits. - Adds dirty tracked deployment-input protection. - Extends validation, runtime state, packaging, reports, and the all workflow for package mode. ## Mode contract ### Source mode - The project is not installed. - The authoritative entry point must be source-import compatible. - Declared source roots are used. - Dependency synchronization retains --no-install-project. ### Package mode - Locked third-party dependencies are synchronized without building the project. - Reviewed exceptional dependency artifacts are installed separately. - A validated first-party application wheel is installed without dependency resolution or builds. - The authoritative entry point is imported from the installed environment. - No application source roots or PYTHONPATH are configured. Typed mode/readiness conditions are: - SOURCE_COMPATIBLE - PACKAGE_PREFERRED - ENTRYPOINT_REQUIRES_PACKAGE_MODE - DEPLOYMENT_MODE_CONFLICT - INSTALLED_PROJECT_REQUIRED - APPLICATION_WHEEL_REQUIRED ## First-party artifact contract --application-wheel is the first-party project artifact and is distinct from --artifact, which remains the reviewed escape hatch for exceptional dependency wheels. PDB records the exact application-wheel SHA-256 and validates its distribution, version, authoritative entry point, module, explicitly named setuptools package-data mappings, RECORD inventory, wheel tags, archive safety, pure-Python policy, and deployment security constraints. The assessed source Git revision and supplied wheel SHA are separate provenance facts. Ordinary wheel metadata is not treated as cryptographic proof that a particular source revision produced the supplied bytes. ## Staging and provenance - Role-aware M6 inventory drives staging. - Git repositories stage tracked, role-approved deployment inputs. - Relevant tracked inputs identified in either the HEAD snapshot or current inventory block release-oriented generation when modified, deleted, or unstaged-renamed. - Deployment support, documentation, examples, mutable state, ignored/untracked files, and caches are excluded. - Non-Git directories and safely materialized archives remain supported under the same role policy. - Package-mode application runtime content comes only from the validated application wheel. ## Runtime and package behavior Package setup order is: 1. Provision managed Python. 2. Perform locked third-party synchronization. 3. Install approved dependency artifacts. 4. Install the first-party application wheel. 5. Run uv pip check. 6. Check the authoritative installed entry point. 7. Write successful state. Failed setup preserves or restores the prior known-good environment. Fast launch, Diagnose, Repair, rollback, and staleness checks share artifact-aware fingerprint semantics. Package-mode releases include the first-party wheel separately from dependency artifacts and retain deterministic ZIP behavior. ## Compatibility - Older source-mode manifests remain statically valid and packageable through compatible defaults. - Package mode begins with M6.1 and requires an application artifact. - Geo Map Explanation Extractor and TN Coordinate Converter retain source mode and their existing launch/sync behavior. ## Acceptance evidence ### SimpleGeorefGUI - Source remained unchanged at f484570d89fb1f9e9170fac915475358dfc1234e. - Package mode selected. - Source-derived authoritative surface: 13 Python plus 77 package-data members; all 90 persisted members validated, with no unexpected Python/startup destinations. - Static validation: STATIC_VALID. - Automated runtime checks: pass. - Final automated state: MANUAL_GUI_VALIDATION_REQUIRED. - Authoritative target imported from managed site-packages. - ArcGIS, GDAL, osgeo, ArcPy, and ArcGIS Pro runtime paths were absent. ### Source-mode regressions - Geo Map Explanation Extractor: source mode, static/runtime automation pass, package success. - TN Coordinate Converter: source mode, reviewed proxy_tools retained, static/runtime automation pass, package success. The same reviewed SGG kit packaged twice with identical ZIP SHA-256: 31875A58658E11AFC968FA9224258B2180A30858B81659DDB6CE1E87BEE37BD6 Acceptance wheels, runtime environments, deployment kits, and ZIPs are not included in this PR. ## Quality - 2031 passed, 4 skipped - Ruff passed - git diff --check passed - The three P2 corrections, unchanged Geo/TN acceptance, and clarified deterministic-packaging evidence are documented in current correction evidence. Detailed SGG traceability retains both historical and current hashes: regeneration changed only intentional timestamp/ID metadata and its indexed digest; the deployment fingerprint and all other ZIP members are unchanged. Review accounting remains 130 total findings, 127 unresolved inline threads, 3 review-level-only findings, and 1 NOT_APPLICABLE inline finding.

Add installed-project deployment with an explicit validated first-party application wheel while preserving source-compatible deployments.

Use role-aware staging and dirty-input provenance protection, isolate source and package launches, extend artifact-aware runtime rollback and fast-path state, and support deterministic package-mode releases.
@alwunder alwunder self-assigned this Sep 3, 2026
Compare role-approved deployment inputs from both HEAD and the working tree so deleted and unstaged-renamed runtime inputs cannot escape dirty-source protection.

Bound wrapper return inference to owned returns and unique callable names, preserving conservative unknown classifications for ambiguous code.
@alwunder

alwunder commented Sep 3, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2aa4b0b38b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

)
if tracked is None:
return selected
selected.intersection_update(tracked)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve an explicitly prepared untracked lockfile

When an external staging directory is used for a Git repository that initially lacks uv.lock, --prepare-lock creates the lockfile after the first staging pass, but the second pass intersects selected inputs with git ls-files, removing the newly created and necessarily untracked lockfile. _staging_files then raises Required deployment input is missing: uv.lock, so the documented prepare-and-generate workflow fails after modifying the source repository. The explicitly authorized lockfile needs to bypass this tracked-only filter (or be staged directly after creation).

Useful? React with 👍 / 👎.

Comment on lines +269 to +270
if application_wheel is not None:
paths.append(f"deployment/application/{application_wheel.name}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove obsolete application wheels during regeneration

When regenerating an existing package-mode kit with an application wheel whose filename changed, only the new filename is planned, while the old wheel recorded in the previous generated-file index is neither removed nor reported as a collision. The new index consequently drops the old path but leaves the file on disk, causing the freshly generated kit to fail NO_UNINDEXED_STAGED_FILES and preventing packaging. Reconciliation should delete unchanged previously owned paths that are no longer planned, or stop before writing.

Useful? React with 👍 / 👎.

Comment on lines +334 to +338
if str(filename_version) != expected_version:
raise PreparationError(
f"Application wheel version mismatch: expected {expected_version}, received "
f"{filename_version}."
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Compare application versions canonically

For projects using a valid but non-canonical PEP 440 version such as 1.0-rc1 or 1.0-1, parse_wheel_filename returns the normalized version (1.0rc1 or 1.0.post1), while expected_version retains the spelling from project metadata. This raw string comparison rejects a correctly built wheel for the same version before generation can proceed; parse the project version and compare Version objects (and apply the same normalization to the METADATA check below).

Useful? React with 👍 / 👎.

@alwunder

alwunder commented Sep 3, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8b57ae648a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if plan.deployment_mode != "package" and application_wheel is not None:
raise PreparationError("--application-wheel is accepted only for package deployment mode.")
application_artifact = (
validate_application_wheel(application_wheel.resolve(), assessment, plan)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Expand the application-wheel path before resolving it

When --application-wheel contains an unexpanded home-directory path (for example, a quoted ~/dist/app.whl or a programmatic Path('~/dist/app.whl')), resolving it here first turns it into <cwd>/~/dist/app.whl. The subsequent expanduser() inside validate_application_wheel() can no longer recognize the home marker, so generation incorrectly reports that an existing wheel is missing. Pass the original path to the validator, which already expands and resolves it in the correct order.

Useful? React with 👍 / 👎.

@alwunder

alwunder commented Sep 3, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 07cc9b0ae0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

from python_deployment_builder.generation.acquisition import PreparationError, sha256_file
from python_deployment_builder.models import ApprovedArtifact, DeploymentPlan
from python_deployment_builder.models import (
ApplicationArtifact,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject all secret-file names inside application wheels

When an application wheel contains a sensitive file already prohibited elsewhere, such as token.json, .pypirc, pip.ini, or .env.production, this reduced name set lets it pass validation and the static kit scan cannot detect it inside the unextracted .whl. The resulting release therefore distributes credentials despite the deployment secret-file policy; apply the same filename and .env.* rules used by static validation to wheel members.

Useful? React with 👍 / 👎.

Comment on lines +157 to +159
def _selected_deployment_paths(assessment, plan) -> set[str]:
selected = {"pyproject.toml", "uv.lock"}
if plan.deployment_mode == "source":

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include ignore rules in dirty-input checks

For a Git repository whose tracked .gitignore is locally changed, neither the current nor HEAD selected-path set includes that ignore file, so the intersection in _dirty_tracked_deployment_paths misses the change even though ignore rules directly control which source/resources are staged and are included in the assessment fingerprint. A local ignore-rule edit can therefore add or omit runtime files while the manifest still claims the unchanged HEAD revision; include all applicable .gitignore files in the dirty deployment inputs.

Useful? React with 👍 / 👎.

"name=os.environ['PDBUILDER_APPLICATION_DISTRIBUTION'];"
"version=os.environ['PDBUILDER_APPLICATION_VERSION'];"
"module=os.environ['PDBUILDER_APPLICATION_MODULE'];"
"sys.exit(m.version(name)!=version or importlib.util.find_spec(module) is None)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize the installed version in the runtime probe

For the newly supported non-canonical PEP 440 spellings (for example, project version 1.0-rc1 with wheel metadata/version 1.0rc1), application-wheel validation now deliberately accepts the wheel but stores the original project spelling in the manifest. This probe then compares importlib.metadata.version() to that spelling as raw strings, causing runtime validation—and all --runtime-validation—to fail after a successful installation; compare parsed Version values or store the canonical version.

Useful? React with 👍 / 👎.

Unify staged-kit and application-wheel security policy, guard Git ignore inputs as analysis provenance, and compare installed application versions with PEP 440 semantics.
@alwunder

alwunder commented Sep 3, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 038ab6c4d9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +161 to +163
item.path.rstrip("/")
for item in assessment.file_inventory
if item.role in RUNTIME_ROLES and not item.path.endswith("/")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Stage declared package data in source mode

When a flat-layout source-mode project declares runtime files in [tool.setuptools.package-data] but accesses them dynamically (for example, app/data/default.json selected through importlib.resources using a variable), those non-Python files remain UNKNOWN because package-data declarations do not promote inventory roles. This filter consequently omits them from the kit, while generation and static validation still succeed; the installed application then fails when it tries to read its declared data. Include files matching authoritative package-data mappings among source-mode runtime inputs.

Useful? React with 👍 / 👎.

Comment on lines +194 to +196
if ".dist-info" in member_path.parts or member_path.suffix.lower() not in TEXT_SUFFIXES:
continue
text = bundle.read(member).decode("utf-8", errors="replace")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Scan textual configuration members inside application wheels

Although prohibited basenames are now caught, this suffix allowlist still skips common textual package-data formats such as .yaml, .yml, .toml, .ini, and .cfg. For example, a wheel member named settings.yaml containing api_key: sk-abcdefghijklmnop bypasses text_security_findings, and the static kit scan cannot inspect it inside the .whl, so the release distributes a credential despite the secret-content policy. Scan all recognized textual resource formats, not only TEXT_SUFFIXES as currently defined.

Useful? React with 👍 / 👎.

Comment on lines +312 to +313
if application_wheel is not None:
paths.append(f"deployment/application/{application_wheel.name}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Classify the resolved application-wheel filename

When --application-wheel is a symlink whose basename differs from its target (for example, latest.whl pointing to a valid versioned wheel), validation resolves the link and _render_owned_files stages the target's ApplicationArtifact.filename, but collision planning uses the unresolved application_wheel.name here. An unowned file already present at the actual versioned destination is therefore absent from preview.collisions and is overwritten during _write_files; derive planned paths from the validated artifact filename instead.

Useful? React with 👍 / 👎.

@alwunder

alwunder commented Sep 3, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f26c0bec41

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

repository_root: Path, provenance_guarded: set[str]
) -> list[str]:
result = subprocess.run(
["git", "-C", str(repository_root), "diff", "--name-only", "-z", "HEAD", "--"],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Include rename sources in the dirty-path set

When a tracked runtime input is staged with git mv into an excluded scope, Git's rename detection makes this command report only the destination path. For example, moving helper.py to docs/helper.py leaves the destination outside the current guarded set while only the old path is in the HEAD guarded set, so the intersection is empty; generation can then omit the helper while still recording the unchanged HEAD revision. Disable rename detection here or parse both sides of rename records.

Useful? React with 👍 / 👎.

Comment on lines +472 to +473
for pattern in patterns:
if not any(fnmatch.fnmatchcase(name, pattern) for name in package_members):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Verify every declared package-data file

When one package-data glob matches multiple source files, this any(...) check accepts a wheel containing only one of them. For example, data/*.json covering both defaults.json and schema.json passes if the wheel contains only defaults.json, allowing generation and static validation to succeed even though the installed application can fail when it reads the omitted resource. Compare wheel members with every concretely resolved packaged resource rather than requiring only one match per pattern.

Useful? React with 👍 / 👎.

Comment on lines +374 to +376
metadata_distribution, metadata_version = _require_core_metadata(
metadata, label="METADATA", wheel=path
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate the wheel's Requires-Python constraint

When the supplied wheel declares a stricter Requires-Python than the assessed project, such as project metadata >=3.12 with a selected 3.12 runtime but wheel metadata >=3.13, the current metadata checks accept it because they inspect only name and version. The generated kit then fails during the end-user uv pip install step. Parse the wheel's Requires-Python and ensure the selected runtime satisfies it before staging the artifact.

Useful? React with 👍 / 👎.

@alwunder

alwunder commented Sep 4, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 82d111199c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

f"{assessment.repository.revision}: {', '.join(dirty)}. Commit or restore "
"those inputs before release-oriented generation."
)
selected.intersection_update(tracked)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject selected runtime files that are untracked

When a tracked source file imports a newly created untracked local module, assessment promotes that module to APPLICATION_SOURCE, but this intersection silently removes it from the staged kit without reporting a provenance error. Generation and static validation can then succeed even though the application fails at setup or launch when the tracked module imports the omitted helper; selected untracked runtime inputs should block generation rather than be discarded.

Useful? React with 👍 / 👎.

Comment on lines +218 to +219
if ".dist-info" in member_path.parts or member_path.suffix.lower() not in TEXT_SUFFIXES:
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Scan extensionless wheel metadata for secrets

When a project README containing a credential is embedded as the wheel's Core Metadata description, the resulting .dist-info/METADATA member has no suffix and is skipped by this allowlist, while static kit validation cannot inspect the opaque wheel. The fresh evidence after the earlier wheel-scanning comments is that the expanded suffix list still excludes this standard extensionless textual member, allowing the generated release to distribute an obvious or configured secret.

Useful? React with 👍 / 👎.

Comment on lines +61 to +63
explicit = project.package_directories.get(package)
if explicit is not None:
candidates.append(root / explicit)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve package data through parent package-dir mappings

For a valid setuptools layout such as package-dir = {app = "lib"} with package data declared for app.sub, the exact lookup for app.sub misses the parent app mapping, so the resolver searches app/sub instead of lib/sub. Consequently the concrete declared resource never reaches expected_members, and a stale or incomplete application wheel can pass validation without it. Fresh evidence beyond the earlier package-data finding is this new resolver's lack of longest-parent mapping semantics.

Useful? React with 👍 / 👎.

@alwunder

alwunder commented Sep 4, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 368c17cbaa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"be inventoried. Generation stopped rather than claiming clean provenance."
)
try:
head_root = safe_extract_zip(archive, extracted)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid full-archive extraction for dirty-path checks

When a Git repository's HEAD contains an unrelated tracked symlink, any tracked working-tree change reaches this extraction; git archive preserves the symlink type, while safe_extract_zip rejects every symbolic-link member. Consequently even an unrelated README edit causes generation to stop before the changed paths are intersected with deployment inputs, despite the documented policy that unrelated tracked documentation should not trigger the provenance gate.

Useful? React with 👍 / 👎.

key = normalized.casefold()
if key in recorded:
raise PreparationError(f"Wheel RECORD contains duplicate paths: {wheel.name}")
recorded[key] = (row[1], row[2])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Verify wheel RECORD hashes and sizes

When a wheel member is modified and the ZIP is rebuilt with a valid CRC while its RECORD retains the original digest and size, this validator merely stores those fields and checks the path inventory. The altered application or dependency wheel is therefore accepted and assigned a new authoritative whole-file SHA-256 even though its internal integrity metadata no longer matches; validate every non-RECORD member's recorded hash and size rather than accepting them unchecked or blank.

Useful? React with 👍 / 👎.

Comment thread src/python_deployment_builder/cli.py Outdated
Comment on lines +590 to +591
write_assessment_reports(assessment, reports_root)
write_deployment_plan_reports(plan, reports_root)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Persist reports before package-mode readiness returns

Because the assessment and plan report writes were moved below generation, the normal first pdbuilder all run for a package-mode project without --application-wheel returns at the new blocker before creating any report files. The command prints the ASSESS and PLAN phases but leaves the developer without the plan needed to inspect the package-mode decision and prepare the wheel; write these reports immediately after planning, before readiness-related early returns.

Useful? React with 👍 / 👎.

@alwunder

alwunder commented Sep 4, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bdf7256448

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

parser = configparser.ConfigParser(interpolation=None)
parser.optionxform = str
parser.read_string(bundle.read(members[entry_points_name]).decode("utf-8-sig"))
entry_group = "gui_scripts" if entry_point.kind == "gui" else "console_scripts"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the declared entry-point group

For a package-mode project declaring [project.scripts] gui-tool = "app:main" (or any console-script name/target containing gui), analysis.metadata._entry_point heuristically sets kind to gui, so this lookup requires the wheel to place the entry point under gui_scripts. A standards-compliant wheel instead records it under console_scripts, causing every valid wheel for that project to be rejected as disagreeing with the authoritative metadata. Record the source metadata group separately or derive this lookup from the declared group rather than the UI-launch heuristic.

Useful? React with 👍 / 👎.

@alwunder

alwunder commented Sep 4, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 86c7065aae

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +428 to +430
configured_package_data = setuptools.get("package-data")
if isinstance(configured_package_data, dict):
package_data = {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Parse package data declared in setup.cfg

For a supported setuptools project whose metadata and entry point come from setup.cfg, an [options.package_data] declaration is never copied into PackagingAssessment.package_data because only [tool.setuptools.package-data] is read here. Consequently, dynamically accessed data can remain UNKNOWN and be omitted from a flat-layout source-mode kit even though setuptools declares it as runtime package data. Fresh evidence beyond the earlier package-data issue is that the new resolver is fed exclusively from this pyproject-specific branch despite inspect_metadata() continuing to support setup.cfg projects.

Useful? React with 👍 / 👎.

Comment on lines +221 to +222
if tracked is None:
return selected

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject excluded package data for non-Git sources

When source mode is generated from an ordinary non-Git directory, this early return bypasses the new authoritative package-data stageability check. If a concretely declared package-data file is classified as IGNORED_OR_LOCAL or MUTABLE_STATE_CANDIDATE, _selected_deployment_paths() omits it, yet generation and static validation can succeed and the deployed application can fail when reading the missing resource. Fresh evidence beyond the earlier package-data finding is that the new exclusion check only runs when Git tracking information exists; non-Git inputs should either stage the authoritative file or fail explicitly.

Useful? React with 👍 / 👎.

Comment on lines +461 to +463
if metadata_version_value != expected_version_value:
raise PreparationError("Application wheel METADATA version is wrong.")
_validate_requires_python(metadata, plan, path)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate application wheel dependency metadata

When a same-name/version application wheel declares an applicable Requires-Dist that is absent from or incompatible with the selected lock graph, these checks accept and stage it because only name, version, Requires-Python, and tags are examined. End-user setup then installs that wheel with --no-deps, after which the generated uv pip check fails, so a kit that passed generation and static validation cannot complete first-run setup. Validate the wheel's applicable dependency requirements against the planned locked environment before accepting it.

Useful? React with 👍 / 👎.

@alwunder

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc682d75b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1095 to +1099
if (
edge.selected_extra
and canonicalize_name(edge.selected_extra) not in activated_parent_extras
):
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Match root-extra edges when validating approved wheels

When an approved package is reached beneath a selected application extra (for example, map -> pywebview -> proxy-tools), inspect_uv_lock() propagates selected_extra="map" onto proxy-tools' normal dependency edges. Here that root-extra label is compared with extras requested of proxy-tools—usually an empty set—so a valid edge such as proxy-tools -> helper is skipped and an approved wheel declaring Requires-Dist: helper>=1 is incorrectly rejected even though the selected locked sync installs helper. Compare selected_extra with the graph's selected application extras, and use activated_dependency_extra when filtering the parent's own optional-dependency edges.

Useful? React with 👍 / 👎.

Comment on lines +498 to +502
for artifact in manifest.approved_artifacts:
if relative := manifest_artifact_wheel_path("wheels", artifact.filename):
expected_wheel_identities[relative] = (
artifact.distribution_name,
artifact.version,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject duplicate approved-artifact filenames

For a consistently re-indexed kit, two distinct locked artifact records can point to the same wheel filename and SHA; this assignment silently lets the later identity replace the earlier one. If the record matching the real wheel comes last, metadata validation sees only that identity, while the independent lock-identity loop can still accept both manifest records. The runtime sync then excludes both distributions but _promote_environment installs the same wheel twice, leaving one required distribution absent. Require approved artifact filenames/paths to be unique before building this map.

Useful? React with 👍 / 👎.

Comment on lines +743 to +746
if len(node.args) != 2:
return function, []
allowed_keywords = {"encoding", "errors"} if function.endswith("text") else set()
if any(keyword.arg not in allowed_keywords for keyword in node.keywords):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bind keyword arguments for legacy resource reads

When supported Python 3.12 source uses importlib.resources.read_text(package="app", resource="defaults.json") (similarly read_binary, open_text, or open_binary), the new legacy resolver returns no path because it requires both arguments to be positional and then rejects package/resource keywords. The concrete file therefore remains UNKNOWN, role-aware source staging omits it, and the deployed application fails when it performs the read. Resolve the two positional-or-keyword parameters with call_argument() before permitting the optional text arguments.

Useful? React with 👍 / 👎.

@alwunder

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 40da085224

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +574 to +578
if any(
evidence.file in {"setup.cfg", "setup.py"}
for evidence in dependency.evidence
)
and not any(evidence.file == "pyproject.toml" for evidence in dependency.evidence)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Ignore legacy dependencies overridden by [project]

When pyproject.toml explicitly declares [project].dependencies (including an empty list) but a retained setup.cfg or setup.py contains stale install_requires, PEP 621 makes the standardized field authoritative and setuptools ignores the legacy value. Metadata analysis nevertheless retains that legacy dependency, and this filter classifies it as backend-only, adding RUNTIME_SYNC_METADATA_UNSUPPORTED even though the valid uv.lock correctly omits it. Only apply this blocker when the dependency field is actually supplied by legacy metadata rather than overridden by [project].

Useful? React with 👍 / 👎.

@alwunder

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bd5ea6081a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +770 to +772
project_dependencies_static_authoritative = (
project_dependencies_present and not project_dependencies_dynamic
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Treat omitted dependencies as an authoritative empty field

When a project has a [project] table, omits dependencies, and does not list it in dynamic, PEP 621 and setuptools 79 treat the field as statically empty and ignore legacy install_requires. This predicate instead requires the field to be present, so stale requirements from setup.cfg or setup.py are retained and later trigger RUNTIME_SYNC_METADATA_UNSUPPORTED, blocking an otherwise valid project. Fresh evidence beyond the earlier explicit-list report is that the new predicate still requires project_dependencies_present rather than treating absence from dynamic as authoritative whenever [project] exists.

Useful? React with 👍 / 👎.

Comment on lines +789 to +795
static_entry_point_groups = {
legacy_group
for group, legacy_group in (
("scripts", "console_scripts"), ("gui-scripts", "gui_scripts")
)
if group in project and group not in project_dynamic
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Suppress legacy entry points when static groups are omitted

When [project] omits scripts or gui-scripts without declaring that field dynamic, setuptools ignores the corresponding legacy setup.cfg/setup.py entry points. This comprehension suppresses legacy values only when the standardized group is explicitly present, so a stale launcher is treated as authoritative; planning can select it and application-wheel validation then rejects the correctly built wheel because it contains no such entry point.

Useful? React with 👍 / 👎.

Comment on lines +798 to +800
# ``anchor=`` is the Python 3.12 spelling. Deliberately leave
# deprecated ``package=`` unresolved rather than treating either
# keyword form as the zero-argument implicit caller anchor.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve the compatible files(package=...) spelling

For deployments targeting Python 3.11 or 3.12, importlib.resources.files(package="app") remains a supported call (deprecated in 3.12 after the parameter was renamed to anchor). This branch deliberately returns no path for it, so a sole call such as files(package="app").joinpath("defaults.json").read_text() leaves the existing resource unresolved and role-aware source staging omits it, producing a runtime failure. Fresh evidence beyond the implicit-anchor report is the explicit rejection of this still-supported keyword spelling.

Useful? React with 👍 / 👎.

@alwunder

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2a7556358e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

trusted_wheels = trusted_artifact_wheel_paths(manifest)
secret_scanability_failures: list[str] = []
try:
secret_values = configured_secret_values(manifest.configuration_secret_names)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve secret scanning for legacy manifests

When validating a pre-M6.1 manifest, configuration_secret_names defaults to an empty list even though configuration_presence_names may contain credentials such as DB_PASSWORD. The previous validator scanned values for every presence name, but this call now scans none of them, so an arbitrary configured credential embedded in indexed source can receive STATIC_VALID as long as it does not match the narrow obvious-secret regex. Fall back conservatively to the legacy presence list when the new field was absent from the serialized manifest.

Useful? React with 👍 / 👎.

Comment on lines +813 to +821
return _resource_package_anchor_values(
anchor,
root=root,
source_path=source_path,
source_roots=source_roots,
project=project,
assignments=assignments,
returns=returns,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve explicit module anchors for resource files

For source-mode code targeting Python 3.12+ that calls importlib.resources.files("app.config").joinpath("defaults.json"), where app.config is a module and the resource is adjacent at app/defaults.json, the explicit anchor is valid and resolves to the module's containing directory. This path delegates only to the package-directory resolver, which searches for an app/config/ directory and returns no resource; defaults.json therefore remains UNKNOWN and is omitted from role-aware staging. Resolve literal module anchors to the containing directory as well as package anchors.

Useful? React with 👍 / 👎.

@alwunder

Copy link
Copy Markdown
Owner Author

@codex review

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0d26ebe9b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1247 to +1248
for expression, mode in _path_uses(node, directory_reads):
resource_values = _importlib_resource_path_values(

ghost Sep 12, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recognize resources passed through as_file

When source mode materializes a resource with the standard form with importlib.resources.as_file(importlib.resources.files("app") / "model.dat") as path, this dispatch finds no path use: the outer as_file() call is not recognized by _path_uses, while the inner / expression is not visited as a read operation. An unconventionally named tracked resource therefore remains UNKNOWN, is omitted by role-aware staging, and is unavailable when as_file() runs in the deployed application.

Useful? React with 👍 / 👎.

Comment on lines +375 to +377
module = target.value
if module and all(part.isidentifier() for part in module.split(".")):
modules.append((module, node.lineno))

ghost Sep 12, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve relative literal dynamic imports

For a flat source-mode project using importlib.import_module(".examples.plugin", package="app"), the empty component created by the leading dot makes this predicate reject the otherwise literal target, and the package argument is never used to make it absolute. If that local module is under an excluded examples/docs scope, it is not promoted into the staged application files, so generation and static validation can succeed before the runtime import raises ModuleNotFoundError.

Useful? React with 👍 / 👎.

@alwunder

ghost commented Sep 12, 2026

Copy link
Copy Markdown
Owner Author

@codex review

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 94b3fd52e3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1623 to +1628
automatic_root = (
source_roots[0]
if len(source_roots) == 1
else "src"
if (root / "src").is_dir()
else "."

ghost Sep 12, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor named package-dir mappings during auto-discovery

When a setuptools project relies on automatic explicit-layout discovery with only package-dir = {app = "lib"} and no packages or py-modules, setuptools 79 discovers the installed package app from lib, but this selects the ordinary ./src root and runs flat/src discovery instead. The assessment consequently records lib (or no package) rather than app, so package-mode validation rejects a correctly built wheel for missing the incorrectly inferred installed members. Handle nonempty package_directories as setuptools's explicit-layout discovery before choosing this fallback root.

Useful? React with 👍 / 👎.

Comment on lines +737 to +738
if len(node.args) > 2:
return function, []

ghost Sep 12, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Accept positional encoding arguments in legacy resource reads

A remaining case after the keyword-binding fix is importlib.resources.read_text("app", "defaults.json", "utf-8") (and the equivalent open_text form): Python 3.11 and 3.12 accept encoding and errors as third and fourth positional arguments, but this unconditional two-argument limit marks the resource unresolved. If the resource has a nonconventional name, role-aware source staging omits it and the deployed application fails when reading it; permit the documented positional text parameters while retaining the two-argument limit for binary helpers.

Useful? React with 👍 / 👎.

@alwunder

ghost commented Sep 12, 2026

Copy link
Copy Markdown
Owner Author

@codex review

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0ce99790f2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +738 to +739
if len(node.args) > (4 if text_function else 2):
return function, []

ghost Sep 12, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Support multi-part resource names on Python 3.13+

When the selected runtime is Python 3.13 or 3.14, importlib.resources.read_binary and open_binary accept multiple path components after the anchor, but this guard rejects every binary call with more than two positional arguments. A valid call such as read_binary("app", "models", "weights.bin") therefore yields no resource evidence; because a .bin file under a nonconventional models directory remains UNKNOWN, source-mode staging can omit it while static validation succeeds, leaving the deployed application to fail when it reads the resource.

Useful? React with 👍 / 👎.

@alwunder

ghost commented Sep 12, 2026

Copy link
Copy Markdown
Owner Author

@codex review

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4888ee2871

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

member.installed_member_path
for member in resolve_packaged_python_sources(source_root, assessment.project)
} if source_root is not None else set()
missing_python_members = sorted(expected_python_members - installed_names)

ghost Sep 12, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject undeclared executable members in application wheels

When a supplied application wheel contains every expected member plus a stale top-level module such as requests/__init__.py or a root .pth file, this subset-only comparison accepts it. The runtime at manage.py:126-175 synchronizes locked dependencies and then installs the application wheel, so the extra file can overwrite dependency code or execute during Python startup; uv pip check --help describes checking compatible dependencies, not installed-file ownership, so it does not detect this collision. Reject unexpected Python and startup-executable destinations outside the authoritative modeled wheel surface.

Useful? React with 👍 / 👎.

Comment on lines +845 to +846
if _is_resource_files_call(node, module_bindings, files_bindings):
if not isinstance(node, ast.Call):

ghost Sep 12, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Follow assigned Traversables when resolving resource reads

After the inline as_file(files(...) / ...) fix, a remaining common form is asset = files("app") / "model.dat" followed by as_file(asset) or asset.read_bytes(). In that case this resolver receives an ast.Name, but it only recognizes a direct files() call, joinpath, or / expression and never follows the supplied assignments; the generic path fallback cannot interpret the assigned files() call either. A nonconventionally named existing resource therefore remains UNKNOWN and is omitted from source-mode staging.

Useful? React with 👍 / 👎.

@alwunder

ghost commented Sep 12, 2026

Copy link
Copy Markdown
Owner Author

@codex review

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 847198a4ac

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +454 to +458
for relative_text in sorted(selected):
relative = Path(relative_text)
if _is_runtime_cache(relative):
continue
path = repository_root / relative

ghost Sep 12, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate staged paths for Windows before copying

When source-mode generation runs on a case-sensitive non-Windows host, a selected path containing a Windows-reserved component such as app/CON.py, an invalid character such as data/foo:bar.json, or a case-insensitive collision is copied and indexed without the Windows materialization checks applied to wheel members. Static validation and packaging can therefore accept a ZIP that cannot be extracted reliably on the target Windows system; validate all selected kit paths and Windows-equivalent collisions before staging them.

Useful? React with 👍 / 👎.

Comment on lines +273 to +275
item.path.rstrip("/")
for item in assessment.file_inventory
if item.role in RUNTIME_ROLES and not item.path.endswith("/")

ghost Sep 12, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve explicit reads with arbitrary filenames

For source code that explicitly reads an existing file with an unlisted suffix or no suffix, such as open("weights.dat", "rb"), resource analysis discards the path because _looks_like_resource_literal() recognizes only its fixed suffix/directory list. The file consequently remains UNKNOWN, this role filter omits it from the source-mode kit, and generation/static validation can succeed before the application raises FileNotFoundError; an explicit file-read operation should promote any safely resolved repository file regardless of naming convention.

Useful? React with 👍 / 👎.

from pathlib import PurePosixPath

FORBIDDEN_SHELL = ("powershell.exe", "pwsh.exe", "executionpolicy")
WINDOWS_ABSOLUTE = re.compile(r"(?i)[a-z]:\\(?:users|home)\\[^\r\n\"]+")

ghost Sep 12, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Match forward-slash Windows developer paths

When staged source or a validated wheel contains a hard-coded path such as C:/Users/alice/project/data.json, Python and Windows accept that spelling but this pattern matches only backslash separators. text_security_findings() therefore omits developer_path, allowing NO_DEVELOPER_PATHS to pass even though the release contains a machine-specific path that will fail for end users; accept either slash direction in the path detector.

Useful? React with 👍 / 👎.

@alwunder

ghost commented Sep 16, 2026

Copy link
Copy Markdown
Owner Author

@codex review

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 83dcead905

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"""Validate raw POSIX components before any host Path can normalize them."""
if not value or "\\" in value or any(p in {"", ".", ".."} for p in value.split("/")):
raise WindowsPathError(f"Windows-unsafe relative path: {value!r}")
for component in value.split("/"):

ghost Sep 16, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject overlong Windows path components

Reject components that exceed the Windows filesystem limit here. A supplied application or approved wheel can contain a member with a 256-character ASCII component; _safe_wheel_members() delegates to this validator, so generation and static validation accept the wheel, but uv pip install cannot materialize that member on NTFS and first-run setup rolls back. Validate each component's Windows length before declaring wheel and kit destinations safe.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant