Skip to content

[codex] Add project-level PHP extension opt-ins#289

Merged
munezaclovis merged 33 commits into
mainfrom
php-extension-opt-ins
Jun 25, 2026
Merged

[codex] Add project-level PHP extension opt-ins#289
munezaclovis merged 33 commits into
mainfrom
php-extension-opt-ins

Conversation

@munezaclovis

@munezaclovis munezaclovis commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds project-level PHP extension opt-ins without named profiles. Projects can continue using scalar php: 8.4, or use object form with version and extensions.

This PR wires the feature end to end:

  • parse and preserve php.extensions in Project config
  • publish optional PHP extension metadata in resource artifacts
  • persist requested, loaded, and ignored extension state
  • group FrankenPHP workers by PHP track plus loaded extensions
  • apply the same runtime overlays to pv php and Composer
  • report ignored extension diagnostics in pv list and pv status
  • document the new config shape and artifact behavior

Validation

  • cargo nextest run -p cli -E 'test(status_reports_warning_project_env_as_success) or test(status_prefers_ignored_php_extension_over_other_project_env_warnings)'
  • cargo check --workspace --all-targets --locked
  • cargo clippy -p cli --all-targets --all-features --locked -- -D warnings
  • Earlier final sweep: focused feature nextest 98/98, pv-release PHP suite 32/32, full workspace clippy, shellcheck for PHP recipes

Notes

This is a draft PR. The feature branch worktree is preserved at .worktrees/php-extension-opt-ins for follow-up iteration.

Summary by CodeRabbit

  • New Features
    • Added project-level PHP extension opt-ins via php.extensions (supported alongside php version selectors) with runtime-key grouping (e.g., 8.4+redis) and automatic activation via generated conf.d overlays.
    • PV now reads PHP extension metadata from release artifacts/manifests, persists requested/loaded/ignored extension state, and injects the overlays for pv shim:php and pv shim:composer about.
    • pv-release can record PHP extension modules; manifests include per-artifact extension metadata.
  • Bug Fixes
    • Improved warnings and status output so unsupported/ignored extensions surface consistently and influence runtime selection and labels.
  • Documentation
    • Updated user docs and extension opt-in design/plan materials for the new php configuration syntax and behavior.

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR implements end-to-end PHP extension opt-ins. Project PHP config now supports version plus extensions, artifact metadata includes PHP extension modules, release tooling stages and records optional modules, state persists PHP runtime extension identity, daemon worker grouping uses runtime keys, and CLI shims inject runtime overlays while ignored extensions surface as warnings.

Changes

PHP Extension Opt-Ins

