Coding instructions for all programming languages:

- Never use emojis anywhere unless explicitly requested.
- If no language is specified, assume the latest version of python.
- If tokens or other secrets are needed, pull them from an environment variable
- Prefer early returns over nested if statements.
- Prefer `continue` within a loop vs nested if statements.
- Prefer smaller functions over larger functions. Break up logic into smaller chunks with well-named functions.
- Prefer constants with separators: `10_000` is preferred to `10000` (or `10_00` over `1000` in the case of a integer representing cents).
- Only add comments if the code is not self-explanatory. Do not add obvious comments.
- Do not remove existing comments.
- Do not capitalize or add periods at the end of single-line comments.
- When I ask you to write code, prioritize simplicity and legibility over covering all edge cases, handling all errors, etc.
- 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.
- Never add error handling to catch an error without being asked to do so. Fail hard and early with assertions and allow exceptions to propagate.
- 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`.
- Do not install missing system packages! Instead, ask me to install them for you.
- If terminal commands are failing because of missing variables or commands which are unrelated to your current task, stop your work and let me know.
- Don't worry about fixing lint errors or running lint scripts unless I specifically ask you to.
- When implementing workarounds for tooling limitations (like using `Any` for unresolvable types) or handling non-obvious edge cases, always add a brief inline comment explaining the technical reasoning.
- Reserve exact-width `#`-box section separators for long files requiring organization, though they should not be necessary in the large majority of cases (separate files is generally better).

Use line breaks to organize code into logical groups. Instead of:

```python
if not client_secret_id:
    raise HTTPException(status.HTTP_400_BAD_REQUEST)
session_id = client_secret_id.split("_secret")[0]
```

Prefer:

```python
if not client_secret_id:
    raise HTTPException(status.HTTP_400_BAD_REQUEST)

session_id = client_secret_id.split("_secret")[0]
```

**DO NOT FORGET**: keep your responses short, dense, and without fluff. I am a senior, well-educated software engineer, and hate long explanations.

### Add Comments for Expert Engineer with Limited Domain Knowledge

The engineer reading your code is a world-class software engineer, but is not familiar with the internals of every system. Include concise one-line comments explaining key hooks, API usage, blocks of logic, etc., to help the reader quickly understand the code you've written.

In other words, embed the business requirements as comments in the code when the code does not self-document.

### Important Workflow Rules

Pay careful attention to these instructions when running tests, generating database migrations, or otherwise figuring out how to operate this project:

- Run `just` to understand the more important workflow commands.
  - Run `just --list` to see all available pre-written workflow development commands.
- **IMPORTANT:** Never manually set environment variables that are required. You can set optional variables for debugging, but any missing required environment variables is an error that should be reported and you should stop your work immediately.
- **NEVER** git commit changes. Always let me run any git commands which are not read-only.
- Do not worry about cleaning up the environment. This is done automatically.
- Run python code with `uv run python`
- Use `pytest` to run tests. If tests fail because of a configuration, environment, or system error: let me know and stop working.
  - Initially run `pytest --ignore=tests/integration` then only run `pytest tests/integration`
  - When debugging integration tests look at `$PLAYWRIGHT_RESULT_DIRECTORY`. There's a directory for each test failure. In that directory you fill find a `failure.html` containing the rendered DOM of the page on failure and a screenshot of the contents. Use these to debug why it failed.
- Do not attempt to create or run database migrations. Pause your work and let me know you need a migration run.
  - If you receive errors about missing migrations, missing tables, database connectivity, etc, stop your work and let me know.


## Alembic Migrations


### Default Content for New Non-Nullable Columns

To add a non-nullable column and set a specific value for all existing rows without a persistent server default:

```python
# 1. Add the column as nullable (no default needed):
op.add_column('distribution', sa.Column('default_campaign_ending_date', sa.DateTime(timezone=True), nullable=True))
# 2. Update existing rows with your desired value (e.g., a specific datetime)
op.execute("UPDATE distribution SET default_campaign_ending_date = %s", [datetime.utcnow()])
# 3. Alter the column to non-nullable:
op.alter_column('distribution', 'default_campaign_ending_date', nullable=False)
```

### Record Backfill Operations

For migrations that include data mutation, and not only schema modifications, use this pattern to setup a session:

```python
from alembic import op
from sqlmodel import Session
from activemodel.session_manager import global_session
from app import log

def run_migration_helper():
  pass

def upgrade() -> None:
  session = Session(bind=op.get_bind())

  with global_session(session):
      run_migration_helper()
      flip_point_coordinates()
      backfill_screening_host_data()

  # flush before running any other operations, otherwise not all changes will persist to the transaction
  session.flush()
```

However, if you don't need the business logic attached to the models, you can execute a query using `op.execute`:

```python
op.execute(
  TheModel.__table__.update().values({"a_field": "a_value"}) # type: ignore
)
```


## Fastapi


- When throwing a `HTTPException`, do not add a `detail=` and use a named status code (`status.HTTP_400_BAD_REQUEST`)
- Do not return a `dict`, instead create a `class RouteNameResponse`
  - Locate these classes right above the `def route_name():` function which uses them.
- Use `Model.one` when a record must exist in order for the business logic to succeed.
- Do not try/except `Model.one` when using a parameter from the request to pull a record. Let this exception bubble up.
- Use `model_id: Annotated[TypeID, Path()]` to represent a model ID as a URL path parameter
- Use the typed route helpers in `app/generated/fastapi_typed_routes.py` for all URL generation.


## Justfiles


- Never use `just_executable()` to reference the executable for `just`. If `just` DNE, then something is wrong adn you should stop your work and let me know.
- You should not have to mutate `$PATH`. If you cannot find an expected binary, stop your work and let me know.


## Python App


- `app/lib/` is for code that is not specified to this application and with some effort could extracted into a external package.
- `app/helpers` is for larger reusable modules that if they weren't specific to this application, could be extracted into their own package.
- `app/utils` are small helper functions that are specific to a particular page or area of the application.
- `app/__init__.py` is the entrypoint for the application which is run when _anything_ is executed (fastapi, celery, etc).
  - It primarily runs `configure_*` commands for any `app.configuration.*` modules. These modules primary setup API clients, database connections, python language configuration, etc.
  - Also makes sure anything that mutates global state loads early.
- FastAPI server and routes are specified in `app/routes/`
- SQLModels are specified in `app/models/`
- Files within `app/commands/` should have:
  - Are not designed for CLI execution, but instead are interactor-style internal commands.
  - Should not be used on the queuing system
  - A `perform` function that is the main entry point for the command.
  - Look at existing commands for examples of how to structure the command.
  - Use `TypeID` for any parameters that are IDs of models.
- Files within `app/jobs/` should have:
  - Are designed for use on the queuing system.
  - A `perform` function that is the main entry point for the job.
  - Look at existing jobs for examples of how to structure the job.
  - Use `TypeID | str` for any parameters that are IDs of models.
- When referencing a command, use the full-qualified name, e.g. `app.commands.transcript_deletion.perform`.
- When queuing a job or `perform`ing it in a test, use the full-qualified name, e.g. `app.jobs.transcript_deletion.perform`.
- `app/cli/` is for scripts or CLI tools that are specific to the application.

### Factories

globs: app/factories/**/.py

* Each model should get it's own file under app/factories/model_name.py
* `ActiveModelFactory` (which is a polyfactory subclass) should be used.
* Use `BaseFactory.__faker__` to generate more specific fake data for important fields (used in routes, etc)
* Prefer `slug = BaseFactory.__faker__.unique.slug` to `slug = Use(lambda: BaseFactory.__faker__.unique.slug())`

#### Factory Example

```python
class ScreeningFactory(ActiveModelFactory[Screening]):
    funding_goal = lambda: BaseFactory.__faker__.random_int(
        min=0, max=2000_00
    )

    ticket_price = DEFAULT_TICKET_PRICE
    status = ScreeningStatus.active

    # always None
    funding_ending_at = None

    # pick entry from a fixed list
    zip_code = lambda: BaseFactory.__faker__.random_element(elements=REAL_ZIP_CODES)

    host_name = BaseFactory.__faker__.name
    host_description = lambda: BaseFactory.__faker__.paragraph(nb_sentences=2)

    # this method runs before the model is persisted to the database
    @classmethod
    def post_build(cls, model):
      # if the user does not pass in a important relationship during creation, you can generate a factory fallback
        if not model.distribution_id:
            model.distribution_id = DistributionFactory.save().id

        return model.save()

    # runs after the model is persisted to the database
    @classmethod
    def post_save(cls, model):
        return model.save()
```

