From 043c555c45f8b261dca2eff76398f54eb711050d Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 3 Sep 2026 09:36:14 +0000 Subject: [PATCH 1/3] docs(agents): record remote-session install and test gate workarounds Claude Code on the web sessions cannot install phpstan/phpstan: the session proxy answers 403 for every GitHub zipball outside the attached repo, and phpstan/phpstan publishes no `source` to fall back to (dist only). Document the diagnosis and a verified recipe: shallow-clone the release tag over git (which the proxy allows), `git archive` it into composer's dist cache under the key composer computes, then `composer install --prefer-source`. Also record the second blocker hit in the same environment: the image's 99-agent.ini enables the tracing JIT for CLI, and under PHP 8.5.10 it miscompiles the goaop/dissect LALR parser loop, failing all 32 Functional tests. `PHP_INI_SCAN_DIR=":"` with `opcache.jit=0` reaches the spawned console processes; `php -d` does not. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01X2syVMk85L4eCjMRbdAjJQ --- AGENTS.md | 1 + tests/AGENTS.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 193339f1..1dc5d965 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,6 +5,7 @@ AOP via source transformation at load time (stream filter, no PECL, no eval). ## Agent gate - PHP 8.4+ required. If PHP 8.3 or less → STOP, report can't run tests/phpstan. - Gate: `composer analyze` before commit (phpstan level 10). +- Remote session (Claude Code on the web): `composer install` dies on phpstan/phpstan and Functional tests fail under PHP 8.5 JIT → recipes in `tests/AGENTS.md` § Remote sessions. ## Commands | Action | Command | diff --git a/tests/AGENTS.md b/tests/AGENTS.md index 509f9774..c5ef261f 100644 --- a/tests/AGENTS.md +++ b/tests/AGENTS.md @@ -23,4 +23,56 @@ ## PHPStan gate - Mandatory before commit - `./vendor/bin/phpstan analyze --memory-limit=512M` -- If phpstan fails: fix errors before offering to commit \ No newline at end of file +- If phpstan fails: fix errors before offering to commit +## Remote sessions (Claude Code on the web) — install and gate pitfalls +Diagnosed 2026-09-03; both symptoms hit every fresh remote session of this repo. + +### `composer install` dies with "Could not authenticate against github.com" on phpstan/phpstan +- Cause 1: the session's egress proxy serves GitHub HTTPS only for repos attached to the session + (this repo). Every other `api.github.com/repos///zipball/...` (and codeload / archive URLs) + answers `403 "GitHub access to this repository is not enabled for this session"` — also with a token. + Composer then falls back to `source` (a `git clone`), and anonymous git reads of public repos DO pass + the proxy, so every other package installs (slowly: ~80 failed dist attempts, some as "Proxy CONNECT + aborted due to timeout"). +- Cause 2: phpstan/phpstan ships dist only. Its packagist metadata and composer.lock entry have + `"source": null` (the repo's composer.json even declares an empty `source` block on purpose; the + code lives in phpstan/phpstan-src, the package is just the phar). No fallback → the whole install aborts. +- Neither `add_repo` (read = git only, API stays closed) nor `COMPOSER_AUTH`/`GITHUB_TOKEN` helps. +- Fix: seed composer's dist cache with a zip built from a shallow git clone of the release tag. + Composer looks up `/phpstan/phpstan/.zip` and, since the lock carries + no shasum for it, uses the file as-is. Then install with `--prefer-source` so the other packages skip + the doomed dist attempts. + +```bash +# composer.lock is gitignored: resolve it first, without downloading (packagist metadata is reachable) +[ -f composer.lock ] || COMPOSER_ALLOW_SUPERUSER=1 composer update --no-install --no-interaction +# version, dist reference and cache key of the locked phpstan/phpstan +eval "$(php -r '$l = json_decode(file_get_contents("composer.lock"), true); + foreach (array_merge($l["packages"], $l["packages-dev"]) as $p) { + if ($p["name"] === "phpstan/phpstan") { + printf("V=%s REF=%s KEY=%s\n", $p["version"], $p["dist"]["reference"], sha1($p["dist"]["url"])); + } + }')" +# shallow-clone the release tag (anonymous git reads of public repos pass the proxy, ~5s) +TMP=$(mktemp -d); git clone -q --depth 1 --branch "$V" https://github.com/phpstan/phpstan.git "$TMP/phpstan" +# zip it into composer's dist cache under the key composer will look for (.gitattributes export-ignore applies) +CACHE="$(composer config --global cache-files-dir 2>/dev/null)/phpstan/phpstan"; mkdir -p "$CACHE" +git -C "$TMP/phpstan" archive --format=zip --prefix="phpstan-phpstan-${REF:0:7}/" -o "$CACHE/$KEY.zip" HEAD +# phpstan comes from the cache, everything else from git; ALLOW_SUPERUSER keeps phpstan/extension-installer active +COMPOSER_ALLOW_SUPERUSER=1 composer install --prefer-source --no-interaction +./vendor/bin/phpstan --version # → PHPStan 2.x +``` + +### 32 Functional failures under PHP 8.5 ("Unexpected token Go ... Expected one of: ..., namePart") +- The environment's `/etc/php/8.5/cli/conf.d/99-agent.ini` turns on `opcache.enable_cli=1` + + `opcache.jit=tracing`. Under PHP 8.5.10 the tracing JIT miscompiles the LALR parser loop in + goaop/dissect (`isset($table[$state][$type])` misses a key that is present), so every pointcut of the + fixture project fails to parse in the spawned `bin/console cache:warmup:aop` process. Same code, + same input: fine with JIT off, fine on PHP 8.4, fine on CI (setup-php leaves CLI opcache off). +- `php -d opcache.jit=0 vendor/bin/phpunit` is NOT enough: BaseFunctionalTestCase spawns plain + `php` subprocesses. Append an ini dir instead (the leading `:` keeps the default scan dir): + +```bash +mkdir -p /tmp/php-ini && printf 'opcache.jit=0\n' > /tmp/php-ini/zz-nojit.ini +PHP_INI_SCAN_DIR=":/tmp/php-ini" ./vendor/bin/phpunit # → OK (2666 tests) +``` From 213e112dc396a64a54dc718b88e5c04f1ef9087d Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 3 Sep 2026 09:41:38 +0000 Subject: [PATCH 2/3] docs(agents): dist installs via packagist mirror in remote sessions Explain what blocks dist downloads (the session proxy gates GitHub zipballs per attached repo, no composer setting can open it) and make the fast fix primary: a packagist mirror that hosts its own dist zips (mirrors.cloud.tencent.com, verified current and byte-identical to the GitHub tags) installs everything from dist in ~30s with no fallbacks, phpstan included. Keep the cache-seeding recipe as the fallback. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01X2syVMk85L4eCjMRbdAjJQ --- AGENTS.md | 2 +- tests/AGENTS.md | 50 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 1dc5d965..663bfb0e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,7 +5,7 @@ AOP via source transformation at load time (stream filter, no PECL, no eval). ## Agent gate - PHP 8.4+ required. If PHP 8.3 or less → STOP, report can't run tests/phpstan. - Gate: `composer analyze` before commit (phpstan level 10). -- Remote session (Claude Code on the web): `composer install` dies on phpstan/phpstan and Functional tests fail under PHP 8.5 JIT → recipes in `tests/AGENTS.md` § Remote sessions. +- Remote session (Claude Code on the web): `composer install` is source-only/slow and dies on phpstan/phpstan (use the packagist mirror), Functional tests fail under PHP 8.5 JIT → recipes in `tests/AGENTS.md` § Remote sessions. ## Commands | Action | Command | diff --git a/tests/AGENTS.md b/tests/AGENTS.md index c5ef261f..9dd74302 100644 --- a/tests/AGENTS.md +++ b/tests/AGENTS.md @@ -27,21 +27,41 @@ ## Remote sessions (Claude Code on the web) — install and gate pitfalls Diagnosed 2026-09-03; both symptoms hit every fresh remote session of this repo. -### `composer install` dies with "Could not authenticate against github.com" on phpstan/phpstan -- Cause 1: the session's egress proxy serves GitHub HTTPS only for repos attached to the session - (this repo). Every other `api.github.com/repos///zipball/...` (and codeload / archive URLs) - answers `403 "GitHub access to this repository is not enabled for this session"` — also with a token. - Composer then falls back to `source` (a `git clone`), and anonymous git reads of public repos DO pass - the proxy, so every other package installs (slowly: ~80 failed dist attempts, some as "Proxy CONNECT - aborted due to timeout"). -- Cause 2: phpstan/phpstan ships dist only. Its packagist metadata and composer.lock entry have - `"source": null` (the repo's composer.json even declares an empty `source` block on purpose; the - code lives in phpstan/phpstan-src, the package is just the phar). No fallback → the whole install aborts. -- Neither `add_repo` (read = git only, API stays closed) nor `COMPOSER_AUTH`/`GITHUB_TOKEN` helps. -- Fix: seed composer's dist cache with a zip built from a shallow git clone of the release tag. - Composer looks up `/phpstan/phpstan/.zip` and, since the lock carries - no shasum for it, uses the file as-is. Then install with `--prefer-source` so the other packages skip - the doomed dist attempts. +### `composer install` is slow (everything from source) and dies on phpstan/phpstan +What blocks dist installs: +- The session's GitHub proxy serves `api.github.com` / `codeload.github.com` / `github.com/*/archive` only + for repos attached to the session (this one). Every other package's zipball answers + `403 "GitHub access to this repository is not enabled for this session"` — with or without a token, + so no `COMPOSER_AUTH`/`github-oauth` setting and no composer option can fix it. `add_repo` does not + open it either (read = git only; API needs an attached repo with credentials, refused cross-owner). +- Composer then falls back to `source` per package (full `git clone` into ~/.cache/composer/vcs): + anonymous git reads of public repos DO pass the proxy, so it works, but ~80 failed dist attempts + (some as "Proxy CONNECT aborted due to timeout") plus 80 clones take 70–85 s instead of ~30 s. +- phpstan/phpstan has no `source` at all: packagist metadata and composer.lock say `"source": null` + (its composer.json declares an empty `source` block on purpose; the code lives in phpstan/phpstan-src, + the package is just the phar). Nothing to fall back to → the whole install aborts with + "Could not authenticate against github.com". + +Fix A (recommended, dist for everything, ~30 s): point composer at a packagist mirror that hosts its own +dist zips. Non-GitHub hosts are not gated by the proxy. Verified: mirrors.cloud.tencent.com rewrites +every dist URL to its own host, is up to date with packagist (same-day releases), and its zips are +byte-identical to the GitHub tags (checked phpstan.phar sha256 against the git tag and the release +asset). Not usable: mirrors.aliyun.com (declares dist mirrors but serves 404), mirrors.huaweicloud.com +(rewrites URLs, serves 404), packagist.jp (metadata only, dists stay on GitHub). + +```bash +# global (session-local) composer config, composer.json stays untouched; composer.lock is gitignored, +# so remove a stale lock (it would pin api.github.com dist URLs) and let install resolve via the mirror +COMPOSER_ALLOW_SUPERUSER=1 composer config -g repos.packagist composer https://mirrors.cloud.tencent.com/composer +rm -f composer.lock +COMPOSER_ALLOW_SUPERUSER=1 composer install --no-interaction # 83 × "Extracting archive", 0 fallbacks +./vendor/bin/phpstan --version # → PHPStan 2.x +``` + +Fix B (fallback when the mirror is unreachable): seed composer's dist cache for phpstan/phpstan with a +zip built from a shallow git clone of the release tag. Composer looks up +`/phpstan/phpstan/.zip` and, since the lock carries no shasum for it, +uses the file as-is. `--prefer-source` makes the other packages skip the doomed dist attempts. ```bash # composer.lock is gitignored: resolve it first, without downloading (packagist metadata is reachable) From 824a96e2ec3d16d3c8b4a44d3802db9e6022f0ce Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 3 Sep 2026 10:14:37 +0000 Subject: [PATCH 3/3] docs(agents): keep the remote-session section to the phpstan install only The PHP 8.5 JIT test failure is a separate concern and gets its own issue. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01X2syVMk85L4eCjMRbdAjJQ --- AGENTS.md | 2 +- tests/AGENTS.md | 18 ++---------------- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 663bfb0e..b98099b8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,7 +5,7 @@ AOP via source transformation at load time (stream filter, no PECL, no eval). ## Agent gate - PHP 8.4+ required. If PHP 8.3 or less → STOP, report can't run tests/phpstan. - Gate: `composer analyze` before commit (phpstan level 10). -- Remote session (Claude Code on the web): `composer install` is source-only/slow and dies on phpstan/phpstan (use the packagist mirror), Functional tests fail under PHP 8.5 JIT → recipes in `tests/AGENTS.md` § Remote sessions. +- Remote session (Claude Code on the web): `composer install` is source-only/slow and dies on phpstan/phpstan → packagist-mirror recipe in `tests/AGENTS.md` § Remote sessions. ## Commands | Action | Command | diff --git a/tests/AGENTS.md b/tests/AGENTS.md index 9dd74302..aaa666c6 100644 --- a/tests/AGENTS.md +++ b/tests/AGENTS.md @@ -24,8 +24,8 @@ - Mandatory before commit - `./vendor/bin/phpstan analyze --memory-limit=512M` - If phpstan fails: fix errors before offering to commit -## Remote sessions (Claude Code on the web) — install and gate pitfalls -Diagnosed 2026-09-03; both symptoms hit every fresh remote session of this repo. +## Remote sessions (Claude Code on the web) — installing phpstan +Diagnosed 2026-09-03; hits every fresh remote session of this repo. ### `composer install` is slow (everything from source) and dies on phpstan/phpstan What blocks dist installs: @@ -82,17 +82,3 @@ git -C "$TMP/phpstan" archive --format=zip --prefix="phpstan-phpstan-${REF:0:7}/ COMPOSER_ALLOW_SUPERUSER=1 composer install --prefer-source --no-interaction ./vendor/bin/phpstan --version # → PHPStan 2.x ``` - -### 32 Functional failures under PHP 8.5 ("Unexpected token Go ... Expected one of: ..., namePart") -- The environment's `/etc/php/8.5/cli/conf.d/99-agent.ini` turns on `opcache.enable_cli=1` + - `opcache.jit=tracing`. Under PHP 8.5.10 the tracing JIT miscompiles the LALR parser loop in - goaop/dissect (`isset($table[$state][$type])` misses a key that is present), so every pointcut of the - fixture project fails to parse in the spawned `bin/console cache:warmup:aop` process. Same code, - same input: fine with JIT off, fine on PHP 8.4, fine on CI (setup-php leaves CLI opcache off). -- `php -d opcache.jit=0 vendor/bin/phpunit` is NOT enough: BaseFunctionalTestCase spawns plain - `php` subprocesses. Append an ini dir instead (the leading `:` keeps the default scan dir): - -```bash -mkdir -p /tmp/php-ini && printf 'opcache.jit=0\n' > /tmp/php-ini/zz-nojit.ini -PHP_INI_SCAN_DIR=":/tmp/php-ini" ./vendor/bin/phpunit # → OK (2666 tests) -```