Layer / File(s) Summary
PhpConfig model, parser, writer, and error variant
crates/config/src/model.rs, crates/config/src/parser.rs, crates/config/src/writer.rs, crates/config/src/error.rs, crates/config/src/lib.rs, crates/config/tests/project_config.rs, crates/cli/tests/project_env.rs
ProjectConfig.php changes from Option<String> to Option<PhpConfig> with version and extensions. Parser supports scalar and map forms; writer preserves extensions when updating track; UnknownPhpKey error variant added. Config tests updated and extended. Test helper register_project derives desired track via version_selector().
PHP extension metadata, resolution, overlay, and manifest parsing
crates/resources/src/php_extensions.rs, crates/resources/src/manifest.rs, crates/resources/src/lib.rs, crates/resources/tests/php_extensions.rs, crates/resources/tests/manifest_foundation.rs
New php_extensions module provides PhpExtensionLoadKind, PhpExtensionModule, PhpExtensionResolution, reads share/pv/php-extensions.json, resolves requests/persisted modules, writes per-runtime conf.d INI overlays, and builds PHP_INI_SCAN_DIR environments. ManifestArtifact gains php_extensions field with validation and accessor.
Release recipe split, record/manifest metadata, build and smoke scripts
crates/pv-release/src/recipe.rs, crates/pv-release/src/record.rs, crates/pv-release/src/record_writer.rs, crates/pv-release/src/manifest.rs, crates/pv-release/src/cli.rs, crates/pv-release/src/archive.rs, release/artifacts/recipes/php/build.sh, release/artifacts/recipes/php/smoke.sh, release/artifacts/recipes/php/tracks.toml, crates/pv-release/tests/*
Recipe splits into default_extensions and optional_extensions with validation. Record, record-writer, and manifest carry php_extensions entries with validation. Build script stages optional .so modules and emits php-extensions.json. Smoke script validates optional extensions via generated overlays. --php-extension CLI flag added to write-release-record. Archive validation includes extension module files. Full test coverage added.
DB migration, php_runtime_key, and state persistence
crates/state/src/sql/008_project_php_runtime_extensions.sql, crates/state/src/migrations.rs, crates/state/src/database.rs, crates/state/src/lib.rs, crates/state/src/paths.rs, crates/state/tests/state_foundation.rs
Migration 8 adds three JSON extension columns. ProjectPhpRuntimeRecord and ProjectPhpRuntimeInput are introduced. php_runtime_key builds canonical track+ext1+ext2 keys. replace_project_php_runtime persists runtime extension lists. PortOwner and RuntimeSubject switch to runtime-key identity. Worker path helpers rename php_track to php_runtime. State tests cover persistence, rejection, and port identity behavior.
Daemon runtime resolution and gateway worker grouping
crates/daemon/src/project_env.rs, crates/daemon/src/gateway.rs, crates/daemon/tests/gateway_reconciliation.rs, crates/daemon/tests/project_env_reconciliation.rs, crates/daemon/tests/real_artifact_gateway_e2e.rs, crates/daemon/tests/daemon_foundation.rs, crates/daemon/src/jobs.rs
ResolvedPhpRuntime replaces track-only resolution in project env reconciliation. PhpWorkerRuntimePlan gains runtime_key and loaded_modules. InstalledFrankenphpRuntime adds artifact_root. Workers are grouped, configured, and cleaned up by runtime_key. FrankenPHP environment uses php_runtime_environment. Ignored-extension warnings are persisted and propagated. Gateway reconciliation resolves extensions and reconstructs persisted runtime state. Reconciliation job wiring is updated. Tests cover runtime plan grouping, extension metadata edge cases, and e2e scenarios.
CLI shims, project linking, and ignored-extension diagnostics
crates/cli/src/commands/php.rs, crates/cli/src/commands/project.rs, crates/cli/src/commands/status.rs, crates/cli/src/commands/doctor.rs, crates/cli/src/commands/logs.rs, crates/cli/tests/php.rs, crates/cli/tests/composer.rs, crates/cli/tests/status.rs, it/cli.rs
PHP and Composer shims resolve PhpShimRuntime and inject runtime overlay into PHP_INI_SCAN_DIR. project.rs link derives track via version_selector(). Project list/status surfaces ignored_php_extension warnings with compact summaries. Doctor and logs commands support PhpRuntimeWorker labeling. Integration tests cover shim overlay injection, missing metadata failure, warning display, and project linking with extensions.
Design docs, ADRs, specs, plans, user docs
DESIGN.md, docs/adr/0014-project-level-php-extension-opt-ins.md, docs/adr/0006-fixed-extension-php-artifacts.md, docs/superpowers/specs/2026-06-22-php-extension-opt-ins-design.md, docs/superpowers/plans/2026-06-22-php-extension-opt-ins.md, docs/user/README.md, release/artifacts/README.md, .superpowers/sdd/task-2-report.md
DESIGN.md updated to runtime-identity model and worker grouping semantics. New ADR 0014 formalizes project-level extension opt-ins; ADR 0006 marked superseded. Design spec and implementation plan added with full task breakdown. User README gains PHP Extensions section. Task 2 completion report documents the implementation and notes.

Sequence Diagram(s)

sequenceDiagram
  participant User as User (pv.yml)
  participant Config as config::PhpConfig
  participant State as state::Database
  participant DaemonEnv as daemon::project_env
  participant Resources as resources::php_extensions
  participant Gateway as daemon::gateway
  participant CLIShim as CLI php/composer shim

  User->>Config: php: {version: "8.4", extensions: [redis]}
  Config->>Config: parse → PhpConfig
  Config->>State: link_project via version_selector()

  DaemonEnv->>Resources: resolve_php_extension_request(...)
  Resources-->>DaemonEnv: PhpExtensionResolution
  DaemonEnv->>State: replace_project_php_runtime(...)
  State-->>DaemonEnv: ProjectRecord.php_runtime

  Gateway->>DaemonEnv: resolve_project_php_runtime(...)
  DaemonEnv-->>Gateway: ResolvedPhpRuntime
  Gateway->>Resources: ensure_php_runtime_overlay(...)
  Resources-->>Gateway: conf.d overlay path
  Gateway->>Resources: php_runtime_environment(...)
  Resources-->>Gateway: PHP_INI_SCAN_DIR overlay

  CLIShim->>State: read project.php_runtime
  CLIShim->>Resources: resolve_persisted_php_extension_modules(...)
  CLIShim->>Resources: php_runtime_exec_environment(...)
  Resources-->>CLIShim: exec env with overlay
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • prvious/pv#245: The main PR extends the same project config/link/list pathways from #245 to carry PHP runtime identity and extension warnings.
  • prvious/pv#256: Both PRs modify daemon gateway runtime reconciliation, and the main PR builds on the worker/runtime scaffolding introduced there.
  • prvious/pv#288: The main PR extends the PHP/Composer shim environment behavior introduced in #288 with runtime-keyed extension overlays.

Poem

🐇 A bunny hopped through INI mist,
With redis and xdebug nicely twinned and kissed,
8.4+redis lit the trail,
Overlays bloomed without a fail,
I twitch my nose—runtime keys are bliss! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 32.87% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title concisely matches the PR’s main change: adding project-level PHP extension opt-ins.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch php-extension-opt-ins

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codspeed-hq

codspeed-hq Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 7 untouched benchmarks


Comparing php-extension-opt-ins (b2db34c) with main (65fa466)

Open in CodSpeed

@munezaclovis munezaclovis marked this pull request as ready for review June 23, 2026 02:04

@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: b8c733c3c1

ℹ️ 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 thread crates/daemon/src/project_env.rs Outdated

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

This PR wires the feature end to end, but the current state handling can silently omit requested extensions before reconciliation and can make rollback reads fail after extension-specific workers are persisted.

Reviewed changes — This review covered the initial implementation of project-level PHP extension opt-ins across config, state, daemon runtime planning, CLI shims, release metadata, recipes, docs, and tests.

  • Parse and preserve php.extensionsconfig::PhpConfig supports scalar PHP selectors and object-form version-plus-extension requests, with writer coverage preserving extension lists when updating PHP tracks.
  • Persist project PHP runtime statestate adds requested, loaded, and ignored extension columns plus helpers for runtime keys and public ProjectRecord::php_runtime access.
  • Resolve extensions during reconciliation — daemon project-env reconciliation resolves requested extensions from installed PHP artifact metadata, persists loaded/ignored state, and reports ignored-extension warnings.
  • Group and launch extension-specific workers — gateway planning groups FrankenPHP workers by PHP track plus loaded extensions and applies PHP runtime overlays to worker environments.
  • Apply overlays to CLI shimspv php and Composer shims reconstruct persisted extension modules and extend PHP_INI_SCAN_DIR for project-local execution.
  • Publish extension metadata — resource manifest parsing, release records, PHP recipes, smoke tests, docs, and snapshots now include optional PHP extension metadata.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using GPT𝕏

Comment thread crates/cli/src/commands/php.rs Outdated
Comment thread crates/daemon/src/gateway.rs Outdated
Comment thread crates/pv-release/src/record.rs

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/pv-release/src/record.rs (1)

162-227: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Validate php_extensions entries during ReleaseRecord::from_json.

php_extensions is currently accepted verbatim. Invalid load_kind/path/duplicate names can pass record parsing and then propagate into manifest/runtime metadata.

Suggested patch
 impl ReleaseRecord {
     pub fn from_json(path: &Utf8Path, json: &str) -> crate::Result<Self> {
         let raw: RawReleaseRecord =
             serde_json::from_str(json).map_err(|error| invalid_release(path, error.to_string()))?;
@@
         validate_relative_file_list(path, "license_files", &raw.license_files)?;
         validate_relative_file_list(path, "notice_files", &raw.notice_files)?;
         validate_relative_path(path, "object_key", &raw.object_key)?;
+        validate_php_extensions(path, &raw.php_extensions)?;
         validate_object_key_layout(path, &raw)?;
         raw.provenance.validate(path)?;
@@
     }
 }
+
+fn validate_php_extensions(path: &Utf8Path, php_extensions: &[PhpExtensionRecord]) -> crate::Result<()> {
+    let mut seen = BTreeSet::new();
+    for extension in php_extensions {
+        let valid_name = !extension.name.is_empty()
+            && extension
+                .name
+                .bytes()
+                .all(|byte| byte.is_ascii_alphanumeric() || byte == b'_');
+        if !valid_name {
+            return Err(invalid_release(
+                path,
+                format!("invalid php_extensions name `{}`", extension.name),
+            ));
+        }
+        if !matches!(extension.load_kind.as_str(), "extension" | "zend_extension") {
+            return Err(invalid_release(
+                path,
+                format!("invalid php_extensions load_kind `{}`", extension.load_kind),
+            ));
+        }
+        validate_relative_path(path, "php_extensions.path", &extension.path)?;
+        if !seen.insert(extension.name.as_str()) {
+            return Err(invalid_release(
+                path,
+                format!("duplicate php_extensions entry `{}`", extension.name),
+            ));
+        }
+    }
+    Ok(())
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/pv-release/src/record.rs` around lines 162 - 227, The php_extensions
field in the ReleaseRecord::from_json method is not being validated and is
assigned directly from the raw data without checking for invalid load_kind
values, invalid paths, or duplicate extension names. Add validation logic for
php_extensions before the final Ok(Self { ... }) return statement in the
from_json method, similar to the existing validate_relative_file_list and
validate_relative_path calls, to ensure each php extension entry has a valid
load_kind, valid path, and that there are no duplicate extension names. This
validation should return an appropriate error using the invalid_release helper
function if any validation fails.
🧹 Nitpick comments (4)
crates/config/tests/project_config.rs (1)

127-143: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the new unknown php key branch.

The parser now has a dedicated UnknownPhpKey path; add one assertion here so typos under php: stay covered.

Suggested test addition
     assert!(matches!(
         ProjectConfig::parse("php:\n  extensions:\n    - true\n"),
         Err(ConfigError::InvalidFieldType { field, .. }) if field == "php.extensions"
     ));
+    assert!(matches!(
+        ProjectConfig::parse("php:\n  unknown: redis\n"),
+        Err(ConfigError::UnknownPhpKey { key }) if key == "unknown"
+    ));
 
     Ok(())
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/config/tests/project_config.rs` around lines 127 - 143, Add an
assertion within the project_config_rejects_invalid_php_extensions_shape test
function to cover the UnknownPhpKey error path. Insert a new assert statement
that tests a scenario with an unknown or misspelled key under the php
configuration section (for example, a typo like "extensios" instead of
"extensions"), and verify it matches the ConfigError variant that handles
unknown php keys. Follow the same assertion pattern as the existing test cases
in the function.
crates/daemon/tests/gateway_reconciliation.rs (1)

1697-1706: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive php_track from runtime_key parameter instead of hardcoding.

The helper hardcodes php_track to "8.4" while accepting runtime_key as a parameter. This creates a mismatch between the function signature (which suggests flexibility) and implementation (which assumes the track is always 8.4). If this helper is called with a different track (e.g., "8.3") or a runtime key with extensions (e.g., "8.4+redis"), the php_track field would be incorrect.

♻️ Extract php_track from runtime_key
 fn php_worker_plan(runtime_key: &str) -> daemon::gateway::PhpWorkerRuntimePlan {
+    let php_track = if let Some((track, _)) = runtime_key.split_once('+') {
+        track
+    } else {
+        runtime_key
+    };
     daemon::gateway::PhpWorkerRuntimePlan {
-        php_track: "8.4".to_owned(),
+        php_track: php_track.to_owned(),
         runtime_key: runtime_key.to_owned(),
         loaded_modules: Vec::new(),
         port: RUNTIME_PORT_FALLBACK_START,
         projects: Vec::new(),
     }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/daemon/tests/gateway_reconciliation.rs` around lines 1697 - 1706, In
the php_worker_plan function, the php_track field is hardcoded to "8.4" instead
of being derived from the runtime_key parameter. Extract the PHP version from
the runtime_key string (which may contain extensions like "+redis" that need to
be stripped) and use that extracted version to set the php_track field instead
of the hardcoded value. This ensures the function correctly handles different
PHP versions passed via runtime_key and creates a proper relationship between
the parameter and the resulting field value.
crates/pv-release/tests/recipe_metadata.rs (1)

858-863: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use top-level imports for helper type names.

Line 858 uses fully-qualified type paths in the signature; prefer imported aliases for consistency with Rust style in this module.

♻️ Proposed refactor
-use camino_tempfile::tempdir;
+use camino_tempfile::{tempdir, Utf8TempDir};
+use camino::Utf8PathBuf;

-fn write_php_recipe(tempdir: &camino_tempfile::Utf8TempDir) -> Result<camino::Utf8PathBuf> {
+fn write_php_recipe(tempdir: &Utf8TempDir) -> Result<Utf8PathBuf> {

As per coding guidelines, "PREFER top-level imports over local imports or fully qualified names in Rust".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/pv-release/tests/recipe_metadata.rs` around lines 858 - 863, The
function write_php_recipe uses fully-qualified type paths in its signature
(camino_tempfile::Utf8TempDir and camino::Utf8PathBuf) instead of imported
aliases. Add top-level imports for Utf8TempDir from camino_tempfile and
Utf8PathBuf from camino at the beginning of the file, then update the function
signature to use these imported type names directly instead of their
fully-qualified paths for consistency with Rust style conventions.

Source: Coding guidelines

crates/pv-release/src/recipe.rs (1)

612-614: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Apply the same list invariants to php.default_extensions (and prevent overlap with optional extensions).

default_extensions currently skips duplicate/shape validation, and overlap with optional_extensions is not rejected. Both can leak ambiguous values into PV_BUILD_EXTENSIONS.

Suggested patch
 impl PhpSettings {
     fn from_raw(path: &Utf8Path, raw: RawPhpSettings) -> crate::Result<Self> {
         validate_deployment_target(path, &raw.deployment_target)?;
         validate_expected_extensions(path, &raw.expected_extensions)?;
+        validate_extension_list(path, "php.default_extensions", &raw.default_extensions)?;
         validate_build_extensions(path, &raw.default_extensions, &raw.expected_extensions)?;
         validate_extension_list(path, "php.optional_extensions", &raw.optional_extensions)?;
+        let default_extensions = raw
+            .default_extensions
+            .iter()
+            .map(String::as_str)
+            .collect::<BTreeSet<_>>();
+        if let Some(extension) = raw
+            .optional_extensions
+            .iter()
+            .find(|extension| default_extensions.contains(extension.as_str()))
+        {
+            return Err(invalid(
+                path,
+                format!(
+                    "php.optional_extensions must not contain default extension `{extension}`"
+                ),
+            ));
+        }
 
         Ok(Self {
             deployment_target: raw.deployment_target,
             default_extensions: raw.default_extensions,
             optional_extensions: raw.optional_extensions,
             expected_extensions: raw.expected_extensions,
         })
     }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/pv-release/src/recipe.rs` around lines 612 - 614, The
`default_extensions` field is missing the same list invariant validations
(duplicate/shape checks) that are applied to `optional_extensions` through the
`validate_extension_list` call. Add a `validate_extension_list` call for
`default_extensions` similar to the one for `optional_extensions`, and add
validation logic to prevent overlaps between `default_extensions` and
`optional_extensions` to ensure no ambiguous values leak into
PV_BUILD_EXTENSIONS. The validation should occur after the existing
`validate_build_extensions` call.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/resources/src/manifest.rs`:
- Around line 592-605: The validate_manifest_php_extension_path function
currently rejects absolute paths and parent directory references but allows
empty strings and current directory paths (.) which can create invalid extension
directives later. Add additional validation checks after the existing
path.is_absolute() and component checks to reject empty paths (check if
path_str.is_empty() or use equivalent) and current directory references (check
if path_str equals "."), returning an appropriate ResourcesError with a
descriptive reason for each rejection case.

In `@crates/resources/src/php_extensions.rs`:
- Around line 146-164: In the php_runtime_environment function, the code
currently only appends the overlay directory to PHP_INI_SCAN_DIR if that key
already exists in the environment dictionary returned from
php_track_environment. If PHP_INI_SCAN_DIR is missing, the overlay is silently
ignored and modules will not load. Instead of using the conditional if let Some
pattern with get_mut on the environment variable, ensure PHP_INI_SCAN_DIR always
receives the overlay path by either inserting it into the environment dictionary
if it doesn't exist, or returning an error to surface the misconfiguration. This
way the overlay created by ensure_php_runtime_overlay is always properly applied
when modules are present.

---

Outside diff comments:
In `@crates/pv-release/src/record.rs`:
- Around line 162-227: The php_extensions field in the ReleaseRecord::from_json
method is not being validated and is assigned directly from the raw data without
checking for invalid load_kind values, invalid paths, or duplicate extension
names. Add validation logic for php_extensions before the final Ok(Self { ... })
return statement in the from_json method, similar to the existing
validate_relative_file_list and validate_relative_path calls, to ensure each php
extension entry has a valid load_kind, valid path, and that there are no
duplicate extension names. This validation should return an appropriate error
using the invalid_release helper function if any validation fails.

---

Nitpick comments:
In `@crates/config/tests/project_config.rs`:
- Around line 127-143: Add an assertion within the
project_config_rejects_invalid_php_extensions_shape test function to cover the
UnknownPhpKey error path. Insert a new assert statement that tests a scenario
with an unknown or misspelled key under the php configuration section (for
example, a typo like "extensios" instead of "extensions"), and verify it matches
the ConfigError variant that handles unknown php keys. Follow the same assertion
pattern as the existing test cases in the function.

In `@crates/daemon/tests/gateway_reconciliation.rs`:
- Around line 1697-1706: In the php_worker_plan function, the php_track field is
hardcoded to "8.4" instead of being derived from the runtime_key parameter.
Extract the PHP version from the runtime_key string (which may contain
extensions like "+redis" that need to be stripped) and use that extracted
version to set the php_track field instead of the hardcoded value. This ensures
the function correctly handles different PHP versions passed via runtime_key and
creates a proper relationship between the parameter and the resulting field
value.

In `@crates/pv-release/src/recipe.rs`:
- Around line 612-614: The `default_extensions` field is missing the same list
invariant validations (duplicate/shape checks) that are applied to
`optional_extensions` through the `validate_extension_list` call. Add a
`validate_extension_list` call for `default_extensions` similar to the one for
`optional_extensions`, and add validation logic to prevent overlaps between
`default_extensions` and `optional_extensions` to ensure no ambiguous values
leak into PV_BUILD_EXTENSIONS. The validation should occur after the existing
`validate_build_extensions` call.

In `@crates/pv-release/tests/recipe_metadata.rs`:
- Around line 858-863: The function write_php_recipe uses fully-qualified type
paths in its signature (camino_tempfile::Utf8TempDir and camino::Utf8PathBuf)
instead of imported aliases. Add top-level imports for Utf8TempDir from
camino_tempfile and Utf8PathBuf from camino at the beginning of the file, then
update the function signature to use these imported type names directly instead
of their fully-qualified paths for consistency with Rust style conventions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f870450-f452-4a88-90de-c94f27e586ae

📥 Commits

Reviewing files that changed from the base of the PR and between 65fa466 and b8c733c.

⛔ Files ignored due to path filters (34)
  • crates/cli/tests/snapshots/doctor__doctor_fails_when_active_pf_redirects_are_missing.snap is excluded by !**/*.snap
  • crates/cli/tests/snapshots/doctor__doctor_fails_when_daemon_socket_is_stale.snap is excluded by !**/*.snap
  • crates/cli/tests/snapshots/doctor__doctor_fails_when_system_ca_trust_is_missing.snap is excluded by !**/*.snap
  • crates/cli/tests/snapshots/doctor__doctor_fails_when_system_resolver_is_missing.snap is excluded by !**/*.snap
  • crates/cli/tests/snapshots/doctor__doctor_fails_with_repair_commands.snap is excluded by !**/*.snap
  • crates/cli/tests/snapshots/doctor__doctor_passes_when_required_checks_pass.snap is excluded by !**/*.snap
  • crates/cli/tests/snapshots/doctor__doctor_warnings_do_not_fail.snap is excluded by !**/*.snap
  • crates/cli/tests/snapshots/php__php_use_latest_preserves_alias_in_config_and_records_resolved_track.snap is excluded by !**/*.snap
  • crates/cli/tests/snapshots/php__php_use_updates_project_config_state_and_reports_missing_daemon.snap is excluded by !**/*.snap
  • crates/cli/tests/snapshots/status__status_prefers_ignored_php_extension_over_other_project_env_warnings.snap is excluded by !**/*.snap
  • crates/cli/tests/snapshots/status__status_reports_warning_project_env_as_success.snap is excluded by !**/*.snap
  • crates/config/tests/snapshots/project_config__project_config_parses_strict_resource_env_shape.snap is excluded by !**/*.snap
  • crates/config/tests/snapshots/project_config__project_config_writer_creates_preferred_file_when_missing.snap is excluded by !**/*.snap
  • crates/config/tests/snapshots/project_config__project_config_writer_preserves_existing_config_file_mode.snap is excluded by !**/*.snap
  • crates/config/tests/snapshots/project_config__project_config_writer_preserves_php_extensions_when_updating_track.snap is excluded by !**/*.snap
  • crates/config/tests/snapshots/project_config__project_config_writer_updates_php_in_alternate_file.snap is excluded by !**/*.snap
  • crates/config/tests/snapshots/project_config__project_config_writer_updates_php_in_discovered_file.snap is excluded by !**/*.snap
  • crates/config/tests/snapshots/project_config__project_config_writer_updates_symlinked_config_target.snap is excluded by !**/*.snap
  • crates/daemon/tests/snapshots/gateway_reconciliation__runtime_plan_defaults_document_root_to_project_root_without_public_directory.snap is excluded by !**/*.snap
  • crates/daemon/tests/snapshots/gateway_reconciliation__runtime_plan_defaults_document_root_to_public_directory_without_config.snap is excluded by !**/*.snap
  • crates/daemon/tests/snapshots/gateway_reconciliation__runtime_plan_groups_linked_projects_by_php_track.snap is excluded by !**/*.snap
  • crates/daemon/tests/snapshots/gateway_reconciliation__runtime_plan_resolves_latest_php_track_from_cached_manifest.snap is excluded by !**/*.snap
  • crates/daemon/tests/snapshots/gateway_reconciliation__runtime_plan_uses_project_root_not_original_or_config_path.snap is excluded by !**/*.snap
  • crates/pv-release/tests/snapshots/recipe_metadata__print_recipe_env_frankenphp.snap is excluded by !**/*.snap
  • crates/pv-release/tests/snapshots/recipe_metadata__print_recipe_env_php.snap is excluded by !**/*.snap
  • crates/pv-release/tests/snapshots/record_writer__release_record_writer_serializes_php_extension_metadata.snap is excluded by !**/*.snap
  • crates/pv-release/tests/snapshots/release_records__release_records_parse_identity_and_required_fields.snap is excluded by !**/*.snap
  • crates/pv-release/tests/snapshots/release_records__release_records_parse_php_extension_metadata.snap is excluded by !**/*.snap
  • crates/resources/tests/snapshots/artifact_cache_download__manifest_cache_fetches_latest_and_falls_back_to_cached_manifest-2.snap is excluded by !**/*.snap
  • crates/resources/tests/snapshots/manifest_foundation__manifest_parses_registry_backed_resources_tracks_and_artifacts.snap is excluded by !**/*.snap
  • crates/state/tests/snapshots/state_foundation__database_runs_migrations_and_exposes_core_schema.snap is excluded by !**/*.snap
  • crates/state/tests/snapshots/state_foundation__linked_projects_preserve_ids_and_refresh_hostnames.snap is excluded by !**/*.snap
  • crates/state/tests/snapshots/state_foundation__migrated_project_resource_state_round_trips_through_public_apis.snap is excluded by !**/*.snap
  • it/snapshots/cli__project_list_reports_ignored_php_extensions.snap is excluded by !**/*.snap
📒 Files selected for processing (51)
  • .superpowers/sdd/task-2-report.md
  • DESIGN.md
  • crates/cli/src/commands/php.rs
  • crates/cli/src/commands/project.rs
  • crates/cli/src/commands/status.rs
  • crates/cli/tests/composer.rs
  • crates/cli/tests/php.rs
  • crates/cli/tests/project_env.rs
  • crates/cli/tests/status.rs
  • crates/config/src/error.rs
  • crates/config/src/lib.rs
  • crates/config/src/model.rs
  • crates/config/src/parser.rs
  • crates/config/src/writer.rs
  • crates/config/tests/project_config.rs
  • crates/daemon/src/gateway.rs
  • crates/daemon/src/project_env.rs
  • crates/daemon/tests/gateway_reconciliation.rs
  • crates/daemon/tests/project_env_reconciliation.rs
  • crates/daemon/tests/real_artifact_gateway_e2e.rs
  • crates/pv-release/src/cli.rs
  • crates/pv-release/src/manifest.rs
  • crates/pv-release/src/recipe.rs
  • crates/pv-release/src/record.rs
  • crates/pv-release/src/record_writer.rs
  • crates/pv-release/tests/manifest_generation.rs
  • crates/pv-release/tests/recipe_metadata.rs
  • crates/pv-release/tests/record_writer.rs
  • crates/pv-release/tests/release_records.rs
  • crates/pv-release/tests/smoke.rs
  • crates/resources/src/lib.rs
  • crates/resources/src/manifest.rs
  • crates/resources/src/php_extensions.rs
  • crates/resources/tests/manifest_foundation.rs
  • crates/resources/tests/php_extensions.rs
  • crates/state/src/database.rs
  • crates/state/src/lib.rs
  • crates/state/src/migrations.rs
  • crates/state/src/paths.rs
  • crates/state/src/sql/008_project_php_runtime_extensions.sql
  • crates/state/tests/state_foundation.rs
  • docs/adr/0006-fixed-extension-php-artifacts.md
  • docs/adr/0014-project-level-php-extension-opt-ins.md
  • docs/superpowers/plans/2026-06-22-php-extension-opt-ins.md
  • docs/superpowers/specs/2026-06-22-php-extension-opt-ins-design.md
  • docs/user/README.md
  • it/cli.rs
  • release/artifacts/README.md
  • release/artifacts/recipes/php/build.sh
  • release/artifacts/recipes/php/smoke.sh
  • release/artifacts/recipes/php/tracks.toml

Comment thread crates/resources/src/manifest.rs
Comment thread crates/resources/src/php_extensions.rs

@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: 3894539c07

ℹ️ 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 thread crates/cli/src/commands/php.rs Outdated
Comment thread crates/pv-release/src/record.rs

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

The new shim fallback fixes fresh-link extension loading, but it also keeps stale extensions active after they are removed from project config.

Reviewed changes — This incremental review covered the new 3894539 fixes on top of the prior Pullfrog review.

  • Resolved fresh-link shim fallbackpv php and Composer shims now resolve extension requests from project config and the installed PHP artifact when persisted runtime state is empty or stale.
  • Separated extension worker observed-state identity — extension-specific FrankenPHP workers now use a PhpRuntimeWorker observed-state subject while legacy track-only workers keep the previous PhpWorker subject.
  • Tightened PHP extension metadata validation — release records, manifests, recipe metadata, artifact metadata, and tests now reject invalid paths, duplicate names, invalid load kinds, and missing module files.
  • Expanded diagnostics and regression coverage — ignored extension messages now preserve multiple warnings, extension-only configs honor the global PHP default, and related snapshots/tests were refreshed.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using GPT𝕏

Comment thread crates/cli/src/commands/php.rs Outdated

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
crates/daemon/tests/daemon_foundation.rs (1)

946-951: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

None blocker path can make this fallback test nondeterministic.

When bind_preferred_dns_port_pair() returns None (Line 946), this test no longer holds the preferred port itself. If the external holder releases the port before startup, the assertions at Lines 950-951 can fail intermittently.

Suggested guard to avoid flaky assertions when precondition is not held locally
-    let _preferred_dns_port_blockers = bind_preferred_dns_port_pair().await?;
+    let preferred_dns_port_blockers = bind_preferred_dns_port_pair().await?;
     let daemon = daemon::RunningDaemon::start(paths.clone()).await?;
     let port = dns_port(&paths)?;

+    if preferred_dns_port_blockers.is_none() && port == DNS_PREFERRED_PORT {
+        daemon.shutdown().await?;
+        return Ok(());
+    }
+
     assert_ne!(port, DNS_PREFERRED_PORT);
     assert!((RUNTIME_PORT_FALLBACK_START..=RUNTIME_PORT_FALLBACK_END).contains(&port));

Also applies to: 1193-1207

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/daemon/tests/daemon_foundation.rs` around lines 946 - 951, The test
relies on bind_preferred_dns_port_pair() to hold the preferred DNS port, but
when this function returns None, the port is no longer protected locally,
allowing external processes to claim it and making the subsequent assertions
nondeterministic. Instead of silently accepting a None return value by assigning
to _preferred_dns_port_blockers, add a guard that ensures
bind_preferred_dns_port_pair() successfully returns a valid blocker (not None)
before proceeding with the daemon startup and port assertions. Apply this same
fix to all instances of this pattern, including the related test cases around
lines 1193-1207 that have the same vulnerability.
crates/state/src/database.rs (1)

2067-2069: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update stale error wording to php runtime key.

Line 2068 still says “php track,” but this branch now validates/stores php_runtime_key. Keeping the message aligned avoids misleading diagnostics.

Suggested wording fix
-                reason: "php worker ports must use owner id `php`, include a php track, and use an empty owner port",
+                reason: "php worker ports must use owner id `php`, include a php runtime key, and use an empty owner port",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/state/src/database.rs` around lines 2067 - 2069, The error message in
the reason field of the validation check for php worker ports is outdated and
references "php track," but the validation logic now uses php_runtime_key
instead. Update the reason string to replace the mention of "php track" with
"php runtime key" to keep the error message consistent with the actual
validation being performed and avoid misleading diagnostics.
🧹 Nitpick comments (3)
crates/daemon/tests/project_env_reconciliation.rs (1)

158-158: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer a top-level import for write_sensitive_file instead of fully qualified calls.

Using state::fs::write_sensitive_file(...) in these new lines goes against the project Rust import preference. Import it once at module scope and call it directly.

Suggested refactor
-    state::fs::write_sensitive_file(&release.join("lib/php/extensions/redis.so"), "")?;
+    write_sensitive_file(&release.join("lib/php/extensions/redis.so"), "")?;
...
-    state::fs::write_sensitive_file(&release.join("lib/php/extensions/redis.so"), "")?;
+    write_sensitive_file(&release.join("lib/php/extensions/redis.so"), "")?;

As per coding guidelines, "PREFER top-level imports over local imports or fully qualified names in Rust".

Also applies to: 203-203

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/daemon/tests/project_env_reconciliation.rs` at line 158, Add a
module-level import statement for write_sensitive_file from state::fs at the top
of the project_env_reconciliation.rs file with other imports. Then replace all
instances of the fully qualified call state::fs::write_sensitive_file(...) with
just write_sensitive_file(...) throughout the file, including the calls at lines
158 and 203 as mentioned in the comment.

Source: Coding guidelines

crates/pv-release/tests/recipe_metadata.rs (1)

474-479: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use single-replacement fixture mutation for strict-case isolation.

Using String::replace here can mutate multiple occurrences and blur which rule each case is exercising. Prefer replacen(..., 1) per variant to keep each invalid input single-cause.

Also applies to: 504-515

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/pv-release/tests/recipe_metadata.rs` around lines 474 - 479, The test
fixtures use String::replace which can mutate multiple occurrences of a pattern,
making it unclear which specific rule each test case exercises. Replace all
occurrences of the replace method calls (in duplicate_default_extension,
invalid_default_extension, overlapping_optional_extension and the similar
mutations mentioned at lines 504-515) with replacen(..., 1) to ensure each test
case mutates only the first occurrence of the pattern, maintaining strict
single-cause isolation for each test variant.
crates/resources/tests/php_extensions.rs (1)

114-148: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make the duplicate-name case deterministic.

The duplicate-name case can fail early as “missing module,” so it doesn’t reliably validate duplicate-name rejection. Isolate fixtures per case (or seed required .so files only for that case) so each case asserts its intended failure mode.

Suggested test-shape adjustment
-    let artifact = tempdir.path().join("php");
@@
-    for (name, metadata) in cases {
+    for (name, metadata) in cases {
+        let artifact = tempdir.path().join(format!("php-{name}"));
         fs::write_sensitive_file(&artifact.join("share/pv/php-extensions.json"), metadata)?;
+        if name == "duplicate-name" {
+            fs::write_sensitive_file(&artifact.join("lib/php/extensions/redis.so"), "")?;
+            fs::write_sensitive_file(&artifact.join("lib/php/extensions/redis-copy.so"), "")?;
+        }
         let result = resolve_php_extension_request(&artifact, &["redis".into()]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/resources/tests/php_extensions.rs` around lines 114 - 148, The test
function
php_extension_metadata_rejects_invalid_paths_duplicates_and_missing_modules runs
all test cases with the same fixture without creating the actual .so files,
causing the duplicate-name case to fail with a missing-module error instead of
validating duplicate-name rejection. Restructure the test to create the
appropriate fixture setup for each case in the loop, ensuring that the
duplicate-name case has the required .so files actually created in the artifact
directory so it properly validates duplicate-name rejection, while the
missing-module case can continue without those files.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@crates/daemon/tests/daemon_foundation.rs`:
- Around line 946-951: The test relies on bind_preferred_dns_port_pair() to hold
the preferred DNS port, but when this function returns None, the port is no
longer protected locally, allowing external processes to claim it and making the
subsequent assertions nondeterministic. Instead of silently accepting a None
return value by assigning to _preferred_dns_port_blockers, add a guard that
ensures bind_preferred_dns_port_pair() successfully returns a valid blocker (not
None) before proceeding with the daemon startup and port assertions. Apply this
same fix to all instances of this pattern, including the related test cases
around lines 1193-1207 that have the same vulnerability.

In `@crates/state/src/database.rs`:
- Around line 2067-2069: The error message in the reason field of the validation
check for php worker ports is outdated and references "php track," but the
validation logic now uses php_runtime_key instead. Update the reason string to
replace the mention of "php track" with "php runtime key" to keep the error
message consistent with the actual validation being performed and avoid
misleading diagnostics.

---

Nitpick comments:
In `@crates/daemon/tests/project_env_reconciliation.rs`:
- Line 158: Add a module-level import statement for write_sensitive_file from
state::fs at the top of the project_env_reconciliation.rs file with other
imports. Then replace all instances of the fully qualified call
state::fs::write_sensitive_file(...) with just write_sensitive_file(...)
throughout the file, including the calls at lines 158 and 203 as mentioned in
the comment.

In `@crates/pv-release/tests/recipe_metadata.rs`:
- Around line 474-479: The test fixtures use String::replace which can mutate
multiple occurrences of a pattern, making it unclear which specific rule each
test case exercises. Replace all occurrences of the replace method calls (in
duplicate_default_extension, invalid_default_extension,
overlapping_optional_extension and the similar mutations mentioned at lines
504-515) with replacen(..., 1) to ensure each test case mutates only the first
occurrence of the pattern, maintaining strict single-cause isolation for each
test variant.

In `@crates/resources/tests/php_extensions.rs`:
- Around line 114-148: The test function
php_extension_metadata_rejects_invalid_paths_duplicates_and_missing_modules runs
all test cases with the same fixture without creating the actual .so files,
causing the duplicate-name case to fail with a missing-module error instead of
validating duplicate-name rejection. Restructure the test to create the
appropriate fixture setup for each case in the loop, ensuring that the
duplicate-name case has the required .so files actually created in the artifact
directory so it properly validates duplicate-name rejection, while the
missing-module case can continue without those files.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 60bee46b-2fff-41fc-b74c-c84286d85f3c

📥 Commits

Reviewing files that changed from the base of the PR and between b8c733c and 3894539.

⛔ Files ignored due to path filters (7)
  • crates/cli/tests/snapshots/status__status_prefers_ignored_php_extension_over_other_project_env_warnings.snap is excluded by !**/*.snap
  • crates/pv-release/tests/snapshots/recipe_metadata__recipe_metadata_rejects_strict_php_metadata.snap is excluded by !**/*.snap
  • crates/pv-release/tests/snapshots/release_records__release_records_reject_invalid_php_extension_metadata.snap is excluded by !**/*.snap
  • crates/resources/tests/snapshots/manifest_foundation__manifest_rejects_invalid_php_extension_metadata.snap is excluded by !**/*.snap
  • crates/state/tests/snapshots/state_foundation__php_worker_port_allocator_persists_one_port_per_track.snap is excluded by !**/*.snap
  • crates/state/tests/snapshots/state_foundation__runtime_observed_state_round_trips_through_observed_states.snap is excluded by !**/*.snap
  • it/snapshots/cli__project_list_reports_ignored_php_extensions.snap is excluded by !**/*.snap