### Database & ORM

When accessing database records:

* SQLModel (wrapping SQLAlchemy) is used
* `Model.one(primary_key)` or `Model.get(primary_key)` should be used to retrieve a single record
* Do not manage database sessions, these are managed by a custom tool
  * Use `TheModel(...).save()` to persist a record
  * Use `TheModel.where(...).order_by(...)` to query records. `.where()` returns a SQLAlchemy select object that you can further customize the query.
  * To iterate over the records, you'll need to end your query chain with `.all()` which returns an interator: `TheModel.where(...)...all()`
* Instead of repulling a record `order = HostScreeningOrder.one(order.id)` refresh it using `order.refresh()`

When writing database models:

* 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)`.
* 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)
* Use `ModelName.foreign_key()` when generating a foreign key field
* Store currency as an integer, e.g. $1 = 100.
* `before_save`, `after_save(self):`, `after_updated(self):` are lifecycle methods (modelled after ActiveRecord) you can use.

Example:

```python
class Distribution(
    BaseModel, TimestampsMixin, SoftDeletionMixin, table=True
):
    """Triple-quoted strings for multi-line class docstring"""

    id: TypeID[Literal["dst"]] = TypeIDPrimaryKey("dst")

    date_field_with_comment: datetime | None = None
    "use a string under the field to add a comment about the field"

    # no need to add a comment about an obvious field; no need for line breaks if there are no field-level docstrings
    title: str = Field(unique=True)
    state: str

    optional_field: str | None = None

    # here's how relationships are constructed
    doctor_id: TypeID = Doctor.foreign_key()
    doctor: Doctor = Relationship()

    @computed_field
    @property
    def order_count(self) -> int:
        return self.where(Order.distribution_id == self.id).count()
```


## Python


When writing Python:

* Assume the latest python, version 3.13.
* Prefer Pathlib methods (including read and write methods, like `read_text`) over `os.path`, `open`, `write`, etc.
* Prefer docstr to multi-line comments at the top of a function or file.
* If a docstr does not span multiple lines, do not use triple-quoted strings.
* Do not create `__init__` files unless specifically instructed
* Use Pydantic models over dataclass or a typed dict.
* Use SQLAlchemy for generating any SQL queries.
* Use `click` for command line argument parsing.
* 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`.
  * Log messages should be lowercase with no leading or trailing whitespace.
  * No variable interpolation in log messages.
  * Do not coerce database IDs, dates, or Path objects to `str`
* Do not fix import ordering or other linting issues.
* Never edit or create any files in `migrations/versions/`
* Place all comments on dedicated lines immediately above the code statements they describe. Avoid inline comments appended to the end of code lines.
* Do not `try/catch` raw `Exceptions` unless explicitly told to. Prefer to let exceptions raise and cause an explicit error.
* Always make an explicit copy before mutating a dictionary that you did not create in the current narrow scope.
* Never use `from __future__`
* Always use `re.compile(pattern, re.VERBOSE)` and inline `#` comments to document the logic of each capture group or condition in any complex regular expression.
* **IMPORTANT** never edit app/generated/ files. These are autogenerated.

### Package Management

- Use `uv add` to add python packages. No need for `pip compile`, `pip install`, etc.

### Typing

* Assume the latest pyright version
* Prefer modern typing: `list[str]` over `List[str]`, `dict[str, int]` over `Dict[str, int]`, etc.
* Prefer to keep typing errors in place than eliminate type specificity:
  * Do not add ignore comments such as `# type: ignore`
  * Never add an `Any` type.
  * Do not `cast(object, ...)`

### Data Manipulation

* Prefer `funcy` utilities to complex list comprehensions or repetitive python statements.
* `import funcy as f` and `import funcy_pipe as fp`
* Some utilities to look at: `f.compact`

For example, instead of:

```python
params: dict[str, str] = {}
if city:
    params["city"] = city
if state_code:
    params["stateCode"] = state_code
```

Use:

```python
params = f.compact({"city": city, "stateCode": stateCode})
```

### Date & DateTime

