Add SessionStart hook to provision web environment#6613
Conversation
The web container image predates this repo's current requirements, so a fresh `uv sync` + test run fails out of the box. Add a SessionStart hook (web-only) that reconciles the environment on every session start: - Upgrade uv from PyPI when below tool.uv.required-version (the astral.sh installer and `uv self update` are blocked / GitHub-rate-limited here). - Install and use stable CPython 3.14.5 instead of the image's pre-baked 3.14 prerelease, whose typing._eval_type signature is incompatible with the pinned pydantic and breaks every import. - Fetch git tags so Reflex's VCS-derived version resolves correctly; otherwise it becomes 0.0.0 and trips downstream version gates such as reflex-hosting-cli's compatibility check. Narrow .gitignore from the whole .claude dir to just .claude/.worktrees so the shared hook and settings are tracked. https://claude.ai/code/session_014Bp9oVCfiq8DSnTK8VfozK
Keep personal settings overrides out of version control alongside the already-ignored .claude/.worktrees, while still tracking the shared hook and settings.json. https://claude.ai/code/session_014Bp9oVCfiq8DSnTK8VfozK
Drop the hardcoded 3.14.5 / --python flag. `uv python install` excludes prereleases, so it fetches the stable patch matching .python-version even when the image only has the prerelease installed; `uv sync` then prefers that stable interpreter over the rc. The hook no longer needs updating when the repo bumps its Python version. https://claude.ai/code/session_014Bp9oVCfiq8DSnTK8VfozK
The 3.14 prerelease is not baked into the image; the image's stale uv (built before Python 3.14.0 final) downloads it because its release manifest only knows 3.14 release candidates. Upgrading uv first is the real fix; `uv python install` remains a guard against reusing a prerelease left by an earlier stale-uv sync. https://claude.ai/code/session_014Bp9oVCfiq8DSnTK8VfozK
Greptile SummaryAdds a
Confidence Score: 4/5Safe to merge for its stated purpose; the script is well-commented, correctly skips non-web environments, and handles the uv/Python/tags bootstrapping problem cleanly. The hook is narrowly scoped and the logic is sound. Two minor behavioral gaps: uv sync can hard-fail the hook in offline scenarios (unlike git fetch, which is guarded), and the PATH update to prefer ~/.local/bin/uv doesn't propagate to Claude Code's process after the hook exits. Neither is a showstopper for the hook's primary goal, but the offline fragility could make the web session unusable when the package registry is unreachable. .claude/hooks/session-start.sh and .gitignore are the files most worth a second look. Important Files Changed
Reviews (1): Last reviewed commit: "Correct the Python-prerelease rationale ..." | Re-trigger Greptile |
| git fetch --tags --quiet || true | ||
|
|
||
| # 4. Install all workspace dependencies (uses .python-version). | ||
| uv sync |
There was a problem hiding this comment.
uv sync failure aborts the session in offline scenarios
git fetch --tags is guarded with || true to tolerate offline/rate-limited runs, but uv sync on the same line is not. Under set -euo pipefail, a network-isolated run that passes the git step will still hard-fail on uv sync, aborting the hook and potentially the entire session start. If the intent is to allow best-effort sync when offline (mirroring the git-fetch tolerance), consider adding || true or wrapping in an if-block that logs but doesn't abort.
| .claude/.worktrees | ||
| .claude/settings.local.json |
There was a problem hiding this comment.
Other
.claude/ runtime artifacts may now appear as untracked
Narrowing from .claude to only .claude/.worktrees and .claude/settings.local.json means any other directories that Claude Code creates at runtime (e.g. .claude/cache/, .claude/todos/, .claude/memory/) will show up as untracked files in git status for all contributors running Claude Code locally. Consider adding a broader catch-all like .claude/* and then un-ignoring only the specific files that should be tracked (!.claude/hooks/ and !.claude/settings.json), or adding explicit ignores for each known runtime subdirectory.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| export PATH="$HOME/.local/bin:$PATH" | ||
| hash -r 2>/dev/null || true |
There was a problem hiding this comment.
PATH update doesn't propagate to Claude Code after the hook exits
export PATH="$HOME/.local/bin:$PATH" only affects the hook script's own subprocess. Once the script exits, Claude Code's process environment is unchanged, so any uv commands Claude Code runs after session start (e.g. uv add, uv sync triggered by tool calls) will still resolve to the old system uv, not the just-upgraded one. For this specific hook the in-script uv python install and uv sync use the right version, so the initial setup is correct — but it's worth being aware that the upgraded binary is only visible within this script's lifetime.
Replace the embedded Python version-comparison with a single idempotent pip install of uv>=<required-version> parsed from pyproject.toml. pip is a near-instant no-op when the constraint is already satisfied, so the explicit version check was redundant. Also pass --root-user-action=ignore to suppress pip's cosmetic root-user warning in the web container. https://claude.ai/code/session_014Bp9oVCfiq8DSnTK8VfozK
Address review feedback: - .gitignore: ignore __pycache__/, .pytest_cache/, .ruff_cache/ (incl. nested) and .mypy_cache/, the scratch dirs that test/lint runs generate. - Persist the ~/.local/bin PATH prepend via $CLAUDE_ENV_FILE so the upgraded uv stays visible to the session's commands after the hook exits, not just within the hook process. - Guard 'uv sync' so a transient install failure warns instead of aborting startup; the container still comes up and sync can be retried. https://claude.ai/code/session_014Bp9oVCfiq8DSnTK8VfozK
.claude/settings.local.json and .claude/.worktrees were already ignored; add CLAUDE.local.md (personal project memory) so user-local Claude state isn't shared. The pattern has no slash, so it also matches nested copies in sub-packages. Shared team files (.claude/settings.json, .claude/hooks, CLAUDE.md) stay tracked. https://claude.ai/code/session_014Bp9oVCfiq8DSnTK8VfozK
The web container image predates this repo's current requirements, so a
fresh
uv sync+ test run fails out of the box. Add a SessionStart hook(web-only) that reconciles the environment on every session start:
installer and
uv self updateare blocked / GitHub-rate-limited here).3.14 prerelease, whose typing._eval_type signature is incompatible with
the pinned pydantic and breaks every import.
otherwise it becomes 0.0.0 and trips downstream version gates such as
reflex-hosting-cli's compatibility check.
Narrow .gitignore from the whole .claude dir to just .claude/.worktrees so
the shared hook and settings are tracked.
https://claude.ai/code/session_014Bp9oVCfiq8DSnTK8VfozK