# WindsurfRules — {{app_title}}

This project was generated with the VeloIQ™ framework. Use the `veloiq` CLI for all scaffolding.

## Project layout

```
backend/
  app/
    main.py               # Entry point — do not edit
    modules/              # Feature modules
      <module>/
        models.py         # SQLModel table definition — edit this
        api.py            # Auto-generated CRUD — do not edit
        custom_api.py     # Custom endpoints (optional)
        admin/
          admin_views.py  # SQLAdmin views (optional)
  alembic/                # Migrations

frontend/
  src/
    allModels.gen.ts                          # Auto-generated — do not edit
    pages/<module>/<module>Schema.gen.ts      # Auto-generated — do not edit
```

## CLI commands

```bash
veloiq add-module <name>                        # scaffold a new module
veloiq add-module <name> --with-custom-api --with-admin
veloiq generate       # regenerate api.py + TS schemas after model changes
veloiq db init        # set up Alembic once (if alembic.ini is missing)
veloiq db upgrade     # run Alembic migration after model changes
veloiq run            # start backend at http://localhost:{{backend_port}}
```

## Workflow for every new feature

1. `veloiq add-module <name>`
2. Edit `backend/app/modules/<name>/models.py` — add SQLModel fields
3. `veloiq generate` — regenerates `api.py` and frontend TypeScript schemas
4. `veloiq db upgrade` — creates the database table
5. Custom logic goes in `custom_api.py`, importing `router` from `api.py`

## Key conventions

- Base class for all models: `TimestampedModel` (from `veloiq_framework`)
- Table names: snake_case, matching module name; never use `safem_` prefix (reserved for auth)
- `eid` in API responses equals `id` — the frontend depends on this field
- Required env vars: `AUTH_SECRET`, `DATABASE_URL`; `VELOIQ_AUTH_DISABLED=1` skips auth in dev
- Copy `backend/.env.example` to `backend/.env` before first run

## Relationships

Use `jm_relationship` from `veloiq_framework`. Never use `relationship()` from SQLAlchemy or `Relationship` from SQLModel.

```python
from typing import TYPE_CHECKING, List, Optional
from sqlmodel import Field
from veloiq_framework import TimestampedModel, jm_relationship

if TYPE_CHECKING:
    from app.modules.other.models import OtherModel
    from app.modules.items.models import Item

class MyModel(TimestampedModel, table=True):
    __tablename__ = "my_model"

    other_id: Optional[int] = Field(default=None, foreign_key="other.id")
    other: Optional["OtherModel"] = jm_relationship(back_populates="my_models")

    items: List["Item"] = jm_relationship(back_populates="my_model")

    parent_id: Optional[int] = Field(default=None, foreign_key="my_model.id")
    children: List["MyModel"] = jm_relationship(
        back_populates="parent",
        sa_relationship_kwargs={"foreign_keys": "[MyModel.parent_id]"},
    )
    parent: Optional["MyModel"] = jm_relationship(
        back_populates="children",
        sa_relationship_kwargs={
            "foreign_keys": "[MyModel.parent_id]",
            "remote_side": "[MyModel.id]",
        },
    )
```

- FK column and relationship attribute are always separate fields
- Cross-module imports must be inside `if TYPE_CHECKING`
- Self-referential or multi-FK relationships need `sa_relationship_kwargs={"foreign_keys": [...]}` on both sides