📒 Files selected for processing (24)
  • crates/cli/src/commands/doctor.rs
  • crates/cli/src/commands/logs.rs
  • crates/cli/src/commands/php.rs
  • crates/cli/src/commands/project.rs
  • crates/cli/src/commands/status.rs
  • crates/cli/tests/composer.rs
  • crates/cli/tests/php.rs
  • crates/cli/tests/status.rs
  • crates/daemon/src/gateway.rs
  • crates/daemon/src/project_env.rs
  • crates/daemon/tests/daemon_foundation.rs
  • crates/daemon/tests/gateway_reconciliation.rs
  • crates/daemon/tests/project_env_reconciliation.rs
  • crates/pv-release/src/recipe.rs
  • crates/pv-release/src/record.rs
  • crates/pv-release/tests/recipe_metadata.rs
  • crates/pv-release/tests/release_records.rs
  • crates/resources/src/manifest.rs
  • crates/resources/src/php_extensions.rs
  • crates/resources/tests/manifest_foundation.rs
  • crates/resources/tests/php_extensions.rs
  • crates/state/src/database.rs
  • crates/state/tests/state_foundation.rs
  • it/cli.rs
🚧 Files skipped from review as they are similar to previous changes (12)
  • crates/cli/tests/status.rs
  • it/cli.rs
  • crates/pv-release/src/recipe.rs
  • crates/cli/tests/php.rs
  • crates/daemon/tests/gateway_reconciliation.rs
  • crates/cli/tests/composer.rs
  • crates/state/tests/state_foundation.rs
  • crates/pv-release/src/record.rs
  • crates/cli/src/commands/php.rs
  • crates/daemon/src/project_env.rs
  • crates/resources/src/manifest.rs
  • crates/daemon/src/gateway.rs

