fix(onboarding): install vendor/ on first make up/upd so a fresh clone boots - #64
Merged
Merged
Conversation
A fresh clone that follows docs/08_getting_started.md hit a PHP fatal on the documented health check: Fatal error: Uncaught Error: Failed opening required '/app/vendor/autoload_runtime.php' in /app/public/index.php:5 The backend-dev anchor bind-mounts ./backend-symfony over /app, which shadows the vendor/ tree the image builds in. vendor/ is gitignored, so on a fresh host clone there is nothing behind the mount and index.php cannot boot. The mount stays — it is what makes local edits visible without a rebuild — so the fix is to populate vendor/ on the host. - Makefile: new `ensure-vendor` target, a prerequisite of `up` and `upd`. Guarded on vendor/autoload_runtime.php, so once vendor/ is populated it costs one `test -f` and the normal path is unchanged. It creates and chmods vendor/, var/ and public/bundles/ first (the image runs as USER 10001 and cannot write into a host-owned directory it did not create), and uses `docker compose run --rm --no-deps` so postgres and redis are not started just for a composer install. - docs/08_getting_started.md: the healthz check moves from section 2 to section 3, after `make composer-install`, with a note explaining why the backend cannot answer before dependencies are installed, plus a troubleshooting entry for the exact error message. - scripts/doctor.sh: new BACKEND DEPENDENCIES check for vendor/autoload_runtime.php, pointing at `make composer-install`. It runs before CONNECTIVITY, because a missing vendor/ presents as a container that is up with every port answering while /healthz returns a PHP fatal. `make quickstart` already handled this and is untouched. CI calls `docker compose up` directly, never `make up`/`make upd`, so no job changes behaviour. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015GM8iTi632qrwApx8KMdfw
Running /factory-bug on the fresh-clone vendor/ failure surfaced a gap in the factory itself, not in the change under test: no pipeline can carry a defect that lives in the build and onboarding tooling. The bug pipeline requires a reproduction test committed before the fix and seen red. The only automated suites run inside a container that needs vendor/ to exist, which is exactly what this class of bug removes, so the test harness is absent in the state that has to be reproduced. Logged, not fixed: building such a harness is a design decision and belongs in its own feature run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015GM8iTi632qrwApx8KMdfw
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pipeline: chore
Spec: n/a — no application code changed
Gates: no gate report — see "Factory pipeline" below, the
/factory-bugrun halted at step 1 and escalatedDeploy-impact:
make upandmake updnow depend on a newensure-vendortarget. On a host wherebackend-symfony/vendor/autoload_runtime.phpalready exists it is a singletest -fand nothing else runs. On a host where it is missing, the firstmake up/updruns one extradocker compose run --rm --no-deps backend-dev composer installbefore starting the stack, so that first start is several minutes longer. No image, compose file or environment template is touched, and nothing changes fordocker compose upinvoked directly — which is what CI and every deployment path use.Summary
An external user followed
docs/08_getting_started.mdon a fresh clone (GitBash onWindows) and the documented health check failed:
The
backend-devanchor indocker-compose.ymlbind-mounts./backend-symfony:/app,which shadows the
vendor/tree the Dockerfile builds into the image.vendor/isgitignored, so a fresh host clone has none and
public/index.phpcannot boot. Theanchor is inherited by
backend-test,backend-e2e,backend-preprod,schedulerand
canary-worker.make quickstartalready handled this — it creates and chmodsbackend-symfony/vendor,then runs
composer installonce the stack is up — so the README path worked and onlythe manual path in
docs/08_getting_started.mdwas broken.The bind mount is deliberately left alone. It is what makes local edits visible
without a rebuild. The fix populates
vendor/on the host instead.Reported in GitHub Discussions; there is no issue number to close.
Changes
Makefile— newensure-vendortarget, a prerequisite ofupandupd. Guardedon
backend-symfony/vendor/autoload_runtime.php, so oncevendor/is populated thenormal path costs one
test -fand is otherwise unchanged. It creates and chmodsvendor/,var/andpublic/bundles/first — the image runs asUSER 10001andcannot write into a host-owned directory it did not create — and uses
docker compose run --rm --no-depsso postgres and redis are not started for acomposer install.
public/bundlesis included because the composerauto-scriptsrun
assets:install %PUBLIC_DIR%;quickstartchmods it for the same reason.docs/08_getting_started.md— thecurl /healthzcheck moves from section 2 tosection 3, after
make composer-install. Section 2 gains a note explaining why thebackend cannot answer before dependencies are installed, and Troubleshooting gains an
entry keyed on the literal
autoload_runtime.phperror string.scripts/doctor.sh— aBACKEND DEPENDENCIEScheck forvendor/autoload_runtime.phppointing atmake composer-install. It runs beforeCONNECTIVITYbecause a missingvendor/presents as a container that is up withevery port answering while
/healthzreturns a PHP fatal.factory/found-issues.md— entry chore(deps): bump recharts from 3.8.0 to 3.8.1 in /frontend-react #9, logged and not fixed (see below).make quickstartis untouched.docker-compose.yml,docker-compose.prod.ymlanddocker-compose.demo.ymlare untouched.Factory pipeline
/factory-bugwas run and halted at step 1 on its own rule: "If the reproductioncannot be written, stop and escalate."
The reproduction cannot be committed. The only automated suites are PHPUnit and Vitest,
and they run inside the container:
make testinvokesvendor/bin/phpunit(
Makefile:218),make staninvokesvendor/bin/phpstan(:221). A missingvendor/is precisely the state to reproduce, so the test harness does not exist in that state.
A PHPUnit test asserting Makefile content would only ever run where the bug is absent
and could never be seen red in CI, which the constitution's testing rules require.
Building a shell/Make harness plus the CI job to run it is a design decision — a
/factory-feature, not a bug fix. That gap is logged asfactory/found-issues.md#9.The constitution also scopes the pipelines to
backend-symfony/andfrontend-react/;this diff touches neither. Hence
Pipeline: chore, whichdocs/factory/pipelines.mddescribes exactly: "process, documentation or CI only — changes no application code."
Reproduction evidence (out of tree)
The red output the bug pipeline requires, produced with a stub
dockerand theMakefiletaken frommainand from this branch. Not committed — it has no placein the repository without the CI job that would run it.
make doctorwas run both ways: it reports the check green with the file present, andfails with exit 1 and the actionable message without it.
Escalation triggers
Every trigger in
factory/gates.yamlwas evaluated. None fired.sensitive_pathsdocker-compose*.ymlandinfra/docker/**are sensitive and were deliberately not touched;Makefile,docs/andscripts/doctor.share not on the listmigration_presentpublic_api_changednew_dependencylarge_diffadversarial_not_convergedfix_to_pr, so no adversarial iteration ranObjections
In a pipeline with no spec, the only route to BLOCKING is a failing executable test, so
these are ADVISORY by construction rather than by judgement.
Reviewer note — what was not verified
There was no Docker daemon in the environment this branch was built in, so
make test,make stanandmake cs-fixerwere not run. The diff contains no PHP, no JS and nocomposer or npm manifest change, so none of them has anything to act on — but that is a
prediction, not a result, and the checklist below is left unticked accordingly.
The one behaviour worth a real run before merge:
ensure-vendorinvokescomposer installwith
--no-deps, so theauto-scripts(cache:clear,assets:install) execute withpostgres down. Doctrine connections are lazy and this is the ordinary Symfony case, but it
was not executed here. A
make updon a clone with nobackend-symfony/vendoris thecheck that matters.
Type of Change
Checklist
make testpasses — not run, no Docker daemon available; no PHP in the diffmake stanpasses — not run, same reason; no PHP in the diffmake cs-fixerapplied and left the worktree unchanged — not run, same reasonFactory gates
Gates:line above — none exists, stated instead🤖 Generated with Claude Code
https://claude.ai/code/session_015GM8iTi632qrwApx8KMdfw
Generated by Claude Code