Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 174 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# AGENTS.md — Deliberate Lab (root)

Deliberate Lab is a platform for running online research experiments on
human + LLM group dynamics. This file orients AI coding assistants to the
monorepo structure, conventions, and workflows.

## Keeping AGENTS.md in sync

These `AGENTS.md` files document conventions that should be followed by
default. If a user request conflicts with the guidance here, **raise the
concern** — ask which takes precedence (their idea or the documented
convention) before proceeding. Whichever direction is chosen, update the
relevant `AGENTS.md` file(s) to match so documentation and code stay in
sync.

## Architecture

This is an npm workspaces monorepo with three packages:

| Workspace | Path | Purpose |
|-----------|------|---------|
| `@deliberation-lab/utils` | `utils/` | Shared TypeScript types, validation, and utilities |
| `functions` | `functions/` | Firebase Cloud Functions (HTTP endpoints + Firestore triggers) |
| `deliberate-lab` | `frontend/` | Lit Element + MobX single-page app (Webpack) |

Other top-level directories:

- `firestore/` — Firestore security rules, database rules, and indexes
- `docs/` — Jekyll documentation site (GitHub Pages)
- `scripts/` — pip-installable Python API client (`deliberate_lab`) with auto-generated Pydantic types + a Node.js doctor script (see `scripts/AGENTS.md`)
- `emulator_test_config/` — Static config for Firebase emulator imports

### Dependency graph

```
utils ──► frontend
└────► functions
·
·····► scripts/types.py (auto-generated via npm run update-schemas)
```

`utils` is a shared library consumed by both `frontend` and `functions`.
**Always build `utils` first** — the other two workspaces depend on it.

## Getting started

- **Node ≥22** is required (see `.nvmrc`)
- Install all dependencies from the repo root: `npm ci`
- Run everything locally: `./run_locally.sh`
- Diagnose setup problems: `npm run doctor`

`run_locally.sh` copies required config files (`.firebaserc`,
`firebase_config.ts`, `index.html`) if they are missing, builds `utils` and
`functions`, starts file watchers for both, launches the Firebase emulators
(with seed data from `emulator_test_config/`), and serves the frontend at
`http://localhost:4201`.

> [!IMPORTANT]
> Always run **npm** commands from the **repository root** using the
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In docs/developers/run-locally.md and run_locally.sh, we do cd into subdirectories for npm. Probably good to move those over - I wouldn't be surprised if a model followed those over an AGENTS.md file.
(Also, more of a personal opinion, but I feel like it's good to limit the degree of "IMPORTANT" flags in AGENTS.md, since the whole thing is always in the prompt whether or not the agent is doing anything relevant. This doesn't feel that important to me, and I'm not sure it'll need a separate callout at all once we get rid of the old pattern.)

> `--workspace` (or `-w`) flag. Do **not** `cd` into subdirectories for
> npm operations.
>
> ```sh
> npm run build -w utils
> npm test -w functions
> npm run start -w frontend
> ```
>
> This matches the convention used in `cloudbuild.yaml` and ensures
> consistent dependency resolution via npm workspaces.
>
> Python tooling (`uv`, `pyright`) in `scripts/` is the exception — those
> commands expect to run from the `scripts/` directory where
> `pyproject.toml` lives.

## Linting & formatting

- **Prettier** formats `.json`, `.ts`, `.html`, `.scss`, and `.css` files
- **ESLint** uses the **flat config** format (`eslint.config.mjs`); the
project does **not** have a `.eslintrc.*` file
- `@typescript-eslint/no-explicit-any` is set to `error` — do not use `any`
- **Husky** + **lint-staged** runs both Prettier and ESLint on pre-commit
for the same file set (`*.{json,ts,html,scss,css}`)
- Frontend files get browser globals; everything else gets Node globals

## CI

`cloudbuild.yaml` drives all builds. The `_DEPLOYMENT_TYPE` substitution
variable controls which steps run:

| Value | What it does |
|-------|-------------|
| `test` | Lint, format check, and unit tests for all workspaces (no deploy) |
| `functions` | Build + deploy Cloud Functions |
| `frontend` | Build + deploy frontend to App Engine |
| `rules` | Deploy Firestore security rules |
| `indexes` | Deploy Firestore indexes |
| `all` | All of the above |

