Avatar for the reflex-dev user
reflex-dev
reflex
BlogDocsChangelog

Performance History

Latest Results

Merge remote-tracking branch 'upstream/main' into disable-implicit-event-handlers
benedikt-bartscher:disable-implicit-event-handlers
2 hours ago
Replace the outdated-dependency shell filters with a configured script (#7037) * Replace the outdated-dependency shell filters with a configured script The check lived as two inline shell blocks in check_outdated_dependencies.yml, so it could not be run outside CI, and the ignored package names were baked into grep patterns over the rendered report. `scripts/check_outdated_deps.py {backend,frontend}` now does the work and the workflow is two one-line steps. Held names move to `[tool.check-outdated-deps]` in the root pyproject.toml, so changing what is ignored is a config edit. Matching is on parsed package names rather than a regex over the table, which removes the anchoring the grep needed to avoid swallowing siblings: a bare `tailwindcss` entry cannot match `tailwindcss-animated`, and a `plotly.js` entry cannot match `plotly.js-locales` or `react-plotly.js`. Entries ending in `*` opt into prefix matching for the cases that need it -- `@chakra-ui/*` for the scope, `ag-grid*` for the three packages that release in lockstep, which the shell filter covered with both `ag-grid` and `ag-grid-react`. Two config keys rather than one, because the two reasons a package is ignored want different treatment: - `*_held` -- blocked on work we own. A held package that is already at its latest version means the blocker is gone, so the entry should be deleted: that errors, which keeps the list from accumulating dead entries. - `*_pinned` -- pinned by policy or owned by another repo (pyright, ruff, the reflex-enterprise packages, the ag-grid family). Reaching latest says nothing about whether the entry can go, so staleness is not checked. Both lists start out as the filters main already had, all of which are `*_pinned`; `*_held` is where blocker-driven entries land. Two details the parser has to get right, both verified against a real bun 1.4.0 install rather than assumed: - bun right-pads the package column, so the name needs trimming out of the cell rather than matching a fixed-width field. - bun annotates rows outside `dependencies` in that cell -- `tailwindcss (dev)`, `react (peer)`, `clsx (optional)`. The annotation is stripped before matching; npm names cannot contain spaces, so the suffix is unambiguous. Correspondingly, the declared-name set used for staleness covers all four sections bun reports on, not just dependencies and devDependencies. Note this job only triggers on release/** pushes and workflow_dispatch, so it does not run on this pull request. That is part of why running it locally matters; `uv run python scripts/check_outdated_deps.py backend` and `--web-dir <app>/.web` for the frontend side both work against a checkout. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RKQ4ARC2vkE5iKoULyaczz * Fix codespell: unparseable -> unparsable Caught by the pre-commit job, which runs codespell on top of ruff. I had run ruff locally but not the full `uv run pre-commit run --all-files`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RKQ4ARC2vkE5iKoULyaczz --------- Co-authored-by: Claude <noreply@anthropic.com>
main
2 hours ago
Add prerelease-test skill for release QA campaigns (#7001) * Add prerelease-test skill for release QA campaigns Captures the workflow used to validate the 0.9.9 release train as a repeatable skill, so the same analysis can be kicked off before any release. SKILL.md lays out the phases: discover what shipped from the pre-release branch changelogs and confirm every package published to PyPI, de-risk with a smoke test, fan out feature clusters that build real apps and drive them in a browser, upgrade-test reflex-examples apps from the previous stable, regression-test reflex-enterprise demos, audit wheel/sdist packaging, then triage into fix-before-release vs file-as-issue. Testing installs only published packages, baselines against the previous stable to tell regressions from pre-existing bugs, and verifies each claimed issue with an independent reproducer before reporting it. references/ holds the brief handed to each test agent (hard rules, environment traps, known-benign console noise), cluster decomposition guidance, the Workflow explore/verify pipeline with schemas and a port map, and report templates. scripts/ bundles three helpers, each validated against the released 0.9.9 train: - check_release_versions.py: changelog versions -> PyPI publication check (16 packages, all published) - audit_pyi.py: stubs present in both wheel and sdist, byte-identical, no foreign stubs, counts vs pyi_hashes.json (121 stubs, clean) - drive_app.py: Chromium driver with console/network capture, a small JSON action language and benign-noise filtering Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x * Format the python snippet in the agent brief ruff-format runs on markdown in pre-commit, so embedded python blocks are formatted too; the import/assert one-liner becomes two statements. Reproduced locally with the locked ruff 0.16.3, which now reports the whole skill directory as already formatted. Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x * Address cubic review findings All eight were valid. check_release_versions: publication status is now tri-state so a network failure is indeterminate (exit 2) rather than a false 'unpublished' blocker, and never-publish-packages from the release config are excluded from the scan. audit_pyi: the missing-artifact early return omitted 'expected' and crashed main() with KeyError, and a package absent from the manifest is now held to zero stubs instead of going unchecked. drive_app: a server that never came up raised out of page.goto before the report was written; startup failures are captured and exit 1. orchestration: a dead verifier no longer leaves verdict:null in verified_issues. SKILL.md: the documented invocation omitted --ref and bare scripts/... paths collide with the repo's own scripts/ directory. Re-validated after the changes: discovery finds 16 published packages, the documented Phase 5 pipe audits 121 stubs clean, and the driver's clean, action-failure and dead-server paths all behave as documented. Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x * Address the second review round Four further valid findings, distinct from the first round: - drive_app: inline action JSON longer than the filesystem's filename limit made Path(raw).exists() raise OSError before parsing, so the documented inline form failed for any non-trivial script (verified: a 1109-byte payload raised 'File name too long' before, runs now). - drive_app: the report was read immediately after the last action, so anything it triggered could land after the browser closed. - audit_pyi: the import root came from a dist-name heuristic; it now comes from the stub manifest, which is authoritative and — unlike deriving it from the artifact — keeps the foreign-stub check meaningful. - scripts: dropped bare python3 shebangs for PEP 723 metadata and 'uv run --script', matching scripts/check_min_deps.py and CLAUDE.md. Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x * Keep indeterminate packages out of the packaging audit --specs feeds a downloader, so a package whose PyPI check could not complete was turning a controlled indeterminate result into a traceback one stage later. Emit only confirmed-published rows, and report the skipped ones on stderr so a short list is never mistaken for a complete one. Guard the download in audit() too, so a package that cannot be fetched for any other reason fails on its own row instead of aborting the whole audit. Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x * Report unchecked packages instead of dropping them A changelog with no version heading was skipped silently, so the one package whose publication went unverified was also the one the report never mentioned. Record it as indeterminate, which keeps it out of --specs and exits 2. Also treat a fully yanked release as not installable, note partial yanks in the detail column, and give the packaging cluster the same repo-root-relative script path the rest of the skill uses. Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x * Stop the packaging pipeline from hiding an unchecked package Documenting phase 5 as a shell pipe meant the audit's exit status replaced the discovery script's, so a package dropped for an incomplete PyPI check took the audit's PASS with it. Document it as two commands instead. Parse --actions before launching the browser: doing it after spent a launch and a page load on a typo and left a traceback where the report should be. A list that is not a list now says so too. Say plainly in the orchestration example that dead explorers are kept as markers, since filter(Boolean) does not drop them and should not. Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x * Bound artifact downloads and survive a corrupt one urlretrieve honours only the global socket timeout, which is unset, so a stalled PyPI transfer would hang the whole audit instead of failing one row. Stream the download through urlopen with a timeout. A corrupt wheel or sdist raised out of audit() and took the rest of the train with it, the same way an unreachable package used to. Handle it per package. Every exit from audit() now goes through one record builder, so the reporting loop can no longer meet a record that is missing a key it reads. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x * Skip the packages whose changelog is not a release version Internal packages are patch-released on every push to main rather than through the changelog, so a changelog heading in one names a version that was never cut and would be checked against PyPI as a false blocker. Exclude them alongside the never-published ones, which the release config already groups the same way. Never audit a yanked artifact when a live one of the same kind exists: it is not what a user installs, so the audit would be reporting on the wrong file. Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x * Separate a package that could not be audited from a broken one An unreachable PyPI or an unreadable archive was being recorded as a packaging defect, so a healthy package would fail the release on infrastructure trouble. Track those as unchecked instead and exit 2, the same missing-versus-unknown split check_release_versions.py already makes, and exit 1 only for a defect observed in artifacts that were actually read. Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x * Account for the work a failed action already started A failing action skipped the settle wait because nothing had been recorded as performed, so a click that reached the server before its assertion failed took the resulting console error and failed request with it. Waiting is exactly what that case needs: the response it triggered is often the whole finding. Give a dead verifier its own marker too. Dropping it left a hole that read the same as an issue the fan-out cap never sent to a verifier, which is the failure the explorer marker two lines below exists to prevent. Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x * Stop the agent preamble from overpromising ports The shared preamble handed every agent a 20-port range, but the map gives a verify agent only 4, so a verifier following the bootstrap verbatim would reach into its sibling's range — the collision the map exists to prevent. Take the ends from the table instead of a fixed span. Say which venv the checkout-shadowing guard should name. It is the brief's most important instruction and it was the one placeholder with three plausible fills. Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x * Keep three more failure paths from ending the run A 200 response that is not PyPI's shape was read as a confirmed missing release: only urls present and empty means that, so anything else is now indeterminate rather than a false blocker. A truncated gzip sdist raises EOFError, which is neither OSError nor TarError, so the audit aborted on exactly the interrupted download the surrounding handler was written for. A page an action crashes or closes made the settle and the screenshot raise outside every guard, so the run ended in a traceback with no report at all -- the report being the one thing the caller was waiting for. Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x * Never leave a partial artifact where the next run will reuse it A download that died partway wrote its bytes straight to the final path, and --keep skips whatever already exists, so every later run reused the truncated file. The EOFError handling added last commit made that worse: it tells you to re-run to tell a bad download from a bad artifact, and re-running could not clear the bad download. Write to .part and rename only once the copy completes. Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x * Gate the audit on discovery, and stop two more raises escaping Two commands on separate lines never gated anything: the audit ran whatever discovery returned, so the truncated-list PASS the prose claims to prevent still happened. Chain them with && so a non-zero discovery skips the audit. A urls list whose entries are not PyPI's raised out of pypi_status instead of reporting one bad check, and a corrupted deflate stream raises zlib.error, which subclasses only Exception and so missed the archive handler entirely. Name exceptions module-qualified unless they are builtins: zlib.error is called just "error", and "could not read artifacts: error" tells the reader nothing. Report an unknown --ref rather than letting CalledProcessError print a stack about subprocess internals over what is almost always a typo. Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x * Guard the destinations too, not just the writes The screenshot's mkdir sat outside the handler added for the screenshot itself, so a destination that cannot be created still ended the run in a traceback -- the same hole the handler was there to close, one line above it. The report write had no guard at all: losing the file should not also lose the findings, so the summary now prints either way. Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x * Stop an unreadable page from reporting a clean run Reading the title and body was guarded, but the failure only reached the body text, so a page that died after a successful load and actions produced RESULT: clean and exit 0. A QA driver that says clean when it inspected nothing is the one result it must never produce, so the read failure is now recorded and counts against the run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x * Fix the pipe left in the audit script's own usage SKILL.md moved to && so a failed discovery cannot hand its dropped packages to a passing audit, but this script still documented the pipe it warns about, so anyone reading the script instead of the skill would copy the masking form. Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x * Fail the run when a requested report cannot be written The guard added two commits ago printed REPORT NOT WRITTEN and carried on, so a healthy app with an unwritable --report path still printed RESULT: clean and exited 0. A caller that asked for a report and got none has no result to trust, and automation would have read that as a pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x * Make the documented commands runnable and reject bad actions early Both usage docstrings showed bare script invocations, but these scripts have no shebang and no exec bit by design -- the PEP 723 metadata is there so uv builds the environment. Copying either documented line failed. Show uv run --script, the same form the skill's canonical command uses. A list whose items are not single-key objects passed validation and failed later inside the action loop, spending a browser launch and a page load on a typo the early exit was added to catch. A yanked flag that is present but not a boolean no longer reads as live: absent still means not yanked, but a value that is not PyPI's cannot be interpreted. Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x * Exclude the root changelog when the root package is excluded never-publish-packages and internal-packages name packages, and reflex-release resolves those names against all_packages(), which puts the root package in the same namespace as the directories under packages-dir. Its config validation accepts root-package in either list on exactly those terms. changelog_paths() appended the root CHANGELOG.md unconditionally, so a repo whose root package never publishes (or patch-releases internally) would have its root changelog checked against PyPI and reported as a false release blocker, and emitted into --specs for the artifact audit to fetch. Turn the exclusion set into changelog paths, mapping the root package name to CHANGELOG.md, so the root is excluded on the same terms as any sub-package. Verified against synthetic refs that add "reflex" to each list: before, 13 changelogs with the root included in both cases; after, 12 with the root dropped. The real config is unchanged — 13 packages, byte-identical output. Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x ---------
main
3 hours ago
fix(assets): link shared assets atomically instead of check-then-act (#7039) * fix(assets): link shared assets atomically instead of check-then-act `asset(shared=True)` created the symlink into `assets/external/` with a sequence that was racy at every step: the `exists()`/`is_symlink()` guard, the `unlink()` in the `FileExistsError` handler, and the retry `symlink_to()` after it. Concurrent compiles into one working directory — pytest-xdist workers, parallel builds, containers on a shared bind mount — lost those races and aborted the compile with `FileNotFoundError` from the `unlink()`, or with an unhandled `FileExistsError` from the retry. Build the link under a unique temporary name in the destination directory and `os.replace()` it into place, which atomically overwrites whatever the loser of the race left behind and needs no retry. Errors still propagate, so `asset()` cannot return a path with no symlink behind it. The old guard is dropped: `exists()` follows the link, so a destination pointing at some other existing file made it skip rather than repoint. The replacement fast path compares `readlink()` against the intended target, keeping the "no needless re-creation for file watchers" property while actually converging on the right target. The `FileExistsError` comment attributed this to docker bind mounts; that is one cause, but the general one is concurrency, with no container involved. * chore: name the news fragment for PR #7039 * docs: tighten comments and the news fragment * fix(assets): bound the staged symlink name so long asset names still link * fix(assets): retry the staged rename when Windows denies a concurrent replace * fix(assets): cut the staged name prefix by encoded bytes, not characters * fix(assets): treat an unresolvable destination as needing replacement * fix(assets): stage the link under a name that does not derive from the asset * refactor(assets): guard only the destination when checking an existing link * test(assets): compare the untouched link after resolution for Windows
main
20 hours ago
test(assets): compare the untouched link after resolution for Windows
claude/asset-symlink-toctou-race-f7c306
21 hours ago

Latest Branches

CodSpeed Performance Gauge
0%
feat: allow explicit only event handlers#7033
2 hours ago
689dc48
benedikt-bartscher:disable-implicit-event-handlers
CodSpeed Performance Gauge
0%
1 day ago
c8e22ce
claude/outdated-dependencies-release-t26evx
CodSpeed Performance Gauge
+5%
17 hours ago
b965d6f
ProgrammerPlus1998:httpxodus/httpx2-migration
© 2026 CodSpeed Technology
Home Terms Privacy Docs