@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: 1cc5698070

ℹ️ 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 thread release/artifacts/recipes/php/tracks.toml
Comment thread crates/cli/src/commands/php.rs Outdated

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
crates/pv-release/tests/archive_validation.rs (1)

509-515: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make the release-record fixture builder structural, not string-fragile.

replacen on formatted JSON can silently stop injecting php_extensions if whitespace/key formatting changes. Prefer constructing/modifying JSON via serde_json::Value so test intent stays stable across formatting refactors.

Suggested refactor
 fn release_record_json_with_php_extensions(sha256: &str, size: u64) -> String {
-    release_record_json(sha256, size).replacen(
-        "  \"provenance\": {",
-        "  \"php_extensions\": [\n    {\n      \"name\": \"redis\",\n      \"load_kind\": \"extension\",\n      \"path\": \"lib/php/extensions/redis.so\"\n    }\n  ],\n  \"provenance\": {",
-        1,
-    )
+    let mut json: serde_json::Value =
+        serde_json::from_str(&release_record_json(sha256, size)).expect("valid fixture JSON");
+    json["php_extensions"] = serde_json::json!([
+        {
+            "name": "redis",
+            "load_kind": "extension",
+            "path": "lib/php/extensions/redis.so"
+        }
+    ]);
+    serde_json::to_string_pretty(&json).expect("serializable fixture JSON")
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/pv-release/tests/archive_validation.rs` around lines 509 - 515, The
release_record_json_with_php_extensions function uses string replacen to inject
php_extensions into JSON, which is fragile and breaks if whitespace or
formatting changes. Instead, parse the result of release_record_json into a
serde_json::Value object, structurally add the php_extensions field as an array
with the redis extension object, and then serialize it back to a string. This
makes the test robust to JSON formatting changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/pv-release/tests/archive_validation.rs`:
- Around line 509-515: The release_record_json_with_php_extensions function uses
string replacen to inject php_extensions into JSON, which is fragile and breaks
if whitespace or formatting changes. Instead, parse the result of
release_record_json into a serde_json::Value object, structurally add the
php_extensions field as an array with the redis extension object, and then
serialize it back to a string. This makes the test robust to JSON formatting
changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6e23405b-193f-4160-8c6a-149eb2fb208a

📥 Commits

Reviewing files that changed from the base of the PR and between 3894539 and 1cc5698.

⛔ Files ignored due to path filters (1)
  • crates/pv-release/tests/snapshots/archive_validation__archive_validation_rejects_advertised_php_extension_modules_that_are_not_files.snap is excluded by !**/*.snap
📒 Files selected for processing (12)
  • crates/cli/src/commands/php.rs
  • crates/cli/tests/composer.rs
  • crates/cli/tests/php.rs
  • crates/daemon/tests/daemon_foundation.rs
  • crates/daemon/tests/project_env_reconciliation.rs
  • crates/pv-release/src/archive.rs
  • crates/pv-release/src/record.rs
  • crates/pv-release/tests/archive_validation.rs
  • crates/pv-release/tests/recipe_metadata.rs
  • crates/resources/src/php_extensions.rs
  • crates/resources/tests/php_extensions.rs
  • crates/state/src/database.rs
🚧 Files skipped from review as they are similar to previous changes (8)
  • crates/cli/tests/composer.rs
  • crates/daemon/tests/daemon_foundation.rs
  • crates/pv-release/tests/recipe_metadata.rs
  • crates/cli/src/commands/php.rs
  • crates/resources/src/php_extensions.rs
  • crates/daemon/tests/project_env_reconciliation.rs
  • crates/pv-release/src/record.rs
  • crates/state/src/database.rs

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

The latest shim fix still misses php.version: latest in one stale-runtime path, so the current config can execute the wrong PHP track until reconciliation runs.

Reviewed changes — This incremental review covered the new shim track-resolution fix added after the prior Pullfrog review.

  • Resolved concrete config tracks before extension recomputationpv php now detects when persisted runtime state is on a different concrete track than the current pv.yml and resolves requested extensions against the config-selected PHP track.
  • Covered stale persisted runtime switchingcrates/cli/tests/php.rs now exercises recomputing extensions against a newer configured track when persisted runtime state still points at an older track.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using GPT𝕏

Comment thread crates/cli/src/commands/php.rs Outdated

@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: cbb5fe0e2d

ℹ️ 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 thread crates/cli/src/commands/php.rs

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ No new issues found in the latest commit; the prior php.version: latest stale-track finding still appears unresolved.

Reviewed changes — This incremental review covered the new shim fallback change after the prior Pullfrog review at cbb5fe0.

  • Resolved extension-only default-track refreshpv php now resolves the current global/default PHP track for extension-only project configs before reusing stale persisted extension runtime state.
  • Covered extension-only stale-runtime switchingcrates/cli/tests/php.rs now exercises a project with php.extensions and no explicit version moving from stale persisted 8.3+redis state to the current global 8.4+redis runtime.

Pullfrog  | Fix it ➔View workflow run | Using GPT𝕏

@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: 7d10838910

ℹ️ 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 thread crates/daemon/src/project_env.rs

@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: 44cc1f1e9f

ℹ️ 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 thread crates/cli/src/commands/php.rs Outdated

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes — This incremental review covered the new system reconciliation refresh added after the prior Pullfrog review at 7d10838.

  • Refreshed PHP extension state after resource installs — System update and reconciliation now run project reconciliation, resource reconciliation, then project reconciliation again so projects that request PHP extensions can record PHP/frankenphp requirements, install missing artifacts, and then refresh loaded/ignored extension state from the installed PHP artifact before gateway reconciliation.
  • Covered missing-install extension refreshcrates/daemon/src/jobs.rs now includes a regression test that seeds cached PHP/frankenphp artifacts, links a project requesting redis, runs the combined reconciliation helper, and verifies the final persisted PHP runtime loads redis instead of leaving it ignored.

Pullfrog  | View workflow run | Using GPT𝕏

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
crates/daemon/src/jobs.rs (1)

1680-1723: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use top-level imports for the fixture helpers.

Line 1681 and Line 1701 onward use fully qualified std::... paths in the new helpers. Import std::{fs, process} at the test-module top and use fs::... / process::Command::new(...) inside the helpers.

As per coding guidelines, "PREFER top-level imports over local imports or fully qualified names in Rust".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/daemon/src/jobs.rs` around lines 1680 - 1723, The fixture helper
functions run_fixture_command, copy_file, file_size, and set_executable use
fully qualified std paths like std::process::Command and std::fs:: throughout.
Add a top-level import statement `use std::{fs, process};` at the test module
level, then replace all fully qualified paths: change std::process::Command to
process::Command in run_fixture_command, and change all std::fs:: calls to fs::
in the copy_file, file_size, and set_executable functions.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/daemon/src/jobs.rs`:
- Around line 1652-1668: The hard-coded absolute paths `/usr/bin/tar` and
`/usr/bin/shasum` in the run_fixture_command calls make the fixture tools
non-portable across different Linux systems. Replace the absolute path
`/usr/bin/tar` with just the command name "tar" to allow PATH resolution. For
the sha256_file method that calls shasum, replace the hard-coded
`/usr/bin/shasum` path with logic that attempts to run "shasum" first, and if
that fails, falls back to running "sha256sum" as an alternative, ensuring the
test works across different Linux runners where either tool might be available.

---

Nitpick comments:
In `@crates/daemon/src/jobs.rs`:
- Around line 1680-1723: The fixture helper functions run_fixture_command,
copy_file, file_size, and set_executable use fully qualified std paths like
std::process::Command and std::fs:: throughout. Add a top-level import statement
`use std::{fs, process};` at the test module level, then replace all fully
qualified paths: change std::process::Command to process::Command in
run_fixture_command, and change all std::fs:: calls to fs:: in the copy_file,
file_size, and set_executable functions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: aee30e7f-7b3a-4394-9af3-bac677cff241

📥 Commits

Reviewing files that changed from the base of the PR and between 1cc5698 and 44cc1f1.

📒 Files selected for processing (4)
  • crates/cli/src/commands/php.rs
  • crates/cli/tests/php.rs
  • crates/daemon/src/jobs.rs
  • crates/daemon/src/project_env.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/cli/src/commands/php.rs
  • crates/daemon/src/project_env.rs

Comment thread crates/daemon/src/jobs.rs

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes — This incremental review covered the two commits after the prior Pullfrog review at 44cc1f1, focused on shim behavior when project config is invalid and daemon test fixture portability.

  • Reused persisted shim runtime for invalid project configpv php and Composer shims now fall back to the persisted project PHP runtime when pv.yml cannot be parsed, while still validating the installed PHP runtime and persisted extension metadata before execution.
  • Made archive fixture tooling portable — daemon reconciliation tests now call tar through PATH and fall back from shasum to sha256sum when computing cached artifact checksums.

Pullfrog  | View workflow run | Using GPT𝕏

@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: a28178a178

ℹ️ 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 thread crates/pv-release/src/record.rs

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes — This incremental review covered the new release archive metadata consistency check added after the prior Pullfrog review at a28178a.

  • Required artifact PHP extension metadata to match release recordspv-release now rejects PHP artifacts whose advertised php_extensions lack share/pv/php-extensions.json, contain invalid artifact metadata JSON, or differ from the release-record extension catalog.
  • Covered missing and mismatched metadata failurescrates/pv-release/tests/archive_validation.rs now verifies both missing artifact metadata and mismatched catalog entries fail archive validation with clear diagnostics.

Pullfrog  | View workflow run | Using GPT𝕏

@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: ef6f9bb7ce

ℹ️ 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 thread crates/cli/src/commands/php.rs Outdated
if let Some(php) = php {
let track = match config_track {
Some(track) => track,
None => resolve_project_config_php_track_for_shim(paths, database, php)?,

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 stored track for latest extension recomputes

When a linked Project has php.version: latest with a persisted concrete runtime, this None arm is reached as soon as the extension list changes, and it resolves latest against the current cached manifest instead of the stored project track. If the manifest default has moved from 8.4 to 8.5, editing only extensions makes pv php/Composer try 8.5+... while gateway reconciliation still applies the explicit latest selector to project.desired_php_track and serves 8.4+..., so CLI and browser runtimes diverge until the config is changed again.

Useful? React with 👍 / 👎.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

The new latest shim test still preserves the stale-track behavior, so pv php and Composer can execute the wrong PHP version until reconciliation updates persisted state.

Reviewed changes — This incremental review covered the new f4bb620 fixes after the prior Pullfrog review at ef6f9bb, focused on shim latest handling, reconciliation refreshes, and PHP extension metadata constraints.

  • Exercised latest shim fallback with stale persisted statecrates/cli/tests/php.rs now adds a php.version: latest scenario with persisted 8.4 state and a manifest default of 8.5.
  • Refreshed project extensions after missing runtime installscrates/daemon/src/jobs.rs now reruns project-env reconciliation after installing missing PHP/frankenphp runtime resources for extension-requesting projects.
  • Scoped PHP extension metadata to PHP runtimes — release records and resource manifests now reject php_extensions on non-php/frankenphp artifacts.
  • Preserved artifact metadata order for loaded extensionsresources::php_extensions now resolves loaded extension modules in artifact metadata order rather than sorted request order.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using GPT𝕏

.unwrap_or_default();
let config_track = if let Some(php) = php {
match php.version_selector() {
Some(selector) if selector != "latest" && selector != track => Some(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ php.version: latest still takes the _ => None branch here, so current_track falls back to the persisted runtime track instead of the manifest-resolved default. The new regression test sets the manifest default to 8.5 but asserts execution of the persisted 8.4 release, which locks in the stale-track behavior this path was meant to prevent.

Technical details
# `latest` shims still reuse stale persisted tracks

## Affected sites
- `crates/cli/src/commands/php.rs:313``latest` never calls `resolve_project_config_php_track_for_shim`, so `config_track` remains `None`.
- `crates/cli/src/commands/php.rs:332``current_track` therefore stays at the persisted `project.php_runtime.track`.
- `crates/cli/tests/php.rs:516` — the new `latest` test currently expects `old_release` even though the cached manifest default is `8.5`.

## Required outcome
- A current `php.version: latest` selector must be resolved through the cached manifest before choosing the shim runtime, regardless of stale persisted project runtime state.
- The regression test should fail on stale `8.4` execution and pass only when the shim uses the manifest-selected `8.5` runtime and `8.5+redis` overlay.

## Suggested approach
Resolve `Some(TrackSelector::Latest)` before the empty-extension and requested-extension reuse paths, then compare the resolved track with the persisted track the same way the concrete-track/default-track branches do.

@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: f4bb62089a

ℹ️ 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".

.join("php-runtimes")
.join(runtime_key)
.join("conf.d");
fs::ensure_user_dir(&conf_dir).map_err(resources_error_from_state)?;

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 Clear stale PHP runtime overlay files

When the artifact metadata order changes for the same loaded extension set, the sorted runtime_key stays the same but this directory is only ensured, not cleared. For example, a runtime previously writing 10-redis.ini/20-xdebug.ini and later seeing metadata ordered as xdebug, redis will add 10-xdebug.ini/20-redis.ini while leaving the old files behind, so pv php and FrankenPHP scan duplicate directives and emit double-load warnings (or worse for zend extensions) until the user manually deletes PV config. Recreate or prune conf_dir before writing the current overlay.

Useful? React with 👍 / 👎.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ No new issues found in the latest commit; the prior php.version: latest stale-track finding remains open.

Reviewed changes — This incremental review covered 1edef98, which makes sockets part of the default PHP artifact extension set.

  • Built sockets by defaultsockets is now included in REQUIRED_PHP_EXTENSIONS, recipe default_extensions, expected extension lists, recipe-env snapshots, and the design text so PHP artifacts smoke-test it as a loaded default extension.

Pullfrog  | Fix it ➔View workflow run | Using GPT𝕏

@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: 1edef9842e

ℹ️ 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 +329 to +333
} else {
None
};
let current_track = config_track.as_deref().unwrap_or(&track).to_string();
if requested_extensions.is_empty() {

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 Re-resolve configless shims through global defaults

For a linked Project with no php key but an old persisted project.php_runtime.track, this branch leaves config_track as None, so the empty-extension return immediately below keeps using the stale persisted track. After pv php:use --global 8.4 only queues reconciliation (or when the daemon is unavailable), pv php/Composer inside that configless Project continue execing the old track even though configless Projects are supposed to follow the current global/default PHP track. Resolve through the global/default flow when php is None instead of falling back to track here.

Useful? React with 👍 / 👎.

Comment on lines +1517 to +1518
artifact_root,
&worker.loaded_modules,

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 FrankenPHP extension catalog before worker load

When the selected PHP and FrankenPHP artifacts for a track do not advertise the same optional modules, worker.loaded_modules has been resolved from the standalone php artifact, but this call writes extension= directives using the FrankenPHP artifact_root. The release tooling validates each record/archive independently, so a mismatched FrankenPHP artifact can still be published and installed; then the CLI loads the extension from the PHP artifact while the browser worker points at a missing .so under the FrankenPHP artifact and runs without it. Validate or re-resolve the loaded catalog against the FrankenPHP artifact before building the worker environment.

Useful? React with 👍 / 👎.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ No new issues found in the latest commit; the prior php.version: latest stale-track finding remains open.

Reviewed changes — This incremental review covered b2db34c, focused on cleanup of generated PHP runtime overlays after extension metadata changes.

  • Pruned stale runtime overlay filesresources::ensure_php_runtime_overlay now removes the existing runtime conf.d directory before rewriting extension ini files, with resource-level coverage for reordered extension metadata no longer leaving obsolete numbered ini files.

Pullfrog  | Fix it ➔View workflow run | Using GPT𝕏

@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: b2db34cbc4

ℹ️ 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".

frankenphp_binary="$artifact_root/bin/frankenphp"
"$frankenphp_binary" php-cli -r 'printf("PHP %s\n", PHP_VERSION);' | grep -F "PHP $expected_version" >/dev/null
check_extensions "$frankenphp_binary" php-cli -r "foreach (get_loaded_extensions() as \$extension) { echo \$extension, PHP_EOL; }"
check_extensions "$expected_extensions" "$frankenphp_binary" php-cli -r "foreach (get_loaded_extensions() as \$extension) { echo \$extension, PHP_EOL; }"

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 optional modules in default smoke

When StaticPHP ends up loading an optional module by default (for example because the shared-build flag does not keep redis or xdebug disabled), this still passes because check_extensions only verifies that every expected module is present and ignores extra loaded modules. Since expected_extensions now intentionally omits the optional catalog, the release smoke can publish artifacts where Projects without php.extensions still get optional modules loaded by default; make the default smoke fail on any advertised optional module before running the opt-in overlay check.

Useful? React with 👍 / 👎.

Comment on lines +84 to +86
PHP_INI_SCAN_DIR=$scan_dir
export PHP_INI_SCAN_DIR
check_extensions "$optional_extensions" "$@"

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 Smoke each optional extension in isolation

When an optional module only loads because another optional module is also enabled, this single scan directory enables the entire advertised catalog and the one check_extensions call can pass, but PV later writes overlays for exactly the Project's requested subset. A Project requesting only that module can then fail at runtime even though the artifact was published; generate a fresh scan dir and verify each metadata entry independently.

Useful? React with 👍 / 👎.

@munezaclovis munezaclovis merged commit 407c84a into main Jun 25, 2026
4 checks passed
@munezaclovis munezaclovis deleted the php-extension-opt-ins branch June 25, 2026 00:52
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