GitHub Actions (`.github/workflows/ci.yaml`) also runs a **schema sync
check**: if types in `utils` change, `docs/assets/api/schemas.json` and
`scripts/deliberate_lab/types.py` must be regenerated or CI will fail.
Run `npm run update-schemas` from the repo root to fix this (requires
Python 3.12+ and `uv` — see `scripts/AGENTS.md` for setup).

## Testing

Each workspace has its own `npm test`:

```sh
npm test -w utils # Jest; unit tests colocated with source
npm test -w functions # Jest; requires Java 21 for Firebase emulator
npm test -w frontend # Jest
```

Functions tests run against the Firebase emulator using
`firebase-test.json`. The integration test in
`cohort_definitions.integration.test.ts` is large and slow.

## Firebase config

| File | Purpose |
|------|---------|
| `firebase.json` | Local dev emulator config |
| `firebase-test.json` | Emulator config for CI / test runs |
| `.firebaserc.example` | Template for project aliases (copy to `.firebaserc`) |
| `firestore/firestore.rules` | Firestore security rules |
| `firestore/database.rules.json` | Realtime Database rules |
| `firestore/storage.rules` | Cloud Storage rules |
| `firestore/indexes.json` | Firestore composite indexes |

## Import convention

Both `frontend` and `functions` import from `utils` using the npm workspace
package name:

```ts
import {StageKind, ExperimentConfig} from '@deliberation-lab/utils';
```

Do **not** use relative paths to reach into `utils/src/` — always import
from `@deliberation-lab/utils`.

## Stage system

Experiments are composed of ordered **stages** (chat, survey, chip
negotiation, ranking, etc.). The `StageKind` enum in
`utils/src/stages/stage.ts` lists all stage types.

Adding a new stage type touches **all three workspaces**:

1. **`utils/src/stages/`** — types, validation, manager, prompts
2. **`functions/src/stages/`** — backend endpoint + trigger logic
3. **`frontend/src/components/stages/`** — config, preview, and answer UI components

See each workspace's `AGENTS.md` for detailed guidance.

## Common pitfalls

1. **Forgetting to rebuild `utils`** — `frontend` and `functions` consume
the compiled output in `utils/dist/`. After changing `utils` source,
rebuild it (`npm run build -w utils`) before testing downstream
workspaces, or changes won't be picked up.
2. **Running `npm` from a subdirectory** — always run from the repo root
with `-w <workspace>` (see above).
3. **Editing `scripts/deliberate_lab/types.py` by hand** — this file is
auto-generated and will be overwritten by `npm run update-schemas`.
4. **Missing Java 21 for `functions` tests** — the Firebase emulator
requires Java 21. Unit-only tests can be run without it via
`npm run test:unit -w functions`.
5. **Forgetting to run `npm run update-schemas`** — if you change types
in `utils`, CI will fail the schema sync check until schemas are
regenerated.
136 changes: 136 additions & 0 deletions frontend/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# AGENTS.md — `frontend`

Lit Element + MobX single-page application built with Webpack.
See also `frontend/README.md` for additional context on routing,
Firebase setup, and experiment configuration.

> See also: [root AGENTS.md](../AGENTS.md) for monorepo-wide conventions.

## Build & test

From the **repository root**:

```sh
npm run start -w frontend # Build + serve (dev, localhost:4201)
npm run build -w frontend # Dev build only
npm run build:prod -w frontend # Production build
npm test -w frontend # Run Jest tests
```

For local dev, copy config files first (or use `run_locally.sh` which
handles this automatically):

```sh
cp frontend/firebase_config.example.ts frontend/firebase_config.ts
cp frontend/index.example.html frontend/index.html
```

## Component architecture

### `src/components/` — Feature components

Organized into 17 subdirectories by feature area:

| Directory | Purpose |
|-----------|---------|
| `stages/` | Stage config, preview, and answer components (largest subdirectory) |
| `experiment_builder/` | Experiment creation/editing UI |
| `experiment_dashboard/` | Experiment monitoring dashboard |
| `experimenter/` | Experimenter-facing views |
| `participant_view/` | Participant-facing experiment UI |
| `chat/` | Chat interface components |
| `header/` | App header (includes routing logic) |
| `sidenav/` | Side navigation (includes routing logic) |
| `login/` | Authentication UI |
| `settings/` | App settings |
| `gallery/` | Experiment gallery/templates |
| `progress/` | Progress indicators |
| `popup/` | Modal/popup components |
| `participant_profile/` | Participant profile display |
| `avatar_picker/` | Avatar selection UI |
| `admin/` | Admin panel |
| `shared/` | Shared component utilities |

### `src/pair-components/` — Reusable primitives

Standalone UI primitives: `button`, `icon`, `icon_button`, `textarea`,
`textarea_template`, `tooltip`, `menu`, `info_popup`. Also contains
`types.ts` (shared primitive types) and `utils.ts` (helper functions).
These are general-purpose and not tied to project state.

`shared.css` in this directory provides base styles used across primitives.

> [!NOTE]
> Primitives use a **mix** of `.css` and `.scss` for styles. `button`,
> `icon`, `icon_button`, `textarea`, `textarea_template`, and `tooltip`
> use plain `.css`; `menu` and `info_popup` use `.scss`. Follow the
> existing pattern when modifying a primitive.

## Service layer

MobX-based services in `src/services/`:

| Service | Role |
|---------|------|
| `service.ts` | Abstract `Service` base class (all services extend this) |
| `initialization.service.ts` | Orchestrates app startup (initializes analytics, Firebase, routing) |
| `firebase.service.ts` | Firebase connection (Firestore, Auth) |
| `auth.service.ts` | Authentication and login state |
| `experiment.manager.ts` | Experiment data management (largest service) |
| `experiment.editor.ts` | Experiment editing state |
| `experiment.service.ts` | Current experiment subscription |
| `participant.service.ts` | Participant state and stage progress |
| `participant.answer.ts` | Participant answer management |
| `cohort.service.ts` | Cohort management |
| `router.service.ts` | App routing and page definitions |
| `home.service.ts` | Home page experiment list |
| `settings.service.ts` | App settings |
| `admin.service.ts` | Admin operations |
| `analytics.service.ts` | Google Analytics |
| `presence.service.ts` | Participant online/offline presence |

Services are wired together via `src/service_provider.ts`, which
creates all service instances and injects dependencies. Components access
services through the service provider — see existing components for the
pattern.

## Stage components

Stage UI components live in `src/components/stages/`. Each stage type
typically has three components:

| Pattern | Purpose |
|---------|---------|
| `<stage_type>_config.ts` | Experimenter-facing configuration editor |
| `<stage_type>_preview.ts` | Participant-facing stage view |
| `<stage_type>_answer.ts` | Answer/response display component |

## Styling

- **Material 3 Design** via SASS variables in `src/sass/`:
- `_colors.scss` — color palettes and theme tokens
- `_common.scss` — shared mixins and layout utilities
- `_typescale.scss` — typography scale definitions
- `src/pair-components/shared.css` — base styles for primitives
- Use SASS variables and mixins — **do not hardcode** colors, spacing, or
font values

## Key files

| File | Role |
|------|------|
| `src/app.ts` | Root app component and page rendering |
| `src/index.ts` | App entry point |
| `src/service_provider.ts` | MobX service dependency injection |
| `src/shared/callables.ts` | Typed wrappers for **all** Cloud Function calls — every new callable endpoint in `functions` needs a corresponding wrapper here or it is unreachable from the UI |
| `src/shared/` | Shared config, constants, and utilities |

## Common pitfalls

1. **Hardcoding colors or spacing** — use SASS variables and mixins from
`src/sass/`. Do not hardcode hex colors, pixel sizes, or font values.
2. **Mixing CSS formats in pair-components** — check the existing style file
format (`.css` vs `.scss`) before editing a primitive's styles.
3. **Forgetting to register a new service** — new services must be
instantiated in `src/service_provider.ts` and extend the `Service` base
class.
Loading