* Use the `whenever` library for datetime + time instead of the stdlib date library. `Instant.now().format_iso()`
* DateTime mutation should explicitly opt in to a specific timezone `SystemDateTime.now().add(days=-7)`


## React Router


- You are using the latest version of React Router (v7).
- Always include the suffix `Page` when naming the default export of a route.
- 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`.
- Use `href("/products/:id", { id: "abc123" })` to generate a url path for a route managed by the application.
  - Look at [routes.ts](mdc:web/app/routes.ts) to determine what routes and path parameters exist.
- Use `export async function clientLoader(loaderArgs: Route.ClientLoaderArgs)` to define a `clientLoader` on a route.
- Do not define `Route.*` types, these are autogenerated and can be imported from `import type { Route } from "./+types/routeFileName"`
- 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`
- Never worry about generating types using `pnpm`
- Use [`<AllMeta />`](web/app/components/shared/AllMeta.tsx) instead of MetaFunction or individual `<meta />` tags
- Use the following pattern to reference query string values (i.e. `?theQueryStringParam=value`)

```typescript
const [searchParams, _setSearchParams] = useSearchParams()
// searchParams contains the value of all query string parameters
const queryStringValue = searchParams.get("theQueryStringParam")
```

### Loading Mock Data

Don't load mock data in the component function with `useEffect`. Instead, load data in a `clientLoader`:

```typescript
// in mock.ts
export async function getServerData(options: any) {
  // ...
}

// in web/app/routes/**/*.ts
export async function clientLoader(loaderArgs: Route.ClientLoaderArgs) {
  // no error reporting is needed, this will be handled by the `getServerData`
  // mock loading functions should return result in a `data` key
  const { data } = await getServerData({
    /* ... */
  });

  // the return result here is available in `loaderData`
  return data;
}
```

### How to Use `clientLoader`

- `export async function clientLoader(loaderArgs: Route.ClientLoaderArgs) {`
- Load any server data required for page load here, not in the component function.
- Use `return redirect(href("/the/url"))` to redirect users
- Use [getQueryParam](web/app/lib/utils.ts) to get query string variables
- `throw new Response` if you need to mimic a 400, 500, etc error
- `loaderArgs` and all sub-objects are all fully typed
- `loaderArgs.params.id` to get URL parameters

### Loading Backend Data

- `~/configuration/client` re-exports all types and functions from `client/*`. Import from `~/configuration/client` instead of anything you find in the `client/` folder/package.
- 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.
  - Do not generate types for API parameters or responses. Reference the autogenerated types that are re-exported in `~/configuration/client`
  - For instance, the `getSignedUrl` function in [web/client/sdk.gen.ts] has a `SignedUrlResponse` type in [web/client/types.gen.ts]
  - This same type is used in the function signature, i.e. `type SignedUrlResponse = Awaited<ReturnType<typeof getSignedUrl>>["data"]`

- When using an import from `~/configuration/client`:
  - use `body:` for request params
  - always `const { data, error } = await theCall()`

`clientLoader` can only be used on initial page load within a route. If you need to load additional server data on component mount:

```tsx
import { useQuery } from "@tanstack/react-query"
import {
  // these options correspond to the server route
  createCheckoutSessionOptions,
  publicClient,
} from "~/configuration/client"

function TheComponent() {
  const { data, error } = useQuery({
    enabled: open,
    ...createCheckoutSessionOptions({
      // or `client` if authenticated
      client: publicClient,
      body: { /* API parameters here */ },
    }),
  })

  // remember to display errors by checking `error`
}
```


## React


- You are using the latest version of React (v19)
- Do not write any backend code. Just frontend logic.
- If a complex skeleton is needed, create a component function `LoadingSkeleton` in the same file.
- Store components for each major page or workflow in `app/components/$WORKFLOW/$COMPONENT.tsx`.
  - If a single page has more than two dedicated components, create a subfolder `app/components/$WORKFLOW/$PAGE/$COMPONENT.tsx`
- Use lowercase dash separated words for file names.
- Use React 19, TypeScript, Tailwind CSS, and ShadCN components.
- Prefer function components, hooks over classes.
- Use ShadCN components in `web/app/components/ui` as your component library. If you need new components, ask for them.
  - Never edit the `web/components/ui/*.tsx` files.
  - You can find a list of components here https://ui.shadcn.com/docs/components
