Jia Chen
90d · built 2026-09-08
Performance
What Jia Chen shipped in the selected window, measured in ETV, and how it compares with the 90 days before it.
Effective capacity
+1.6engineers
delivers like 2.6 (2.6x pre-AI)
Output (ETV)
16.6ETV
+122.5% vs 7.5 prior
Features share
42.1%
+15.2 pp vs prior window
Fixes share
24.2%
−19.8 pp vs prior window
Work mix
42.1% Features23% Maintenance6% Tests4.7% Docs24.2% Fixes
111 commits over 90 days, ending 2026-09-08.
Daily performance
Daily ETV, stacked by Features, Maintenance, Tests, Docs and Fixes.
Repository spread
Where this developer's commits land. Concentrated work (top1 > 80%) vs polymath spread (top1 < 30%).
Most impactful commits
Top 10 by ETV in the last 90 days.
- 1.8ETVUpgrade typeshed Summary: Refresh the typeshed stubs bundled into the Pyrefly binary so the type checker resolves against current stdlib and third-party type information. The bundled snapshot was pinned to typeshed `1d3abc4` (2026-05-28); this bumps it to upstream `main` HEAD `a7bcce8` (2026-07-07). Fixes https://github.com/facebook/pyrefly/issues/4059 Reviewed By: yangdanny97 Differential Revision: D110970459 fbshipit-source-id: 363b39d2def8493b9e52c342ddf2aef094bb5922github.com-facebook-pyrefly · 2e1819d8 · 2026-07-08
- 1.0ETVMove reusable module resolver to `pyrefly_build` crate (1/2) Summary: justmovingthingsaround Bazel check, and other build-system integrations, need to resolve dependency imports directly from search roots using the same package, stub, namespace, and compiled-module lookup semantics as Pyrefly's checker. That logic previously lived inside the checker's `finder.rs`, coupled to checker command code, so it could not be reused outside the checker. This moves the filesystem module-resolution core into a new `module_resolver` module in the `pyrefly_build` crate (registered in `lib.rs`). The module owns the low-level package/stub/namespace/compiled walk -- `find_one_part`, `continue_find_module`, `find_module_components`, `find_module_results`, `is_pkgutil_namespace`, and the `FindResult` type -- plus a `ModuleResolver` entry point for build-system callers. The extraction is a pure move of the filesystem-walking logic with no checker-specific policy attached: bundled stubs, typeshed ordering, import-ignore handling, and py.typed diagnostics all stay in the checker. As a result `pyrefly_build` can depend on the resolver without pulling in checker command code, and the resolver's own unit tests move with it so the crate that owns the internals owns their tests. This diff only copies the current logic into `pyrefly_build`. The next diff will complete the moving by deleting pre-existing logic, and adjust downstream logic to point to the new code. Reviewed By: ndmitchell Differential Revision: D110825229 fbshipit-source-id: e4ed786a1be2535b1c2cd9470fe89dd3e9466631github.com-facebook-pyrefly · 840c8098 · 2026-07-08
- 0.7ETVHonor Pydantic `populate_by_name` Summary: Pydantic models configured with an alias generator and `populate_by_name=True` accept both generated aliases and declared field names. Pyrefly recognized the newer `validate_by_name` and `validate_by_alias` flags but ignored the legacy `populate_by_name` option, so alias-generator support synthesized only the alias keyword and incorrectly rejected the field name. This also affected inherited model configuration. Teach Pydantic config parsing to retain `populate_by_name` and normalize it using runtime semantics: when `validate_by_name` is unset, use the `populate_by_name` value and force `validate_by_alias=True`. Preserve unresolved validation settings as `Option<bool>` in class metadata so subclasses distinguish an absent option from an explicit `False`; existing `__init__` synthesis then emits the correct keyword forms. Tests cover `ConfigDict`, class-keyword, precedence, and inheritance cases. Fixes https://github.com/facebook/pyrefly/issues/4365 Reviewed By: yangdanny97 Differential Revision: D114140809 fbshipit-source-id: 440a6558fedafbf4ea570fc37fefc50d4a5a4249github.com-facebook-pyrefly · faed8eb7 · 2026-07-30
- 0.7ETVAdd serializable diagnostic rendering Summary: Here's a problem that arises when building the IG daemon: who's responsible for rendering the type errors, client or server? This questions surprisingly do not have a 100% perfect answer for the following reasons: - The client-side do not really have all the necessary information to print errors. This is because the full text printing logic requires the full `Error` structure, but `Error` is not a serializable format: it contains pointer-like structures such as `Module`, so we can't simply, say, sending the `Error` over the wire from server to client and let the latter do the printing. We could convert `Error` into `LegacyError` which is serializable before sending, but then `LegacyError` won't support e.g. `full-text` mode (which contains all the nice snippets and pointers). - The server side do not really have all the necessary information to do type error telemetry. Currently, type errors telemetry (i.e. the `pyrefly_typing_errors` scuba table happens *after* the type checking on the client side, and it requires the JSON-serialized string of the type errors. If we have server-side do the diagnostics rendering, client is just going to get a text blob which is not suitable for telemetry/logging purpose. We'd either have to do additional setup so that server would do telemetry on client's behalf (which is tricky since client has this "fallback" mode where it will attempt to try server mode first then fallback to local checks -- we don't want to double-logging those cases, so the server needs additional info regarding whether client did the fallback or not, and that requires further protocol changes...), or sending JSON serialized string always, duplicating the output string. The bottom line here is that given how the system is setup today, we'd have to pay some prices regardless of which decisions we make. This diff and its successors land us to a position where we ask the server to do half-rendering -- i.e. have it send over `LegacyError` plus some additional info, so client side can reconstruct the full-text output, while at the same time has 100% information for telemetry. This requires us to refactor define a new serializable structure, plus extract some pre-existing type error rendering logic, and that's what this diff is about. For now, I'm only allowing the `full-text`, `min-text`, and `json` output format. Other formats like sarif or github I figured would not be super relevant for IG. Reviewed By: stroxler Differential Revision: D117578647 fbshipit-source-id: ba57ed15203231efe411721e256a0ff5c5dcee1bgithub.com-facebook-pyrefly · ee3891b2 · 2026-09-02
- 0.6ETVAdd path model for Bazel short-path to Pyrefly import resolution Summary: The `bazel-check` command currently receives raw JSON from the Bazel aspect with short paths, path overlays, and search path metadata, but had no typed model to turn those into something Pyrefly check can easily understand. This diff introduce a path model in `bazel_check.rs` and validate it before subsequent processing. The command still writes empty output, but now exercises parsing invariants so the next diff can plug the model into the module finder without changing the input contract. We keeps validation at the input boundary and makes the conversion helpers operate only on already-validated paths. Short paths and overlay physical paths now reject absolute paths, parent traversal, current-directory components, empty components from repeated separators, and trailing slashes. Logical-to-physical import conversion is component-aware, so workspace-prefixed imports are stripped as path components rather than by raw string prefix matching. Follow-up diff will wire `BazelFileInput` and `BazelSearchRoot` into Pyrefly's module finder and state initialization to actually type-check Bazel targets. No user-visible behavior change yet; `bazel-check` still outputs empty diagnostics but now fails fast on malformed input, keeping invariants exercised. Reviewed By: maggiemoss Differential Revision: D110555018 fbshipit-source-id: 8e5c2be3bfdc889a9cc096424464484a3f2fb172github.com-facebook-pyrefly · 1ec5114f · 2026-07-09
- 0.5ETVAdd KeyClassDisjointBase scaffolding Summary: Introduce a new class-scoped solver key, `KeyClassDisjointBase`, which is intended to eventually handle PEP 800 disjoint-base propagation. No functional changes intended: the solver continues to return the same representative as computed by `ClassMetadata::is_disjoint_base()` and `ClassMro::nearest_disjoint_base()`. Currently, no caller uses the new key, and `ClassMro` still maintains its cached disjoint-base field. The purpose of landing this plumbing separately is to ensure that the upcoming behavioral diff remains focused solely on the algorithm migration, without mixing in table, binding, or state boilerplate changes. Reviewed By: yangdanny97 Differential Revision: D108246271 fbshipit-source-id: c38e6c930a64e9523e304b80d168269e3d296c12github.com-facebook-pyrefly · 4ebb45d0 · 2026-06-12
- 0.5ETVPromote `@dataclass(slots=True)` to PEP 800 disjoint base Summary: When using Pyrefly, there was a confusing behavior around classes that use `__slots__`—especially those created with `dataclass(slots=True)`: - **Explicit `__slots__`**: If you defined `__slots__` directly in your class, Pyrefly correctly treated the class as a "disjoint base" (meaning, it prevents certain types of multiple inheritance and helps with type narrowing). - **Dataclass-generated `__slots__`**: If your class got its `__slots__` from `dataclass(slots=True)`, Pyrefly did NOT treat it as a disjoint base yet. This led to: - Multiple inheritance of two `dataclass(slots=True)` bases (or one such base plus an explicit-`__slots__` base) was allowed, even though it should not be. - Type narrowing with `isinstance` did not recognize these classes as disjoint, which could cause bugs. ## Why Was This Happening? - The information about whether a class should be a disjoint base was stored in `ClassMetadata`. - However, the actual `__slots__` for dataclasses are only created later, during field solving (`ClassField`), so Pyrefly couldn't check them during metadata construction without causing a dependency cycle. - Inheriting `kws.slots = true` from a base would incorrectly promote every subclass of a slotted dataclass, which is not what CPython does. ## What Is the Fix? The new approach models CPython's behavior more closely: 1. **Promotion to Disjoint Base**: A class is promoted to a new disjoint-base representative only if: - It has a fresh, local `dataclass(slots=True)`-like decorator applied. - There is NOT an explicit class-body `__slots__` present (having both is a runtime error and disables dataclass slot synthesis). - When walking the class's inheritance chain (MRO), at least one instance field (local or inherited) is not already covered by an ancestor's slot. 2. **Precise Detection**: The code now tracks whether the most recent local dataclass-like decorator requested `slots=True`, separate from whether the final `kws.slots` is true (important for inheritance cases). 3. **Explicit vs. Synthesized Slots**: `SlotsInfo` only refers to explicit class-body `__slots__`. Dataclass field synthesis is unchanged. 4. **Tests**: Tests that previously failed now pass. ## Known Follow-Up - If both an explicit `dataclass(...)` decorator and a `dataclass_transform`-derived spec apply to the same class, the transform branch overwrites both the dataclass metadata and the slots flag. The boolean stays in sync with the final `kws.slots`, but the merge policy will be addressed in a future change. Reviewed By: samwgoldman Differential Revision: D108108047 fbshipit-source-id: e67c8cd3f006a4e1826630ff7c9f06cde7a20df6github.com-facebook-pyrefly · 8eb21bce · 2026-06-11
- 0.5ETVPreserve parameter references in unopened files Summary: An earlier change fixed incoming call hierarchy for files that the client did not open. It requested `Require::Everything` for every file that depends on the target module. This made the required ASTs available. However, the same helper also serves find references and find implementations. Those requests then performed full analysis on every dependent file. D117389890 (PR) limits that work. Incoming call hierarchy performs full analysis only on files that reference the target. Type hierarchy performs full analysis on its candidate files. Most find-reference requests and all find-implementation requests use only the retained index. This keeps incoming call hierarchy correct and makes common reference requests faster. D117389890 leaves one correctness problem. Parameter references do not use only the index. To find a keyword argument such as `greet(message="Hello")`, Pyrefly scans the caller AST. It also uses bindings to confirm that the keyword resolves to the selected parameter. An unopened file has neither at `Require::Indexing`. Find references therefore omits the keyword argument. Rename can change the parameter definition but leave `message=` unchanged in the unopened caller. This diff requests `Require::Everything` for all dependent files only when the selected definition is a parameter. Other definitions keep the index-only path from D117389890. This restores parameter references and rename without removing the main performance improvement. This diff also removes a duplicate type-hierarchy request for `Require::Everything`. The caller already makes the same request before it reads the candidate files. The tests cover parameter references and parameter rename in an unopened caller file. The call hierarchy test now has two unopened caller files. This verifies that one batch prepares every selected caller file. Reviewed By: stroxler Differential Revision: D117455220 fbshipit-source-id: f8ba135ace020fbcd0e4ba9b0e7509773ba832begithub.com-facebook-pyrefly · 1321aa43 · 2026-08-28
- 0.4ETVAdd source database for bazel-check import resolution Summary: The previous diff made `bazel-check` parse its Bazel-provided input into file entries and search roots, but it still discarded them without building anything the type checker could use. This diff transforms those intermediate structures into a dedicated `SourceDatabase`: a resolver that answers “which file backs module `foo.bar.baz`?” and “which modules is this target actually responsible for checking?”. This is the last data-massaging step before we can hand data to Pyrefly and run a real check. The core difficulty is that Bazel describes each file in two coordinate systems at once: a logical path, where Python’s import system sees it and which determines the module name, and a physical path, the actual on-disk artifact, often generated and living elsewhere. Compounding this, one module name can be served by several files: a runtime `.py`, a `.pyi` stub, or a PEP 561 `-stubs` package. Imports also resolve through an ordered set of search roots mirroring Bazel’s runfiles and repository layout. The source database’s job is to collapse all of this into deterministic module candidates and lookup behavior. The workflow is to derive a module name for each explicit file input by matching its logical path against the search roots in priority order, stripping the `-stubs` suffix so a stub package types the package it stands in for. Each file is registered as a ranked candidate for its module, and lookups resolve by rank. The rank encodes Bazel’s precedence rules: stub packages first, then earlier search roots, then interface files over executable ones. A caller-supplied style filter is treated as a preference among a module’s candidates rather than a hard constraint, falling back to the best candidate when nothing matches. Files the target owns (aka its "check roots") are separated from files present only so imports resolve. A check root that cannot be named as a module is a hard error, while a non-importable overlay is skipped. The resulting shape deliberately matches the existing `buck-check` source database contract, so both build systems drive Pyrefly through the same interface. Reviewed By: maggiemoss Differential Revision: D110555020 fbshipit-source-id: 75c8260a512930b5c52d324e1632fa6c2ea25498github.com-facebook-pyrefly · fb3e1775 · 2026-07-09
- 0.4ETVDefine `bazel-check` command input parsing Summary: Flesh out the `bazel-check` command to parse JSON inputs that Bazel may hand to us. The JSON format currently coms from the following design: https://www.internalfb.com/phabricator/paste/view/P2402942225?view=markdown This is obviously subject to lots of changes as I gradually flesh out the implementation of `rules_pyrefly`. But to bootstrap the implementation iterations we need a concrete place (i.e. the current design) to start from. Reviewed By: ndmitchell Differential Revision: D110249180 fbshipit-source-id: 2f30cf5b76e360177134d27e89e2b6fdb4d434d3github.com-facebook-pyrefly · addc551a · 2026-07-01