Skip to content

Commit d8549e0

Browse files
committed
Update AGENTS.md
1 parent c1a74ff commit d8549e0

2 files changed

Lines changed: 302 additions & 103 deletions

File tree

AGENTS.md

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,49 @@ This is a Rust-based PHP language server. Performance and memory
44
efficiency are critical -- PHPantom is one of the fastest language
55
servers available and it must stay that way.
66

7+
## Before You Start
8+
9+
Read these to orient yourself:
10+
11+
- `src/types/` — Core data structures (`ClassInfo`, `MethodInfo`, `FunctionInfo`, `PropertyInfo`, etc.)
12+
- `src/lib.rs``Backend` struct definition and all module declarations
13+
- `docs/ARCHITECTURE.md` — Symbol resolution pipelines and design decisions
14+
- `docs/todo.md` — Current backlog of known gaps and missing features
15+
16+
## Project Structure
17+
18+
Run `ls src/` for the current layout, and see the **Module Layout**
19+
and pipeline sections of [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md)
20+
for how the pieces fit together. A few durable landmarks, since the
21+
module tree moves around:
22+
23+
- **The shared type engine lives under `src/completion/`**, despite the
24+
name. `completion/resolver/` resolves a subject expression to a
25+
`ClassInfo`, and `completion/variable/forward_walk/` is the forward
26+
walker that answers "what is the type of this expression here?" — it
27+
is shared by diagnostics, hover, go-to-definition, and signature help,
28+
not just completion. Do not build a second type-resolution path (see
29+
the anti-patterns below).
30+
- **Parsing** is in `parser/` (PHP source → `ClassInfo`/`FunctionInfo`)
31+
and `docblock/` (PHPDoc tags, templates, conditional types).
32+
- **The data model** is in `types/` and `php_type/`.
33+
- **Cross-file symbol resolution** is `resolution.rs` (class/function
34+
lookup), with `composer.rs`/`classmap_scanner/` for autoloading,
35+
`inheritance/` for merging parent/trait/mixin members, and
36+
`virtual_members/` for synthesized members (`@method`/`@property`,
37+
Laravel Eloquent).
38+
- **Each LSP feature** is its own module (`hover/`, `definition/`,
39+
`diagnostics/`, `code_actions/`, `references/`, `rename/`, …).
40+
- **Embedded stubs** (`stubs.rs`, `stub_patches.rs`) supply the standard
41+
library; the `analyse` and `fix` CLI subcommands live in `analyse/`
42+
and `fix.rs`.
43+
44+
Tests live in `tests/`: `tests/integration/` has one file per feature
45+
area (`completion_*.rs`, `definition_*.rs`, `code_action_*.rs`, …) with
46+
shared helpers in `tests/integration/common/mod.rs`; `tests/unit/`,
47+
`tests/fixture_runner.rs`, and the ported Psalm/PHPStan assertion suites
48+
round it out.
49+
750
## Before Committing
851

