.llms/rules/agents.md
Pyrefly is a fast language server and type checker for Python.
Architecture:
As described in the README, our architecture follows 3 phases:
Here's an overview of some important directories:
Coding style: All code must be clean, documented and minimal. That means:
unreachable!("explanation") or .expect("explanation") — never
_ => default, .unwrap_or_default(), or silent fallbacks. A type checker
that silently produces wrong results is far worse than one that crashes with a
clear message. Silent fallbacks hide bugs and confuse maintainers by making
unreachable states look reachable.pyrefly_types crate before manually
creating or destructuring a Type.Expr nodes are passed around and the number of
times they are parsed. Generally, this means extracting semantic information
as early as possible.use imports at the top of the file rather than using
inline qualified paths (e.g., write use crate::foo::Bar; and then Bar,
not crate::foo::Bar inline). The only exception is when there is a name
collision between two imports, which is rare.Do not write a laundry list of implementation changes. Focus on:
A reader should be able to understand the intent and rationale from the commit message, without following all the code changes in details.
There are three possible development environments:
cargo is available. The buck and arc
commands do not exist.buck is available. The cargo command may not
be configured.buck and cargo are available.How to detect the environment: Check for the presence of a BUCK file in
the project root. BUCK files are not exported to GitHub, so:
BUCK exists → internal checkout, buck and arc are availableBUCK does not exist → GitHub checkout, only cargo worksDo not assume git. This repo may be either a Git checkout or a Sapling (Mercurial-based) checkout. Before running any source-control commands, detect which VCS is in use:
.git exists at the repo root → Git. Use git commands..sl exists at the repo root → Sapling. Use sl commands (sl status,
sl diff, sl commit, sl amend, etc.). Do not use git.You can check with: test -d "$(sl root 2>/dev/null)/.sl" && echo sapling || echo git
The internal (Meta) checkout always uses Sapling. The GitHub checkout uses Git.
buck test pyrefly:pyrefly_library -- <name of test>
(from within the project folder)cargo test <name of test>Note: The heavyweight lsp_interaction tests live in a separate
rust_unittest target for faster iteration. Run them with
buck test pyrefly:pyrefly_lsp_interaction_tests -- <name of test>.
Running buck test pyrefly:pyrefly triggers both test targets.
./test.py runs linters and tests. It is heavyweight, so only run it when
you are confident the feature is complete.test.py auto-detects the build tool based on BUCK file presence.
You can override this with --mode buck or --mode cargo.python3 test.py instead of ./test.py../test.py --no-test --no-conformance --no-jsonschemaarc autocargo to regenerate Cargo.toml files and validate changesAlways run formatting and linting before committing, updating a commit, or
handing code off to a human for review:
./test.py --no-test --no-conformance --no-jsonschema
This applies whether you are committing autonomously or preparing code for a human to commit. Do not skip this step during human-in-the-loop iteration.
bug marker in testsThe testcase! macro supports a bug = "<description>" marker to indicate that
a test captures undesirable behavior. Important points:
bug must pass. The marker documents that the behavior is
wrong, not that the test itself should fail. Do not expect a bug-marked test
to be a failing test.bug = "..." to explain what's wrong. This can be
done to track issues or as part of a stack where a later diff fixes the bug.bug marker
and update the test expectations to reflect the correct behavior.bug marker but update the message if it
has become stale.bug message concise. For complicated bugs, add
detailed explanations as comments inside the test body rather than making the
marker message very long.