.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.
Pyrefly is developed both on GitHub and inside Meta's monorepo, and the
available tooling differs. How to detect which one you are in: check for a
BUCK file in the project root — BUCK files are not exported to GitHub.
BUCK → GitHub checkout. Only cargo is available, buck and arc do
not exist, and source control is git. The rest of this file assumes this case.BUCK present → Meta-internal checkout. Read facebook/AGENTS.md, which
covers the internal tooling and conventions (buck, arc, Sapling, Phabricator
diffs) and overrides this file where they conflict.cargo test <name of test>./test.py runs linters and tests. It is heavyweight, so only run it when
you are confident the feature is complete.python3 test.py instead of ./test.py../test.py --no-test --no-tensor-shapes --no-conformance --no-jsonschemaAlways run formatting and linting before committing, updating a commit, or
handing code off to a human for review:
./test.py --no-test --no-tensor-shapes --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. If there is an associated Github issue, linking to it
in a comment is often sufficient without paraphrasing the issue in the test.testcase! header hygieneThe macro uses line!() to map errors in the embedded source back to the test file,
assuming a fixed layout. Extra lines in the header shift every reported line number.
testcase!(, never between it and the r#"..."# content.bug = "..." on one line, with no blank lines in the header.rustfmt re-splits a bug = line past 100 cols, so keep the message short
enough to fit; put longer detail in a comment above the macro.assert_type over reveal_typeassert_type checks for type equivalence, whereas reveal_type expectations
do a more fragile text-based match. Prefer to use assert_type when possible.
It's acceptable to use reveal_type in cases in which the expected type cannot
be expressed in a type annotation - for example, a complex function signature.