- Break up large components into smaller components, but keep them in the same file unless they can be generalized.
- Put any "magic" strings like API keys, hosts, etc into a "constants.ts" file.
- 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.
- Put the interface definition right above the related function
- Internally, store all currency values as integers and convert them to floats when rendering visually
- When building forms use React Hook Form.
- Include a two line breaks between any `useHook()` calls and any `useState()` definitions for a component.
- 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.
- When writing React components, always hoist complex conditional expressions into descriptively named constants at the top of the component function for better readability and maintainability.
- When managing API response data, store the entire response object (or relevant subset) in a single `useState` rather than creating separate state variables for each field. Derive individual values from the response object when passing to child components using optional chaining (e.g., response?.field || defaultValue).
- Refactor ternary to &&: `{condition ? <A/> : <B/>}` → `{condition && <A/>}{!condition && <B/>}`
- Use the following pattern to reference query string values (i.e. `?theQueryStringParam=value`):

```typescript
const [searchParams, _setSearchParams] = useSearchParams();
// searchParams contains the value of all query string parameters
const queryStringValue = searchParams.get("theQueryStringParam")
```

### Mock Data

- 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.
- When creating mock data, always specify it in a dedicated `web/app/mock.ts` file
- Load mock data using a react router `clientLoader`. Use the Skeleton component to present a loading state.

### React Hook Form

Follow this structure when generating a form.

```tsx

// add a mock function simulating server communication
async function descriptiveServerSendFunction(values: any) {
  const mockData = getMockReturnData(/* ... */)
  return new Promise(resolve => setTimeout(() => resolve(mockData), 500));
}

const formSchema = z.object({
  field_name: z.string(),
  // additional schema definition
})

const form = useForm<z.infer<typeof formSchema>>({
  resolver: zodResolver(formSchema),
})

const {
  formState: { isSubmitting, errors },
  setError,
  clearErrors,
} = form


async function onSubmit(values: z.infer<typeof formSchema>) {
  clearErrors("root")

  // ...
  const { data, error } = await descriptiveSendFunction(values)

  if (error) {
    setError("root.serverError", { message: error.detail?.[0]?.msg })
    return
  }
  // ...
}

return (
  <Form {...form}>
    <form onSubmit={form.handleSubmit(onSubmit)}>
      {/* form fields */}

      <ServerErrorAlert error={errors.root?.serverError} />

      <Button
        type="submit"
        disabled={isSubmitting}
      >
        {isSubmitting ? "Submitting..." : "Submit"}
      </Button>
    </form>
  </Form>
)
```

### Styling

* Use `text-blue-link` for styling any simple `<a>` tags


## Shell


- 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.


## Typescript


- Use `pnpm` or `pnpx` and not `npm` or `npx`.
  - Use `just js_shadcn`, `just pnpm`, and `just js_lint` instead of executing these operations exactly. @just/javascript.just
- Node libraries are not available
- Use `lib/` for generic code, `utils/` for project utilities, `hooks/` for React hooks, and `helpers/` for page-specific helpers.
- Prefer `function theName() {` over `const theName = () =>`
- Use `import { invariant } from @epic-web/invariant` instead of another invariant library
- Use `requireEnv("VITE_THE_ENV_VAR")` instead of `process.env.THE_ENV_VAR`
- Don't use `console.{log,error}`. Use `from ~/configuration/logging import log` and `log.info("string", {structured: "log"})` instead.

Here's how frontend code is organized in `web/app/`:

- `lib/` not specific to the project. This code could be a separate package at some point.
- `utils/` project-specific code, but not specific to a particular page.
- `helpers/` page- or section-specific code that is not a component, hook, etc.
- `hooks/` react hooks.
- `configuration/` providers, library configuration, and other setup code.
- `components/` react components.
  - `ui/` reusable ShadCN UI components (buttons, forms, etc.).
  - `shared/` components shared across multiple pages.
  - create additional folders for route- or section-specific components.

### Dates & Times

* Always use the ISO 8601 format when sending dates in an API request.
* Use `Temporal` for any date or time manipulation. You can assume it's available in the browser.
* DateTime objects should always be converted to UTC before included in any API request. Never send a timestamp with the user's timezone.
* Unless otherwise specified, do not shift server-provided times based on the user's timezone.