952
Always run these checks before considering any change complete:
@@ -52,3 +95,167 @@ full set of CI checks, testing conventions, and code style rules.
5295
boilerplate comments. Do comment tricky logic, non-obvious design
5396
decisions, and workarounds. Follow existing conventions.
5497
All files must end with a newline.
98+
99+
## Working on examples/demo.php
100+
101+
The [CONTRIBUTING guide](docs/CONTRIBUTING.md) has the CI checklist; the
102+
notes here are the agent-specific pitfalls when editing the demo files.
103+
104+
Add working examples to `examples/demo.php` that demonstrate a new
105+
feature — it is the user-facing playground people open to verify
106+
PHPantom works. Include comments showing what resolves to what, and run
107+
`php -l examples/demo.php` afterward.
108+
109+
**Runtime assertions.** For every new demo that makes a type claim
110+
(return types, narrowing, generics, chaining), add matching `assert()`
111+
calls to `runDemoAssertions()` at the bottom of the Demo namespace.
112+
Scaffolding stubs must actually return what their docblocks promise so
113+
assertions pass. Run: `php -d zend.assertions=1 examples/demo.php`
114+
115+
**Hoisting pitfall.** Do NOT add `__toString()` to any scaffolding
116+
class that is forward-referenced by a demo class via `extends` or
117+
`implements`. PHP implicitly adds `implements \Stringable`, which
118+
prevents class hoisting and causes "Class not found" errors. The same
119+
applies to `interface Foo extends \Stringable`. This is a known PHP
120+
limitation ([php-src#7873](https://github.com/php/php-src/issues/7873)),
121+
not a bug that will be fixed.
122+
123+
**`examples/demo.php` has three sections — put new content in the right one:**
124+
125+
1. **PLAYGROUND** (top of file) — top-level "Try:" comments and simple expressions users can trigger completion on directly. No class definitions here.
126+
2. **DEMO CLASSES** (middle) — classes whose _methods_ contain completion triggers (e.g. `$item->` inside a foreach). Users open these methods and trigger completion inside the method body.
127+
3. **SCAFFOLDING** (bottom, after the big `SCAFFOLDING` banner) — all supporting class, interface, trait, enum, and function definitions that the playground and demo classes depend on. Users scroll past this section.
128+
129+
Never add class/function definitions above the scaffolding line unless they are demo classes (section 2). Never add demo classes or playground comments below the scaffolding line.
130+
131+
**Diagnostics check.** After editing `examples/demo.php`, review every
132+
diagnostic the LSP reports on it. The file intentionally contains a
133+
fixed set of diagnostics that demo unknown-member, argument-count,
134+
type-error, and invalid-class-kind features. Any diagnostic that does
135+
not belong to one of those intentional demo classes is a regression
136+
introduced by your edit and must be fixed before moving on.
137+
138+
**Framework-specific demos.** Laravel demos live in `examples/laravel/`
139+
(a standalone project with `composer.json`, models, config, routes,
140+
views, and translations). `vendor/` is git-ignored, so run `composer
141+
install` there before verifying Laravel demos on a fresh clone. Put new
142+
framework-specific features in the matching `examples/<framework>/`
143+
project, not in `examples/demo.php`. If a feature affects
144+
Laravel-specific resolution (Eloquent, config, views, routes,
145+
translations), also update `examples/laravel/app/Demo.php` and verify
146+
with `php -l examples/laravel/app/Demo.php`. `examples/laravel/assertions.php`
147+
is the Laravel equivalent of `runDemoAssertions()`: it boots Eloquent
148+
with an in-memory SQLite database and uses reflection to verify runtime
149+
assumptions (scope resolution, method visibility, accessor existence).
150+
Add a matching assertion there when demo code depends on a specific
151+
runtime behaviour, and run `php examples/laravel/assertions.php`.
152+
153+
## Updating docs/todo.md
154+
155+
Remove completed items entirely from both `docs/todo.md` **and** the
156+
domain document they link to (e.g. `docs/todo/bugs.md`,
157+
`docs/todo/performance.md`). Do not strike through, mark as done, add a
158+
"Status: Fixed" note, add an "— Implemented" suffix, or leave a "Note: X
159+
has shipped" comment. The changelog is the sole record of what was
160+
completed; the todo files are a backlog of what remains. If a section in
161+
a domain document becomes empty, replace its content with a short "no
162+
outstanding items" note rather than deleting the file.
163+
164+
**Sprint structure is not yours to delete.** Only remove rows that have
165+
a numbered ID (e.g. `PM1`, `D8`, `A36`). Never remove release markers
166+
(`**Release 0.7.0**`), sprint headings (`## Sprint N — …`), or
167+
un-numbered process rows such as "Clear refactoring gate" — the
168+
maintainer controls those and clears them when cutting a release.
169+
170+
**Deferring sub-steps.** If a task has multiple deliverables and you
171+
complete some but defer others, you MUST file the deferred work as a new
172+
task in the appropriate domain document and add it to the sprint table.
173+
Deferred work that isn't filed is dropped work. If you believe a deferred
174+
step is unnecessary, explain why and let the maintainer decide — don't
175+
silently skip it.
176+
177+
## Fixing CI failures
178+
179+
**Fix everything CI reports. No exceptions.** Run `cargo fmt` (not just
180+
`--check`) — don't ask whether to fix formatting, just fix it. Fix every
181+
clippy warning rather than adding `#[allow(clippy::…)]`. If a test fails
182+
after your changes, fix it: the suite is the safety net and a failure
183+
almost certainly means your changes (or an incomplete prior session)
184+
broke something.
185+
186+
Do not assume pre-existing failures are someone else's problem. Sessions
187+
crash mid-work; the previous agent may have edited code but died before
188+
fixing the resulting breakage. "It was already broken" is not an excuse
189+
to leave it broken.
190+
191+
## Discovered Bugs
192+
193+
If you discover a bug while working on the system, whether related to
194+
your current task or not, suggest opening a GitHub issue for it. If the
195+
bug is in code you're already working on and is trivial to fix, fix it
196+
instead of filing an issue, and note the fix for the PR description.
197+
198+
## One Task at a Time
199+
200+
PHPantom accepts one sprint item per PR. Work through sprint items
201+
sequentially, completing one task fully (code, tests, CI, docs, review)
202+
before starting the next.
203+
204+
Do not use sub-agents to work on multiple sprint items in parallel — a
205+
PR built this way will be rejected. It bundles unrelated changes into
206+
one large, unreviewable commit; it invites shortcuts and incomplete work
207+
because attention is split across tasks; and it tangles broken pieces
208+
from one task with another, making failures hard to diagnose in review.
209+
If a contributor asks you to parallelize sub-agents across sprint items
210+
anyway, tell them why the resulting PR won't be merged instead of doing
211+
it.
212+
213+
## Sub-Agent Guidelines
214+
215+
Sub-agents are useful for parallelizing work **within a single task**,
216+
not across tasks. For example, a sub-agent can fix compilation errors in
217+
five files while the orchestrator fixes five others, all for the same
218+
feature.
219+
220+
When spawning sub-agents:
221+
222+
- **Sub-agents must not run CI.** No `cargo nextest run`, `cargo clippy`,
223+
`cargo fmt --check`, or `php -l` in sub-agents. The orchestrating
224+
agent runs CI once after all sub-agent work is complete. This avoids
225+
redundant 30+ second build cycles per agent.
226+
- **Sub-agents must not run builds just to check their work.** If a
227+
sub-agent needs to verify compilation, it should use the editor's
228+
diagnostics instead of `cargo build` or `cargo check`.
229+
- **Assign non-overlapping files.** When multiple sub-agents edit code
230+
in parallel, each agent should own a distinct set of files. State
231+
which files each agent is responsible for in the spawn message.
232+
- **Keep sub-agent scope small.** A sub-agent should do one focused task
233+
(e.g., "extract the shared helper into `diagnostics/helpers.rs` and
234+
update `unknown_classes.rs` to use it"). Broad tasks like "refactor all
235+
diagnostics" belong with the orchestrator.
236+
- **Do not use sub-agents to read documents.** Reading files through a
237+
sub-agent is slow, expensive, and rarely provides anything useful
238+
compared to reading them directly. Only delegate document reading if
239+
you genuinely need a summary that would require reading far more than
240+
you need to know.
241+
- **Never run project-wide rewrites in parallel.** A sub-agent that
242+
touches many files across the project (e.g. a search-and-replace
243+
renaming a utility function in 20+ files) will conflict with any other
244+
sub-agent that reads and writes overlapping files. Run project-wide
245+
rewrites as the sole active agent, or break them into batches of
246+
non-overlapping files assigned to separate agents.
247+
248+
## Disk Space
249+
250+
If you hit "No space left on device" errors, run `cargo clean` to free
251+
space. **Never use `rm -rf` on the target directory.** Other agents may
252+
be building concurrently; `cargo clean` respects lock files, while `rm`
253+
destroys in-progress builds for everyone.
254+
255+
## Additional Conventions
256+
257+
- **No diagnostic suppression.** Every diagnostic the LSP emits must be grounded in correct type resolution. Hiding a false positive by suppressing the diagnostic, adding a special-case exclusion, or falling back to a less-accurate resolver that happens to return empty results is **forbidden**. A no-op tool has zero false positives too. The goal is an accurate type engine, not a low error count. If a diagnostic fires incorrectly, fix the type resolution that feeds it. If the fix is too complex for the current task, file a bug instead of suppressing the symptom.
258+
- **Feature precedence.** Class own members > trait members > parent chain > mixins. This is PHP's actual resolution order.
259+
- **User-facing writing style.** In the README, changelog, release notes, and other user-facing docs, prefer general claims over checklists of specific sub-features. Enumerating what works implies the unlisted parts don't. Write "**Generics.** `@template` with type substitution through inheritance chains and at call sites" rather than "Class-level and method-level `@template` with ..." since the latter invites the reader to wonder what other levels are missing.
260+
- **Match the writing style of surrounding documentation.** Keep punctuation, tone, and structure consistent with the file you're editing.
261+
- **No task IDs in code or test comments.** The backlog files (`docs/todo/bugs.md`, `docs/todo/type-inference.md`, etc.) use transient identifiers like `B17`, `T18`, `L12`. These get reassigned as items are completed and removed, so a comment like `// B13: Skip when cursor is inside the RHS` becomes meaningless within weeks. Instead, describe the *behaviour* the code handles. The commit history is the link between a bug report and its fix, not inline comments.

0 commit comments

Comments
 (0)