Skip to content

Commit f692b10

Browse files
committed
build: copy instructions.md to AGENT.md and clean up build artifacts
Generated-by: aiautocommit
1 parent d66c4bb commit f692b10

2 files changed

Lines changed: 293 additions & 1 deletion

File tree

AGENT.md

Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
Coding instructions for all programming languages:
2+
3+
- If no language is specified, assume the latest version of python.
4+
- If tokens or other secrets are needed, pull them from an environment variable
5+
- Prefer early returns over nested if statements.
6+
- Prefer `continue` within a loop vs nested if statements.
7+
- Prefer smaller functions over larger functions. Break up logic into smaller chunks with well-named functions.
8+
- Only add comments if the code is not self-explanatory. Do not add obvious code comments.
9+
- Do not remove existing comments.
10+
- When I ask you to write code, prioritize simplicity and legibility over covering all edge cases, handling all errors, etc.
11+
- When a particular need can be met with a mature, reasonably adopted and maintained package, I would prefer to use that package rather than engineering my own solution.
12+
- Never add error handling to recover gracefully from an error without being asked to do so. Fail hard and early with assertions and allowing exceptions to propagate whenever possible
13+
- When naming variables or functions, use names that describe the effect. For example, instead of `function handleClaimFreeTicket` (a function which opens a dialog box) use `function openClaimFreeTicketDialog`.
14+
15+
Use line breaks to organize code into logical groups. Instead of:
16+
17+
```python
18+
if not client_secret_id:
19+
raise HTTPException(status.HTTP_400_BAD_REQUEST)
20+
session_id = client_secret_id.split("_secret")[0]
21+
```
22+
23+
Prefer:
24+
25+
```python
26+
if not client_secret_id:
27+
raise HTTPException(status.HTTP_400_BAD_REQUEST)
28+
29+
session_id = client_secret_id.split("_secret")[0]
30+
```
31+
32+
**DO NOT FORGET**: keep your responses short, dense, and without fluff. I am a senior, well-educated software engineer, and do not need long explanations.
33+
34+
### Agent instructions
35+
36+
- When running python tests, use an already open terminal and the `pytest` binary.
37+
- If you added models, generate a migration with `just migration {add,delete,update}_model_other_description`
38+
39+
## Fastapi
40+
41+
- When throwing a `HTTPException`, do not add a `detail=` and use a named status code (`status.HTTP_400_BAD_REQUEST`)
42+
- Do not return a `dict`, instead create a `class RouteNameResponse`
43+
44+
## Pytest Integration Tests
45+
46+
- Look to tests/factories.py to generate any required database state
47+
- Here's an example of how to create + persist a factory `DistributionFactory.build(domain=PYTHON_TEST_SERVER_HOST).save()`
48+
- Add the `server` factory to each test
49+
- Use the `faker` factory to generate emails, etc.
50+
- Don't add obvious `assert` descriptions
51+
52+
## Python App
53+
54+
* Files within `app/commands/` should have:
55+
* Are not designed for CLI execution, but instead are interactor-style internal commands.
56+
* Should not be used on the queuing system
57+
* A `perform` function that is the main entry point for the command.
58+
* Look at existing commands for examples of how to structure the command.
59+
* Use `TypeIDType` for any parameters that are IDs of models.
60+
* Files within `app/jobs/` should have:
61+
* Are designed for use on the queuing system.
62+
* A `perform` function that is the main entry point for the job.
63+
* Look at existing jobs for examples of how to structure the job.
64+
* Use `TypeIDType | str` for any parameters that are IDs of models.
65+
* When referencing a command, use the full-qualified name, e.g. `app.commands.transcript_deletion.perform`.
66+
* When queuing a job or `perform`ing it in a test, use the full-qualified name, e.g. `app.jobs.transcript_deletion.perform`.
67+
68+
## Python Route Tests
69+
70+
- Polyfactory is the [factory](tests/factories.py) library in use. `ModelNameFactory.build()` is how you generate factories.
71+
- Use `assert_status(response)` to check the response of a client
72+
73+
## Python
74+
75+
When writing Python:
76+
77+
* Assume the latest python, version 3.13.
78+
* Prefer Pathlib methods (including read and write methods, like `read_text`) over `os.path`, `open`, `write`, etc.
79+
* Prefer modern typing: `list[str]` over `List[str]`, `dict[str, int]` over `Dict[str, int]`, etc.
80+
* Use Pydantic models over dataclass or a typed dict.
81+
* Use SQLAlchemy for generating any SQL queries.
82+
* Use `click` for command line argument parsing.
83+
* Use `log.info("the message", the_variable=the_variable)` instead of `log.info("The message: %s", the_variable)` or `print` for logging. This object can be found at `from app import log`.
84+
* Log messages should be lowercase with no leading or trailing whitespace.
85+
* No variable interpolation in log messages.
86+
* Do not coerce database IDs or dates to strings
87+
* Do not fix import ordering or other formatting issues.
88+
89+
### Date & DateTime
90+
91+
* Use the `whenever` library for datetime + time instead of the stdlib date library. `Instant.now().format_common_iso()`
92+
93+
### Database & ORM
94+
95+
When accessing database records:
96+
97+
* SQLModel (wrapping SQLAlchemy) is used
98+
* `Model.one(primary_key)` or `Model.get(primary_key)` should be used to retrieve a single record
99+
* Do not manage database sessions, these are managed by a custom tool
100+
* Use `TheModel(...).save()` to persist a record
101+
* Use `TheModel.where(...).order_by(...)` to query records. `.where()` returns a SQLAlchemy select object that you can further customize the query.
102+
103+
When writing database models:
104+
105+
* Don't use `Field(...)` unless required (i.e. when specifying a JSON type for a `dict` or pydantic model using `Field(sa_type=JSONB)`). For instance, use `= None` instead of `= Field(default=None)`.
106+
* Add enum classes close to where they are used, unless they are used across multiple classes (then put them at the top of the file)
107+
* Use `ModelName.foreign_key()` when generating a foreign key field
108+
* Store currency as an integer, e.g. $1 = 100.
109+
110+
Example:
111+
112+
```python
113+
class Distribution(
114+
BaseModel, TimestampsMixin, SoftDeletionMixin, TypeIDMixin("dst"), table=True
115+
):
116+
"""Triple-quoted strings for multi-line class docstring"""
117+
118+
date_field_with_comment: datetime | None = None
119+
"use a string under the field to add a comment about the field"
120+
121+
# no need to add a comment about an obvious field; no need for line breaks if there are no field-level docstrings
122+
title: str = Field(unique=True)
123+
state: str
124+
125+
optional_field: str | None = None
126+
127+
# here's how relationships are constructed
128+
doctor_id: TypeIDType = Doctor.foreign_key()
129+
doctor: Doctor = Relationship()
130+
131+
@computed_field
132+
@property
133+
def order_count(self) -> int:
134+
return self.where(Order.distribution_id == self.id).count()
135+
```
136+
137+
## React Router
138+
139+
- You are using the latest version of React Router (v7).
140+
- The primary export in a routes file should specify `loaderData` like `export default function RouteNamePage({ loaderData }: Route.ComponentProps)`. `loaderData` is the return value from `clientLoader`.
141+
- Use `href("/products/:id", { id: "abc123" })` to generate a url path for a route managed by the application.
142+
- Look at [routes.ts](mdc:web/app/routes.ts) to determine what routes and path parameters exist.
143+
- Use `export async function clientLoader(loaderArgs: Route.ClientLoaderArgs)` to define a `clientLoader` on a route.
144+
- Do not define `Route.*` types, these are autogenerated and can be imported from `import type { Route } from "./+types/routeFileName"`
145+
- If URL parameters or query string values need to be checked before rendering the page, do this in a `clientLoader` and not in a `useEffect`
146+
- Never worry about generating types using `pnpm`
147+
- Use [`<AllMeta />`](web/app/components/shared/AllMeta.tsx) instead of MetaFunction or individual `<meta />` tags
148+
- Use the following pattern to reference query string values (i.e. `?theQueryStringParam=value`)
149+
150+
```typescript
151+
const [searchParams, _setSearchParams] = useSearchParams();
152+
// searchParams contains the value of all query string parameters
153+
const queryStringValue = searchParams.get("theQueryStringParam")
154+
```
155+
156+
### Loading Mock Data
157+
158+
Don't load mock data in the component function with `useEffect`. Instead, load data in a `clientLoader`:
159+
160+
```typescript
161+
162+
// in mock.ts
163+
export async function getServerData(options: any) {
164+
// ...
165+
}
166+
167+
// in web/app/routes/**/*.ts
168+
export async function clientLoader(loaderArgs: Route.ClientLoaderArgs) {
169+
// no error reporting is needed, this will be handled by the `getServerData`
170+
// mock loading functions should return result in a `data` key
171+
const { data } = await getServerData({ /* ... */ })
172+
173+
// the return result here is available in `loaderData`
174+
return data
175+
}
176+
```
177+
178+
### How to use clientLoader
179+
180+
- `export async function clientLoader(loaderArgs: Route.ClientLoaderArgs) {`
181+
- Load any server data required for page load here, not in the component function.
182+
- Use `return redirect(href("/the/url"))` to redirect users
183+
- Use [getQueryParam](web/app/lib/utils.ts) to get query string variables
184+
- `throw new Response` if you need to mimic a 400, 500, etc error
185+
- `loaderArgs` and all sub-objects are all fully typed
186+
- `loaderArgs.params.id` to get URL parameters
187+
188+
### Using API Data
189+
190+
- `~/configuration/client` re-exports all types and functions from `client/*`. Import from `~/configuration/client` instead of anything you find in the `client/` folder/package.
191+
- For each API endpoint, there's a fully typed async function that can be used to call it. Never attempt to call an API endpoint directly.
192+
- When using an import from `~/configuration/client`:
193+
- use `body:` for request params
194+
- always `const { data, error } = await theCall()`
195+
196+
## React
197+
198+
- You are using the latest version of React (v19)
199+
- Do not write any backend code. Just frontend logic.
200+
- If a complex skeleton is needed, create a component function `LoadingSkeleton` in the same file.
201+
- Store components for each major page or workflow in `app/components/$WORKFLOW/$COMPONENT.tsx`.
202+
- If a single page has more than two dedicated components, create a subfolder `app/components/$WORKFLOW/$PAGE/$COMPONENT.tsx`
203+
- Use lowercase dash separated words for file names.
204+
- Use React 19, TypeScript, Tailwind CSS, and ShadCN components.
205+
- Prefer function components, hooks over classes.
206+
- Use ShadCN components in `web/app/components/ui` as your component library. If you need new components, ask for them.
207+
- Never edit the `web/components/ui/*.tsx` files.
208+
- You can find a list of components here https://ui.shadcn.com/docs/components
209+
- Break up large components into smaller components, but keep them in the same file unless they can be generalized.
210+
- Put any "magic" strings like API keys, hosts, etc into a "constants.ts" file.
211+
- For React functional components with three or fewer props, always inline the prop types as an object literal directly in the function signature after the destructured parameters (e.g., `function Component({ prop1, prop2 }: { prop1: string; prop2?: number }) { ... })`. Include default values in destructuring and mark optional props with ? in the type object. Do not use separate interfaces or type aliases; keep types inline. For complex types, add inline comments if needed.
212+
- Put the interface definition right above the related function
213+
- Internally, store all currency values as integers and convert them to floats when rendering visually
214+
- When building forms use React Hook Form.
215+
- Include a two line breaks between any `useHook()` calls and any `useState()` definitions for a component.
216+
- When using a function prop inside a `useEffect`, please use a pattern that avoids including the function in the dependency array, like the `useRef` trick.s
217+
- Use the following pattern to reference query string values (i.e. `?theQueryStringParam=value`):
218+
219+
```typescript
220+
const [searchParams, _setSearchParams] = useSearchParams();
221+
// searchParams contains the value of all query string parameters
222+
const queryStringValue = searchParams.get("theQueryStringParam")
223+
```
224+
225+
### Mock Data
226+
227+
- For any backend communication, create mock responses. Use a async function to return mock data that I will swap out later for a async call to an API.
228+
- When creating mock data, always specify it in a dedicated `web/app/mock.ts` file
229+
- Load mock data using a react router `clientLoader`. Use the Skeleton component to present a loading state.
230+
231+
### React Hook Form
232+
233+
Follow this structure when generating a form.
234+
235+
```tsx
236+
237+
// add a mock function simulating server communication
238+
async function descriptiveServerSendFunction(values: any) {
239+
const mockData = getMockReturnData(/* ... */)
240+
return new Promise(resolve => setTimeout(() => resolve(mockData), 500));
241+
}
242+
243+
const formSchema = z.object({
244+
field_name: z.string(),
245+
// additional schema definition
246+
})
247+
248+
const form = useForm<z.infer<typeof formSchema>>({
249+
resolver: zodResolver(formSchema),
250+
})
251+
252+
async function onSubmit(values: z.infer<typeof formSchema>) {
253+
// ...
254+
await descriptiveSendFunction(values)
255+
// ...
256+
}
257+
258+
return (
259+
<Form {...form}>
260+
<form onSubmit={form.handleSubmit(onSubmit)}>
261+
{/* form fields */}
262+
</form>
263+
</Form>
264+
)
265+
```
266+
267+
## Shell
268+
269+
- Assume zsh for any shell scripts. The latest version of modern utilities like ripgrep (rg), fdfind (fd), bat, httpie (http), zq (zed), jq, procs, rsync are installed and you can request I install additional utilities.
270+
271+
## Typescript
272+
273+
- Use `pnpm`, not `npm`
274+
- Node libraries are not available
275+
- Use `lib/` for generic code, `utils/` for project utilities, `hooks/` for React hooks, and `helpers/` for page-specific helpers.
276+
- Prefer `function theName() {` over `const theName = () =>`
277+
- Use `import { invariant } from @epic-web/invariant` instead of another invariant library
278+
279+
Here's how frontend code is organized in `web/app/`:
280+
281+
- `lib/` not specific to the project. This code could be a separate package at some point.
282+
- `utils/` project-specific code, but not specific to a particular page.
283+
- `helpers/` page- or section-specific code that is not a component, hook, etc.
284+
- `hooks/` react hooks.
285+
- `configuration/` providers, library configuration, and other setup code.
286+
- `components/` react components.
287+
- `ui/` reusable ShadCN UI components (buttons, forms, etc.).
288+
- `shared/` components shared across multiple pages.
289+
- create additional folders for route- or section-specific components.
290+

Justfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
build:
22
python explode.py
3+
# for amp
4+
cp instructions.md AGENT.md
35

46
clean:
5-
rm -rf .github .cursor
7+
rm -rf .github .cursor AGENT.md

0 commit comments

Comments
 (0)